Files
edr-platform/docker-compose.it.yaml
2026-08-03 10:51:43 +00:00

166 lines
6.4 KiB
YAML

# EDR Freight — API integration stack (headless).
#
# Overlay on docker-compose.e2e.yaml. Same base services (postgres, minio,
# mocks, freight-api), except the payment microservice is REAL here instead of
# `payment-mock-e2e`, and only the bank gateways are stubbed:
#
# freight-api-it ──HTTP──> payment-api-it ──HTTP──> gateway-mock-it
# ^ │
# └────── RabbitMQ ───────┘ (outbox → payment.events → consumer)
#
# Its own compose project (`name:` below overrides the base) and its own host
# ports, so it can run side by side with the Cypress e2e stack.
#
# node integration/scripts/it.mjs up|test|down|logs
#
# Never start it with plain `docker compose -f docker-compose.it.yaml` — it is
# an OVERLAY and needs the base file first:
# docker compose -f docker-compose.e2e.yaml -f docker-compose.it.yaml ...
name: edr-freight-it
services:
# Outbox transport. The payment API publishes payment.succeeded/failed here
# and freight consumes it — the production path. Copied from the passenger
# harness (e2e/docker-compose.yml).
rabbitmq-it:
image: rabbitmq:3-management
environment:
RABBITMQ_DEFAULT_USER: edr
RABBITMQ_DEFAULT_PASS: edr_secret
RABBITMQ_DEFAULT_VHOST: payment
ports:
- "${IT_RABBIT_PORT:-5772}:5672"
- "${IT_RABBIT_UI_PORT:-15772}:15672"
healthcheck:
test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"]
interval: 5s
timeout: 5s
retries: 20
# Stand-in for every bank/wallet gateway the payment API talks to, plus a
# control plane the tests drive (force a provider to fail/hang, fire a
# correctly-signed webhook, read back what was called). See
# integration/gateway-mock/server.js.
gateway-mock-it:
image: node:20-alpine
volumes:
- ./integration/gateway-mock:/app:ro
working_dir: /app
environment:
PORT: "4600"
# Same secrets the payment API gets — so webhooks the mock signs pass the
# API's REAL signature verification instead of bypassing it.
CBE_SECRET_KEY: it-cbe-secret
CBE_MERCHANT_ID: it-cbe-merchant
PAYMENT_API_URL: http://payment-api-it:3003
command: ["node", "server.js"]
ports:
- "${IT_GATEWAY_PORT:-4600}:4600"
healthcheck:
test:
[
"CMD",
"node",
"-e",
"fetch('http://localhost:4600/__control/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))",
]
interval: 3s
timeout: 3s
retries: 10
payment-api-it:
build:
context: .
dockerfile: apps/edr-payment-api/Dockerfile
secrets:
- npmrc
depends_on:
postgres-freight-e2e:
condition: service_healthy
rabbitmq-it:
condition: service_healthy
gateway-mock-it:
condition: service_healthy
environment:
PORT: "3003"
NODE_ENV: test
# Payment tables live in their own schema of the same throwaway DB;
# main.ts ensurePaymentSchema() creates it, migrationsRun does the rest.
DB_HOST: postgres-freight-e2e
DB_PORT: "5432"
DB_USER: edr_e2e
DB_PASSWORD: edr_e2e
DB_NAME: edr_freight_e2e
DB_SCHEMA: edr_payment
# Same token freight already uses in the base stack.
SERVICE_AUTH_TOKEN: e2e-service-token
PUBLISHER_TRANSPORT: rabbitmq
PAYMENT_RABBITMQ_URL: amqp://edr:edr_secret@rabbitmq-it:5672/payment
# HTTP fallback targets (only used with PUBLISHER_TRANSPORT=http).
PAYMENT_NOTIFY_FREIGHT_URL: http://freight-api-e2e:3001/api/internal/payments/mark-paid
# Fast relay + sweep so retry/reconciliation are observable inside a test
# rather than a minute later.
OUTBOX_RELAY_INTERVAL_MS: "1000"
RECONCILE_STALE_AFTER_MS: "5000"
# Every gateway points at the one mock. Paths are per-provider prefixes.
CBE_BASE_URL: http://gateway-mock-it:4600/cbe-birr
CBE_MERCHANT_ID: it-cbe-merchant
CBE_SECRET_KEY: it-cbe-secret
CBE_NOTIFY_URL: http://payment-api-it:3003/webhooks/cbe-birr
CBE_RETURN_URL: http://localhost/return
TELEBIRR_BASE_URL: http://gateway-mock-it:4600/telebirr
TELEBIRR_WEB_BASE_URL: http://gateway-mock-it:4600/telebirr/web
TELEBIRR_FABRIC_APP_ID: it-fabric
TELEBIRR_APP_SECRET: it-secret
TELEBIRR_MERCHANT_APP_ID: it-merchant-app
TELEBIRR_MERCHANT_CODE: "999999"
TELEBIRR_NOTIFY_URL: http://payment-api-it:3003/webhooks/telebirr
# Telebirr PSS-signs every request object — a throwaway key generated per
# launch by it.mjs (nothing key-shaped lives in git).
TELEBIRR_PRIVATE_KEY: ${IT_TELEBIRR_PRIVATE_KEY}
EBIRR_BASE_URL: http://gateway-mock-it:4600/ebirr
DMONEY_BASE_URL: http://gateway-mock-it:4600/dmoney
CARD_BASE_URL: http://gateway-mock-it:4600/card
WAAFI_BASE_URL: http://gateway-mock-it:4600/waafi
CAC_BASE_URL: http://gateway-mock-it:4600/cac
CAC_USERNAME: it-cac
CAC_PASSWORD: it-cac
CAC_APP_KEY: it-cac-key
CAC_API_KEY: it-cac-api
CAC_COMPANY_SERVICES_ID: "1"
# Inbound CBE Unified Bill — we are the biller; bill-query hops back into
# the freight API, so this direction runs real code on both sides.
CBE_BILL_ENABLED: "true"
CBE_BILL_CLIENT_ID: it-cbe-bill
CBE_BILL_CLIENT_SECRET: it-cbe-bill-secret
CBE_BILL_JWT_SECRET: it-cbe-bill-jwt
FREIGHT_API_BASE_URL: http://freight-api-e2e:3001/api
ports:
- "${IT_PAYMENT_PORT:-3113}:3003"
healthcheck:
test:
[
"CMD",
"node",
"-e",
"fetch('http://localhost:3003/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))",
]
interval: 5s
timeout: 5s
retries: 12
start_period: 40s
# Base-stack service, re-pointed at the real payment API.
freight-api-e2e:
depends_on:
payment-api-it:
condition: service_healthy
environment:
PAYMENT_API_URL: http://payment-api-it:3003
# Freight's payment module skips RabbitMQModule entirely when this is
# unset (payment.module.ts) — without it, outbox events never arrive.
PAYMENT_RABBITMQ_URL: amqp://edr:edr_secret@rabbitmq-it:5672/payment
# RABBITMQ_ENABLED stays "false" (base stack) — it only gates the SMS/email
# clients, which must remain off. The payment consumer is wired by
# PAYMENT_RABBITMQ_URL alone.