131 lines
11 KiB
Markdown
131 lines
11 KiB
Markdown
---
|
|
phase: 02-api-parity-harness-bootstrap
|
|
plan: 04
|
|
type: execute
|
|
wave: 4
|
|
depends_on: [02-03]
|
|
files_modified:
|
|
- ../fonoteka.go/go.mod
|
|
- ../fonoteka.go/go.sum
|
|
- ../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
|
|
- ../fonoteka.go/parity/README.md
|
|
autonomous: true
|
|
requirements: [QA-02, QA-03]
|
|
must_haves:
|
|
truths:
|
|
- "D-09 D-10: A recorded seed and a named temporary seed hook can establish matching state before a route fixture is replayed."
|
|
- "D-12: `go test ./parity` starts a testcontainers Postgres and serves a synthetic SQL-backed handler through httptest."
|
|
- "D-16: Each flow is a selectable subtest, reports path differences and route coverage, and never counts an unported PHP route as a Go pass."
|
|
- "The Phase 3 genres route has a clear handler and seed-hook seam without claiming a Phase 2 Go implementation of PHP endpoints."
|
|
artifacts:
|
|
- path: ../fonoteka.go/parity/parity_test.go
|
|
provides: App-owned corpus runner, pending/ported subtests and coverage
|
|
- path: ../fonoteka.go/parity/synthetic_test.go
|
|
provides: SQL-backed httptest handler and Postgres lifecycle
|
|
- path: ../fonoteka.go/parity/testdata/synthetic-seed.yaml
|
|
provides: Integration seed flow with one real HTTP write
|
|
- path: ../fonoteka.go/parity/testdata/synthetic-read.yaml
|
|
provides: Integration read flow and named seed hook
|
|
key_links:
|
|
- from: ../fonoteka.go/parity/parity_test.go
|
|
to: ../fonoteka.go/parity/manifest.yaml
|
|
via: Ordered flow subtests and pending/ported status
|
|
- from: ../fonoteka.go/parity/parity_test.go
|
|
to: ../fonoteka.go/parity/synthetic_test.go
|
|
via: Injected handler factory and seed-hook registry
|
|
- from: ../fonoteka.go/parity/synthetic_test.go
|
|
to: tide/replay.go
|
|
via: httptest target replaying SQL-backed fixtures
|
|
---
|
|
|
|
<objective>
|
|
**As a** port developer, **I want to** run the recorded corpus from the app test suite with real database state, **so that** later routes turn green one at a time without hiding unported coverage.
|
|
|
|
Purpose: Prove the in-process Go replay path before the first Fonoteka handler exists.
|
|
Output: App-owned testcontainers Postgres integration, synthetic write/read handler, seed-hook seam, selectable flow subtests and honest pending coverage.
|
|
</objective>
|
|
|
|
<execution_context>
|
|
@/home/jin/.codex/get-shit-done/workflows/execute-plan.md
|
|
@/home/jin/.codex/get-shit-done/templates/summary.md
|
|
</execution_context>
|
|
|
|
<context>
|
|
@.planning/phases/02-api-parity-harness-bootstrap/02-CONTEXT.md
|
|
@.planning/phases/02-api-parity-harness-bootstrap/02-RESEARCH.md
|
|
@.planning/phases/02-api-parity-harness-bootstrap/02-VALIDATION.md
|
|
@.planning/phases/02-api-parity-harness-bootstrap/02-03-SUMMARY.md
|
|
@../fonoteka.go/go.mod
|
|
@../fonoteka.go/parity/manifest.yaml
|
|
@../fonoteka.go/parity/fixtures/seed/bootstrap.yaml
|
|
<interfaces>
|
|
`tide` exposes `LoadFlow`, `ReplayFlow`, manifest loading and a `Result`/coverage report. The app owns its `parity/` directory and imports `git.golem15.com/golem15/summercms/tide`, using a local `replace` in app `go.mod` if the framework module is unpublished. Testcontainers Postgres is a test-only dependency in the app module. `parity_test.go` should centralize a `newTarget(t, db) http.Handler` function and `seedHooks` map that Phase 3 can replace or extend without changing the generic harness.
|
|
</interfaces>
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto" tdd="true">
|
|
<name>Task 1: Replay a synthetic write/read flow against Postgres</name>
|
|
<files>../fonoteka.go/go.mod, ../fonoteka.go/go.sum, ../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</files>
|
|
<read_first>../fonoteka.go/go.mod, ../fonoteka.go/parity/manifest.yaml, ../fonoteka.go/parity/fixtures/seed/bootstrap.yaml, tide/flow.go, tide/replay.go, tide/variables.go, .planning/phases/02-api-parity-harness-bootstrap/02-CONTEXT.md, .planning/phases/02-api-parity-harness-bootstrap/02-RESEARCH.md</read_first>
|
|
<behavior>A testcontainers Postgres starts; an httptest handler writes a row through POST /synthetic/items, a later GET reads it, and the replay compares fixture response bodies. A declared seed hook inserts a second row through SQL before its read fixture. A wrong row value produces a JSON-path diff. The test does not pass if Docker or Postgres is unavailable.</behavior>
|
|
<action>Review RESEARCH.md Package Legitimacy Audit, then add test-only `github.com/testcontainers/testcontainers-go/modules/postgres` and `github.com/jackc/pgx/v5/stdlib` for `database/sql`, both named in project research; scan `../fonoteka.go/go.mod` with `/home/jin/.local/bin/slopcheck scan` before dependency download and again after resolution, and inspect `git -C ../fonoteka.go diff -- go.mod go.sum` before commit. Keep versions from Go module resolution and no dependency in framework production code (QA-03). Start one Postgres container/database for the integration suite, clean it up with `t.Cleanup`, and create a small synthetic table. Implement a temporary `http.Handler` that persists POST then reads GET through that `*sql.DB`; serve it with `httptest.NewServer`. Add two app-owned synthetic fixtures separate from PHP corpus: seed write and read, with captured id variable and a named `synthetic-item` seed hook inserting through SQL (D-09, D-10, D-11, D-12). Register only declared hook names; unknown hooks fail. First write a failing integration test and observe RED without committing, then implement the synthetic handler and replay call; commit only when both modules pass full vet/test. Make a single `newTarget`/hook registry seam in `parity_test.go` that Phase 3 can replace with the real app handler and temporary genres hook. Do not add a fake PHP route response or mark a PHP route ported.</action>
|
|
<verify><automated>/home/jin/.local/bin/slopcheck scan ../fonoteka.go/go.mod && go vet ./... && go test ./... && (cd ../fonoteka.go && go vet ./... && go test ./parity -run TestParitySynthetic -count=1 && go test ./...)</automated></verify>
|
|
<acceptance_criteria>
|
|
- `go test ./parity -run TestParitySynthetic` starts real Postgres via testcontainers and passes HTTP write/read replay.
|
|
- The declared SQL hook is exercised and an unknown hook is rejected.
|
|
- Altered SQL-backed response fails at a named JSON path; unavailable Docker does not create a false green skip.
|
|
</acceptance_criteria>
|
|
<done>The app module has a real DB-backed in-process parity integration path.</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 2: Expose the full corpus as honest selectable subtests</name>
|
|
<files>../fonoteka.go/parity/parity_test.go, ../fonoteka.go/parity/README.md</files>
|
|
<read_first>../fonoteka.go/parity/parity_test.go, ../fonoteka.go/parity/manifest.yaml, ../fonoteka.go/parity/fixtures/seed/bootstrap.yaml, tide/manifest.go, tide/replay.go, tide/report.go, .planning/phases/02-api-parity-harness-bootstrap/02-CONTEXT.md</read_first>
|
|
<action>Load and validate all 154 manifest route ids and their fixtures in `TestParityCorpus`; create stable `t.Run` names from flow ids so `go test ./parity -run 'TestParityCorpus/...'` selects one (D-08, D-12, D-16). Before ported route fixtures, replay their seed flow against the Go handler; for writes not yet ported, invoke only a manifest-declared temporary named hook, with its replacing route id and removal condition documented (D-09, D-10). In Phase 2, the real PHP seed endpoints and all 154 PHP routes are unported, so classify them `pending`, show them as recorded/pending in the coverage report, and do not send them to the synthetic handler or count them as passing. The separate synthetic seed/read subtests prove replay semantics. As Phase 3 provides the real genres handler, `newTarget` switches to it and its manifest row becomes `ported`; temporary genres SQL hook gives it the row until POST genres is ported. A `ported` missing fixture, missing hook, response mismatch or capture failure must fail its subtest and preserve path diagnostics. Print recorded/passing/failing/unrecorded/pending totals without equating pending with pass. Keep PHP self-replay strict over all fixtures independent of Go status.</action>
|
|
<verify><automated>go vet ./... && go test ./... && (cd ../fonoteka.go && go vet ./... && go test ./parity -run 'TestParitySynthetic|TestParityCorpus' -count=1 && go test ./...)</automated></verify>
|
|
<acceptance_criteria>
|
|
- All 154 route ids appear as recorded and pending; zero PHP routes appear as Go passing in Phase 2.
|
|
- Synthetic write/read subtests pass, and `-run` can select one named flow.
|
|
- A deliberately `ported` route without matching Go behavior fails with the actual path diff.
|
|
- Both root and sibling Go modules pass vet/test after the task commit.
|
|
</acceptance_criteria>
|
|
<done>The corpus is wired into CI with accurate pending/ported semantics and a Phase 3 handler seam.</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<threat_model>
|
|
## Trust Boundaries
|
|
|
|
| Boundary | Description |
|
|
|---|---|
|
|
| Fixture/manifest → app test and SQL seed hooks | Data files select HTTP requests and named privileged DB setup functions. |
|
|
| Test suite → container runtime | Testcontainers creates and destroys a disposable database. |
|
|
|
|
## STRIDE Threat Register
|
|
|
|
| Threat ID | Category | Severity | Component | Disposition | Mitigation Plan |
|
|
|---|---|---|---|---|---|
|
|
| T-02-04 | Tampering | high | `parity_test.go` status gate | mitigate | Require exact manifest route set, distinguish pending from passing, fail ported mismatches and unknown hooks. |
|
|
| T-02-06 | Elevation of privilege | high | SQL seed-hook dispatch | mitigate | Allow only named functions registered in test code; never execute SQL text from YAML, and use parameterized inserts. |
|
|
| T-02-SC | Tampering | high | App Go module installs | mitigate | Apply RESEARCH.md Package Legitimacy Audit and slopcheck before testcontainers module addition; block ASSUMED/SUS packages for human verification. |
|
|
</threat_model>
|
|
|
|
<verification>
|
|
Run app `go vet ./...`, `go test ./...`, the selected synthetic subtest and full corpus subtests; run root vet/test too. Confirm the report says 154 recorded/pending and zero passing PHP routes.
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
An actual Postgres-backed synthetic flow passes through the same replay engine; the full PHP corpus is visible as selectable pending subtests without false passes.
|
|
</success_criteria>
|
|
|
|
<output>
|
|
Create `.planning/phases/02-api-parity-harness-bootstrap/02-04-SUMMARY.md` after completion.
|
|
</output>
|