Some checks failed
Test / pytest (pull_request) Failing after 35s
Add scripts/graph_visualizer.py — standalone tool that: - Builds knowledge graph from knowledge/index.json - Renders ASCII tree for terminal - Exports DOT for Graphviz - Extracts subgraphs by seed + max_depth - Filters by domain and category Includes test_graph_visualizer.py smoke test (8/8) Addresses #151
106 lines
4.4 KiB
Python
Executable File
106 lines
4.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Tests for graph_visualizer.py — smoke test + subgraph logic.
|
|
Run: python3 scripts/test_graph_visualizer.py
|
|
"""
|
|
|
|
import json, sys, tempfile
|
|
from pathlib import Path
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
import graph_visualizer as gv
|
|
|
|
|
|
def make_index(facts, tmp_dir):
|
|
p = tmp_dir / "index.json"
|
|
p.write_text(json.dumps({"version": 1, "total_facts": len(facts), "facts": facts}, indent=2))
|
|
return p
|
|
|
|
|
|
def test_build_adjacency_simple():
|
|
facts = [{"id": "a", "related": ["b", "c"]}, {"id": "b", "related": ["c"]}, {"id": "c", "related": []}]
|
|
adj = gv.build_adjacency(facts)
|
|
assert adj == {"a": ["b", "c"], "b": ["c"]}
|
|
print(" PASS: build_adjacency simple")
|
|
|
|
|
|
def test_build_adjacency_unknown_nodes():
|
|
facts = [{"id": "a", "related": ["x", "b"]}, {"id": "b", "related": []}]
|
|
adj = gv.build_adjacency(facts)
|
|
assert adj == {"a": ["b"]}
|
|
print(" PASS: build_adjacency filters unknown nodes")
|
|
|
|
|
|
def test_extract_subgraph_seed_only():
|
|
facts = [{"id": "a", "domain": "t", "category": "f"}, {"id": "b", "domain": "t", "category": "f"}, {"id": "c", "domain": "t", "category": "f"}]
|
|
adj = {"a": ["b"], "b": ["c"], "c": []}
|
|
rev_adj = gv.build_reverse_adjacency(adj)
|
|
sub = gv.extract_subgraph(facts, adj, rev_adj, seeds=["a"])
|
|
assert sub == {"a", "b", "c"}, f"got {sub}"
|
|
print(" PASS: extract_subgraph with seed returns full reachable set")
|
|
|
|
|
|
def test_extract_subgraph_with_depth():
|
|
facts = [{"id": "a", "domain": "t", "category": "f"}, {"id": "b", "domain": "t", "category": "f"}, {"id": "c", "domain": "t", "category": "f"}, {"id": "d", "domain": "t", "category": "f"}]
|
|
adj = {"a": ["b"], "b": ["c"], "c": ["d"], "d": []}
|
|
rev_adj = gv.build_reverse_adjacency(adj)
|
|
sub = gv.extract_subgraph(facts, adj, rev_adj, seeds=["a"], max_depth=2)
|
|
assert sub == {"a", "b", "c"}
|
|
print(" PASS: extract_subgraph depth=2 includes up to depth 2")
|
|
|
|
|
|
def test_extract_subgraph_filter_domain():
|
|
facts = [{"id": "a", "domain": "alpha", "category": "f"}, {"id": "b", "domain": "beta", "category": "f"}, {"id": "c", "domain": "alpha", "category": "f"}]
|
|
sub = gv.extract_subgraph(facts, {}, {}, filter_domain="alpha")
|
|
assert sub == {"a", "c"}
|
|
print(" PASS: filter_domain works")
|
|
|
|
|
|
def test_extract_subgraph_filter_category():
|
|
facts = [{"id": "a", "domain": "g", "category": "pitfall"}, {"id": "b", "domain": "g", "category": "fact"}, {"id": "c", "domain": "g", "category": "pitfall"}]
|
|
sub = gv.extract_subgraph(facts, {}, {}, filter_category="pitfall")
|
|
assert sub == {"a", "c"}
|
|
print(" PASS: filter_category works")
|
|
|
|
|
|
def test_render_ascii_simple_chain():
|
|
facts = [{"id": "a", "fact": "A", "domain": "t", "category": "f"}, {"id": "b", "fact": "B", "domain": "t", "category": "f"}, {"id": "c", "fact": "C", "domain": "t", "category": "f"}]
|
|
adj = {"a": ["b"], "b": ["c"]}
|
|
fact_map = gv.build_fact_map(facts)
|
|
out = gv.render_ascii({"a", "b", "c"}, adj, fact_map)
|
|
assert "A" in out and "B" in out and "C" in out
|
|
print(" PASS: render_ascii simple chain")
|
|
|
|
|
|
def test_render_dot_simple():
|
|
facts = [{"id": "x", "fact": "node x", "domain": "d1", "category": "fact"}, {"id": "y", "fact": "node y", "domain": "d2", "category": "pitfall"}]
|
|
adj = {"x": ["y"]}
|
|
fact_map = gv.build_fact_map(facts)
|
|
out = gv.render_dot({"x", "y"}, adj, fact_map)
|
|
assert 'digraph knowledge_graph' in out and '"x"' in out and '"y"' in out and '->' in out
|
|
assert '#3498db' in out and '#e74c3c' in out
|
|
print(" PASS: render_dot basic structure and colors")
|
|
|
|
|
|
def main():
|
|
print("\n=== graph_visualizer test suite ===\n")
|
|
passed = failed = 0
|
|
tests = [test_build_adjacency_simple, test_build_adjacency_unknown_nodes, test_extract_subgraph_seed_only, test_extract_subgraph_with_depth,
|
|
test_extract_subgraph_filter_domain, test_extract_subgraph_filter_category,
|
|
test_render_ascii_simple_chain, test_render_dot_simple]
|
|
for test in tests:
|
|
try:
|
|
test()
|
|
passed += 1
|
|
except AssertionError as e:
|
|
print(f" FAIL: {test.__name__} — {e}")
|
|
failed += 1
|
|
except Exception as e:
|
|
print(f" ERROR: {test.__name__} — {e}")
|
|
failed += 1
|
|
print(f"\n=== Results: {passed}/{passed+failed} passed, {failed} failed ===")
|
|
return failed == 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(0 if main() else 1)
|