Files
ServerKit/backend/tests/test_migration_inventory.py
Juan DenisandClaude Opus 5 550dd47585 fix(ratchets): regenerate the boundary baseline and the migration inventory
Two more shards were red on generated artifacts nobody regenerated:

- api_controller_boundary_baseline.json: the attachment round rewrote
  _persist_user_message to build the row before adding it, so the fingerprint
  moved from `db.session.add(AiMessage(...))` to `db.session.add(message)`.
  Same crossing, same category, count unchanged at 509 -- one line.

- docs/MIGRATION_INVENTORY.md: three measured numbers had moved without a
  regeneration -- bare @jwt_required() routes 577 -> 576, and the two frontend
  rows whose totals come from maps inside check-frontend-boundaries.mjs
  (setInterval pollers 10 -> 9, hex colour literals 151 -> 150). All three are
  deterministic AST/constant counts, so these are CI's numbers, not this box's.

The reason nobody caught the inventory locally is the third fix here: the
generator hardcoded `node`, and under WSL the Windows nvm directory is on PATH
but exposes only `node.exe`, so test_migration_inventory.py skipped on the dev
box. Both the generator and the test now resolve $NODE / node / node.exe, and
the test runs instead of skipping.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-21 20:14:34 -04:00

50 lines
1.9 KiB
Python

"""The committed migration inventory must match the measured state (plan 76, H).
``docs/MIGRATION_INVENTORY.md`` is generated from the same censuses and checks
that enforce each ratchet, so the plan cannot claim a migration is further
along (or a door more adopted) than the code measures. This test is the
freshness half of that closure: whenever a migration commit moves a number,
regenerating the document is part of the commit::
python scripts/generate-migration-inventory.py
Skipped only when no node binary is reachable at all — the generator shells
out to the frontend boundary checker for its half of the rows, and resolves
``$NODE`` / ``node`` / ``node.exe`` so a WSL run still covers it.
"""
import importlib.util
import os
import subprocess
import sys
import pytest
HERE = os.path.dirname(os.path.abspath(__file__))
REPO = os.path.dirname(os.path.dirname(HERE))
GENERATOR = os.path.join(REPO, 'scripts', 'generate-migration-inventory.py')
DOC = os.path.join(REPO, 'docs', 'MIGRATION_INVENTORY.md')
def test_the_committed_inventory_matches_the_measured_state():
assert os.path.exists(DOC), (
'docs/MIGRATION_INVENTORY.md is missing; run '
'python scripts/generate-migration-inventory.py')
spec = importlib.util.spec_from_file_location('_mig_inventory', GENERATOR)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
# Same resolution the generator uses, so WSL (where the Windows nvm dir
# exposes only node.exe) runs this instead of skipping it.
if mod.node_command() is None:
pytest.skip('node is not available to run the frontend half')
expected = mod.render()
with open(DOC, encoding='utf-8') as f:
committed = f.read()
assert committed == expected, (
'docs/MIGRATION_INVENTORY.md is stale — a ratchet number moved without '
'regenerating it. Run: python scripts/generate-migration-inventory.py'
)