Testing
The test suite is organized in tiers. Unit tests run everywhere with no services; the integration tiers need the Docker Compose stack.
flowchart TB
subgraph tiers[Test tiers]
direction TB
U["<b>Unit</b><br/>pytest -m 'not integration'<br/>mocked clients, fast"]
I["<b>Integration</b><br/>live ES / OpenSearch / MongoDB"]
F["<b>Fault injection</b><br/>real TCP faults via Toxiproxy"]
M["<b>Mutation</b><br/>mutmut, weekly + on demand"]
U --> I --> F
M -.->|hardens| U
end
Unit tests
make test # pytest -m 'not integration'
Covers signature generation, record/word encoding, driver logic with mocked
backend clients, and negative paths — backend connection/write/delete
failures, malformed responses, worker and cursor errors, invalid inputs, and
migration failure modes (tests/test_faults_unit.py).
Integration tests
make test-integration-local # starts services, runs tests, stops them
Runs against live Elasticsearch, OpenSearch 2.x/3.x and MongoDB. This
includes the fault-injection tier (tests/test_faults_integration.py),
which routes a driver through a Toxiproxy
proxy and injects real transport faults — proxy kill, latency, connection
reset — that the mock layer cannot reproduce:
flowchart LR
T["pytest"] -->|search / insert| D["image-match driver"]
D -->|"HTTP :22300"| P["Toxiproxy proxy"]
P --> OS["OpenSearch :9201"]
A["Toxiproxy API :8475"] -.->|"toxics:<br/>latency, reset_peer, down"| P
make db-up starts toxiproxy alongside the databases (API on :8475,
dynamic proxy listeners on :22300-22310).
Mutation testing
mutmut generates mutants of
src/image_match/ and tools/migrate_to_knn.py and runs the unit suite
against each. It is deliberately not part of PR validation — a full run takes
a while — so it runs weekly and on demand via the mutation workflow:
make mutation # mutmut run + results
mutmut run is incremental: re-running only re-tests mutants in functions
whose source changed. Lines that can never change behaviour (CLI help text,
print reporting, the vestigial handle_mpo parameter, typing.cast)
are excluded via do_not_mutate_patterns in pyproject.toml.