Files
summercms/.planning/phases/02-api-parity-harness-bootstrap/02-05-SUMMARY.md
2026-09-17 14:15:37 +02:00

158 lines
8.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
phase: 02-api-parity-harness-bootstrap
plan: 05
subsystem: testing
tags: [parity, contract-tests, race, testcontainers, check-phase2, mariadb]
requires:
- phase: 02-api-parity-harness-bootstrap
provides: tide record/replay/proxy, capture rules, named vars, manifest --next-batch, summer parity:*
- 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: App-owned testcontainers Postgres lifecycle for go test ./parity
- phase: 02-api-parity-harness-bootstrap
provides: TestParityCorpus: 154 recorded/pending selectable subtests, zero Go passes
provides:
- Public-API contract tests for every named QA-02 parity class
- Proxy/CLI security regressions and app pending-vs-passing contracts
- Repeatable scripts/check-phase2.sh --fresh-php gate with disposable MariaDB
- Measured 02-VALIDATION.md evidence with nyquist_compliant true
affects: [03]
tech-stack:
added: []
patterns:
- Contract tests go through RecordFlow/ReplayFlow/manifest public APIs, not private helpers
- check-phase2.sh owns PHP_PARITY_TARGET=http://127.0.0.1:8423 and refuses caller overrides
- Fresh PHP gate uses unique fonoteka_parity_* MariaDB, artisan preflight tables=0, then winter:up plus oauth-client
key-files:
created:
- tide/diff_contract_test.go
- tide/flow_contract_test.go
- tide/manifest_contract_test.go
- tide/proxy_security_test.go
- cmd/summer/parity_contract_test.go
- ../fonoteka.go/parity/parity_contract_test.go
- scripts/check-phase2.sh
modified:
- .planning/phases/02-api-parity-harness-bootstrap/02-VALIDATION.md
key-decisions:
- "Fresh PHP self-replay uses disposable MariaDB fonoteka_parity_* plus process-local hex credentials, never the developer DB or caller-supplied PHP_PARITY_TARGET"
- "Client flows run on a second winter:up after dropping tables so they are not replayed after the mutating 154-route suite"
- "Capture-by-reference mismatch is a two-step {{share:item}} flow with a live token change on the second /show"
patterns-established:
- "Pattern: Phase 2 sign-off is one script — scripts/check-phase2.sh --fresh-php — and Phase 1 remains scripts/check-phase1.sh"
- "Pattern: MariaDB readiness is mariadb SELECT 1; passwords are token_hex so -p never sees a leading dash"
- "Pattern: pkce.vars merge from /tmp/summercms-parity/pkce.vars before mcp-oauth replay"
requirements-completed: [QA-01, QA-02, QA-03]
duration: 15min
completed: 2026-09-17
---
# Phase 2 Plan 5: Complete contract, security and integration tests Summary
**Public-API negative tests lock every named parity class, proxy/CLI/pending-state contracts fail closed, and `scripts/check-phase2.sh --fresh-php` proves 154/154 PHP self-replay plus testcontainers Postgres**
## Performance
- **Duration:** 15 min
- **Started:** 2026-09-17T12:00:43Z
- **Completed:** 2026-09-17T12:15:30Z
- **Tasks:** 3
- **Files modified:** 8 (7 created, 1 validation doc) plus this SUMMARY
## Accomplishments
- Table-driven `TestDiffContract` / `TestFlowContract` / `TestManifestContract` cover null vs `[]`, Carbon `+00:00` vs `Z`, null vs absent dates, tri-state bools, envelope/conditional keys, money string vs number, integer ids, exact slug, capture-by-reference, missing variables, and per-step normalize disable — each failure names a JSON path.
- `TestProxySecurity` and `TestParityCommandContract` pin loopback-only proxy, body caps, traversal, scrub of JWT/`inv_`/OAuth secrets, and nonzero CLI errors without printing secrets.
- App `TestParityContract` keeps 154 routes recorded/pending with zero Go passes, unknown seed hooks fail, and synthetic SQL still uses testcontainers Postgres.
- `scripts/check-phase2.sh --fresh-php` is the repeatable gate: root and app vet/test/race, `TestParitySynthetic` Postgres, corpus `--require-recorded --require-clients --check-secrets`, CLI smoke, disposable MariaDB + artisan bootstrap + 154-route and client-flow PHP self-replay.
## Task Commits
Each task was committed atomically:
1. **Task 1: Lock down every response parity class with negative tests** - `59b5276` (test, summercms.go)
2. **Task 2: Cover proxy, scrub, CLI and Go pending-state boundaries** - `5ed920b` (test, summercms.go) and `d93392d` (test, fonoteka.go)
3. **Task 3: Run and document the repeatable phase gate** - `a296e98` (feat), `066c3d3` (fix), `84a5f00` (docs VALIDATION)
**Plan metadata:** this SUMMARY commit
## Files Created/Modified
- `tide/diff_contract_test.go` — QA-02 parity-class mutations with path-level diffs
- `tide/flow_contract_test.go` — Record/Replay continuation, capture-by-reference, headers/binary
- `tide/manifest_contract_test.go` — coverage reports, duplicate/missing ids, 154-route snapshot
- `tide/proxy_security_test.go` — loopback bind/upstream, caps, scrub, traversal, atomic write
- `cmd/summer/parity_contract_test.go` — CLI discovery, error exit, unclassified JWT with `--vars`
- `../fonoteka.go/parity/parity_contract_test.go` — pending ≠ passing, Postgres, unknown hooks
- `scripts/check-phase2.sh` — root/app vet/test/race, corpus, CLI smoke, `--fresh-php` MariaDB/PHP
- `.planning/phases/02-api-parity-harness-bootstrap/02-VALIDATION.md` — measured evidence, `nyquist_compliant: true`
## Decisions Made
- Keep MariaDB for `--fresh-php` (plan), not the 02-03 SQLite file; uniqueness is `fonoteka_parity_<run-id>` on an ephemeral loopback port.
- Hex-only process credentials so `mysql`/`mariadb` `-p` cannot treat a leading `-` as a flag.
- Client OAuth replay still merges `/tmp/summercms-parity/pkce.vars`; `client_secret` is redacted from gate logs.
- Do not re-record PHP and do not implement Fonoteka Go API endpoints in this plan.
## Deviations from Plan
### Auto-fixed Issues
**1. [Rule 1 - Bug] MariaDB urlsafe passwords broke `-p` and readiness**
- **Found during:** Task 3 (`bash scripts/check-phase2.sh --fresh-php`)
- **Issue:** `token_urlsafe` passwords could start with `-`, so `mariadb -p$PASS` parsed them as flags. Container also needed a real SQL ping, not only `docker inspect`.
- **Fix:** Switch to `token_hex` credentials and wait on `mariadb ... -e 'SELECT 1'`.
- **Files modified:** `scripts/check-phase2.sh`
- **Verification:** Second `--fresh-php` run exited 0 in 116s with `phase2 check passed`
- **Committed in:** `066c3d3`
---
**Total deviations:** 1 auto-fixed (1 blocking MariaDB readiness)
**Impact on plan:** Required for a true empty-DB PHP self-replay. No PHP/Nuxt/MCP source changes. No Go API endpoints.
## Issues Encountered
- First `--fresh-php` attempt also found leftover `php artisan serve` on 8423 (pid 3289319); the process was killed so the gate could bind. Not a plan change.
- `TestFlowContract/unknown_capture_source` needed `OpenStore` so `CaptureStep` runs; a nil store skipped capture and falsely passed.
- Unclassified JWT CLI record succeeded until `--vars` was passed so `ScrubStep` ran. Both were fixed before the Task 1/2 commits.
- `PHP_PARITY_TARGET=http://example.com bash scripts/check-phase2.sh --fresh-php` exits 1 with `refuse: caller-supplied PHP_PARITY_TARGET is not permitted`.
## User Setup Required
None - no external service configuration required. Local Docker is required for `--fresh-php` and for `go test ./parity` without `-short`.
## Next Phase Readiness
Phase 2 complete. Ready for Phase 3 (first vertical slice: `GET /_fonoteka/api/v1/genres`) to swap `newTarget` for the real app handler, mark that route `ported`, and keep PHP fixtures as the acceptance test.
## Verification
- Task 1: `go test ./tide -run 'TestFlowContract|TestDiffContract|TestManifestContract' -count=1` pass
- Task 2: `go test ./tide ./cmd/summer -run 'TestProxySecurity|TestParityCommandContract'` pass; app `go test ./parity -run 'TestParitySynthetic|TestParityCorpus|TestParityContract' -count=1` **4.888s** (Postgres started)
- Task 3: `bash scripts/check-phase2.sh --fresh-php` **116s**, `phase2 check passed`; corpus `recorded 154/154`; PHP self-replay **passing 154 failing 0 unrecorded 0**; seed, nuxt-browse, mcp-tools, mcp-oauth matched
- Root and app `go vet ./...`, `go test ./...`, `go test -race ./...` green
- `scripts/check-phase1.sh` still present; `PHP_PARITY_TARGET` override refused
## Self-Check: PASSED
- [x] Key files exist on disk
- [x] `git log --grep=02-05` returns Task 1–3 commits in summercms.go; fonoteka.go has `d93392d`
- [x] Task acceptance criteria re-run and pass
- [x] Plan verification command `bash scripts/check-phase2.sh --fresh-php` passed
- [x] `02-VALIDATION.md` records measured evidence and `nyquist_compliant: true`
---
*Phase: 02-api-parity-harness-bootstrap*
*Completed: 2026-09-17*