Appearance
Notification Recipient Fanout Atomicity Proof
Status: local, isolated notification-service owner-storage proof. This does not promote a Gateway route, run a configured delivery provider, or substitute for inbox browser parity.
Scope
The notification write path records one source event and fans it out to every recipient allowed by notification preferences. A partial recipient insert would otherwise make source-event replay look complete while omitting an inbox row or a durable delivery job.
The focused PostgreSQL integration test covers the weak-topic parent alert shape after its recipients have already been resolved by the owning profile or classroom boundary. It does not query another service database and does not implement recipient discovery or configured provider dispatch.
Reproducible Command
bash
NOTIFICATION_EVENT_FANOUT_POSTGRES_CONFIRM=disposable-postgres \
make test-notification-event-fanout-postgresThe Make target first runs a no-Docker self-test that checks evidence artifact generation. The confirmed command then runs a named postgres:16-alpine container on a random loopback port and removes it through an EXIT trap. NOTIFICATION_SERVICE_POSTGRES_TEST_DATABASE_URL is constructed only for the child test process; no Compose configuration or service DSN is read.
Assertions
TestPostgresNotificationEventFanoutTransaction applies notification migrations to a temporary schema and uses a database trigger to fail the second recipient insert. It proves all of the following:
- The injected second-recipient failure leaves zero
notification_events, zeronotifications, and zeronotification_delivery_jobsrows. - Retrying the identical source event after the trigger is removed creates one source event, two inbox rows, and two deterministic delivery jobs.
- A later replay returns the same source event and leaves the counts at one event, two inbox rows, and two jobs.
The runner preserves a time-stamped artifact directory with summary.json and go-test.log. The latest local artifact is output/qa/notification-event-fanout-postgres-20260715-034023/.
Boundaries
This proof is intentionally narrower than route and browser evidence:
- Recipient ids are already resolved; profile/classroom joins remain outside notification-service.
- SMTP, push, and SMS provider credentials are not exercised. The worker's injected-provider retry/DLQ behavior is covered by service unit tests only.
- Default Gateway routes remain legacy-proxied; non-default inbox and parent alert rehearsal remain documented in the route rehearsal pages.
- The disposable container and temporary schema are deleted after the run.