"""Tests for graphify/cache.py.""" import pytest from pathlib import Path from graphify.cache import file_hash, cache_dir, load_cached, save_cached, cached_files, clear_cache, _body_content @pytest.fixture def tmp_file(tmp_path): f = tmp_path / "sample.txt" f.write_text("hello world") return f @pytest.fixture def cache_root(tmp_path): return tmp_path def test_file_hash_consistent(tmp_file): """Same file gives same hash on repeated calls.""" h1 = file_hash(tmp_file) h2 = file_hash(tmp_file) assert h1 == h2 assert isinstance(h1, str) assert len(h1) == 64 # SHA256 hex digest length def test_file_hash_changes(tmp_path): """Different file contents give different hashes.""" f1 = tmp_path / "a.txt" f2 = tmp_path / "b.txt" f1.write_text("content one") f2.write_text("content two") assert file_hash(f1) != file_hash(f2) def test_cache_roundtrip(tmp_file, cache_root): """Save then load returns the same result dict.""" result = {"nodes": [{"id": "n1", "label": "Node1"}], "edges": []} save_cached(tmp_file, result, root=cache_root) loaded = load_cached(tmp_file, root=cache_root) assert loaded == result def test_cache_miss_on_change(tmp_file, cache_root): """After file content changes, load_cached returns None.""" result = {"nodes": [], "edges": [{"source": "a", "target": "b"}]} save_cached(tmp_file, result, root=cache_root) # Modify the file tmp_file.write_text("completely different content") assert load_cached(tmp_file, root=cache_root) is None def test_cached_files(tmp_path, cache_root): """cached_files returns the set of cached hashes.""" f1 = tmp_path / "file1.py" f2 = tmp_path / "file2.py" f1.write_text("alpha") f2.write_text("beta") save_cached(f1, {"nodes": [], "edges": []}, root=cache_root) save_cached(f2, {"nodes": [], "edges": []}, root=cache_root) hashes = cached_files(cache_root) assert file_hash(f1, cache_root) in hashes assert file_hash(f2, cache_root) in hashes def test_clear_cache(tmp_file, cache_root): """clear_cache removes all .json files from graphify-out/cache/ (all subdirs).""" save_cached(tmp_file, {"nodes": [], "edges": []}, root=cache_root) # Since v0.5.3 entries go into cache/ast/, not the flat cache/ dir cache_base = cache_root / "graphify-out" / "cache" assert len(list(cache_base.rglob("*.json"))) > 0 clear_cache(cache_root) assert len(list(cache_base.rglob("*.json"))) == 0 def test_md_frontmatter_only_change_same_hash(tmp_path): """Changing only frontmatter fields in a .md file does not change the hash.""" f = tmp_path / "doc.md" f.write_text("---\nreviewed: 2026-01-01\n---\n\n# Title\n\nBody text.") h1 = file_hash(f) f.write_text("---\nreviewed: 2026-04-09\n---\n\n# Title\n\nBody text.") h2 = file_hash(f) assert h1 == h2 def test_md_body_change_different_hash(tmp_path): """Changing the body of a .md file produces a different hash.""" f = tmp_path / "doc.md" f.write_text("---\nreviewed: 2026-01-01\n---\n\n# Title\n\nOriginal body.") h1 = file_hash(f) f.write_text("---\nreviewed: 2026-01-01\n---\n\n# Title\n\nChanged body.") h2 = file_hash(f) assert h1 != h2 def test_md_no_frontmatter_hashed_normally(tmp_path): """A .md file with no frontmatter is hashed by its full content.""" f = tmp_path / "doc.md" f.write_text("# Just a heading\n\nNo frontmatter here.") h1 = file_hash(f) f.write_text("# Just a heading\n\nDifferent content.") h2 = file_hash(f) assert h1 != h2 def test_non_md_file_hashed_fully(tmp_path): """Non-.md files are still hashed by their full content.""" f = tmp_path / "script.py" f.write_text("# comment\nx = 1") h1 = file_hash(f) f.write_text("# changed comment\nx = 1") h2 = file_hash(f) assert h1 != h2 def test_body_content_strips_frontmatter(): """_body_content correctly strips YAML frontmatter.""" content = b"---\ntitle: Test\n---\n\nActual body." assert _body_content(content) == b"\n\nActual body." def test_body_content_no_frontmatter(): """_body_content returns content unchanged when no frontmatter present.""" content = b"No frontmatter here." assert _body_content(content) == content