Files
ServerKit/backend/tests/check_test_count.py
T
Juan DenisandClaude Opus 4.8 297c1e5864 ci(tests): add test-count ratchet + changelog recovery-parity (plan 42 ph6)
Install the guard the data loss taught us to want: code and its proving tests
died together, so the suite stayed green (2572->2209 passing) while whole
features silently vanished — nobody diffs a green number.

- backend/tests/BASELINE_COUNT (2475) + check_test_count.py: compares pytest's
  collected count to the floor; CI (backend-ci.yml new step) fails if it drops.
  Lowering the floor requires editing BASELINE_COUNT in the same commit, so a
  silent test loss surfaces as a red X instead of a still-green suite. Verified:
  passes at baseline, exits 1 on a simulated drop.
- CHANGELOG: document the recovery-parity restore (Cloudflare R2 engine,
  email-bounce, DNS-cutover staging, survey routes, cron CSS, 18 test suites)
  and the ratchet.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 05:27:22 -04:00

85 lines
3.0 KiB
Python

#!/usr/bin/env python3
"""Test-count ratchet — the guard the 2026-07 data loss taught us to want.
Back then code and its proving tests died TOGETHER, so the suite stayed green
(2572 -> 2209 passing) while whole features silently vanished — nobody diffs a
green number. This checks the collected-test count against a checked-in floor
(``backend/tests/BASELINE_COUNT``) and FAILS if collection has dropped below it.
- Raising the floor is expected as tests are added — run with ``--update`` to
bump ``BASELINE_COUNT`` to the current count (or just edit the file).
- Lowering it requires editing ``BASELINE_COUNT`` in the same commit — an
explicit, reviewable statement that tests were intentionally removed, instead
of a silent regression.
Usage:
python tests/check_test_count.py # verify (CI mode) — exit 1 if below
python tests/check_test_count.py --update # write current count as the new floor
Run from the ``backend/`` directory.
"""
import os
import re
import subprocess
import sys
HERE = os.path.dirname(os.path.abspath(__file__))
BASELINE_FILE = os.path.join(HERE, 'BASELINE_COUNT')
def read_baseline():
try:
with open(BASELINE_FILE) as f:
return int(f.read().strip())
except (OSError, ValueError):
return 0
def collect_count():
"""Return the number of tests pytest collects, without running them."""
proc = subprocess.run(
[sys.executable, '-m', 'pytest', '--collect-only', '-q',
'-p', 'no:cacheprovider'],
cwd=os.path.dirname(HERE), # backend/
capture_output=True, text=True,
)
out = proc.stdout + proc.stderr
# pytest prints e.g. "2475 tests collected in 6.55s" (or "1 test collected")
m = re.search(r'(\d+)\s+tests?\s+collected', out)
if not m:
sys.stderr.write('Could not parse a collected-test count from pytest.\n')
sys.stderr.write(out[-2000:] + '\n')
sys.exit(2)
return int(m.group(1))
def main():
update = '--update' in sys.argv[1:]
collected = collect_count()
baseline = read_baseline()
if update:
with open(BASELINE_FILE, 'w') as f:
f.write(f'{collected}\n')
print(f'BASELINE_COUNT updated: {baseline} -> {collected}')
return 0
print(f'collected={collected} baseline={baseline}')
if collected < baseline:
sys.stderr.write(
f'\nTEST-COUNT RATCHET FAILED: collected {collected} tests, but the '
f'floor is {baseline}.\n'
'Tests disappeared. If this is an intentional removal, lower '
f'{os.path.relpath(BASELINE_FILE)} in THIS commit; otherwise a '
'feature (and its proving tests) went missing — investigate before '
'merging.\n')
return 1
if collected > baseline:
print(f'note: {collected - baseline} new test(s) since the floor was '
'set — run this with --update to raise BASELINE_COUNT.')
return 0
if __name__ == '__main__':
sys.exit(main())