"""Tests for language extractors: Java, C, C++, Ruby, C#, Kotlin, Scala, PHP, Swift.""" from __future__ import annotations from pathlib import Path import pytest from graphify.extract import ( extract_java, extract_c, extract_cpp, extract_ruby, extract_csharp, extract_kotlin, extract_scala, extract_php, extract_swift, ) FIXTURES = Path(__file__).parent / "fixtures" def _labels(r): return [n["label"] for n in r["nodes"]] def _relations(r): return {e["relation"] for e in r["edges"]} def _calls(r): node_by_id = {n["id"]: n["label"] for n in r["nodes"]} return { (node_by_id.get(e["source"], e["source"]), node_by_id.get(e["target"], e["target"])) for e in r["edges"] if e["relation"] == "calls" } # ── Java ────────────────────────────────────────────────────────────────────── def test_java_no_error(): r = extract_java(FIXTURES / "sample.java") assert "error" not in r def test_java_finds_class(): r = extract_java(FIXTURES / "sample.java") assert any("DataProcessor" in l for l in _labels(r)) def test_java_finds_interface(): r = extract_java(FIXTURES / "sample.java") assert any("Processor" in l for l in _labels(r)) def test_java_finds_methods(): r = extract_java(FIXTURES / "sample.java") labels = _labels(r) assert any("addItem" in l for l in labels) assert any("process" in l for l in labels) def test_java_finds_imports(): r = extract_java(FIXTURES / "sample.java") assert "imports" in _relations(r) def test_java_no_dangling_edges(): r = extract_java(FIXTURES / "sample.java") node_ids = {n["id"] for n in r["nodes"]} for e in r["edges"]: assert e["source"] in node_ids # ── C ──────────────────────────────────────────────────────────────────────── def test_c_no_error(): r = extract_c(FIXTURES / "sample.c") assert "error" not in r def test_c_finds_functions(): r = extract_c(FIXTURES / "sample.c") labels = _labels(r) assert any("process" in l for l in labels) assert any("main" in l for l in labels) def test_c_finds_includes(): r = extract_c(FIXTURES / "sample.c") assert "imports" in _relations(r) def test_c_emits_calls(): r = extract_c(FIXTURES / "sample.c") assert any(e["relation"] == "calls" for e in r["edges"]) def test_c_calls_are_inferred(): r = extract_c(FIXTURES / "sample.c") for e in r["edges"]: if e["relation"] == "calls": assert e["confidence"] == "INFERRED" # ── C++ ─────────────────────────────────────────────────────────────────────── def test_cpp_no_error(): r = extract_cpp(FIXTURES / "sample.cpp") assert "error" not in r def test_cpp_finds_class(): r = extract_cpp(FIXTURES / "sample.cpp") assert any("HttpClient" in l for l in _labels(r)) def test_cpp_finds_methods(): r = extract_cpp(FIXTURES / "sample.cpp") labels = _labels(r) # C++ extractor captures the constructor and public-visible methods assert any("HttpClient" in l for l in labels) def test_cpp_finds_includes(): r = extract_cpp(FIXTURES / "sample.cpp") assert "imports" in _relations(r) # ── Ruby ───────────────────────────────────────────────────────────────────── def test_ruby_no_error(): r = extract_ruby(FIXTURES / "sample.rb") assert "error" not in r def test_ruby_finds_class(): r = extract_ruby(FIXTURES / "sample.rb") assert any("ApiClient" in l for l in _labels(r)) def test_ruby_finds_methods(): r = extract_ruby(FIXTURES / "sample.rb") labels = _labels(r) assert any("get" in l for l in labels) assert any("post" in l for l in labels) def test_ruby_finds_function(): r = extract_ruby(FIXTURES / "sample.rb") assert any("parse_response" in l for l in _labels(r)) # ── C# ─────────────────────────────────────────────────────────────────────── def test_csharp_no_error(): r = extract_csharp(FIXTURES / "sample.cs") assert "error" not in r def test_csharp_finds_class(): r = extract_csharp(FIXTURES / "sample.cs") assert any("DataProcessor" in l for l in _labels(r)) def test_csharp_finds_interface(): r = extract_csharp(FIXTURES / "sample.cs") assert any("IProcessor" in l for l in _labels(r)) def test_csharp_finds_methods(): r = extract_csharp(FIXTURES / "sample.cs") labels = _labels(r) assert any("Process" in l for l in labels) def test_csharp_finds_usings(): r = extract_csharp(FIXTURES / "sample.cs") assert "imports" in _relations(r) # ── Kotlin ─────────────────────────────────────────────────────────────────── def test_kotlin_no_error(): r = extract_kotlin(FIXTURES / "sample.kt") assert "error" not in r def test_kotlin_finds_class(): r = extract_kotlin(FIXTURES / "sample.kt") assert any("HttpClient" in l for l in _labels(r)) def test_kotlin_finds_data_class(): r = extract_kotlin(FIXTURES / "sample.kt") assert any("Config" in l for l in _labels(r)) def test_kotlin_finds_methods(): r = extract_kotlin(FIXTURES / "sample.kt") labels = _labels(r) assert any("get" in l for l in labels) assert any("post" in l for l in labels) def test_kotlin_finds_function(): r = extract_kotlin(FIXTURES / "sample.kt") assert any("createClient" in l for l in _labels(r)) # ── Scala ───────────────────────────────────────────────────────────────────── def test_scala_no_error(): r = extract_scala(FIXTURES / "sample.scala") assert "error" not in r def test_scala_finds_class(): r = extract_scala(FIXTURES / "sample.scala") assert any("HttpClient" in l for l in _labels(r)) def test_scala_finds_object(): r = extract_scala(FIXTURES / "sample.scala") assert any("HttpClientFactory" in l for l in _labels(r)) def test_scala_finds_methods(): r = extract_scala(FIXTURES / "sample.scala") labels = _labels(r) assert any("get" in l for l in labels) assert any("post" in l for l in labels) # ── PHP ─────────────────────────────────────────────────────────────────────── def test_php_no_error(): r = extract_php(FIXTURES / "sample.php") assert "error" not in r def test_php_finds_class(): r = extract_php(FIXTURES / "sample.php") assert any("ApiClient" in l for l in _labels(r)) def test_php_finds_methods(): r = extract_php(FIXTURES / "sample.php") labels = _labels(r) assert any("get" in l for l in labels) assert any("post" in l for l in labels) def test_php_finds_function(): r = extract_php(FIXTURES / "sample.php") assert any("parseResponse" in l for l in _labels(r)) def test_php_finds_imports(): r = extract_php(FIXTURES / "sample.php") assert "imports" in _relations(r) # ── Swift ──────────────────────────────────────────────────────────────────── def test_swift_no_error(): r = extract_swift(FIXTURES / "sample.swift") assert "error" not in r def test_swift_finds_class(): r = extract_swift(FIXTURES / "sample.swift") assert any("DataProcessor" in l for l in _labels(r)) def test_swift_finds_protocol(): r = extract_swift(FIXTURES / "sample.swift") assert any("Processor" in l for l in _labels(r)) def test_swift_finds_struct(): r = extract_swift(FIXTURES / "sample.swift") assert any("Config" in l for l in _labels(r)) def test_swift_finds_methods(): r = extract_swift(FIXTURES / "sample.swift") labels = _labels(r) assert any("addItem" in l for l in labels) assert any("process" in l for l in labels) def test_swift_finds_function(): r = extract_swift(FIXTURES / "sample.swift") assert any("createProcessor" in l for l in _labels(r)) def test_swift_finds_imports(): r = extract_swift(FIXTURES / "sample.swift") assert "imports" in _relations(r) def test_swift_no_dangling_edges(): r = extract_swift(FIXTURES / "sample.swift") node_ids = {n["id"] for n in r["nodes"]} for e in r["edges"]: assert e["source"] in node_ids def test_swift_finds_actor(): r = extract_swift(FIXTURES / "sample.swift") assert any("CacheManager" in l for l in _labels(r)) def test_swift_finds_enum(): r = extract_swift(FIXTURES / "sample.swift") assert any("NetworkError" in l for l in _labels(r)) def test_swift_finds_enum_methods(): r = extract_swift(FIXTURES / "sample.swift") assert any("describe" in l for l in _labels(r)) def test_swift_finds_enum_cases(): r = extract_swift(FIXTURES / "sample.swift") labels = _labels(r) assert any("timeout" in l for l in labels) assert any("connectionFailed" in l for l in labels) def test_swift_enum_cases_have_case_of_edge(): r = extract_swift(FIXTURES / "sample.swift") case_edges = [e for e in r["edges"] if e["relation"] == "case_of"] assert len(case_edges) >= 2 def test_swift_finds_deinit(): r = extract_swift(FIXTURES / "sample.swift") assert any("deinit" in l for l in _labels(r)) def test_swift_finds_subscript(): r = extract_swift(FIXTURES / "sample.swift") assert any("subscript" in l for l in _labels(r)) def test_swift_extension_methods_attach_to_type(): r = extract_swift(FIXTURES / "sample.swift") node_by_id = {n["id"]: n["label"] for n in r["nodes"]} method_edges = [e for e in r["edges"] if e["relation"] == "method"] found = False for e in method_edges: src_label = node_by_id.get(e["source"], "") tgt_label = node_by_id.get(e["target"], "") if "Config" in src_label and "isValid" in tgt_label: found = True break assert found, "extension method isValid should attach to Config" def test_swift_extension_does_not_duplicate_type_node(): r = extract_swift(FIXTURES / "sample.swift") config_nodes = [n for n in r["nodes"] if n["label"] == "Config"] assert len(config_nodes) == 1, f"Config should appear once, got {len(config_nodes)}" def test_swift_conformance_edge(): r = extract_swift(FIXTURES / "sample.swift") inherits_edges = [e for e in r["edges"] if e["relation"] == "inherits"] node_by_id = {n["id"]: n["label"] for n in r["nodes"]} found = False for e in inherits_edges: src_label = node_by_id.get(e["source"], "") tgt_label = node_by_id.get(e["target"], "") if "DataProcessor" in src_label and "Processor" in tgt_label: found = True break assert found, "DataProcessor should have inherits edge to Processor" def test_swift_extension_conformance_edge(): r = extract_swift(FIXTURES / "sample.swift") inherits_edges = [e for e in r["edges"] if e["relation"] == "inherits"] node_by_id = {n["id"]: n["label"] for n in r["nodes"]} found = False for e in inherits_edges: src_label = node_by_id.get(e["source"], "") tgt_label = node_by_id.get(e["target"], "") if "DataProcessor" in src_label and "Loggable" in tgt_label: found = True break assert found, "extension should add conformance edge DataProcessor -> Loggable" def test_swift_emits_calls(): r = extract_swift(FIXTURES / "sample.swift") calls = _calls(r) assert any("process" in src and "validate" in tgt for src, tgt in calls)