Skip to content

ADR-0002 — the suite reaches no service, and the Rego bundle is the exception

Date: 2026-08-15 Status: accepted

Context

This service talks to PostgreSQL, Keycloak's JWKS, a web-push service and an SMTP relay. A suite that needed any of them would be a suite nobody runs before pushing, and the argument for testing this repository at all is that its failures are invisible — a suite that is skipped removes the only thing that would notice.

Two sibling repositories had already answered this under the same constraint: ../celine-grid (247 tests, ~11s) and ../celine-policies (480 tests, <7s). Inventing a third shape would have cost the reader who moves between them.

Decision

Fake every outbound boundary at the narrowest point that leaves our code running, and evaluate the real Rego bundle.

Real thing Replaced by Left real
PostgreSQL in-memory SQLite carrying Base.metadata every query, the schema, the constraints
Keycloak JWKS a token registry replacing JwtUser.from_token the middleware, the open-path list, the 401 branches
a web-push service a callable replacing pywebpush.webpush subscription selection, the payload, the delivery log
an SMTP relay an object replacing the smtplib module message construction, the TLS branch, the delivery log
policies/celine/nudging/authz.rego nothing the whole bundle

The bundle is real because celine.sdk.policies evaluates Rego in process through regorus — no server, no socket — so there is nothing to fake and no cost to not faking it. conftest.py calls the same init_policy_engine() the service calls at startup, and refuses to run the suite unless the celine.nudging.authz package is in the loaded bundle.

Environment is pinned in conftest.py before the first celine.nudging import, because settings.py builds its Settings() at import time and src/celine/nudging/db/session.py builds the engine from it. That includes DATABASE_URL — pointed at an unroutable DSN so a missed override fails rather than connects — and SMTP_HOST, emptied so that a test reaching the email publisher by accident fails locally instead of dialling a developer's relay.

Consequences

The suite runs anywhere, in seconds, with no docker compose. That is the property being bought, and it is the reason the tests will still be run in a year.

An allow from the policy means something here, unlike in ../celine-grid: this service fails closed (REQ-0003, REQ-0004). The loaded-bundle guard is kept anyway, because a bundle that failed to load would deny everything and every 403 in the suite would pass for the wrong reason.

Nothing tests the wiring to the real services. A JWKS misconfiguration, an unreachable push endpoint, a relay that rejects the sender address: none of them appear here. What is faked is what the fake asserts — pywebpush is called with the right arguments, not that a browser received anything.

A fake can drift from the library it stands for. FakeWebPush raises the real WebPushException for exactly this reason: the publisher catches it by name, and a look-alike would escape the handler and quietly change what is being tested.

Superseded only by a decision to run the real services, which would mean accepting that the suite is not run on every change.