Testing¶
No broker, no core, no Docker. The forty lines every app's test suite
used to carry live in opennvr_app_sdk.testing, versioned with the SDK
so the shapes stay right when the contracts move.
def build(**overrides):
"""The three lines that stand in for a whole deployment.
`app_config` is a plain namespace: give it every key your rule
reads, exactly as config.yml would."""
recorder = RecorderChannel()
settings = {"min_confidence": 0.5, **overrides}
return app.build(app_config(**settings), recorder.dispatcher()), recorder
def test_fires_on_a_person():
detector, recorder = build()
fired = feed(detector, inference_event(detection("person", confidence=0.9),
camera_id="cam-1"))
assert [a.title for a in fired] == ["Person on cam-1"]
# `feed` returns what fired; the recorder proves it was dispatched.
assert recorder.alerts == fired
The three tests worth writing¶
- It fires on the thing. One event, one alert, and the envelope carries the camera and correlation id a downstream consumer needs.
- It stays quiet on the near-miss. The unwatched label, the confidence just below the floor, the object just outside the zone. This is the test that catches a rule which alerts on everything.
- The state machine re-arms. For anything with
dwellorcooldown: it fires once, stops, and fires again after the object leaves and returns.
The builders¶
| Helper | Builds |
|---|---|
inference_event(*detections, camera_id=, completed_at=) |
an adapter InferenceCompletedEvent |
detection(label, confidence=, x=, y=, w=, h=, track_id=) |
one contract-shaped detection |
tier0_event(*tracks) / tier0_track(...) |
a Tier-0 event (pixel boxes, a frame size) |
domain_event(schema, payload, camera_id=) |
an EVENT_CONTRACTS.md envelope |
app_config(**keys) |
a config namespace — give it every key your rule reads |
RecorderChannel() |
an in-memory alert channel; .dispatcher(), .alerts, .titles |
feed(app, *events) |
drives events through the real decode path, returns what fired |
FakeCore(cameras=[...]) |
a tiny in-process platform for apps that use OpenNVR() |
Pass completed_at explicitly whenever a rule measures duration — that
is how you test a dwell timer in microseconds instead of thirty seconds.
Fixtures¶
# conftest.py
pytest_plugins = ["opennvr_app_sdk.testing.pytest_plugin"]
gives you recorder, app_config_factory and fake_core.
Before you ship¶
opennvr-app dev # watch the rule against a simulated camera
opennvr-app validate . # what a reviewer would check
opennvr-app spec # the OpenAPI document your app will serve
Full example:
16_testing.py.