test(s31): close 1 review MAJOR — guards cover the store functions and dynamic imports
This commit is contained in:
parent
da8779fc7f
commit
5481781ca5
2 changed files with 51 additions and 0 deletions
|
|
@ -273,6 +273,20 @@ def test_link_in_index_success_preserves_existing_bytes_and_order(tmp_path) -> N
|
|||
assert result == original + "- [Promotert](promoted-verdict-x.md)\n"
|
||||
|
||||
|
||||
def _dynamic_import_targets(node: ast.Call) -> list[str]:
|
||||
"""Name any dynamic-import call: ``__import__(...)`` or ``<anything>.import_module(...)``.
|
||||
|
||||
Shared by the MAF-free guard here and the no-network guard in
|
||||
``tests/test_semretrieval_loadbearing.py`` — both walk imports statically, and both are blind
|
||||
to a dynamic import by construction."""
|
||||
func = node.func
|
||||
if isinstance(func, ast.Name) and func.id == "__import__":
|
||||
return ["__import__"]
|
||||
if isinstance(func, ast.Attribute) and func.attr == "import_module":
|
||||
return ["import_module"]
|
||||
return []
|
||||
|
||||
|
||||
@pytest.mark.parametrize("module_name", _MAF_FREE_MODULES)
|
||||
def test_okf_is_maf_free(module_name: str) -> None:
|
||||
"""D7 portability: each framework-neutral module IMPORTS no ``agent_framework`` / ``mcp`` (a
|
||||
|
|
@ -283,10 +297,22 @@ def test_okf_is_maf_free(module_name: str) -> None:
|
|||
Path(__file__).resolve().parents[1] / "src" / "portfolio_optimiser" / module_name
|
||||
).read_text(encoding="utf-8")
|
||||
imported: list[str] = []
|
||||
dynamic: list[str] = []
|
||||
for node in ast.walk(ast.parse(src)):
|
||||
if isinstance(node, ast.Import):
|
||||
imported += [a.name for a in node.names]
|
||||
elif isinstance(node, ast.ImportFrom):
|
||||
imported.append(node.module or "")
|
||||
elif isinstance(node, ast.Call):
|
||||
dynamic += _dynamic_import_targets(node)
|
||||
forbidden = [m for m in imported if m.split(".")[0] in {"agent_framework", "mcp"}]
|
||||
assert forbidden == [], f"{module_name} must not import MAF/mcp, found: {forbidden}"
|
||||
# A RATCHET, green today: none of these modules imports ``importlib`` or calls ``__import__``.
|
||||
# The sweep above walks only ``ast.Import``/``ast.ImportFrom``, so a single
|
||||
# ``importlib.import_module("portfolio_optimiser.verdicts")`` would sail straight past it and
|
||||
# pull MAF into a module this guard certifies as MAF-free. Any dynamic import is refused
|
||||
# outright rather than argument-inspected: a computed target cannot be judged statically.
|
||||
assert dynamic == [], (
|
||||
f"{module_name} performs dynamic import(s) {dynamic} — the MAF-free guard is a STATIC "
|
||||
"check and cannot see through them; use a normal import so it stays enforceable"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -100,6 +100,18 @@ def test_semretrieval_import_and_rank_pull_in_no_maf_and_no_verdicts() -> None:
|
|||
"hybrid = mod.HybridRanker(mod.FakeEmbedder(), sim).rank(query, candidates, 2)\n"
|
||||
"assert len(hybrid) == 2, hybrid\n"
|
||||
"\n"
|
||||
"# The vector-store functions are exercised BEFORE the sweep too: they are public API and a\n"
|
||||
"# lazy import placed inside either one would otherwise never run under this probe.\n"
|
||||
"import tempfile\n"
|
||||
"tmp = tempfile.mkdtemp()\n"
|
||||
"assert mod.load_vector_store(tmp) is None, 'a directory with no artifacts must load as None'\n"
|
||||
"mod.save_vector_store(tmp, candidates, mod.FakeEmbedder())\n"
|
||||
"loaded = mod.load_vector_store(tmp)\n"
|
||||
"assert loaded is not None, 'the store round trip returned None'\n"
|
||||
"ids, matrix = loaded\n"
|
||||
"assert ids == ['A', 'B'], ids\n"
|
||||
"assert matrix.shape == (2, mod.EMBED_DIM), matrix.shape\n"
|
||||
"\n"
|
||||
"for name in ('agent_framework', 'mcp', 'portfolio_optimiser.verdicts', 'portfolio_optimiser'):\n"
|
||||
" assert name not in sys.modules, name + ' leaked into the semretrieval runtime graph'\n"
|
||||
)
|
||||
|
|
@ -158,16 +170,29 @@ def test_blas_thread_pins_are_set_before_numpy_is_imported() -> None:
|
|||
def test_semretrieval_imports_no_network_modules() -> None:
|
||||
"""SC8 — the offline default must not be able to reach the network. Detach point: add
|
||||
``import urllib.request`` (or any ``_NETWORK_ROOTS`` member) to semretrieval.py → RED."""
|
||||
from tests.test_okf import _dynamic_import_targets
|
||||
|
||||
tree = ast.parse(_SEMRETRIEVAL.read_text(encoding="utf-8"))
|
||||
imported_roots: set[str] = set()
|
||||
dynamic: list[str] = []
|
||||
for node in ast.walk(tree):
|
||||
if isinstance(node, ast.Import):
|
||||
imported_roots.update(alias.name.split(".")[0] for alias in node.names)
|
||||
elif isinstance(node, ast.ImportFrom) and node.module:
|
||||
imported_roots.add(node.module.split(".")[0])
|
||||
elif isinstance(node, ast.Call):
|
||||
dynamic += _dynamic_import_targets(node)
|
||||
|
||||
leaked = imported_roots & _NETWORK_ROOTS
|
||||
assert not leaked, f"semretrieval imports network module(s): {sorted(leaked)}"
|
||||
# RATCHET (green today): this walk sees only static imports, so a single
|
||||
# ``importlib.import_module("httpx")`` would defeat the whole guard. Refuse dynamic imports
|
||||
# outright — a computed target cannot be judged statically. The ``importlib.util`` used by the
|
||||
# probes in this file lives inside the probe STRINGS, not in the module under test.
|
||||
assert dynamic == [], (
|
||||
f"semretrieval performs dynamic import(s) {dynamic} — the no-network guard is a STATIC "
|
||||
"check and cannot see through them"
|
||||
)
|
||||
|
||||
|
||||
# --- SC2: the cosine term is load-bearing on a structurally tied 500+ base ---------------------
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue