Story NOTIF-S0: Dev environment + test infrastructure for NOTIF (v0.0.0)¶
Module: notifications Slice: NOTIF-S0 (Tier 0, serial) Side: [BACKEND] Version target: V0.0.0 Sub-version: v0.0.0 (architecture-backed) Priority: 1 Depends on: TENANT S0 merged; AUDIT S0 merged Can develop concurrently with: None (Tier 0 is serial) Merge order: Before NOTIF-001a/b/c/d Estimated complexity: S PRD User Stories: N/A — Technical Story Wireframe: N/A — backend only
Objective¶
Establish the dev + test infrastructure required to develop any NOTIF v0.0.0 slice: WireMock service for SMS/WhatsApp provider stubs, Testcontainers wiring (reusing Postgres from TENANT S0), and a NotifFixtures helper that loads a CI tenant + a seeded country_profile.CI row + signed-link fixtures into the platform DB. This unblocks NOTIF-001a/b/c/d to develop in parallel.
Backend Scope¶
Entities¶
None new (this story creates no production tables — those land in NOTIF-001a).
Migrations¶
None.
Service Layer¶
NotifTestContainersConfig— Spring@TestConfiguration: declares the Postgres testcontainer (reused from TENANT S0), starts a per-suite WireMockWireMockExtensionfor SMS + WA stubs, injects a fixedClockbean.NotifFixtures— test-only helper bean. Methods:Tenant tenant(String countryCode)— inserts a tenant + acountry_profilerow for the country (seedsCIby default).DispatchRequest dispatchRequest(Tenant t)— builds a validPOST /dispatchpayload pointing at the test tenant.Jwt operatorJwt(Tenant t)— mints a Keycloak-shaped JWT withtenant_id+operatorrole for tests.
API Endpoints¶
None.
Validation Rules¶
None (no production code path introduced).
Multi-Tenant Considerations¶
NotifFixtures.tenant(...) MUST produce a fresh tenant_id per call; tests rely on this to assert tenant-isolation. The CI seed row in country_profile columns notif_provider_sms='letstalk_sms_ci', notif_provider_wa='meta_wa_cloud_ci', sms_sender_id_default='PAPILLON' (per architecture §9.1 step 4 — TENANT-owned migration; this story only references it).
Audit & Logging¶
None.
Frontend Scope¶
N/A — backend only.
Acceptance Criteria¶
AC-S0.1: WireMock services boot via docker-compose
Given the repository at this story's HEAD
When the developer runs `docker-compose up -d wiremock` from the project root
Then the WireMock container starts on host port 8089
And it serves the SMS stub library at /sms-letstalk/* (200 OK for POST /v2/messages with a JSON body)
And it serves the WA stub library at /wa-meta/* (200 OK for POST /v17.0/{phone_number_id}/messages)
AC-S0.2: Testcontainers + NotifFixtures sample test passes
Given a Spring Boot test class importing NotifTestContainersConfig
When it calls `notifFixtures.tenant("CI")` then inserts and queries a row in a NOTIF-owned table scoped by tenant_id
Then the test passes against the Postgres testcontainer
And Flyway migrations from db/migration/platform/ replay cleanly per suite
AC-S0.3: CI country_profile seed row is present in test DB
Given the testcontainer is freshly migrated
When the test queries `SELECT notif_provider_sms, notif_provider_wa, sms_sender_id_default FROM country_profile WHERE country_code='CI'`
Then it returns exactly one row with values ('letstalk_sms_ci', 'meta_wa_cloud_ci', 'PAPILLON')
Compliance Rules¶
None (no production data path).
Standards & Conventions¶
docs/standards/critical-rules.md(full)docs/standards/tech-stack.md— Postgres / Flyway / Spring versionsdocs/standards/java-spring-guidelines.md—@TestConfiguration, JdbcClient usagedocs/standards/multi-datasource-patterns.md—platformDataSourceis the target for all NOTIF tables (TV-05)docs/standards/git-workflow.md
Testing Requirements¶
Unit Tests¶
None — this story IS the test infrastructure.
Integration Tests¶
NotifSmokeTestContainersTest— boots the testcontainer, applies migrations, runs AC-S0.2 and AC-S0.3 assertions.- Verify WireMock stub library responds to
POST /sms-letstalk/v2/messageswith a 200 + JSON{"provider_message_id":"..."}.
What QA Will Validate¶
docker-compose up -d wiremocksucceeds on a clean checkout../gradlew :backend:testpasses the smoke test suite.- No flaky cross-test pollution: each test gets a fresh tenant + a fresh notification row.
Out of Scope¶
- Production dispatch code path — lands in NOTIF-001a/b/c/d.
- Redis (cache / rate-limit) — Redis is inherited from TENANT S0; NOTIF does not add Redis primitives in v0.0.0.
- Production WireMock — WireMock is dev/test only; production points at real provider URLs (configured via env vars per architecture §14.1).
- The
country_profilemigration itself — TENANT-owned (architecture §9.1 step 4). This story references the seeded row but does not own the schema change.
Definition of Done¶
- [ ]
docker-compose.ymlincludes thewiremockservice per architecture §14 - [ ]
backend/src/test/resources/wiremock/sms-letstalk/*.jsonandwiremock/wa-meta/*.jsonstub libraries present - [ ]
NotifTestContainersConfig.java+NotifFixtures.javapresent and used by at least one sample test - [ ] AC-S0.1, AC-S0.2, AC-S0.3 pass
- [ ] Tenant isolation pattern verified (every fixture row carries
tenant_id; both V1 DB profilesSHAREDandBYOboot — though NOTIF tables stay onplatformDataSourceper TV-05) - [ ] No silent failures: missing WireMock stub returns 404, test fails loudly