#!/usr/bin/env bash # Counts test-suite invariant violations. HARD GATE — a nonzero count fails. # # WHAT THIS IS FOR # # docs/TEST_ARCHITECTURE.md §3.6.3. Every category below is something the # refactor removed, and this is what stops it coming back. It ran in warn mode # through phases 0-5 as the migration's progress meter (911 violations at the # phase-1 baseline); phase 6 flipped it to a failure, which is only honest # because the counts reached their floor first. # # THE FLOOR IS ZERO — with exemptions that are declared, not assumed. # # Every check here is a grep over text, and text has legitimate reasons to # contain the thing being counted: a URL-parser test's inputs are Bluesky URLs, # a config test's assertions are the production defaults it parses, and a test # that proves a cache entry expires has to let the deadline pass. Those are # exempted individually, in the source, with a reason — so each one is a # decision someone made and can be found by grepping for the marker: # # someCall(x) // coves:allow-sleep: # someCall(x) // coves:allow-host-literal: # someCall(x) // coves:allow-public-host: # someCall(x) // coves:allow-raw-body-decode: # # and, where per-line markers would be noise because the whole file's subject # matter IS the literal (the Bluesky URL parser; the config parser), a # file-scope form declared once near the top of the file: # # // coves:allow-public-host-file: # # File-scope exemptions are printed on EVERY run with their reason and hit # count, so a broad exemption cannot go quiet. Adding either kind is a visible # edit in review; that is the whole enforcement model — the greps stop drift, # the annotations record the exceptions, and neither pretends to be a proof. # # THESE ARE TRIPWIRES, NOT PROOFS. A grep is bypassable by construction — a URL # built by concatenation, a bare IP literal, a sleep hidden behind a helper. # They catch drift cheaply. The actual guarantee that tests never reach the # public network is the egress-blocked CI network (docker-compose.ci.yml, # `internal: true`). # # SCOPE. "test code" means every *_test.go file under cmd/, internal/ and # tests/, plus every .go file under tests/ — shared helpers that do not end in # _test.go (tests/testkit, tests/fixtures) are test code too, and historically # some of the worst offenders lived in exactly those files. Production sources # are out of scope for the test-hygiene categories, because production code # legitimately dials websockets and names public hosts, and auditing it here # would mean exempting the AppView from a rule written for its tests. Three # categories run wider on purpose: testing.Short() scans everything (nothing # in this tree may call it), and the raw-request-body and unchecked-DecodeJSON # rules scan production sources only — they are API-hardening fences, not test # hygiene. # # Usage: # scripts/test-audit.sh summary table; exits nonzero on any violation # scripts/test-audit.sh -v table plus every offending file:line set -uo pipefail REPO_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) cd "$REPO_ROOT" VERBOSE=0 if [[ ${1:-} == "-v" || ${1:-} == "--verbose" ]]; then VERBOSE=1 fi CYAN='\033[36m' YELLOW='\033[33m' GREEN='\033[32m' RED='\033[31m' RESET='\033[0m' # --------------------------------------------------------------------------- # File scopes # --------------------------------------------------------------------------- test_code_files() { { find cmd internal tests -type f -name '*_test.go' find tests -type f -name '*.go' } 2>/dev/null | sort -u } all_go_files() { find cmd internal tests -type f -name '*.go' 2>/dev/null | sort -u } production_go_files() { find cmd internal -type f -name '*.go' ! -name '*_test.go' 2>/dev/null | sort -u } # --------------------------------------------------------------------------- # Scanning # --------------------------------------------------------------------------- TOTAL=0 declare -a ROWS=() declare -a DETAILS=() declare -a EXEMPT_FILES=() # scan