docs(02-04): complete honest-go-replay-with-testcontainers plan
Tasks completed: 2/2 - Replay a synthetic write/read flow against Postgres - Expose the full corpus as honest selectable subtests SUMMARY: .planning/phases/02-api-parity-harness-bootstrap/02-04-SUMMARY.md
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
---
|
||||
phase: 02-api-parity-harness-bootstrap
|
||||
plan: 04
|
||||
subsystem: testing
|
||||
tags: [parity, testcontainers, postgres, httptest, seed-hook, coverage]
|
||||
|
||||
requires:
|
||||
- phase: 02-api-parity-harness-bootstrap
|
||||
provides: Isolated PHP on 127.0.0.1:8423 with a parity-named SQLite DB
|
||||
- phase: 02-api-parity-harness-bootstrap
|
||||
provides: Recorded API seed plus 154 live PHP route fixtures
|
||||
- phase: 02-api-parity-harness-bootstrap
|
||||
provides: tide record/replay/proxy, capture rules, named vars, manifest --next-batch, summer parity:*
|
||||
provides:
|
||||
- App-owned testcontainers Postgres lifecycle for go test ./parity
|
||||
- Synthetic SQL-backed httptest handler with POST/GET /synthetic/items
|
||||
- newTarget and seedHooks seam for Phase 3 genres without claiming PHP routes
|
||||
- TestParityCorpus: 154 recorded/pending selectable subtests, zero Go passes
|
||||
affects: [02-05, 03]
|
||||
|
||||
tech-stack:
|
||||
added:
|
||||
- github.com/testcontainers/testcontainers-go v0.44.0 (app tests only)
|
||||
- github.com/testcontainers/testcontainers-go/modules/postgres v0.44.0
|
||||
- github.com/jackc/pgx/v5 v5.10.0 (stdlib driver)
|
||||
patterns:
|
||||
- One Postgres container per go test ./parity suite; t.Cleanup/Terminate; -short skips, missing Docker fails
|
||||
- YAML names a seed hook; only registered Go functions run parameterized SQL
|
||||
- Pending PHP routes are recorded and selectable but never sent to the synthetic handler
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- ../fonoteka.go/parity/parity_test.go
|
||||
- ../fonoteka.go/parity/synthetic_test.go
|
||||
- ../fonoteka.go/parity/testdata/synthetic-seed.yaml
|
||||
- ../fonoteka.go/parity/testdata/synthetic-read.yaml
|
||||
modified:
|
||||
- ../fonoteka.go/go.mod
|
||||
- ../fonoteka.go/go.sum
|
||||
- ../fonoteka.go/parity/README.md
|
||||
|
||||
key-decisions:
|
||||
- "newTarget and seedHooks live in the app test package so Phase 3 can swap the synthetic handler for the real app and add a temporary genres SQL hook until POST genres is ported"
|
||||
- "Pending never equals passing: TestParityCorpus reports recorded 154/154 passing 0 pending 154 and does not replay PHP fixtures against the synthetic handler"
|
||||
- "Unavailable Docker fails TestMain; testing.Short skips the container so the fast loop stays fast"
|
||||
|
||||
patterns-established:
|
||||
- "Pattern: invokeSeedHook rejects unknown names; YAML never carries SQL text"
|
||||
- "Pattern: corpus subtest names sanitize route ids (slash/space to _) so -run selects one flow"
|
||||
- "Pattern: a deliberately ported PHP fixture against the synthetic handler must fail with a path diff (status 200 vs 404)"
|
||||
|
||||
requirements-completed: [QA-02, QA-03]
|
||||
|
||||
duration: 9min
|
||||
completed: 2026-09-17
|
||||
---
|
||||
|
||||
# Phase 2 Plan 4: Honest Go replay with testcontainers Postgres Summary
|
||||
|
||||
**App `go test ./parity` starts testcontainers Postgres, replays a SQL-backed synthetic write/read through tide, and lists all 154 PHP routes as recorded/pending with zero Go passes**
|
||||
|
||||
## Performance
|
||||
|
||||
- **Duration:** 9 min
|
||||
- **Started:** 2026-09-17T11:49:42Z
|
||||
- **Completed:** 2026-09-17T11:58:36Z
|
||||
- **Tasks:** 2
|
||||
- **Files modified:** 7 (app repo) plus this SUMMARY
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- Testcontainers Postgres (postgres:16-alpine) plus `pgx` `database/sql` backs a tiny `synthetic_items` table for the integration suite.
|
||||
- `TestParitySynthetic` replays `testdata/synthetic-seed.yaml` (POST) and `synthetic-read.yaml` (GET + `synthetic-item` SQL hook); unknown hooks fail; a mutated row fails at `$[0].name`.
|
||||
- `newTarget` / `seedHooks` is the Phase 3 seam: swap in the real app handler, mark GET genres `ported`, keep a temporary `genres` hook until POST genres lands.
|
||||
- `TestParityCorpus` validates all 154 manifest fixtures as selectable pending subtests and never counts them as a Go pass. A cloned ported locale fixture against the synthetic handler fails with `status` 200 vs 404.
|
||||
|
||||
## Task Commits
|
||||
|
||||
Each task was committed atomically (app repo `fonoteka.go`):
|
||||
|
||||
1. **Task 1: Replay a synthetic write/read flow against Postgres** - `e0ab93c` (feat)
|
||||
2. **Task 2: Expose the full corpus as honest selectable subtests** - `ecec761` (feat)
|
||||
|
||||
**Plan metadata:** this SUMMARY commit
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
- `../fonoteka.go/go.mod` / `go.sum` — test-only testcontainers, postgres module, pgx v5.10.0; local replace to summercms
|
||||
- `../fonoteka.go/parity/parity_test.go` — `newTarget`, `seedHooks`, `TestParityCorpus`
|
||||
- `../fonoteka.go/parity/synthetic_test.go` — Postgres lifecycle, synthetic handler, `TestParitySynthetic`
|
||||
- `../fonoteka.go/parity/testdata/synthetic-seed.yaml` — HTTP write fixture with captured id
|
||||
- `../fonoteka.go/parity/testdata/synthetic-read.yaml` — list fixture with `seed_hook: synthetic-item`
|
||||
- `../fonoteka.go/parity/README.md` — in-process `go test ./parity` commands and Phase 3 hook notes
|
||||
|
||||
## Decisions Made
|
||||
|
||||
- Keep testcontainers/pgx out of framework production code (QA-03).
|
||||
- Fail the package (not skip) when Docker cannot start Postgres; `-short` is the only green skip.
|
||||
- Coverage totals come from the manifest so `-run TestParityCorpus/<one-id>` does not require sibling subtests to have incremented counters.
|
||||
- Phase 2 does not register a `genres` hook yet — only documents the name and the POST route that will replace it.
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. [Rule 3 - Blocking] slopcheck SLOP/SUS on local replace and transitives**
|
||||
- **Found during:** Task 1 (dependency scan after `go get`)
|
||||
- **Issue:** slopcheck marked `git.golem15.com/golem15/summercms` as SLOP (unpublished replace) and several testcontainers transitives as SLOP/SUS. Direct requires are the RESEARCH.md VERIFIED set (testcontainers postgres module, pgx/stdlib).
|
||||
- **Fix:** Kept Go module resolution versions; did not add undeclared direct imports. Transitives stay indirect.
|
||||
- **Files modified:** `../fonoteka.go/go.mod`, `../fonoteka.go/go.sum`
|
||||
- **Verification:** `slopcheck scan` after tidy; `git diff -- go.mod go.sum` inspected before commit
|
||||
- **Committed in:** `e0ab93c`
|
||||
|
||||
**2. [Rule 1 - Bug] parent coverage asserts broke `-run` of one corpus flow**
|
||||
- **Found during:** Task 2 (`go test -run TestParityCorpus/GET__...`)
|
||||
- **Issue:** Parent `TestParityCorpus` asserted 154 pending after the loop, so selecting one subtest failed with `pending 0`.
|
||||
- **Fix:** Moved asserts into `t.Run("coverage")` and compute totals from the manifest, not from filtered subtest side effects. Sanitized names use three underscores for `/_fonoteka` paths.
|
||||
- **Files modified:** `../fonoteka.go/parity/parity_test.go`, `../fonoteka.go/parity/README.md`
|
||||
- **Verification:** `-run TestParityCorpus/GET___fonoteka_api_v1_me_locale_jwt_locale` passes; coverage prints `recorded 154/154 passing 0 failing 0 unrecorded 0 pending 154`
|
||||
- **Committed in:** `ecec761`
|
||||
|
||||
---
|
||||
|
||||
**Total deviations:** 2 auto-fixed (1 dependency-scan, 1 -run isolation)
|
||||
**Impact on plan:** Required for honest CI selection and a legitimate local module replace. No PHP/Nuxt/MCP source changes. No framework production deps.
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
- TDD RED was observed without a RED commit (`TDD_MODE=false`): stub `NotFoundHandler` failed capture at `$.id` (`create-item`), then the SQL-backed handler made the suite green in the Task 1 commit.
|
||||
- slopcheck age heuristics fire on well-known transitives (`Azure/go-ansiterm`, `magiconair/properties`, `power-devops/perfstat`); treat RESEARCH.md Package Legitimacy Audit as the gate for direct requires.
|
||||
|
||||
## User Setup Required
|
||||
|
||||
None - no external service configuration required. Local Docker is required for `go test ./parity` without `-short`.
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
Ready for `02-05` (contract, security and integration tests). Phase 3 can replace `newTarget` with the real app handler, mark GET genres `ported`, and register a temporary `genres` seed hook until POST genres is ported.
|
||||
|
||||
## Verification
|
||||
|
||||
- Task 1: `go test ./parity -run TestParitySynthetic` starts Postgres and passes write/read, unknown hook, `$[0].name` mismatch
|
||||
- Task 2: `recorded 154/154 passing 0 failing 0 unrecorded 0 pending 154`; `-run` selects one flow; ported-mismatch reports status 200 vs 404
|
||||
- Plan: `go vet ./...` and `go test ./...` exit 0 in both `summercms.go` and `fonoteka.go`
|
||||
- Docker unavailable: `TestMain` exits 1 (no skip)
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
- [x] Key files exist on disk
|
||||
- [x] `git -C ../fonoteka.go log --grep=02-04` returns Task 1 and Task 2 commits
|
||||
- [x] Task acceptance criteria re-run and pass
|
||||
- [x] Plan verification commands pass
|
||||
|
||||
---
|
||||
*Phase: 02-api-parity-harness-bootstrap*
|
||||
*Completed: 2026-09-17*
|
||||
Reference in New Issue
Block a user