mirror of
https://github.com/jhd3197/ServerKit.git
synced 2026-11-09 05:24:49 +09:00
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>
85 lines
3.0 KiB
Python
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())
|