test(19.3-05): gate plugins, layers and docs stages

- plugins: vet and -short in FAQ, menu and Journal, purge helper and
  deprecated-key suites by name; their Docker admin purge suites in docker
- layers: cmp of the shared purge files, 11 sitePurge: names per layer
- docs: TestDocsTree, docs:build --check, no consuming application named in
  framework lines added since the phase base
This commit is contained in:
Jakub Zych
2026-10-15 19:56:21 +02:00
parent 78ba385793
commit f57fc12a78

View File

@@ -117,6 +117,51 @@ DOCKER_COOLER_TESTS=(
TestSiteCacheAfterCommitOnly
TestSiteCacheFailureNeverFails
)
DOCKER_FAQ_TESTS=(TestFaqAdminPurgesSiteCache)
DOCKER_MENU_TESTS=(TestMenuAdminPurgesSiteCache)
DOCKER_JOURNAL_TESTS=(
TestCategoriesAdminPurgesSiteCache
TestPostsAdminPurgesSiteCache
TestCategoriesAdminFrameworkKeysWin
)
# plugins stage: the purge helpers and the Journal's deprecated keys (-short).
FAQ_CONTROLLERS_TESTS=(
TestPurgeSiteCacheWithoutTx
TestPurgeSiteCacheWithoutPurger
)
MENU_CONTROLLERS_TESTS=("${FAQ_CONTROLLERS_TESTS[@]}")
JOURNAL_ROOT_TESTS=(
TestSiteCacheFrameworkKeysWin
TestSiteCacheAliasKeysDeprecated
TestSiteCacheNoKeys
)
JOURNAL_CLASSES_TESTS=(TestSiteCachePurgeConfig TestSiteCachePurgeRequest)
# layers stage: the shared purge spec, byte-identical in the three layers.
LAYER_REQUIRE=(
"sitePurge: the shared files match the pinned hashes"
"sitePurge: a missing or short token disables the route"
"sitePurge: a proxied request is refused before the token is checked"
"sitePurge: authorize ok with the token and unauthorized otherwise"
"sitePurge: purge removes only nitro:routes: keys"
"sitePurge: purge event kinds and slugs"
"sitePurge: ok in constant time over sha256 digests, whatever the bearer length"
"sitePurge: purge works with a synchronous storage and skips the bare base key"
"sitePurge: purge event edge cases"
"sitePurge: route answers 404 when disabled or proxied and 401 for a wrong bearer"
"sitePurge: route purges, answers the count and logs kind and slug only"
)
# Files every layer ships byte for byte.
LAYER_SHARED=(
server/lib/siteCachePurge.ts
server/api/__site__/cache/purge.post.ts
tests/sitePurge.test.ts
)
# docs stage: names of consuming applications that framework lines added by
# this phase must not carry (split so this file does not match itself).
APP_NAMES=("fono""teka" "p[lł]yta""rium" "grzy""by" "summercms""io" "wave""path" "stu""dia")
usage() {
cat >&2 <<'EOF'
@@ -368,11 +413,6 @@ require_dir() {
}
}
not_wired() {
echo "refuse: stage $1 is not wired yet" >&2
return 1
}
run_go() {
(cd "$ROOT" && go vet ./...)
echo "vet: $ROOT"
@@ -400,19 +440,115 @@ run_docker() {
go_named "$ROOT" ./modules/cabana "-timeout 20m" "${DOCKER_CABANA_TESTS[@]}"
go_named "$ROOT" ./modules/lagoon/attach "-timeout 20m" "${DOCKER_ATTACH_TESTS[@]}"
go_named "$ROOT" ./modules/cooler "-timeout 20m" "${DOCKER_COOLER_TESTS[@]}"
require_dir "$FAQ_PLUGIN" "FAQ plugin"
require_dir "$MENU_PLUGIN" "menu plugin"
require_dir "$JOURNAL_PLUGIN" "Journal plugin"
go_named "$FAQ_PLUGIN" . "-timeout 20m" "${DOCKER_FAQ_TESTS[@]}"
go_named "$MENU_PLUGIN" . "-timeout 20m" "${DOCKER_MENU_TESTS[@]}"
go_named "$JOURNAL_PLUGIN" . "-timeout 20m" "${DOCKER_JOURNAL_TESTS[@]}"
echo "phase19.3 docker passed"
}
run_plugins() {
not_wired plugins
require_dir "$FAQ_PLUGIN" "FAQ plugin"
require_dir "$MENU_PLUGIN" "menu plugin"
require_dir "$JOURNAL_PLUGIN" "Journal plugin"
local repo
for repo in "$FAQ_PLUGIN" "$MENU_PLUGIN" "$JOURNAL_PLUGIN"; do
go -C "$repo" vet ./...
echo "vet: $repo"
# The Docker suites skip here and run by name in the docker stage.
go_short "$repo"
done
go_named "$FAQ_PLUGIN" ./controllers -short "${FAQ_CONTROLLERS_TESTS[@]}"
go_named "$MENU_PLUGIN" ./controllers -short "${MENU_CONTROLLERS_TESTS[@]}"
go_named "$JOURNAL_PLUGIN" . -short "${JOURNAL_ROOT_TESTS[@]}"
go_named "$JOURNAL_PLUGIN" ./classes -short "${JOURNAL_CLASSES_TESTS[@]}"
echo "phase19.3 plugins passed"
}
# pnpm_tap runs `pnpm -C <dir> test` (node --test, TAP) and checks the output
# with detect_tap. It never installs anything: when pnpm refuses to run
# because node_modules was installed against another store, it prints the
# offline, frozen-lockfile recovery and fails.
pnpm_tap() {
local dir="$1" log rc=0 dc=0
log="$(mktemp)"
pnpm -C "$dir" test >"$log" 2>&1 || rc=$?
if grep -q 'ERR_PNPM_ABORTED_REMOVE_MODULES_DIR_NO_TTY' "$log"; then
rm -f "$log"
cat >&2 <<MSG
refuse: pnpm wants to reinstall node_modules in $dir (store-dir mismatch) and
aborted without a TTY. Restore the local install without changing the lockfile:
pnpm -C "$dir" install --frozen-lockfile --offline --store-dir <the store named in node_modules/.modules.yaml without /v11>
then run this stage again.
MSG
return 1
fi
detect_tap "$log" || dc=$?
if [[ "$rc" -ne 0 || "$dc" -ne 0 ]]; then
grep -E '^\s*(not ok|# )|Error' "$log" | tail -n 40 >&2 || true
rm -f "$log"
echo "refuse: pnpm test in $dir (test=$rc detect=$dc)" >&2
return 1
fi
echo "tap: $(grep -E '^# (tests|pass|fail) ' "$log" | tr '\n' ' ')in $dir"
rm -f "$log"
}
run_layers() {
not_wired layers
require_dir "$FAQ_LAYER" "FAQ layer"
require_dir "$MENU_LAYER" "menu layer"
require_dir "$JOURNAL_LAYER" "Journal layer"
local file layer
for file in "${LAYER_SHARED[@]}"; do
for layer in "$MENU_LAYER" "$JOURNAL_LAYER"; do
if ! cmp -s "$FAQ_LAYER/$file" "$layer/$file"; then
echo "refuse: $file differs between $FAQ_LAYER and $layer (the shared files ship byte for byte)" >&2
return 1
fi
done
done
echo "cmp: ${#LAYER_SHARED[@]} shared files identical in the three layers"
for layer in "$FAQ_LAYER" "$MENU_LAYER" "$JOURNAL_LAYER"; do
REQUIRE_NAMES="$(printf '%s\n' "${LAYER_REQUIRE[@]}")" pnpm_tap "$layer"
done
echo "phase19.3 layers passed"
}
# phase_base prints the parent of the phase's first commit; PHASE193_BASE
# overrides it.
phase_base() {
if [[ -n "${PHASE193_BASE:-}" ]]; then
echo "$PHASE193_BASE"
return
fi
local first
first="$(git -C "$ROOT" log --format=%H --reverse --grep='(19\.3' | head -n 1)"
if [[ -z "$first" ]]; then
echo "refuse: no commit of phase 19.3 found; set PHASE193_BASE" >&2
return 1
fi
echo "${first}^"
}
run_docs() {
not_wired docs
go_named "$ROOT" ./cmd/summer "" TestDocsTree
(cd "$ROOT" && go run ./cmd/summer docs:build --check)
local base pattern hits
base="$(phase_base)"
pattern="$(IFS='|'; echo "${APP_NAMES[*]}")"
# Lines this phase added to the framework (committed or not), planning
# docs excluded.
hits="$(git -C "$ROOT" diff "$base" -- . ':(exclude).planning' |
grep -E '^\+' | grep -vE '^\+\+\+ ' | grep -inE "$pattern" || true)"
if [[ -n "$hits" ]]; then
printf '%s\n' "$hits" | head -n 20 >&2
echo "refuse: framework lines added since $base name a consuming application" >&2
return 1
fi
echo "names: no consuming application named in framework lines since $base"
echo "phase19.3 docs passed"
}
run_all() {