"""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. A second plugin had one 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') # Whole-tree copies a build stages somewhere under the repo root. Build output, # not source, so a test file inside one is not a stray. Empty today; add a path # here rather than loosening IGNORED above, which matches on any path part. IGNORED_TREES = () 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).' )