5. Tests, container, and local integration
Status: Implemented process; individual service conformance varies. Local success is an integration gate, not production evidence.
Step 1 — minimum test suite
Add domain, application, handler, OpenAPI, repository, migration, event/outbox, consumer/idempotency, workload identity, subject asserter, OpenFGA model, organisation isolation, default deny, revocation, shutdown, race, and smoke tests. Add OPA/obligation tests only when those planned contracts are approved and implemented; until then test that unsupported context is denied.
Verification:
gofmt -w .
go build ./...
go vet ./...
go test ./...
go test -race ./...
govulncheck ./...
Expected: clean output and no skipped security suite without a recorded reason.
Step 2 — container image
Use a multi-stage build, pinned Go/base images, a minimal runtime, non-root user, read-only root filesystem, no compiler/shell where unnecessary, and one binary that supports serve/config-check/migrate-only.
FROM golang:1.25 AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /out/service ./cmd/server
FROM gcr.io/distroless/static-debian12:nonroot
COPY /out/service /service
USER nonroot:nonroot
ENTRYPOINT ["/service"]
Common mistake: floating image tags, secrets in build args/layers, root execution, writable root, or a migration image built from different code.
Verification: image scan/SBOM, run as non-root/read-only, liveness/readiness, SIGTERM drain. Expected: no critical known vulnerability exception without owner/date.
Step 3 — place the repository
Clone the service inside the CDPG orchestration workspace beside dx-common-go, because Compose build contexts and local module replacements expect that structure. Update the orchestrator’s repository clone inventory; do not commit the nested service repository into the orchestrator.
Step 4 — infrastructure and database
Declare only required PostgreSQL/PostGIS, Redis, RabbitMQ, Elasticsearch, S3-compatible, Keycloak, or OpenFGA dependencies. Add the service database creation step and a migration-only command. Use claude-docs/PORTS.md to allocate ports; never copy one from this tutorial.
Verification: start from empty volumes, create database, migrate, run readiness; restart each dependency. Expected: correct dependency failure behavior and recovery without manual hidden state.
Step 5 — Compose and gateway
Add the image/build, internal network, environment, secret references, health check, depends-on only for boot convenience, and no unnecessary published host port. Add the gateway route and workload identity provisioning. Keep enabled: false until the service and authorization projection are ready.
Step 6 — smoke workflow
Test through the gateway, not only the host-bound service:
- obtain a development user token without recording credentials in docs/logs;
- create or select a resource/grant through the owning Control Plane service;
- wait for relationship projection;
- call the allowed operation;
- call with a denied user and swapped organisation/resource;
- revoke and verify denial;
- inspect audit, traces, outbox, queue, projection, and service metrics;
- restart service/broker during work and confirm recovery/idempotency.
Expected: allowed path succeeds, every negative path fails at the intended enforcement point, and correlated signals explain why.
Local topology
Use the ports page as the live registry. Service-to-service traffic uses Compose DNS/container ports; host ports are developer conveniences, not internal discovery.
Checkpoint
Attach command output, contract reports, security matrix, smoke evidence, and unresolved gaps to the review. Next: GitOps and readiness.