Files
shopdb-flask/tests/test_no_hidden_tests.py
cproudlock 5f27f81e88
All checks were successful
CI / naming (push) Successful in 3s
CI / frontend (push) Successful in 13s
CI / migrations-mysql (push) Successful in 42s
CI / backend (push) Successful in 4m18s
Clear the review blockers on the merged wilm work
A multi-agent review of the merged branch produced 29 findings that survived
adversarial verification. This clears the blockers and the four worth fixing
before it reaches main. The design held up well: path-traversal defence on all
four serving routes is clean, the agent's SHA-256 check fails closed, enqueue
authority is proportionate and audited, and no site literals or credential leaks
in the new C#.

THE TESTS NEVER RAN

pytest.ini sets testpaths = tests, so plugins/endpoints/tests/ was collected ZERO
times. Fifty-five lines of delivery assertions were dead code, and run directly
five of them failed. plugins/printedparts/tests/ had been invisible for longer.

That is the worst shape a gap can take: the suite is green, the coverage is
imaginary, and the collector auth on a download route could be deleted without
anything going red.

Both files moved under tests/. tests/test_no_hidden_tests.py now fails if a test
file appears anywhere pytest will not look; it caught a staged copy inside the
installer bundle on its first run, and was verified by planting a violation.

The five failures were stale expectations, and they split by cause. His: the
applications panel made six panels where his test said five, and 'applications'
belongs in KNOWN_PAYLOAD_KEYS now that it is persisted - his own test contradicted
his own feature. Ours: we renamed the events card to activity, moved the
fleet-wide cards to endpoints.fleet, and made the list route a bare route('')
which Flask rejects unless the blueprint is mounted under a prefix.

FOUR FIXES

The snapshot table's key was a 32-bit autoincrement on a table rewritten
wholesale every check-in, so it climbs at (endpoints x apps) per report and
exhausts on MySQL. Widened while the table is unreleased. Widening it broke every
insert on SQLite, which only autoincrements a column typed exactly INTEGER
PRIMARY KEY, so it carries a dialect variant - and nothing caught either fault,
because no test inserted into that table at all. One does now.

Ingest truncated displayname but not displayversion or publisher. On strict MySQL
an over-long registry value fails the whole check-in transaction, so one machine
with a malformed Uninstall entry goes silent entirely. SQLite does not enforce
lengths, so the suite would never have shown it.

update_app's version: branch pinned AppVersion.sha256, but that route resolves the
version to its APPLICATION and serves application.installpath - so the digest
described an artifact the route never sends, and where it was null the agent had
nothing to verify and failed open. It now pins the file actually served.

The same branch ended the delivery URL at the id, so an MSI arrived with no
extension and could not be routed through msiexec. It carries the filename now.

Agent packaging moved off require_role('admin') to a new endpoints.agentpackage
permission. The generated ZIP carries a live collector key, so this is a heavier
grant than deploying to one endpoint - and a role cannot be given to one person
for one job the way a permission can.

Full suite 2231 passed (2198 before, plus the 22 that never ran and 11 new),
frontend 314.
2026-08-30 17:17:13 -04:00

51 lines
2.1 KiB
Python

"""No test file may sit where pytest will not collect it.
pytest.ini sets `testpaths = tests`, so a test file anywhere else is never run
by `pytest` and never run in CI. It still passes review, still gets maintained,
and still reports nothing.
That is not hypothetical. plugins/endpoints/tests/test_plugin.py arrived with a
merge carrying 55 lines of delivery assertions and was collected ZERO times; run
directly, five of its tests failed. plugins/printedparts/tests/test_plugin.py had
been sitting uncollected for longer. Both are now under tests/.
The failure mode is the worst kind: the suite is green, the coverage is imaginary,
and the authorisation on a route can be deleted without anything going red.
"""
from pathlib import Path
REPO = Path(__file__).resolve().parents[1]
# Where pytest actually looks (pytest.ini: testpaths = tests).
COLLECTED = REPO / 'tests'
# Trees that legitimately hold test-shaped files pytest must not collect:
# vendored dependencies, build output, and the frontend's own vitest specs.
IGNORED = ('node_modules', 'venv', '.git', 'dist', '__pycache__',
'site-packages', '.plugins-staged', 'frontend')
# The Windows installer stages a whole copy of the app under here. It is build
# output, not source, and the export excludes it - caught by this test on its
# first run, which is the sort of thing the ignore list exists for.
IGNORED_TREES = ('deploy/windows/installer/bundle',)
def test_every_test_file_lives_where_pytest_looks():
stray = []
for path in REPO.rglob('test_*.py'):
if any(part in IGNORED for part in path.parts):
continue
relative = path.relative_to(REPO).as_posix()
if any(relative.startswith(tree) for tree in IGNORED_TREES):
continue
if COLLECTED in path.parents:
continue
stray.append(path.relative_to(REPO))
assert not stray, (
'These test files are never collected, because pytest.ini limits\n'
'collection to tests/. They pass review and report nothing:\n '
+ '\n '.join(str(p) for p in sorted(stray))
+ '\n\nMove them under tests/ (tests/test_plugins/ for plugin tests).'
)