mirror of
https://github.com/safishamsi/graphify.git
synced 2026-09-23 22:15:46 +00:00
Index .psm1, anchor Swift import targets, dedupe no-cluster edges
- #1315: add .psm1 to CODE_EXTENSIONS + _DISPATCH so PowerShell modules are indexed - #1327: synthesize a module node for Swift import targets (new LanguageConfig flag synthesize_import_module_nodes) so imports edges survive build.py pruning; strengthen the Swift dangling-edge test to also assert edge targets - #1317: dedupe parallel edges by (source,target,relation) in the --no-cluster and incremental update write paths so edge counts are deterministic and `update` is idempotent Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+27
-1
@@ -2,10 +2,36 @@ import json
|
||||
from pathlib import Path
|
||||
import networkx as nx
|
||||
from networkx.readwrite import json_graph
|
||||
from graphify.build import build_from_json, build, build_merge, edge_data, edge_datas
|
||||
from graphify.build import build_from_json, build, build_merge, edge_data, edge_datas, dedupe_edges
|
||||
|
||||
FIXTURES = Path(__file__).parent / "fixtures"
|
||||
|
||||
|
||||
def test_dedupe_edges_collapses_exact_parallels():
|
||||
# #1317: --no-cluster / incremental update concatenate edge lists raw.
|
||||
edges = [
|
||||
{"source": "a", "target": "b", "relation": "calls", "source_location": "L1"},
|
||||
{"source": "a", "target": "b", "relation": "calls", "source_location": "L9"}, # dup
|
||||
{"source": "a", "target": "b", "relation": "imports"}, # different relation: kept
|
||||
{"source": "b", "target": "c", "relation": "calls"},
|
||||
]
|
||||
out = dedupe_edges(edges)
|
||||
keys = [(e["source"], e["target"], e["relation"]) for e in out]
|
||||
assert keys == [("a", "b", "calls"), ("a", "b", "imports"), ("b", "c", "calls")]
|
||||
# first occurrence wins (keeps L1, not L9)
|
||||
assert out[0]["source_location"] == "L1"
|
||||
|
||||
|
||||
def test_dedupe_edges_is_idempotent():
|
||||
edges = [
|
||||
{"source": "a", "target": "b", "relation": "calls"},
|
||||
{"source": "a", "target": "b", "relation": "calls"},
|
||||
]
|
||||
once = dedupe_edges(edges)
|
||||
twice = dedupe_edges(once + edges) # simulate a second `update` re-concatenating
|
||||
assert len(once) == 1
|
||||
assert len(twice) == 1
|
||||
|
||||
def load_extraction():
|
||||
return json.loads((FIXTURES / "extraction.json").read_text())
|
||||
|
||||
|
||||
@@ -11,6 +11,10 @@ def test_classify_python():
|
||||
def test_classify_typescript():
|
||||
assert classify_file(Path("bar.ts")) == FileType.CODE
|
||||
|
||||
def test_classify_powershell_module():
|
||||
# #1315: .psm1 modules were never indexed (CODE_EXTENSIONS gap).
|
||||
assert classify_file(Path("Utils.psm1")) == FileType.CODE
|
||||
|
||||
def test_classify_markdown():
|
||||
assert classify_file(Path("README.md")) == FileType.DOCUMENT
|
||||
|
||||
|
||||
@@ -596,6 +596,28 @@ def test_swift_no_dangling_edges():
|
||||
node_ids = {n["id"] for n in r["nodes"]}
|
||||
for e in r["edges"]:
|
||||
assert e["source"] in node_ids
|
||||
# #1327: targets must resolve to a node too, else build.py prunes the edge.
|
||||
assert e["target"] in node_ids, f"dangling target {e['target']} ({e['relation']})"
|
||||
|
||||
|
||||
def test_swift_imports_survive_build():
|
||||
# #1327: `import Foundation` / `import UIKit` previously emitted edges to bare
|
||||
# module ids with no backing node, so build.py dropped 100% of Swift imports.
|
||||
from graphify.build import build_from_json
|
||||
r = extract_swift(FIXTURES / "sample.swift")
|
||||
import_edges = [e for e in r["edges"] if e["relation"] == "imports"]
|
||||
assert import_edges, "extractor should emit Swift import edges"
|
||||
node_ids = {n["id"] for n in r["nodes"]}
|
||||
for e in import_edges:
|
||||
assert e["target"] in node_ids # synthesized module node exists
|
||||
# No private bookkeeping key should leak into output edges.
|
||||
assert all("_import_label" not in e for e in r["edges"])
|
||||
# Edges must survive the build (which prunes edges with unknown endpoints).
|
||||
G = build_from_json(r)
|
||||
surviving = [
|
||||
(u, v) for u, v, d in G.edges(data=True) if d.get("relation") == "imports"
|
||||
]
|
||||
assert surviving, "Swift import edges must survive build_from_json (#1327)"
|
||||
|
||||
def test_swift_finds_actor():
|
||||
r = extract_swift(FIXTURES / "sample.swift")
|
||||
@@ -1005,6 +1027,20 @@ def test_powershell_no_error():
|
||||
assert "error" not in r
|
||||
|
||||
|
||||
def test_powershell_psm1_dispatched_and_extracted(tmp_path):
|
||||
# #1315: .psm1 modules were never indexed — no dispatch entry, no CODE_EXTENSIONS.
|
||||
from graphify.extract import _get_extractor
|
||||
mod = tmp_path / "Utils.psm1"
|
||||
mod.write_text(
|
||||
"function Get-Greeting { param([string]$Name) return \"Hi $Name\" }\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
assert _get_extractor(mod) is extract_powershell
|
||||
r = extract_powershell(mod)
|
||||
assert "error" not in r
|
||||
assert any("Get-Greeting" in n["label"] for n in r["nodes"])
|
||||
|
||||
|
||||
def test_powershell_finds_class_and_method():
|
||||
r = extract_powershell(FIXTURES / "sample.ps1")
|
||||
labels = [n["label"] for n in r["nodes"]]
|
||||
|
||||
Reference in New Issue
Block a user