make test is effectively a no-op: Go tests are compile stubs, frontend has no test framework
#21
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
REPO_POLICIES.md: "All repos with software must have tests that run via the platform-standard test framework... There is no excuse formake testto be a no-op." Both halves of this repo fail that. Verified onmainatfbfe1df.Frontend: no test framework at all
script/testin its entirety:The script's own comment admits it: "This repo has no unit tests; the production build serves as the test (fails on broken code)." A production bundle asserts nothing about behaviour — it proves the code parses, nothing more.
package.jsondevDependencies contain no test runner: novitest,jest,mocha, or@testing-library/*.package.jsonscriptshas onlydev,build,preview— notestscript.CODE_STYLEGUIDE_JS.mdrequires thatyarn run testandnpm run testboth work; today both fail.*.test.js/*.spec.js/__tests__files exist.Backend: both test files are compile-check stubs
backend/internal/handlers/handlers_test.goandbackend/internal/reportbuf/reportbuf_test.goare 13 lines each and identical in shape:They assert nothing. Coverage by package:
internal/reportbufis 199 lines of buffered zstd compression and flushing — the most logic-dense and most failure-prone package in the repo — and has zero behavioural tests.make testalso lacks the mandated flags and rerun patternbackend/Makefile:test: timeout 30 go test ./...No
-race. No-cover.No conditional-verbose-rerun.
REPO_POLICIES.mdrequires:Grepping the repo for
Rerunningreturns zero hits — the pattern is absent from both the Go and the frontend side.Definition of done
internal/reportbufhas real behavioural tests: append then read back round-trips correctly; zstd output actually decompresses to the input; flush/rotation behaves at the buffer boundary; concurrentAppendis safe under-race; error paths (unwritable target) are handled without panicking.internal/handlershas real tests usinghttptest:HandleReportreturns 200 on a valid body; returns 400 on malformed JSON; rejects a body overmaxReportBodyBytes;HandleHealthCheckreturns the documented JSON shape withContent-Type: application/json.internal/confighas a test proving env-var override and default precedence.TestImportstubs are deleted or replaced — a compile check that duplicates whatgo buildalready proves is not a test.vitestis the default choice — it is the standard runner for a Vite project, shares the Vite config, and needs no separate build pipeline. Do not pick a niche alternative.package.jsongains atestscript soyarn run testworks.humanDurationformatting, the latency-to-colour threshold functions,HostStatemin/max/average/median over a history buffer including the empty and all-unreachable cases, and the health-state classifier's four states.src/main.jscallsinit()at import time, which makes the module hard to import under test. Extracting the pure helpers into importable modules is in scope insofar as it is needed to make them testable; a full modularization of the 1262-line file is not in scope here and is tracked separately.make testimplementations use the conditional-verbose-rerun pattern fromREPO_POLICIES.md, and the Go one adds-raceand-cover.make testcompletes in under 20 seconds with a 30-second timeout enforced, on both sides.make checkandcd backend && make checkboth pass.README.md's TODO no longer lists "Add unit tests"; the Entrypoints description ofscript/testis corrected — it currently describes running the build as the test.TODO.mdupdated in the same commit.(closes #N).Implementation requirements
vitestfor JS; on the Go side prefer stdlibtestingplusnet/http/httptestand do not add an assertion library unless there is a clear reason.make checkmust not modify tracked files.maketargets andscript/entrypoints only.Additional scope: the 30s test timeout is a real Docker-gate flake, not just a policy gap
Surfaced during the review of PR #31 and worth folding into this issue, because it turns an abstract compliance item into a concrete "main always green" problem.
A reviewer's first cold
docker build -f Dockerfile.backend .at head4d70317died here:That is
backend/Makefile'stimeout 30 go test ./.... The cause is an empty Go build cache in a fresh container — compilation alone ate the budget before any test ran. The immediate retry, with a warm cache, compiled in 11 seconds and went green.So the Docker gate can fail for reasons that have nothing to do with the code, on exactly the cold-cache path CI takes most often. That is a false red against the "
mainmust always passmake check" policy, and false reds are corrosive: they train people to re-run rather than read.What this changes about this issue
The existing definition of done already requires adopting the conditional-verbose-rerun pattern and
-race -cover. Add to it:REPO_POLICIES.mdrequiresmake testto complete in under 20 seconds with a 30-second timeout — but that budget is about test execution, not first-compile. Either raise the timeout, or arrange for the build stage to have compiled the packages beforemake testruns, so the timeout measures what it is meant to measure.docker build -f Dockerfile.backend .three times running, all green. One passing run proves nothing here — the flake only appears cold.Note the interaction with
-race: adding it will make compilation slower, not faster. Whatever budget is chosen must be validated with-raceenabled, not before.Also note that
timeout 30uses the externaltimeout(1)binary, which does not exist on stock macOS. Go's own-timeout 30sflag is the portable way to express this and is whatREPO_POLICIES.md's Go example uses. Switching to it fixes the portability problem and makes the timeout apply to test execution rather than to compilation-plus-execution — which is very likely the correct fix for the flake as well.Correction: the right fix is Go's
-timeoutflag, not a bigger budgetMy earlier comment on this issue offered two options for the cold-cache flake — "either raise the timeout, or arrange for the build stage to have compiled the packages before
make testruns." Both were worse than the obvious answer, and I want to supersede that guidance before someone implements it.The distinction I missed, raised by another repo in the org:
timeout 30 go test ./...— what this repo uses (backend/Makefile:25, and carried intobackend/script/teston PR #38's branch) — wraps the whole invocation, so the budget covers compilation plus execution. On an empty Go build cache in a fresh container, compilation alone can consume it before a single test runs. That is exactly the failure observed:make: *** [Makefile:35: test] Terminated, then a warm-cache retry passing in 11s.-timeout 30sflag bounds test execution only. Compilation is not counted, so a cold cache cannot trip it.So the exposure is not universal across the org's Go repos — it is specific to the shell-wrapper form, which this repo has.
Revised fix
Convert the shell wrapper to Go's flag. This is a one-line change and it fixes three things at once:
timeout(1)is coreutils and does not exist on stock macOS.-timeoutis part of the toolchain and works everywhere.REPO_POLICIES.md's Go example isgo test -timeout 30s -race -cover ./...— the flag form, not the wrapper. The existing definition of done already requires adopting that exact line, so this converges rather than adding scope.Do not raise the budget. A larger number would mask the flake rather than fix it, and would weaken the real 20-second execution ceiling the policy is trying to enforce.
Amended definition of done
Replacing the two bullets I added earlier:
make testuses Go's-timeout 30sflag. The externaltimeout(1)wrapper is gone from bothbackend/Makefileandbackend/script/test.docker build --no-cache, neverdocker builder prune, which is shared state on this host) and confirm the gate passes. Do this with-raceenabled, since race instrumentation makes compilation slower, not faster.Note for whoever implements: PR #38 relocates the backend test implementation from
backend/Makefileintobackend/script/test, carrying the shell-wrapper form with it. Read the tree as it exists at implementation time; if #38 has landed, the line to change is inbackend/script/test.