test: cover every verify-build failure mode from make check (closes #227)
All checks were successful
check / check (push) Successful in 28s

script/verify-build is the build-integrity guard and nothing in make check
tested it. Three separate reviews of it each found a fresh vacuous pass --
the grep exit-2 conflation, the discarded find status, the line-delimited
walk -- and each was caught by someone building a tree by hand.

script/test-verify-build makes that battery a target. It builds a fixture
tree under mktemp -d holding script/verify-build as a symlink to the real
script: verify-build takes its ROOT from dirname "$0"/.., so it runs against
the fixture's dist/ and never touches the repo's build output, and the
symlink rather than a copy is what makes a break in the real script fail
here. Its own mktemp is pointed inside the work dir, so a run leaves nothing
behind. Each of the 18 cases asserts the exit status AND a substring of the
message, because a guard that fails for the wrong reason is itself a defect.

The obstacle was that the CI image declares no USER, so CI runs as root, and
root is not subject to file permissions: a chmod 000 case passes vacuously
there while passing honestly on a developer machine. The harness therefore
proves its runner before counting the three permission-dependent cases -- a
mode-644 file and script/verify-build itself must be readable through it, a
mode-000 file must not be -- and drops to uid 65534 via setpriv or runuser
when running as root, both present in the pinned base image. If no candidate
passes the probes the cases are skipped, named in a banner, and the final
line says SKIPPED AND NOT PROVEN rather than passed. A green run cannot mean
"the permission cases did not run" without saying so.
This commit is contained in:
2026-08-12 09:03:21 +00:00
parent afe6ddaea0
commit 07780c3d8c
4 changed files with 462 additions and 3 deletions

View File

@@ -88,13 +88,21 @@ provide:
- `script/lint` — run the linter
- `script/fmt` — format all files (writes)
- `script/fmt-check` — check formatting (read-only)
- `script/check` — run test, lint, and fmt-check
- `script/check` — run test, test-verify-build, lint, and fmt-check
- `script/verify-build` — assert the compiled `DEBUG` state of the bundles in
`dist/`: every bundle containing `src/shared/constants.js` must have `DEBUG`
off, or on when `AUTISTMASK_DEBUG=1`. Run automatically at the end of
`make build` and `make build-debug`; fails loudly rather than passing if it
cannot determine a bundle's state. Not part of `make check`, which does not
depend on build artifacts existing.
- `script/test-verify-build` — exercise every failure mode of
`script/verify-build` against a fixture tree in a temp dir, asserting the exit
status and the message of each. Part of `make check`; it reads no build
artifacts and writes nothing under `dist/`. The cases that depend on file
permissions cannot mean anything for a process that is not subject to them, so
the harness proves its runner against a mode-000 file before counting them,
dropping to an unprivileged user when run as root; if it cannot, it skips
those cases and says so in a banner rather than passing them.
- `script/docker` — build the Docker image tagged via `script/projectname`
- `script/cibuild` — CI entrypoint: plain `docker build .`
- `script/precommit` — run by the git pre-commit hook; runs `script/check`