--- phase: 15-journal-plugin plan: 04 subsystem: testing tags: [journal, phpunit, testcontainers, race, phase-gate, asvs, typesense] requires: - phase: 15-03 provides: "Anonymous GET /_journal/api/v1, Bearer writes, JOURNAL-005, media, RSS, Typesense gate, named wave-3 tests at plugin root" provides: - "TestJournalEndToEnd on real Postgres through party/surf/cabana" - "D-05 PHPUnit map: TestFillable, TestJournal005DraftShow, TestJournal006MediaUpload, TestFormatHTMLRejectsUnsafeHTML, rollback/remigrate" - "scripts/check-phase15.sh fail-closed gate ending with Phase 15 gate passed" - "15-SECURITY-REVIEW.md ASVS L1 with T-15-01..15 and T-15-SC closed" affects: [16] actuals: tokens: 28000 tasks: 3 commits: 6 plan_head_before: 05b77b781825b279ff82f387717b83ac9d334a1e plan_head_after: 7307b36baaad660c84b5b365f2dac17ca7313941 plugin_repo_head_after: d09edde080ee32bdac9caca9e4c92d8d12791020 host_repo_head_after: ff20e6cce25628341c4c1e6b5879f1f3979baa2d tech-stack: added: [] patterns: - "Wave-3 HTTP tests stay at plugin root sharing admin_harness TestMain; controllers/api tests cover clampPerPage and media path units" - "Phase gate uses go -C for sibling plugin and proof host; Docker/Postgres failure is a failure except explicit -short" - "Security review without a dedicated auditor still re-runs every cited TestName and records that fallback" key-files: created: - ../sm-journal-plugin/models/fillable_test.go - ../sm-journal-plugin/models/translatable_test.go - ../sm-journal-plugin/classes/format_html_test.go - ../sm-journal-plugin/updates/migrations_test.go - ../sm-journal-plugin/controllers/api/posts_test.go - ../sm-journal-plugin/controllers/api/media_test.go - ../sm-journal-plugin/search_test.go - ../sm-journal-plugin/journal_api_matrix_test.go - ../sm-journal-plugin/integration_test.go - ../sm-journal-plugin/admin_harness_test.go - scripts/check-phase15.sh - .planning/phases/15-journal-plugin/15-SECURITY-REVIEW.md modified: - ../sm-journal-plugin/journal_api_writes_test.go - ../sm-journal-plugin/journal_api_task3_test.go - ../sm-journal-plugin/plugin_test.go - ../sm-journal-plugin/updates/postgres_test.go - ../sm-grzybyfunkcjonalne-app/boot_test.go - ../sm-grzybyfunkcjonalne-app/plugins/golem15/journal - .planning/phases/15-journal-plugin/15-VALIDATION.md key-decisions: - "Reuse wave-3 named HTTP tests at plugin root rather than relocating them into controllers/api (dispatch-authorized); JOURNAL-005/401/media/RSS remain named" - "15-04 security review was executor-self-performed (no gsd-security-auditor in this Cursor session); every cited TestName was re-run" - "TestSearchGateOff must not skip; unpublished ShouldBeSearchable stays false even if the in-memory gate is flipped on" patterns-established: - "Fail-closed phase gate: named PASS lines, refuse skip/no-tests/race, sibling go -C, PHP SHA pin 02110eb1, forbidden-surface scan including D-16 tide" - "Host journal gitlink is a nested clone; bump via file:// fetch with protocol.file.allow=always" requirements-completed: [D-01, D-02, D-03, D-04, D-05, D-06, D-07, D-08, D-09, D-10, D-11, D-12, D-13, D-14, D-15, D-16, D-17, D-18, D-19, D-20, D-21, D-22, D-23] coverage: - id: D1 description: "Real-Postgres e2e migrates, admin-creates an mlmarkdown post, lists published rows, 401s anonymous writes, and 404s draft show" requirement: D-05 verification: - kind: e2e ref: "../sm-journal-plugin#TestJournalEndToEnd" status: pass - kind: integration ref: "../sm-grzybyfunkcjonalne-app#TestBootUserTranslateJournal" status: pass human_judgment: false - id: D2 description: "PHPUnit map, migrations rollback/remigrate, FormatHTML unsafe tags, search-gate zero HTTP, categories/tags/RSS/featured-image auth" requirement: D-05 verification: - kind: unit ref: "../sm-journal-plugin/models#TestFillable" status: pass - kind: integration ref: "../sm-journal-plugin/updates#TestJournalMigrationsRollbackAndRemigrate" status: pass - kind: unit ref: "../sm-journal-plugin/classes#TestFormatHTMLRejectsUnsafeHTML" status: pass - kind: integration ref: "../sm-journal-plugin#TestSearchGateOff" status: pass - kind: integration ref: "../sm-journal-plugin#TestJournalPublicCategories" status: pass - kind: integration ref: "../sm-journal-plugin#TestJournalPublicTags" status: pass - kind: integration ref: "../sm-journal-plugin#TestJournalRSS" status: pass - kind: integration ref: "../sm-journal-plugin#TestJournalFeaturedImageUnauthenticated" status: pass human_judgment: false - id: D3 description: "Fail-closed check-phase15.sh --all plus ASVS L1 review closing T-15-01..15 and T-15-SC" requirement: D-07 verification: - kind: other ref: "bash scripts/check-phase15.sh --all" status: pass - kind: other ref: ".planning/phases/15-journal-plugin/15-SECURITY-REVIEW.md" status: pass human_judgment: false - id: D4 description: "Proof-host Journal SPA check as an administrator holding golem15.journal.*" requirement: D-19 verification: [] human_judgment: true rationale: "Needs a running host, admin login, and visual confirmation of Journal nav plus mlmarkdown create; recorded as end-of-phase UAT" duration: 75min completed: 2026-10-06 status: complete --- # Phase 15 Plan 04: Journal unit/integration tests and phase gate Summary **Real-Postgres e2e, PHPUnit/security test matrices, fail-closed `check-phase15.sh`, and an ASVS L1 review that closes T-15-01..15 plus T-15-SC** ## Performance - **Duration:** 75 min - **Started:** 2026-10-06T17:07:37Z - **Completed:** 2026-10-06T17:22:19Z - **Tasks:** 3 - **Files modified:** 22 ## Accomplishments - Proven the user-visible Journal path on real Postgres (`TestJournalEndToEnd`): migrate, admin mlmarkdown en/pl, anonymous list hides drafts, Bearer create, JOURNAL-005 404 without `data`. - Completed the D-05 PHPUnit map with named tests (`TestFillable`, `TestJournal005DraftShow`, `TestJournal006MediaUpload`, `TestFormatHTMLRejectsUnsafeHTML`, `TestSearchGateOff`, categories/tags/RSS/featured-image 401) and migrate/rollback/remigrate on Postgres. - `scripts/check-phase15.sh --all` printed `Phase 15 gate passed`. `15-SECURITY-REVIEW.md` closes every T-15-* threat; VALIDATION is `nyquist_compliant: true`. ## Task Commits Each task was committed atomically: 1. **Task 1: Tracer e2e on Postgres** - plugin `c5b44b6a5d32030e3bac4de156e4df46c07ea51f` (test); host `e60f6c4e64d3c90d718e1acd6cbcc7fe4ad59ff3` (test CORS) 2. **Task 2: PHPUnit map, migrations, FormatHTML, search-gate** - plugin `d09edde080ee32bdac9caca9e4c92d8d12791020` (test); host gitlink `ff20e6cce25628341c4c1e6b5879f1f3979baa2d` 3. **Task 3: Phase gate, security review, validation** - `7307b36baaad660c84b5b365f2dac17ca7313941` (test) **Plan metadata:** this docs commit (docs: complete plan) ## Files Created/Modified - `../sm-journal-plugin/integration_test.go` - `TestJournalEndToEnd` production path - `../sm-journal-plugin/admin_harness_test.go` - fail-closed testcontainers harness - `../sm-journal-plugin/models/fillable_test.go` - JOURNAL-001/002 / redactor_id - `../sm-journal-plugin/models/translatable_test.go` - MorphName + Tag not translatable - `../sm-journal-plugin/classes/format_html_test.go` - JOURNAL-003/004 substitute - `../sm-journal-plugin/updates/migrations_test.go` - rollback and remigrate - `../sm-journal-plugin/search_test.go` - `TestSearchGateOff` zero HTTP - `../sm-journal-plugin/journal_api_matrix_test.go` - per_page, neighbors, mass-assign, admin 403 - `../sm-journal-plugin/controllers/api/posts_test.go` - `clampPerPage` plus EndToEnd stub - `../sm-journal-plugin/controllers/api/media_test.go` - `/journal/` path units - `scripts/check-phase15.sh` - fail-closed `--all` gate - `.planning/phases/15-journal-plugin/15-SECURITY-REVIEW.md` - ASVS L1 - `.planning/phases/15-journal-plugin/15-VALIDATION.md` - signed off ## Decisions Made - Reused wave-3 HTTP tests at plugin root instead of moving them into `controllers/api` so they keep sharing `TestMain`. Named JOURNAL-005/401/media/RSS tests still PASS. - Self-performed the security review (no `gsd-security-auditor` subagent), matching 14.2.1-04, and disclosed that in review frontmatter. ## Deviations from Plan ### Auto-fixed Issues **1. [Reuse - authorized] HTTP tests stayed at plugin root** - **Found during:** Task 2 - **Issue:** Plan artifacts named `controllers/api/posts_test.go` as the home of `TestJournal005DraftShow`. - **Fix:** Kept the named tests in `journal_api_writes_test.go` / `journal_api_task3_test.go`; added `posts_test.go` for `clampPerPage` and the EndToEnd stub required by `go test ./...`. - **Verification:** `go test ./... -race` PASS lines for every required TestName. - **Committed in:** `d09edde` (Task 2) **Total deviations:** 1 auto-fixed (authorized reuse) **Impact on plan:** No missing named tests; gate `--all` green. ## Issues Encountered - `go get` of `sm-user-plugin` from the plugin module hit private-module auth; tests blank-import it with a `go.mod` require and `go.work` replace only (no `go.mod` replace). - Nested host gitlink checkout is detached; bump with `protocol.file.allow=always` and `file://` fetch, then `git add` the gitlink. ## User Setup Required None - no external service configuration required. ## Next Phase Readiness Phase 15 plans are complete. Ready for `$gsd-verify-work 15`. Human UAT remains: proof-host SPA Journal nav and mlmarkdown create. Phase 16 can consume `sm-journal-plugin` at `d09edde`. --- *Phase: 15-journal-plugin* *Completed: 2026-10-06*