docs(11.2-03): complete the unit test coverage and phase gate plan

This commit is contained in:
Jakub Zych
2026-10-01 16:40:36 +02:00
parent a2f2d1e5c7
commit c07db9a321

View File

@@ -0,0 +1,305 @@
---
phase: 11.2-ready-to-share-summercms-io-website-and-newsletter-plugin
plan: 03
subsystem: testing
tags: [go-test, fstest, httptest, node-test, coverage, phase-gate, bash]
requires:
- phase: 11.2-01
provides: vue-summercmsio-app with terminal.json, copyPayload, activeSection and the node:test suites
- phase: 11.2-02
provides: sm-summercmsio-plugin static handler and routes, the app's build/smoke/check-deploy scripts and terminal check, docsite site_url/site_label
provides:
- table-driven tests of every static serving rule and route behaviour of the site plugin (98.2% statement coverage)
- every branch of checkSiteURL, siteLabel, the site_url/site_label parsing and the option-over-yaml precedence, the escaped header, the 404 and section pages, and the CLI flags in help
- ungated tests of the D-40 terminal-check helpers and the remaining scroll-spy edge case
- scripts/check-phase11.2.sh, a fail-closed gate across the four repositories (framework, plugin, app, site, built, smoke, deploy, terminal, full, all)
- a validated 11.2-VALIDATION.md
affects: [11.2 verify-work, cutover, 11.3]
actuals:
tokens: 16305
tasks: 3
commits: 4
commits_all_repos: 9
plan_head_before: 2229ca3c6028fea4dd75a645f82e5f49f07fd638
plan_head_after: a2f2d1e5c78c5aeb40363fac69ce65801d0c6031
tech-stack:
added: []
patterns:
- "Phase gate per repository stage: go test -json through a detector that refuses a fail, a skip, a missing test, zero tests and no tests to run"
- "Static-handler tests set r.URL.Path directly so the handler's own path cleaning is what is tested"
- "Mutation checks in a scratch copy (plugin) or a git archive export (framework), never in the working tree"
key-files:
created:
- scripts/check-phase11.2.sh
- ../sm-summercmsio-app/plugins/golem15/summercms/static_test.go
- ../sm-summercmsio-app/plugins/golem15/summercms/routes_test.go
modified:
- internal/docsite/load_test.go
- internal/docsite/theme_test.go
- cmd/summer/docs_test.go
- ../sm-summercmsio-app/plugins/golem15/summercms/links_test.go
- ../sm-summercmsio-app/terminal_check_test.go
- ../sm-summercmsio-app/vue-summercmsio-app/tests/scrollSpy.test.ts
- .planning/phases/11.2-ready-to-share-summercms-io-website-and-newsletter-plugin/11.2-VALIDATION.md
key-decisions:
- "The gate's --built stage builds in release mode only when the framework checkout build.sh uses (SUMMERCMS_FRAMEWORK, default summercms.go) has v0.1.0; with the tag deferred it runs a dev build and says so"
- "The coexistence test reads the plugin's real patterns from surf.BuildRouter(...).Routes() and registers them on a fresh ServeMux beside literal copies of cabana's admin patterns"
- "TestTerminalScript pins the shipped override form 'git clone <override> summercms' (11.2-02 decision), not the bare 'git clone <override>' the plan text described"
- "TestPageLinks pins the shipped pageLinks contract: duplicates kept in document order (callers dedupe), fragment links kept, og:image appended"
patterns-established:
- "check-phase11.2.sh named_tests DIR PKG NAME...: per-repository go -C runs with -json and a required-PASS list; environment changes run in a subshell"
requirements-completed: []
coverage:
- id: D1
description: "Every D-07/D-47 static serving rule of the site plugin is pinned: content types, immutable only for _nuxt (not _nuxt/builds) and _fonts, no-cache with a strong ETag elsewhere, 304, HEAD, Range, dot-segment and traversal 404s, docs 301s, redirect Locations inside /docs/ and never //, tree 404 pages, no X-Robots-Tag/CSP/X-Frame-Options"
verification:
- kind: unit
ref: "sm-summercmsio-plugin static_test.go#TestStaticSite,TestStaticDocs,TestStaticConditionalAndRange,TestStaticNoBlockingHeaders,TestStaticRedirectLocations,TestStaticMissing404Page,TestNewHandlersMissingIndex,TestContentType,TestSiteImmutable"
status: pass
- kind: other
ref: "scripts/check-phase11.2.sh --plugin (17 named tests, coverage 98.2% >= 90.0%)"
status: pass
human_judgment: false
- id: D2
description: "The plugin assembles alone through surf.Assemble (GET only, POST 405, /backend falls to the site 404), fails closed on an unbuilt tree, registers via init as golem15.summercms with no admin controllers, and its patterns coexist with cabana's on one ServeMux"
verification:
- kind: unit
ref: "sm-summercmsio-plugin routes_test.go#TestRoutesAssemble,TestRoutesFailClosed,TestRoutesCoexistWithAdminPatterns,TestPluginIdentity,TestPluginEmbeddedTree"
status: pass
human_judgment: false
- id: D3
description: "Every branch of checkSiteURL, siteLabel, site_url/site_label parsing and option precedence; header escaping, link on index, section and 404 pages, exact unset header bytes; --site-url/--site-label in help and the label-without-URL error; docsite coverage 94.2%"
verification:
- kind: unit
ref: "internal/docsite#TestCheckSiteURL,TestSiteLabel,TestSiteURLPrecedence,TestParseSite,TestSiteLink; cmd/summer#TestDocsBuildSiteFlags,TestDocsSiteFlagsInHelp"
status: pass
- kind: other
ref: "scripts/check-phase11.2.sh --framework (named tests, coverage >= 85.0%, check-phase11.1.sh --docs and --forbidden)"
status: pass
human_judgment: false
- id: D4
description: "D-40 terminal-check helpers loadTerminal, terminalScript and terminalEnv have ungated tests; the scroll-spy negative-tops edge case is covered"
verification:
- kind: unit
ref: "sm-summercmsio-app terminal_check_test.go#TestLoadTerminal,TestTerminalScript,TestTerminalEnv"
status: pass
- kind: unit
ref: "vue-summercmsio-app tests/scrollSpy.test.ts#sections scrolled past (negative tops) still count, so the last one stays active"
status: pass
human_judgment: false
- id: D5
description: "scripts/check-phase11.2.sh --all runs framework, plugin, app, site, built, smoke, deploy, terminal and full, and prints phase11.2 all passed"
verification:
- kind: e2e
ref: "scripts/check-phase11.2.sh --all -> nine 'phase11.2 <stage> passed' lines, 'phase11.2 all passed', exit 0"
status: pass
human_judgment: false
- id: D6
description: "11.2-VALIDATION.md is validated: per-task map filled and green, nyquist_compliant and wave_0_complete true, manual rows kept for visual UAT, rome bring-up, external links, the verbatim clone and the release build from the real tag"
verification:
- kind: other
ref: "grep 'status: validated' / 'nyquist_compliant: true' / TestExternalLinks match; grep -c '⬜ pending' = 0"
status: pass
human_judgment: false
- id: D7
description: "External link reachability and the verbatim terminal clone at cutover (D-38), and the release build from the real v0.1.0 tag (D-42)"
verification: []
human_judgment: true
rationale: "Needs the user to make golem15/summercms public and create the v0.1.0 tag; the gate has --terminal --verbatim and the release branch of --built ready for that run"
duration: 13min
completed: 2026-10-01
status: complete
---
# Phase 11.2 Plan 03: Unit test coverage and the phase gate Summary
**Table-driven fstest tests pin every static serving and route rule of the site plugin (98.2% coverage). Every site_url/site_label branch and the D-40 helpers are tested, and one fail-closed `scripts/check-phase11.2.sh --all` proves the phase across the four repositories.**
## Performance
- **Duration:** 13 min
- **Started:** 2026-10-01T14:25:51Z
- **Completed:** 2026-10-01T14:39:19Z
- **Tasks:** 3 (tracer + 2 auto)
- **Files modified:** 11 (4 in summercms.go plus VALIDATION.md, 3 in the plugin, 1 in the app, 1 in the site, plus 2 gitlinks)
## Accomplishments
- **Site plugin (98.2% statement coverage, from fixtures only).**
- `static_test.go` covers content types, cache rules, ETag/304, HEAD, Range, dot-segment and traversal 404s, docs 301s, redirect Locations, a tree with no 404 page and the load errors.
- `routes_test.go` covers the surf assembly, fail-closed boot, coexistence with the admin patterns, the plugin's identity and init registration, and the embedded tree.
- `TestPageLinks` and `TestResolve` cover the link helpers.
- **Framework (docsite coverage 94.2%).**
- `TestCheckSiteURL` has 21 cases (5 accepted, 16 rejected).
- `TestSiteLabel` and `TestSiteURLPrecedence` (12 cases) cover labels and option-over-yaml precedence. `TestParseSite` gains rows for blank and two-line labels.
- `TestSiteLink` now checks the escaped label, the section page, the 404 page and the exact unset header bytes.
- `TestDocsSiteFlagsInHelp` checks both commands' help, and `docs:build --site-label` without a URL now has an error test.
- **App and site.**
- `TestLoadTerminal`, `TestTerminalScript` and `TestTerminalEnv` run ungated.
- One scroll-spy case was added for sections scrolled past. The other listed TS edge cases were already covered by the 11.2-01 tests.
- **Gate.** `scripts/check-phase11.2.sh` has the stages `--framework`, `--plugin`, `--app`, `--site`, `--built`, `--smoke`, `--deploy`, `--terminal [--verbatim]`, `--full` and `--all`. Unknown modes exit 2. `--all` passed end to end.
- **VALIDATION.md** is validated, with one row per task across the three plans and the cutover checks kept as manual rows.
## Task Commits
| Task | Commit | Repo | Message |
|------|--------|------|---------|
| 1 (tracer) | `549adaf` | sm-summercmsio-plugin | test: cover every static serving rule and the plugin routes |
| 1 (tracer) | `e7d9a2e` | sm-summercmsio-app | test: bump the site plugin to its static and route tests (gitlink) |
| 1 (tracer) | `18fe100` | summercms.go | test(11.2): add the phase gate with the plugin stage |
| 2 | `c3ddd5c` | vue-summercmsio-app | test: cover terminal and scroll-spy edge cases |
| 2 | `8c718a8` | sm-summercmsio-app | test: cover the terminal check helpers |
| 2 | `f34c335` | sm-summercmsio-app | test: bump the site to its scroll-spy edge case (gitlink) |
| 2 | `f5f9387` | summercms.go | test(11.2): cover site_url and site_label and gate the framework, app and site stages |
| 3 | `29adc23` | summercms.go | test(11.2): complete the phase gate |
| 3 | `a2f2d1e` | summercms.go | docs(11.2): validate the phase verification map |
Nothing was pushed. No commit has a co-author trailer. No v0.1.0 tag was created.
## Verification Evidence
**Task 1 (tracer gate).** The run is interactive in `end-of-phase` mode and the tracer's verify is automated-only, so the verify was re-run and passed:
- `go vet` is clean.
- The plan's verify regex gives 16 top-level PASS lines.
- `TestStatic.*|TestRoutes.*|TestPlugin.*` gives 47 `--- PASS` lines, subtests included.
- `scripts/check-phase11.2.sh --plugin` passes: 17 named tests and 58 passing tests, subtests included.
- Coverage is 98.2%.
- `bash -n` is clean, and `--bogus` exits 2.
- A gate copy with a renamed test refuses with "named tests did not pass (missing, renamed or filtered out)".
**Task 1 mutation check (siteImmutable).**
- Setup: in a scratch copy of the plugin (`GOWORK=off`), `siteImmutable` was changed to `return true` for `_nuxt/`.
- Result: `TestStaticSite` failed on `/_nuxt/builds/latest.json` and `/_nuxt/builds/meta/x.json` with `Cache-Control = "public, max-age=31536000, immutable", want "no-cache"`.
**Task 2.**
- `go vet ./...` is clean.
- Eight top-level PASS lines from the plan's verify regex.
- `TestCheckSiteURL` prints 21 `--- PASS: TestCheckSiteURL/` lines.
- The app helpers give three PASS lines and no SKIP.
- `--framework`, `--app` and `--site` pass. The site stage had 30 tests: `# pass 30`, `# fail 0`, `# skipped 0`.
**Task 2 mutation check (checkSiteURL).**
- Setup: in a `git archive HEAD` export of summercms.go with the new `load_test.go`, the `//` rejection in `checkSiteURL` was removed.
- Result: `TestCheckSiteURL/protocol-relative` failed with `checkSiteURL("//acme.example") = <nil>, want errSiteURL`.
**Task 3.** `scripts/check-phase11.2.sh --all` exited 0. It printed all nine `phase11.2 <stage> passed` lines and `phase11.2 all passed`, with zero `refuse:`, `FAIL`, `--- FAIL` or `--- SKIP` lines. Stage by stage:
- **built:** a dev build, because summercms.go has no v0.1.0 tag. The three build-dependent tests passed.
- **smoke:** `smoke: ok` on postgres:15.
- **deploy:** `check-deploy: ok`.
- **terminal:** used the clone override (this checkout) and passed.
- **full:** `go vet ./...` and `go test ./... -count=1` ran with the Docker database suites.
**Release branch of `--built`.**
- Setup: `SUMMERCMS_FRAMEWORK=<scratchpad>/fw-release`, the 11.2-02 scratch clone that carries a local v0.1.0 tag.
- Result: `built: v0.1.0 found …, release build`, then `build: bin/summercms-io (release v0.1.0) ready` and `phase11.2 built passed`.
**Plan verification.**
- All four repositories have an empty `git status --porcelain`. summercms.go still has the user's untracked `SummerCMS landing page.zip`, which predates this plan.
- Plugin coverage is 98.2% (≥ 90.0%). Docsite coverage is 94.2% (≥ 85.0%).
## Files Created/Modified
**summercms.go**
- `scripts/check-phase11.2.sh`: the phase gate, with stages and a `go test -json` detector modelled on `check-phase11.1.sh`.
- `internal/docsite/load_test.go`: `TestCheckSiteURL`, `TestSiteLabel`, `TestSiteURLPrecedence` and new `TestParseSite` rows.
- `internal/docsite/theme_test.go`: `TestSiteLink` covers escaping, the section page, the 404 page and the exact unset bytes.
- `cmd/summer/docs_test.go`: `TestDocsSiteFlagsInHelp`, and the label-without-URL case in `TestDocsBuildSiteFlags`.
- `11.2-VALIDATION.md`: validated.
**sm-summercmsio-plugin**
- `static_test.go` and `routes_test.go` are new.
- `links_test.go` gains `TestPageLinks` and `TestResolve`. The build-gated tests are unchanged.
**sm-summercmsio-app**
- `terminal_check_test.go` gains the three ungated helper tests. The two gitlink bumps are in the commit table.
**vue-summercmsio-app**
- `tests/scrollSpy.test.ts` gains the negative-tops case.
## Decisions Made
See `key-decisions` in the frontmatter. In short:
- `--built` follows the same framework checkout as `build.sh`.
- The coexistence test takes the plugin's real patterns from surf's route table.
- The tests pin the shipped `terminalScript` override form (`git clone <override> summercms`) and the shipped `pageLinks` contract (duplicates kept).
## Deviations from Plan
### Interpretation notes (no production change)
**1. `terminalScript` override form**
- **Plan text:** "the first command becomes `git clone /tmp/fw`".
- **What was tested:** the shipped behaviour from the 11.2-02 Rule 1 fix, `git clone /tmp/fw summercms`. The plan says to test the shipped name and behaviour.
**2. `pageLinks` duplicates**
- **Plan text:** "including duplicates removed and fragment-only links kept". This is ambiguous.
- **What was tested:** the shipped function keeps duplicates in document order, and `TestLandingLinks` dedupes. The test pins that, along with kept fragment links, decoded entities and the appended og:image.
**3. Two plugin statements are not covered**
- `plugin.go:48` and `static.go:89` are the `fs.Sub` error returns.
- `fs.Sub` fails only for an invalid directory name, and the names here are the constants `"public"`, `"site"` and `"docs"`, so the branches cannot be reached.
- Coverage is 98.2% without them.
**4. Extra tests beyond the plan's list**
- `TestResolve` in `links_test.go` covers the redirect follower and the `SUMMERCMS_SITE_DIR` override.
- The gate requires it too.
**5. Extra commits in sm-summercmsio-app**
- The gitlink bumps (`e7d9a2e`, `f34c335`) are separate commits from the app's own test commit.
- The sequential executor instructions require committing the updated gitlink whenever a submodule changes.
---
**Total deviations:** none needed a production fix. The new tests exposed no defect, so no production code changed in any repository. There are 5 interpretation or scope notes.
**Impact on plan:** none. All acceptance criteria pass.
## Issues Encountered
- **Scratch-copy mutation setup.** The first run needed `GOFLAGS=-mod=mod GOPROXY=off`, because the scratch module's `go.sum` lacked entries that the app's `go.work.sum` normally provides. No network was used.
- **`env -u` and bash functions.** `env -u` cannot call a bash function, so the gate unsets variables in subshells. `named_tests` cleans up its own log.
## Known Stubs
None.
## Threat Flags
None. This plan adds tests and a local gate script only. The T-11.2-15 mitigation (the detector refuses a skip, a fail, a missing test or zero tests, and `--bogus` exits 2) and the T-11.2-16 mitigation (the gate sets `SUMMERCMS_REQUIRE_BUILD=1` and `SUMMERCMS_TERMINAL_CHECK=1` itself and requires PASS) are in place.
## User Setup Required
None for this plan. These cutover items are unchanged from 11.2-02:
1. Push the three site repositories.
2. Make `golem15/summercms` public (D-38).
3. Create and push v0.1.0 (D-42).
4. Run `TestExternalLinks`, `scripts/check-phase11.2.sh --terminal --verbatim`, and `--built`, which will report a release build.
5. Deploy on rome.
## Next Phase Readiness
- Phase 11.2 has all three plans complete, and the gate is green. It is ready for `/gsd-verify-work`, where the manual rows are visual UAT and the cutover checks.
- Phase 11.3 can reuse `TestRoutesCoexistWithAdminPatterns` when it activates the admin beside the site.
## Self-Check: PASSED
- Files exist: `scripts/check-phase11.2.sh`, `internal/docsite/load_test.go`, `internal/docsite/theme_test.go` and `cmd/summer/docs_test.go`; the plugin's `static_test.go`, `routes_test.go` and `links_test.go`; the app's `terminal_check_test.go`; the site's `tests/scrollSpy.test.ts`; and `11.2-VALIDATION.md`.
- Commits were found in each repository:
- summercms.go: `18fe100`, `f5f9387`, `29adc23`, `a2f2d1e`
- app: `e7d9a2e`, `8c718a8`, `f34c335`
- plugin: `549adaf`
- site: `c3ddd5c`
- The summercms.go commit count was measured: `git rev-list --count 2229ca3..HEAD` = 4.
- `git tag -l v0.1.0` in summercms.go prints nothing.
---
*Phase: 11.2-ready-to-share-summercms-io-website-and-newsletter-plugin*
*Completed: 2026-10-01*