docs(02-05): complete contract-security-integration-tests plan
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
---
|
||||
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*
|
||||
Reference in New Issue
Block a user