"""Tests for the per-module code cache (Plan 1.3 Phase 6, Workstream I).""" import shutil import sys import tempfile import unittest from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "stage3_build")) from module_cache import ( # noqa: E402 get_module, invalidate_attached, module_cache_key, put_module, ) class ModuleCacheTests(unittest.TestCase): def setUp(self): self.tmp = Path(tempfile.mkdtemp()) self.manifest = self.tmp / ".cache" / "module_cache.json" self.generator = self.tmp / "generator.py" self.generator.write_bytes(b"# generator v1\nprint('hi')\n") def tearDown(self): shutil.rmtree(self.tmp, ignore_errors=True) def test_key_changes_with_spec(self): key_a = module_cache_key({"id": "blade", "len": 10}, self.generator) key_b = module_cache_key({"id": "blade", "len": 20}, self.generator) self.assertNotEqual(key_a, key_b) def test_key_changes_with_generator_source(self): spec = {"id": "blade", "len": 10} gen2 = self.tmp / "generator2.py" gen2.write_bytes(b"# generator v2 -- different bytes\nprint('yo')\n") self.assertNotEqual( module_cache_key(spec, self.generator), module_cache_key(spec, gen2), ) def test_key_stable_regardless_of_dict_order(self): self.assertEqual( module_cache_key({"a": 1, "b": 2}, self.generator), module_cache_key({"b": 2, "a": 1}, self.generator), ) def test_put_get_roundtrip(self): spec = {"id": "blade", "len": 10} code = "function makeBlade(){ return mesh; }" put_module(self.manifest, spec, self.generator, code) self.assertEqual(get_module(self.manifest, spec, self.generator), code) # unknown spec -> miss self.assertIsNone( get_module(self.manifest, {"id": "ghost"}, self.generator) ) def test_invalidate_attached_removes_dependents_only(self): specs = { "root": {"id": "root"}, "blade": {"id": "blade", "parent": "root"}, "handle": {"id": "handle", "parent": "root"}, "pommel": {"id": "pommel", "attachment": {"parentId": "handle"}}, } for spec in specs.values(): put_module(self.manifest, spec, self.generator, f"code_{spec['id']}") components = list(specs.values()) invalidated = invalidate_attached(self.manifest, "handle", components) self.assertEqual(invalidated, ["pommel"]) # pommel gone, everyone else remains self.assertIsNone( get_module(self.manifest, specs["pommel"], self.generator) ) self.assertEqual( get_module(self.manifest, specs["root"], self.generator), "code_root" ) self.assertEqual( get_module(self.manifest, specs["blade"], self.generator), "code_blade" ) self.assertEqual( get_module(self.manifest, specs["handle"], self.generator), "code_handle" ) def test_invalidate_returns_empty_when_no_dependents(self): specs = { "root": {"id": "root"}, "leaf": {"id": "leaf", "parent": "root"}, } for spec in specs.values(): put_module(self.manifest, spec, self.generator, f"code_{spec['id']}") # leaf has no dependents attached to it self.assertEqual(invalidate_attached(self.manifest, "leaf", list(specs.values())), []) # nothing removed self.assertEqual( get_module(self.manifest, specs["leaf"], self.generator), "code_leaf" ) if __name__ == "__main__": unittest.main(verbosity=2)