Story ING-001: Dev environment + test infrastructure + empty migrations¶
Module: ingestion Slice: Tier 0 — ING-S0 (architecture §11) Side: [BACKEND] Version target: V0.0.0 Priority: 1 Depends on: TENANT-S0 (MinIO + Postgres + Flyway), AUDIT-S0 (TestContainers patterns) Can develop concurrently with: None (foundation slice) Merge order: Before ING-002 Estimated complexity: S PRD User Stories: N/A — Technical Story (enabler for ING-US-01..03) Wireframe: N/A — backend only
Objective¶
Stand up the local + CI infrastructure required for every subsequent ING story: MinIO papillon-ingestion-inbox bucket verified/provisioned, per-tenant prefix helper, TestContainers fixture, sample CSV/XLSX fixtures, and empty Flyway migration shells (V010, V011). No business logic. Without this slice, ING-002..006 cannot boot a test.
Backend Scope¶
Entities¶
None (this slice creates only empty migration files; business tables are defined in ING-004's filled-out V010/V011).
Migrations¶
Two empty migration files committed (bodies filled by ING-004):
backend/src/main/resources/db/migration/tenant/V010__ingestion_create_tables.sql
backend/src/main/resources/db/migration/tenant/V011__ingestion_customer_contract_columns.sql
Each file contains only a header comment: -- ING-001 scaffold — body added by ING-004.
Service Layer¶
None. This slice provides:
- IngestionTestContainersConfig (Spring @TestConfiguration) bootstrapping Postgres-platform, Postgres-tenant, and MinIO with papillon-ingestion-inbox pre-created.
- IngestionMinioPrefixHelper (test util) creating <tenantId>/{inbox,processed,rejected,duplicate}/ prefixes.
- docker-compose.yml MinIO init extension if TENANT S0 did not already create the bucket (verify first; do not duplicate).
API Endpoints¶
None.
Validation Rules¶
None.
Multi-Tenant Considerations¶
TestContainers fixture must seed at least two tenants in papillon_platform.tenant so subsequent integration tests can prove cross-tenant isolation (architecture §13.1). Same DDL serves both SHARED (v0.0.0) and BYO (v0.0.4) DB profiles — no profile-specific code here.
Audit & Logging¶
None emitted in this slice. AUDIT catalog extension is a prerequisite (Cross-module ask OQ-X10, owned by AUDIT) and must land before ING-002 starts dev, not here.
Frontend Scope¶
N/A — backend only.
Pages & Routes¶
None.
Components¶
None.
UI States (ALL REQUIRED)¶
| State | Behavior | French Copy |
|---|---|---|
| Loading | N/A — backend only | — |
| Empty | N/A — backend only | — |
| Error | N/A — backend only | — |
| Offline / low network | N/A — backend only | — |
| Success | N/A — backend only | — |
Responsive Behavior¶
N/A.
Interactions¶
N/A.
Acceptance Criteria¶
AC-001: Test stack boots
Given a clean checkout of the repo
When the developer runs `./gradlew :backend:test --tests "*IngestionTestContainersSmokeTest"`
Then an ephemeral Postgres-platform + Postgres-tenant + MinIO container start
And the `papillon-ingestion-inbox` bucket exists with at least two seeded tenant prefixes
And the smoke test passes in < 60 s on a developer laptop.
AC-002: Empty migrations apply cleanly
Given the test stack from AC-001
When Flyway runs on the tenant DB
Then migrations `V010__ingestion_create_tables.sql` and `V011__ingestion_customer_contract_columns.sql` are present, applied, and idempotent
And no `ingestion_*` table yet exists (bodies are empty).
AC-003: Fixture files parseable
Given the fixture files committed by this story
When a smoke test loads `fixtures/ingestion/clean-5-rows.csv` via Jackson CSV streaming
And loads `fixtures/ingestion/blocking-anomalies.xlsx` via Apache POI SAX (`XSSFReader`)
Then both fixtures parse without exception
And the CSV reports exactly 5 data rows + 1 header row
And the XLSX reports at least one row that will later trigger `TELEPHONE_INVALIDE`.
Compliance Rules¶
None applicable to this scaffolding slice. The production gate feature.artci.phone-auth.granted is wired in ING-005; this story only documents the env-var name in .env.example.
Standards & Conventions¶
docs/standards/multi-datasource-patterns.md— TestContainers conventions for platform + tenant DBs.docs/standards/database-guidelines.md§ migrations layout —db/migration/tenant/vsdb/migration/platform/.docs/standards/java-spring-guidelines.md§ test slices.docs/standards/git-workflow.md— branchfeat/ingestion-ING-001.
Testing Requirements¶
Unit Tests¶
- N/A (no production code in this slice beyond test utils).
Integration Tests¶
IngestionTestContainersSmokeTest— proves the three containers boot and the bucket + tenant prefixes exist.IngestionMigrationsSmokeTest— provesV010+V011apply with empty bodies.IngestionFixtureParseSmokeTest— proves CSV + XLSX fixture files are readable by the chosen parsers.
What QA Will Validate¶
./gradlew :backend:test --tests "*Ingestion*Smoke*"passes locally.- Running
docker compose up -d minio postgres-platform postgres-tenantthenmc ls local/papillon-ingestion-inbox/returns the bucket.
Out of Scope¶
- Any business logic (parsers, validators, scheduler, commit, audit) — those are ING-002..005.
- Filling the migration bodies — ING-004 owns the
ingestion_import+ingestion_import_rowDDL and thecustomer/contractALTER additions. - Multi-agency support (V0.0.2+).
- UI upload endpoint / atelier (V0.0.3+).
- Operator notification of rejection (V0.0.1 badge / V2 email-SMS).
- BYO-DB provisioning (V0.0.4).
Definition of Done¶
- [ ] All backend endpoints implemented and returning correct responses (N/A — no endpoints)
- [ ] All frontend pages render correctly at 360 / 768 / 1024 px (N/A — backend only)
- [ ] All 5 UI states implemented (N/A — backend only)
- [ ] French micro-copy matches design spec exactly (N/A — backend only)
- [ ] All acceptance criteria pass manually
- [ ] Unit + integration tests written and passing (three smoke tests above)
- [ ] Tenant isolation verified (test fixture seeds ≥2 tenants; SHARED profile validated)
- [ ] [CUSTOMER stories] payload + Slow-3G + resumability (N/A — backend only)
- [ ] Audit log entries present for every required event (N/A — no audit emissions here)
- [ ] No silent failures on network errors (TestContainers boot failures fail the build)