From 282afaac7df502172cd83a90cc7881d12bb342f4 Mon Sep 17 00:00:00 2001 From: Safi Date: Fri, 29 May 2026 14:07:03 +0100 Subject: [PATCH] add missing test coverage for #1077 scope guard and markdown fence fixes Co-Authored-By: Claude Sonnet 4.6 --- tests/test_languages.py | 75 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/tests/test_languages.py b/tests/test_languages.py index 5cb8502d..fefc1380 100644 --- a/tests/test_languages.py +++ b/tests/test_languages.py @@ -1110,6 +1110,45 @@ def test_js_local_const_does_not_emit_phantom_node(tmp_path): assert "exportedConst" in labels, f"exported const 'exportedConst' missing: {labels}" +def test_js_module_level_arrow_produces_node_and_call_edges(tmp_path): + """Module-level arrow functions must still emit a node and capture their calls (#1077). + + The scope guard must not accidentally suppress top-level arrow functions. + """ + src = ( + "function helper() { return 1; }\n" + "const handler = () => {\n" + " helper();\n" + "};\n" + ) + f = tmp_path / "arrows.js" + f.write_text(src) + r = extract_js(f) + labels = _labels(r) + relations = _relations(r) + + assert any("handler" in l for l in labels), f"module-level arrow 'handler' missing: {labels}" + assert "calls" in relations, f"expected 'calls' edge from handler->helper: {relations}" + + +def test_ts_local_const_does_not_emit_phantom_node(tmp_path): + """Scope guard applies to TypeScript files too (shared _js_extra_walk path).""" + src = ( + "describe('suite', () => {\n" + " const inner: Set = new Set([1, 2]);\n" + "});\n" + "\n" + "export const topLevel = { a: 1 };\n" + ) + f = tmp_path / "scope_guard.ts" + f.write_text(src) + r = extract_js(f) + labels = _labels(r) + + assert "inner" not in labels, f"phantom TS node for arrow-body local 'inner': {labels}" + assert "topLevel" in labels, f"module-level TS const 'topLevel' missing: {labels}" + + # ── Markdown ───────────────────────────────────────────────────────────────── from graphify.extract import extract_markdown @@ -1150,7 +1189,41 @@ def test_markdown_contains_edges(): r = extract_markdown(FIXTURES / "deploy_guide.md") assert "contains" in _relations(r) contains_edges = [e for e in r["edges"] if e["relation"] == "contains"] - assert len(contains_edges) >= 4 # file->h1, h1->h2s, h2->h3 + # deploy_guide.md has: file->h1, h1->h2(Prerequisites), h1->h2(Full Deploy), + # h2(Full Deploy)->h3(Database Migration), h1->h2(Rollback) = 5 edges + assert len(contains_edges) >= 5, f"expected >= 5 contains edges, got {len(contains_edges)}" + + +def test_markdown_fenced_heading_not_parsed(): + """A '## heading' inside a fenced block must not produce a heading node (#1077). + + The fence-toggle skips over fenced contents so interior markdown syntax + is not misread as document structure. + """ + import tempfile, os + src = ( + "# Real Heading\n" + "\n" + "```bash\n" + "## Not A Heading\n" + "echo hello\n" + "```\n" + "\n" + "## Another Real Heading\n" + ) + with tempfile.NamedTemporaryFile(suffix=".md", mode="w", delete=False) as fh: + fh.write(src) + fpath = fh.name + try: + r = extract_markdown(Path(fpath)) + labels = _labels(r) + finally: + os.unlink(fpath) + + assert any("Real Heading" in l for l in labels), f"'Real Heading' missing: {labels}" + assert any("Another Real Heading" in l for l in labels), f"'Another Real Heading' missing: {labels}" + assert not any("Not A Heading" in l for l in labels), \ + f"fenced '## Not A Heading' was incorrectly parsed as a node: {labels}" def test_markdown_no_dangling_edges(): r = extract_markdown(FIXTURES / "deploy_guide.md")