test: invert UI boundary guard to allowlist
Quality Gate / gate (push) Failing after 47s

Co-authored-by: Copilot App <[email protected]>
This commit is contained in:
Jim Lancaster
2026-08-23 18:57:13 -05:00
co-authored by Copilot App
parent 803237371e
commit 12f125761a
+28 -18
View File
@@ -15,23 +15,15 @@ UI_DIR = Path(__file__).resolve().parents[1] / "src" / "transcription" / "ui"
PAGES_DIR = UI_DIR / "pages"
COMPONENTS_DIR = UI_DIR / "components"
# Names a page must not pull in: they hand the page a session, a transaction, ORM
# loader introspection, or process-global configuration.
FORBIDDEN_PAGE_IMPORTS = frozenset(
{
"session_scope",
"transaction_scope",
"get_session_factory",
"resolve_session_factory",
"get_settings",
"get_engine",
"upgrade_schema",
"create_all",
}
)
FORBIDDEN_PAGE_MODULES = frozenset({"sqlalchemy", "sqlmodel"})
# Sensitive modules are allowlisted, not blocklisted, so newly added persistence
# helpers cannot slip through by using an unlisted name.
PAGE_IMPORT_ALLOWLIST: dict[tuple[str, int], frozenset[str]] = {
("db.session", 3): frozenset({"SessionFactoryDep"}),
("transcription.config", 0): frozenset({"Settings"}),
}
def _page_paths() -> list[Path]:
return sorted(path for path in PAGES_DIR.glob("*.py") if path.stem != "__init__")
@@ -57,6 +49,21 @@ def _imports(tree: ast.Module) -> tuple[set[str], set[str]]:
return names, modules
def _page_allowlist_violations(tree: ast.Module) -> set[str]:
violations: set[str] = set()
for node in ast.walk(tree):
if not isinstance(node, ast.ImportFrom) or not node.module:
continue
allowed = PAGE_IMPORT_ALLOWLIST.get((node.module, node.level))
if allowed is None:
continue
for alias in node.names:
imported_name = alias.name
if imported_name not in allowed:
violations.add(f"{'.' * node.level}{node.module}.{imported_name}")
return violations
def test_page_modules_are_discovered():
"""Guard the guard: the rules below are meaningless if nothing is scanned."""
discovered = {path.stem for path in _page_paths()}
@@ -67,8 +74,10 @@ def test_no_page_imports_persistence_or_process_globals():
"""HIGH-07: pages orchestrate services; they do not own sessions or settings."""
violations: dict[str, list[str]] = {}
for path in _page_paths():
names, modules = _imports(ast.parse(path.read_text(encoding="utf-8")))
found = sorted((names & FORBIDDEN_PAGE_IMPORTS) | (modules & FORBIDDEN_PAGE_MODULES))
tree = ast.parse(path.read_text(encoding="utf-8"))
_, modules = _imports(tree)
allowlist_violations = _page_allowlist_violations(tree)
found = sorted((modules & FORBIDDEN_PAGE_MODULES) | allowlist_violations)
if found:
violations[path.stem] = found
assert violations == {}
@@ -79,7 +88,8 @@ def test_no_component_resolves_request_or_application_state():
violations: dict[str, list[str]] = {}
for path in _component_paths():
names, modules = _imports(ast.parse(path.read_text(encoding="utf-8")))
found = sorted((names & FORBIDDEN_PAGE_IMPORTS) | (modules & (FORBIDDEN_PAGE_MODULES | {"fastapi"})))
forbidden_names = {"get_settings", "get_engine", "session_scope", "transaction_scope"}
found = sorted((names & forbidden_names) | (modules & (FORBIDDEN_PAGE_MODULES | {"fastapi"})))
if found:
violations[str(path.relative_to(COMPONENTS_DIR))] = found
assert violations == {}