mirror of
https://github.com/safishamsi/graphify.git
synced 2026-07-12 10:27:11 +00:00
6a549e42d5
#1174: affected.py load_graph now forces directed=True before node_link_graph, matching the identical fix in serve.py and __main__.py. Undirected graphs (directed:false in graph.json) were causing in_edges to fall back to a direction-blind scan, missing true callers and reporting false positives. Regression test added. #1173: post-commit and post-checkout hook bodies now read graphify-out/.graphify_root before calling _rebuild_code, falling back to Path('.') if absent. A scoped build (graphify src/) no longer gets silently expanded to the full repo on the next commit. Tests added. #1172: Step 9 cleanup split into rm -f for fixed files and find -maxdepth 1 -delete for the chunk glob. Under fish/zsh an unmatched glob aborts the entire rm -f line, leaving temp files on disk. Fixed in the three skillgen source fragments and regenerated. #1163: detect_incremental type guard on stored mtime — if the manifest contains a dict-valued mtime (schema drift from older versions), coerce to None rather than propagating a non-numeric into comparisons. Regression test added. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
95 lines
3.6 KiB
Python
95 lines
3.6 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
|
|
import networkx as nx
|
|
from networkx.readwrite import json_graph
|
|
|
|
import graphify.__main__ as mainmod
|
|
|
|
|
|
def _write_graph(tmp_path):
|
|
graph = nx.DiGraph()
|
|
graph.add_node("target", label="Foo", source_file="pkg/foo.py", source_location="L1")
|
|
graph.add_node("caller", label="X()", source_file="app.py", source_location="L4")
|
|
graph.add_node("barrel", label="__init__.py", source_file="pkg/__init__.py", source_location=None)
|
|
graph.add_node("consumer", label="app.py", source_file="app.py", source_location=None)
|
|
graph.add_edge("caller", "target", relation="calls", context="call", confidence="EXTRACTED")
|
|
graph.add_edge("barrel", "target", relation="re_exports", context="export", confidence="EXTRACTED")
|
|
graph.add_edge("consumer", "target", relation="imports", context="import", confidence="EXTRACTED")
|
|
graph_path = tmp_path / "graph.json"
|
|
graph_path.write_text(json.dumps(json_graph.node_link_data(graph, edges="links")), encoding="utf-8")
|
|
return graph_path
|
|
|
|
|
|
def test_affected_cli_reverse_traverses_impact_edges(monkeypatch, tmp_path, capsys):
|
|
graph_path = _write_graph(tmp_path)
|
|
monkeypatch.setattr(mainmod, "_check_skill_version", lambda _: None)
|
|
monkeypatch.setattr(
|
|
mainmod.sys,
|
|
"argv",
|
|
["graphify", "affected", "Foo", "--graph", str(graph_path)],
|
|
)
|
|
|
|
mainmod.main()
|
|
|
|
out = capsys.readouterr().out
|
|
assert "Affected nodes for Foo" in out
|
|
assert "X()" in out
|
|
assert "calls" in out
|
|
assert "__init__.py" in out
|
|
assert "re_exports" in out
|
|
assert "app.py" in out
|
|
assert "imports" in out
|
|
|
|
|
|
def test_affected_cli_relation_filter_limits_reverse_traversal(monkeypatch, tmp_path, capsys):
|
|
graph_path = _write_graph(tmp_path)
|
|
monkeypatch.setattr(mainmod, "_check_skill_version", lambda _: None)
|
|
monkeypatch.setattr(
|
|
mainmod.sys,
|
|
"argv",
|
|
["graphify", "affected", "Foo", "--relation", "calls", "--graph", str(graph_path)],
|
|
)
|
|
|
|
mainmod.main()
|
|
|
|
out = capsys.readouterr().out
|
|
assert "Relations: calls" in out
|
|
assert "X()" in out
|
|
assert "__init__.py" not in out
|
|
|
|
|
|
def test_affected_cli_forces_directed_on_undirected_graph(monkeypatch, tmp_path, capsys):
|
|
"""A graph persisted with directed=false must still recover caller->callee
|
|
direction (#1174): affected on the callee returns the caller, not the callee
|
|
or nothing. Without forcing directed=True, node_link_graph builds an
|
|
undirected Graph, predecessors() collapses, and the reverse traversal breaks.
|
|
"""
|
|
graph = nx.DiGraph()
|
|
graph.add_node("A", label="caller_fn", source_file="a.py", source_location="L1")
|
|
graph.add_node("B", label="callee_fn", source_file="b.py", source_location="L2")
|
|
graph.add_edge("A", "B", relation="calls", context="call", confidence="EXTRACTED")
|
|
|
|
data = json_graph.node_link_data(graph, edges="links")
|
|
# Persist as undirected on disk to reproduce the bug condition.
|
|
data["directed"] = False
|
|
graph_path = tmp_path / "graph.json"
|
|
graph_path.write_text(json.dumps(data), encoding="utf-8")
|
|
|
|
monkeypatch.setattr(mainmod, "_check_skill_version", lambda _: None)
|
|
monkeypatch.setattr(
|
|
mainmod.sys,
|
|
"argv",
|
|
["graphify", "affected", "B", "--relation", "calls", "--graph", str(graph_path)],
|
|
)
|
|
|
|
mainmod.main()
|
|
|
|
out = capsys.readouterr().out
|
|
# A (the caller) is affected by a change to B (the callee).
|
|
assert "caller_fn" in out
|
|
assert "calls" in out
|
|
# B is the query node, not an affected node, and the result is not empty.
|
|
assert "No affected nodes found." not in out
|