test_phase7_no_msgpack_in_storage.py
python
sha256:d11a87833d5fad6059b7662844bf5448a8911a17cce7a51811f71ad394f248eb
bump to v0.2.0rc13
Human
patch
7 days ago
| 1 | """TDD — Phase 7: msgpack confined to wire-protocol files only. |
| 2 | |
| 3 | Phase 7 requirements (issue #12): |
| 4 | - `import msgpack` must appear ONLY in wire-protocol files (transport, object |
| 5 | store, gc, migration, bundle, pack/unpack commands). |
| 6 | - Every file in the allowlist must exist (guard against stale allowlist). |
| 7 | - No file under muse/core/, muse/plugins/, or muse/cli/commands/ outside the |
| 8 | allowlist may import the msgpack library. |
| 9 | """ |
| 10 | |
| 11 | from __future__ import annotations |
| 12 | |
| 13 | import ast |
| 14 | import pathlib |
| 15 | |
| 16 | # --------------------------------------------------------------------------- |
| 17 | # Wire-only allowlist — files that legitimately import msgpack |
| 18 | # --------------------------------------------------------------------------- |
| 19 | |
| 20 | _REPO_ROOT = pathlib.Path(__file__).parent.parent |
| 21 | |
| 22 | _WIRE_ALLOWLIST: frozenset[pathlib.Path] = frozenset({ |
| 23 | _REPO_ROOT / "muse" / "core" / "io.py", |
| 24 | _REPO_ROOT / "muse" / "core" / "gc.py", |
| 25 | _REPO_ROOT / "muse" / "core" / "transport.py", |
| 26 | _REPO_ROOT / "muse" / "core" / "migrate.py", |
| 27 | _REPO_ROOT / "muse" / "core" / "mpack.py", |
| 28 | _REPO_ROOT / "muse" / "cli" / "commands" / "bundle.py", |
| 29 | _REPO_ROOT / "muse" / "cli" / "commands" / "pack_objects.py", |
| 30 | _REPO_ROOT / "muse" / "cli" / "commands" / "unpack_objects.py", |
| 31 | _REPO_ROOT / "muse" / "cli" / "commands" / "verify_pack.py", |
| 32 | }) |
| 33 | |
| 34 | |
| 35 | def _imports_msgpack(path: pathlib.Path) -> bool: |
| 36 | """Return True if *path* contains any import of the msgpack library.""" |
| 37 | try: |
| 38 | tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path)) |
| 39 | except SyntaxError: |
| 40 | return False |
| 41 | for node in ast.walk(tree): |
| 42 | if isinstance(node, ast.Import): |
| 43 | for alias in node.names: |
| 44 | if alias.name == "msgpack" or alias.name.startswith("msgpack."): |
| 45 | return True |
| 46 | elif isinstance(node, ast.ImportFrom): |
| 47 | if node.module and ( |
| 48 | node.module == "msgpack" or node.module.startswith("msgpack.") |
| 49 | ): |
| 50 | return True |
| 51 | return False |
| 52 | |
| 53 | |
| 54 | # --------------------------------------------------------------------------- |
| 55 | # Test: allowlist files all exist |
| 56 | # --------------------------------------------------------------------------- |
| 57 | |
| 58 | |
| 59 | class TestWireAllowlistFilesExist: |
| 60 | def test_all_allowlist_files_exist(self) -> None: |
| 61 | """Every file in the wire allowlist must actually exist on disk.""" |
| 62 | missing = [p for p in _WIRE_ALLOWLIST if not p.is_file()] |
| 63 | assert missing == [], ( |
| 64 | f"Stale allowlist — these files no longer exist: " |
| 65 | + ", ".join(str(p.relative_to(_REPO_ROOT)) for p in missing) |
| 66 | ) |
| 67 | |
| 68 | |
| 69 | # --------------------------------------------------------------------------- |
| 70 | # Test: no msgpack in muse/core/ (except allowlist) |
| 71 | # --------------------------------------------------------------------------- |
| 72 | |
| 73 | |
| 74 | class TestNoMsgpackInCore: |
| 75 | def test_no_msgpack_import_in_core_storage(self) -> None: |
| 76 | """No file in muse/core/ outside the allowlist may import msgpack.""" |
| 77 | violations: list[str] = [] |
| 78 | for path in (_REPO_ROOT / "muse" / "core").rglob("*.py"): |
| 79 | if path in _WIRE_ALLOWLIST: |
| 80 | continue |
| 81 | if _imports_msgpack(path): |
| 82 | violations.append(str(path.relative_to(_REPO_ROOT))) |
| 83 | assert violations == [], ( |
| 84 | "Non-wire muse/core/ files import msgpack:\n " |
| 85 | + "\n ".join(sorted(violations)) |
| 86 | ) |
| 87 | |
| 88 | def test_no_msgpack_import_in_plugins(self) -> None: |
| 89 | """No file in muse/plugins/ may import msgpack.""" |
| 90 | violations: list[str] = [] |
| 91 | for path in (_REPO_ROOT / "muse" / "plugins").rglob("*.py"): |
| 92 | if path in _WIRE_ALLOWLIST: |
| 93 | continue |
| 94 | if _imports_msgpack(path): |
| 95 | violations.append(str(path.relative_to(_REPO_ROOT))) |
| 96 | assert violations == [], ( |
| 97 | "Plugin files import msgpack:\n " |
| 98 | + "\n ".join(sorted(violations)) |
| 99 | ) |
| 100 | |
| 101 | |
| 102 | # --------------------------------------------------------------------------- |
| 103 | # Test: no msgpack in muse/cli/commands/ (except allowlist) |
| 104 | # --------------------------------------------------------------------------- |
| 105 | |
| 106 | |
| 107 | class TestNoMsgpackInCliCommands: |
| 108 | def test_no_msgpack_import_in_cli_storage(self) -> None: |
| 109 | """No cli/commands/ file outside the wire allowlist may import msgpack.""" |
| 110 | violations: list[str] = [] |
| 111 | for path in (_REPO_ROOT / "muse" / "cli" / "commands").rglob("*.py"): |
| 112 | if path in _WIRE_ALLOWLIST: |
| 113 | continue |
| 114 | if _imports_msgpack(path): |
| 115 | violations.append(str(path.relative_to(_REPO_ROOT))) |
| 116 | assert violations == [], ( |
| 117 | "Non-wire cli/commands/ files import msgpack:\n " |
| 118 | + "\n ".join(sorted(violations)) |
| 119 | ) |
File History
1 commit
sha256:d11a87833d5fad6059b7662844bf5448a8911a17cce7a51811f71ad394f248eb
bump to v0.2.0rc13
Human
patch
7 days ago