Files
shater/scripts/run-panel-tests.sh
omarandClaude Opus 5 56a276bcc1 ci: make the tests a gate instead of a decoration
The fork had a full suite and no CI that ran it. Upstream's test workflows
trigger on stable/testing/unstable; this repo only has main. And Gitea does not
read .github/workflows at all once .gitea/workflows exists, so those files were
decoration here. 115 of the 116 test files under shater/** had never executed in
CI even once, which is how TestDNSFilterRemoteBlocklistHTTPClient stayed red
across two published releases without anyone noticing.

The gate is a job inside release.yml that build-apk needs, because a separate
workflow cannot block another one. It runs the suite under the shipped tag set,
on Linux — 6 of 7 test files in transport/wireguard and 12 in shater/generate
compile only there or only under those tags, and those are exactly the files
covering AmneziaWG.

Three guards stop it from passing by running nothing, which is the failure this
whole change is about. The tag set may only ADD test files, never remove one.
Every package go list says has tests must appear as "ok <pkg>" in the output, so
a suite that collapses to "no test files" fails instead of passing. And the
panel run counts its test files first, because node --test exits 0 with "pass 0"
when the glob matches nothing.

The publish step used to exit 0 having published nothing: its assertions all
live inside a loop over artifacts, so an empty directory ran the body zero times
and reported success. It now counts what it published and fails on zero.

Verified by extracting the shipped step text and running it against stubs: empty
artifacts gives exit 0 before and exit 10 after; the rolling-release readback
still fires its own exit 14.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 04:53:14 +03:00

89 lines
3.4 KiB
Bash
Raw Permalink 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.
#!/usr/bin/env bash
#
# run-panel-tests.sh — the admin-panel half of the release test gate.
#
# panel/package.json has declared a `test` script since the SPA was scaffolded
# and nothing had ever called it: not release.yml, not build-shaterd.sh (which
# only runs `npm ci` + `npm run build`). This script is what CI calls, and it
# does the one thing `npm test` alone cannot: prove that tests actually RAN.
#
# `node --test src/*.test.ts` with no matching file leaves the glob unexpanded;
# node then reports `pass 0` and exits 0 — a green CI step that ran nothing,
# which is the exact failure class this whole change is about. So: the test
# files are counted BEFORE the run (a rename to *.spec.ts is named as such
# rather than showing up as a mystery), and the pass count is asserted > 0 and
# the fail count 0 after it.
#
# KNOWN LIMIT: node --test counts a *.test.ts file that declares no cases at
# all as one passing "test" (the module loaded). So an emptied-out file still
# reads as pass 1 here. Deleting, renaming or breaking the file is caught;
# gutting its contents while keeping the name is not.
#
# NODE VERSION: >= 22.6. The tests are TypeScript executed directly by
# `node --test`; type stripping does not exist before then, so on node 20 the
# run dies with a syntax error. CI pins node 24 for this step (the SPA *build*
# still uses node 20 — that one goes through vite/tsc and does not care).
#
# Usage: scripts/run-panel-tests.sh
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO="$(cd "$SCRIPT_DIR/.." && pwd)"
cd "$REPO/panel"
node_major="$(node -p 'process.versions.node.split(".")[0]')"
node_minor="$(node -p 'process.versions.node.split(".")[1]')"
if [ "$node_major" -lt 22 ] || { [ "$node_major" -eq 22 ] && [ "$node_minor" -lt 6 ]; }; then
echo " ERROR: panel tests are TypeScript under \`node --test\` and need node >= 22.6" >&2
echo " (got $(node --version)). Type stripping does not exist before that." >&2
exit 1
fi
# The glob `npm test` itself uses. Counted here so "somebody renamed the tests"
# is reported as that, instead of as a suspiciously fast green step.
shopt -s nullglob
files=(src/*.test.ts)
shopt -u nullglob
if [ "${#files[@]}" -eq 0 ]; then
echo " ERROR: no panel/src/*.test.ts — panel/package.json's \`test\` script" >&2
echo " would match nothing and still exit 0. Fix the glob or the files." >&2
exit 1
fi
echo "== panel tests (node $(node --version)), ${#files[@]} file(s) =="
if [ ! -d node_modules ]; then
npm ci
fi
out=""
rc=0
set +e
out="$(npm test --silent 2>&1)"
rc=$?
set -e
sed 's/^/ /' <<<"$out"
if [ "$rc" -ne 0 ]; then
echo " FAILED: npm test exited $rc" >&2
exit 1
fi
# node --test's summary is `ℹ pass N` (spec reporter) or `# pass N` (tap).
passed="$(sed -n 's/.*[[:space:]]pass[[:space:]]\{1,\}\([0-9]\{1,\}\).*/\1/p' <<<"$out" | tail -1)"
failed="$(sed -n 's/.*[[:space:]]fail[[:space:]]\{1,\}\([0-9]\{1,\}\).*/\1/p' <<<"$out" | tail -1)"
if [ -z "$passed" ]; then
echo " FAILED: could not find a pass count in node --test output — the gate" >&2
echo " cannot tell a green run from an empty one." >&2
exit 1
fi
if [ "$passed" -lt 1 ]; then
echo " FAILED: 0 panel tests ran. \`npm test\` returned success having done nothing." >&2
exit 1
fi
if [ -n "$failed" ] && [ "$failed" -gt 0 ]; then
echo " FAILED: $failed panel test(s) failed." >&2
exit 1
fi
echo " OK: $passed panel test(s) passed."