docker-compose.test.yml
Rama main · réplica de lectura del repositorio original.
# Its own project, deliberately. Without a name this file shared the project
# `alexclaw` with docker-compose.yml, so the two stacks shared one network, and
# the teardown that ends every test run — `docker compose ... down` — removed
# it. With production stopped that succeeded, and production's database then
# would not start: its container still pointed at a network that no longer
# existed. With production running it failed only because production was still
# attached, which is luck rather than protection.
#
# Named apart, the test stack gets alexclaw-test_default and nothing it tears
# down can reach production.
name: alexclaw-test
services:
db-test:
container_name: alexclaw-db-test
image: pgvector/pgvector:pg17
environment:
POSTGRES_USER: alexclaw
POSTGRES_PASSWORD: testpass
POSTGRES_DB: alex_claw_test
# The application role, created by the same script a fresh production
# volume runs — so every test run also proves a fresh install works. The
# suite connects as it, so a privilege the application lacks fails here
# rather than there.
DATABASE_APP_USERNAME: alexclaw_app
DATABASE_APP_PASSWORD: apptest
volumes:
- ./db-init/01-app-role.sh:/docker-entrypoint-initdb.d/01-app-role.sh:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U alexclaw -d alex_claw_test"]
interval: 3s
timeout: 2s
retries: 5
test-elixir:
container_name: alexclaw-test-elixir
build:
context: .
target: test
depends_on:
db-test:
condition: service_healthy
# A real OpenBao, initialised for this run (test/alex_claw/vault_test.exs).
openbao-test:
condition: service_started
openbao-test-init:
condition: service_completed_successfully
# The AppRole is bound to this address: one test-elixir container at a
# time. Credo, format and other side work run in `tools`.
networks:
default: {}
vault:
ipv4_address: 10.213.64.10
volumes:
- ./.openbao-test/bootstrap:/run/alexclaw/openbao:ro
environment:
MIX_ENV: test
DATABASE_USERNAME: alexclaw_app
DATABASE_PASSWORD: apptest
# The owner, for creating and migrating only — see the ecto.* aliases.
DATABASE_OWNER_USERNAME: alexclaw
DATABASE_OWNER_PASSWORD: testpass
DATABASE_HOSTNAME: db-test
DATABASE_NAME: alex_claw_test
SECRET_KEY_BASE: test_secret_key_base_that_is_at_least_64_bytes_long_for_phoenix_to_accept_it
# The test image, for Credo, mix format and other work beside a suite run:
# no database, no OpenBao, not on the vault network.
tools:
build:
context: .
target: test
environment:
MIX_ENV: test
# The same OpenBao as production, hardened the same way. Its data is a tmpfs:
# every run starts an empty server, initialised by openbao-test-init.
openbao-test:
image: openbao/openbao:2.6.3@sha256:a60afafda36337abe833c4a63894bf1095098f29abea4091e7e555a33dd52889
user: "100:1000"
read_only: true
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
command: ["server", "-config=/openbao/config/config.hcl"]
environment:
SKIP_CHOWN: "1"
BAO_API_ADDR: https://openbao-test:8200
BAO_CLUSTER_ADDR: https://openbao-test:8201
tmpfs:
- /openbao/file:uid=100,gid=1000,mode=0700
volumes:
- ./openbao/config.hcl:/openbao/config/config.hcl:ro
- ./.openbao-test/tls:/openbao/tls:ro
# A fixed test key, committed: it seals nothing but this run's data.
- ./test/fixtures/openbao/unseal:/openbao/unseal:ro
networks:
vault:
ipv4_address: 10.213.64.2
depends_on:
openbao-test-init:
condition: service_healthy
# openbao-init, unattended: certificates, initialisation, AppRole, bootstrap
# files — rewritten every run.
openbao-test-init:
build:
context: ./openbao
dockerfile: init.Dockerfile
read_only: true
tmpfs:
- /tmp
cap_drop:
- ALL
cap_add:
- CHOWN
- DAC_OVERRIDE
- FOWNER
security_opt:
- no-new-privileges:true
environment:
OPENBAO_ADDR: https://openbao-test:8200
OPENBAO_HOSTNAME: openbao-test
ALEXCLAW_ADDRESS: 10.213.64.10
BACKUP_ADDRESS: 10.213.64.11
OPENBAO_INIT_UNATTENDED: "1"
volumes:
- ./.openbao-test/tls:/openbao/tls
- ./.openbao-test/bootstrap:/bootstrap
- ./.openbao-test/backup:/backup-credentials
networks:
- vault
healthcheck:
test: ["CMD", "test", "-f", "/tmp/ready"]
interval: 1s
timeout: 2s
retries: 60
# Runs under the production sidecar's constraints (test/compose_hardening_test.exs),
# so the suite — real Chromium included — proves the browser works that way.
# The test dependencies are in the image's `test` stage, pinned.
test-python:
build:
context: ./web-automator
target: test
entrypoint: ["sh", "-c"]
command: ["cd /app && python -m pytest tests/ -v -p no:cacheprovider"]
working_dir: /app
user: "10001:10001"
read_only: true
tmpfs:
- /tmp:size=512m,mode=1777
cap_drop:
- ALL
security_opt:
- "no-new-privileges:true"
environment:
PYTHONDONTWRITEBYTECODE: "1"
volumes:
- ./web-automator/tests:/app/tests
# A subnet of its own: production's vault network is 10.213.63.0/24, and two
# networks with one subnet cannot exist on one host.
networks:
vault:
internal: true
ipam:
config:
- subnet: 10.213.64.0/24
# Automatic addresses (the init service) from the upper half, away
# from the pinned ones.
ip_range: 10.213.64.128/25