Local test lab
Run pipelines against deterministic WireMock + PostgreSQL fixtures.
The cannectors repository ships a local test lab under
test-lab/. It spins up a Postgres + WireMock stack via Docker
Compose, loads fixtures, and gives you a deterministic sandbox to
run pipelines against — no calls to real external services.
Why it exists
--dry-run still hits the real input (its source API, your database).
That's great for a one-off smoke test, less great for:
- CI that needs to be deterministic
- Pipelines that aren't built yet (the source doesn't exist)
- Reproducing a bug with a specific record shape
The test lab solves all three by mocking the source and the destination behind URLs your pipelines can point at.
Layout
cannectors/test-lab/
├── docker-compose.yml postgres + wiremock
├── pipelines/ example pipelines pointing at the stack
├── scenarios/ named scenarios with expected outputs
├── wiremock/__files/ response bodies served by WireMock
├── wiremock/mappings/ WireMock request → response rules
├── postgres/ init SQL for the source/dest databases
└── run.py scenario runnerBringing the stack up
cd cannectors
make test-lab-up # docker compose up -d, waits for healthchecksAfter ~10 seconds, WireMock is at http://localhost:8080 and Postgres
is at postgres://lab:lab@localhost:5432/lab.
Running a scenario
make test-lab-run # runs every scenario under test-lab/scenarios/Each scenario:
- Resets WireMock mappings and Postgres tables to a known state.
- Runs a specific pipeline from
test-lab/pipelines/. - Checks the result against expected output (records in Postgres, requests in WireMock's journal).
The runner is test-lab/run.py — a Python script, ~500 lines.
Reading the scenarios is the fastest way to learn the patterns.
Tearing it down
make test-lab-down # docker compose down -vThe -v flag drops volumes too, so the next make test-lab-up
starts clean.
CI usage
In CI, run the lab as part of the integration step:
- run: make test-lab-up
- run: make test-lab-run
- run: make test-lab-down
if: always()The compose stack starts under 15s on a warm runner. Total scenario time is a few seconds.