/* * test_edge_imports.c — Pipeline/edge-creation reproduction suite for IMPORTS * edges across all 9 hybrid-LSP languages. * * ── CONTEXT ───────────────────────────────────────────────────────────────── * This suite tests the GRAPH LEVEL (pipeline / edge-creation), NOT extraction. * A real-repo sanity check (2026-06) found IMPORTS edges ≈ 0 for several * languages even though CBMFileResult.imports IS populated at extraction time: * * Language import keyword real-repo edges status * ---------- -------------- --------------- -------- * Rust use 2168 uses → 0 BUG (expected RED) * Kotlin import 6110 → ~0 BUG (expected RED) * Java import many → 0 BUG (expected RED) * C# using many → 0 BUG (expected RED) * PHP use many → ~0 BUG (expected RED) * Python import/from working OK (expected GREEN) * TypeScript import working OK (expected GREEN) * Go import working OK (expected GREEN) * * ── WHAT THIS FILE TESTS ──────────────────────────────────────────────────── * Each test indexes a small multi-file fixture through the FULL production * pipeline (index_repository → graph DB), then asserts: * cbm_store_count_edges_by_type(store, project, "IMPORTS") >= N * * GREEN (guard) tests: Python, TypeScript, Go — these already produce IMPORTS * edges and MUST keep doing so. A RED here is a real regression. * * RED (bug reproduction) tests: Rust, Kotlin, Java, C#, PHP — the pipeline * does not yet turn extracted imports into resolved IMPORTS graph edges for * these languages. Each test should FAIL until the bug is fixed, at which * point it becomes a permanent regression guard. * * ── FIXTURE DESIGN ────────────────────────────────────────────────────────── * Every fixture uses two files in the same project: one defines a module/type, * the other imports it by the language's normal internal mechanism. Single-file * fixtures cannot produce inter-file IMPORTS edges; the import must cross files * so the resolver has a resolvable target in the same project graph. * * ── REGISTRATION ──────────────────────────────────────────────────────────── * SUITE(edge_imports) is declared here. Do NOT register it in test_main.c * (another agent owns that file); the suite runs standalone via its own runner * when linked. */ #include "../src/foundation/compat.h" #include "test_framework.h" #include "test_helpers.h" #include "cbm.h" #include #include #include #include #include #include #include #include #include /* ── Harness (mirrors test_lang_contract.c) ─────────────────────────────── */ typedef struct { char tmpdir[256]; char dbpath[512]; char *project; cbm_mcp_server_t *srv; } EILangProj; typedef struct { const char *name; /* relative filename, may include '/' for subdirs */ const char *content; } EILangFile; static void ei_to_fwd_slashes(char *p) { for (; *p; p++) { if (*p == '\\') *p = '/'; } } /* Write files, run index_repository, open graph DB. Returns NULL on failure. */ static cbm_store_t *ei_index_files(EILangProj *lp, const EILangFile *files, int nfiles) { memset(lp, 0, sizeof(*lp)); snprintf(lp->tmpdir, sizeof(lp->tmpdir), "/tmp/cbm_ei_XXXXXX"); if (!cbm_mkdtemp(lp->tmpdir)) return NULL; ei_to_fwd_slashes(lp->tmpdir); for (int i = 0; i < nfiles; i++) { char path[700]; snprintf(path, sizeof(path), "%s/%s", lp->tmpdir, files[i].name); /* Create intermediate directories for sub-path fixtures. */ char *slash = strrchr(path, '/'); if (slash && slash > path + strlen(lp->tmpdir)) { *slash = '\0'; cbm_mkdir_p(path, 0755); *slash = '/'; } FILE *f = fopen(path, "wb"); if (!f) return NULL; fputs(files[i].content, f); fclose(f); } lp->project = cbm_project_name_from_path(lp->tmpdir); if (!lp->project) return NULL; const char *home = getenv("HOME"); if (!home) home = "/tmp"; char cache_dir[512]; snprintf(cache_dir, sizeof(cache_dir), "%s/.cache/codebase-memory-mcp", home); cbm_mkdir(cache_dir); snprintf(lp->dbpath, sizeof(lp->dbpath), "%s/%s.db", cache_dir, lp->project); unlink(lp->dbpath); lp->srv = cbm_mcp_server_new(NULL); if (!lp->srv) return NULL; char args[700]; snprintf(args, sizeof(args), "{\"repo_path\":\"%s\"}", lp->tmpdir); char *resp = cbm_mcp_handle_tool(lp->srv, "index_repository", args); if (resp) free(resp); return cbm_store_open_path(lp->dbpath); } static void ei_cleanup(EILangProj *lp, cbm_store_t *store) { if (store) cbm_store_close(store); if (lp->srv) { cbm_mcp_server_free(lp->srv); lp->srv = NULL; } free(lp->project); lp->project = NULL; th_rmtree(lp->tmpdir); unlink(lp->dbpath); char wal[600], shm[600]; snprintf(wal, sizeof(wal), "%s-wal", lp->dbpath); unlink(wal); snprintf(shm, sizeof(shm), "%s-shm", lp->dbpath); unlink(shm); } /* Index `files`, check IMPORTS count >= `floor`. Dumps a diagnostic on * failure so failures are self-diagnosable without re-running manually. */ static int ei_edge_present(const EILangFile *files, int nfiles, const char *edge_type, int floor) { EILangProj lp; cbm_store_t *store = ei_index_files(&lp, files, nfiles); int got = store ? cbm_store_count_edges_by_type(store, lp.project, edge_type) : -1; if (got < floor) { fprintf(stderr, " [%s] FAIL count=%d expected>=%d\n", edge_type, got, floor); } ei_cleanup(&lp, store); return got >= floor; } /* ═══════════════════════════════════════════════════════════════════════════ * GREEN GUARD — Python * * Python `from .mod import x` and `import mod` already resolve to IMPORTS * edges via the relative-import resolver. These tests MUST stay GREEN. * ═══════════════════════════════════════════════════════════════════════════ */ /* Python: `from .util import helper` — canonical relative import. */ TEST(ei_python_relative_from_import) { static const EILangFile f[] = { {"util.py", "def helper(x):\n return x + 1\n"}, {"main.py", "from .util import helper\n\ndef run(y):\n return helper(y)\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* Python: bare `import util` (absolute, same directory). */ TEST(ei_python_absolute_import) { static const EILangFile f[] = { {"util.py", "def compute(x):\n return x * 2\n"}, {"main.py", "import util\n\ndef run(y):\n return util.compute(y)\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* Python: `from util import compute` — named absolute import. */ TEST(ei_python_from_absolute_import) { static const EILangFile f[] = { {"util.py", "def compute(x):\n return x * 2\n"}, {"main.py", "from util import compute\n\ndef run(y):\n return compute(y)\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* Python: multiple names in one `from` statement. */ TEST(ei_python_from_multi_names) { static const EILangFile f[] = { {"ops.py", "def add(a, b):\n return a + b\n\ndef mul(a, b):\n return a * b\n"}, {"client.py","from ops import add, mul\n\ndef run(x, y):\n return add(x, mul(x, y))\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* Python: aliased import `import util as u`. */ TEST(ei_python_aliased_import) { static const EILangFile f[] = { {"util.py", "def helper(x):\n return x + 1\n"}, {"main.py", "import util as u\n\ndef run(y):\n return u.helper(y)\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* Python: sub-package path `from pkg.util import fn`. */ TEST(ei_python_subpackage_import) { static const EILangFile f[] = { {"pkg/__init__.py", ""}, {"pkg/util.py", "def fn(x):\n return x\n"}, {"main.py", "from pkg.util import fn\n\ndef run(y):\n return fn(y)\n"}}; ASSERT_TRUE(ei_edge_present(f, 3, "IMPORTS", 1)); PASS(); } /* Python: wildcard `from util import *`. */ TEST(ei_python_wildcard_import) { static const EILangFile f[] = { {"util.py", "X = 42\n\ndef helper():\n return X\n"}, {"main.py", "from util import *\n\ndef run():\n return helper()\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* Python: sibling relative `from .sibling import x` in a package. */ TEST(ei_python_package_sibling_import) { static const EILangFile f[] = { {"pkg/__init__.py", ""}, {"pkg/a.py", "def alpha():\n return 1\n"}, {"pkg/b.py", "from .a import alpha\n\ndef beta():\n return alpha() + 1\n"}}; ASSERT_TRUE(ei_edge_present(f, 3, "IMPORTS", 1)); PASS(); } /* ═══════════════════════════════════════════════════════════════════════════ * GREEN GUARD — TypeScript * * TypeScript `import { x } from './mod'` already resolves via the relative- * import resolver. These tests MUST stay GREEN. * ═══════════════════════════════════════════════════════════════════════════ */ /* TypeScript: named relative import — the canonical GREEN guard. */ TEST(ei_typescript_named_relative_import) { static const EILangFile f[] = { {"util.ts", "export function helper(x: number): number { return x + 1; }\n"}, {"main.ts", "import { helper } from './util';\n\n" "export function run(y: number): number { return helper(y); }\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* TypeScript: default import `import helper from './util'`. */ TEST(ei_typescript_default_import) { static const EILangFile f[] = { {"util.ts", "export default function helper(x: number): number { return x + 1; }\n"}, {"main.ts", "import helper from './util';\n\n" "export function run(y: number): number { return helper(y); }\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* TypeScript: namespace import `import * as util from './util'`. */ TEST(ei_typescript_namespace_import) { static const EILangFile f[] = { {"util.ts", "export const VALUE = 42;\n" "export function compute(x: number): number { return x * VALUE; }\n"}, {"main.ts", "import * as util from './util';\n\n" "export function run(): number { return util.compute(util.VALUE); }\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* TypeScript: aliased named import `import { helper as h } from './util'`. */ TEST(ei_typescript_aliased_import) { static const EILangFile f[] = { {"util.ts", "export function helper(x: number): number { return x + 1; }\n"}, {"main.ts", "import { helper as h } from './util';\n\n" "export function run(y: number): number { return h(y); }\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* TypeScript: multi-name import `import { a, b } from './ops'`. */ TEST(ei_typescript_multi_names_import) { static const EILangFile f[] = { {"ops.ts", "export function add(a: number, b: number): number { return a + b; }\n" "export function mul(a: number, b: number): number { return a * b; }\n"}, {"client.ts","import { add, mul } from './ops';\n\n" "export function run(x: number, y: number): number { return add(x, mul(x, y)); }\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* TypeScript: subdirectory import `import { fn } from './pkg/util'`. */ TEST(ei_typescript_subdir_import) { static const EILangFile f[] = { {"pkg/util.ts", "export function fn(x: number): number { return x; }\n"}, {"main.ts", "import { fn } from './pkg/util';\n\n" "export function run(y: number): number { return fn(y); }\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* TypeScript: re-export `export { fn } from './util'`. */ TEST(ei_typescript_re_export) { static const EILangFile f[] = { {"util.ts", "export function fn(x: number): number { return x; }\n"}, {"index.ts", "export { fn } from './util';\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* TypeScript: `import type { T } from './types'` (type-only import). */ TEST(ei_typescript_type_import) { static const EILangFile f[] = { {"types.ts", "export interface Config { value: number; }\n"}, {"main.ts", "import type { Config } from './types';\n\n" "export function run(c: Config): number { return c.value; }\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* ═══════════════════════════════════════════════════════════════════════════ * GREEN GUARD — Go * * Go `import "mod/pkg"` resolves via the Go module resolver. These MUST * stay GREEN. * ═══════════════════════════════════════════════════════════════════════════ */ /* Go: simple same-module cross-package import. */ TEST(ei_go_same_module_import) { static const EILangFile f[] = { {"go.mod", "module example.com/demo\n\ngo 1.21\n"}, {"util/util.go", "package util\n\nfunc Helper(x int) int { return x + 1 }\n"}, {"main.go", "package main\n\nimport \"example.com/demo/util\"\n\n" "func main() { _ = util.Helper(1) }\n"}}; ASSERT_TRUE(ei_edge_present(f, 3, "IMPORTS", 1)); PASS(); } /* Go: grouped import block `import ( "pkg1"; "pkg2" )`. */ TEST(ei_go_grouped_import_block) { static const EILangFile f[] = { {"go.mod", "module example.com/grp\n\ngo 1.21\n"}, {"math/math.go", "package math\n\nfunc Add(a, b int) int { return a + b }\n"}, {"strutil/str.go","package strutil\n\nfunc Join(a, b string) string { return a + b }\n"}, {"main.go", "package main\n\nimport (\n" "\t\"example.com/grp/math\"\n" "\t\"example.com/grp/strutil\"\n)\n\n" "func main() {\n" "\t_ = math.Add(1, 2)\n" "\t_ = strutil.Join(\"a\", \"b\")\n}\n"}}; ASSERT_TRUE(ei_edge_present(f, 4, "IMPORTS", 1)); PASS(); } /* Go: aliased import `import util "example.com/demo/util"`. */ TEST(ei_go_aliased_import) { static const EILangFile f[] = { {"go.mod", "module example.com/alias\n\ngo 1.21\n"}, {"util/util.go", "package util\n\nfunc Helper(x int) int { return x + 1 }\n"}, {"main.go", "package main\n\nimport u \"example.com/alias/util\"\n\n" "func main() { _ = u.Helper(1) }\n"}}; ASSERT_TRUE(ei_edge_present(f, 3, "IMPORTS", 1)); PASS(); } /* Go: dot import `import . "example.com/demo/util"` (members into current ns). */ TEST(ei_go_dot_import) { static const EILangFile f[] = { {"go.mod", "module example.com/dot\n\ngo 1.21\n"}, {"util/util.go", "package util\n\nfunc Helper(x int) int { return x + 1 }\n"}, {"main.go", "package main\n\nimport . \"example.com/dot/util\"\n\n" "func main() { _ = Helper(1) }\n"}}; ASSERT_TRUE(ei_edge_present(f, 3, "IMPORTS", 1)); PASS(); } /* Go: sub-package path import (multi-level directory). */ TEST(ei_go_subpackage_import) { static const EILangFile f[] = { {"go.mod", "module example.com/sub\n\ngo 1.21\n"}, {"pkg/math/ops.go", "package math\n\nfunc Mul(a, b int) int { return a * b }\n"}, {"main.go", "package main\n\nimport \"example.com/sub/pkg/math\"\n\n" "func main() { _ = math.Mul(2, 3) }\n"}}; ASSERT_TRUE(ei_edge_present(f, 3, "IMPORTS", 1)); PASS(); } /* Go: blank import `import _ "example.com/demo/util"` (side-effects only). */ TEST(ei_go_blank_import) { static const EILangFile f[] = { {"go.mod", "module example.com/blank\n\ngo 1.21\n"}, {"util/util.go", "package util\n\nfunc init() {}\n"}, {"main.go", "package main\n\nimport _ \"example.com/blank/util\"\n\nfunc main() {}\n"}}; ASSERT_TRUE(ei_edge_present(f, 3, "IMPORTS", 1)); PASS(); } /* Go: two files importing the same internal package (both should yield edges). */ TEST(ei_go_two_consumers_same_package) { static const EILangFile f[] = { {"go.mod", "module example.com/two\n\ngo 1.21\n"}, {"util/util.go", "package util\n\nfunc Helper(x int) int { return x + 1 }\n"}, {"a/a.go", "package a\n\nimport \"example.com/two/util\"\n\n" "func Run() int { return util.Helper(1) }\n"}, {"b/b.go", "package b\n\nimport \"example.com/two/util\"\n\n" "func Run() int { return util.Helper(2) }\n"}}; ASSERT_TRUE(ei_edge_present(f, 4, "IMPORTS", 2)); PASS(); } /* ═══════════════════════════════════════════════════════════════════════════ * RED REPRODUCTION — Rust * * The pipeline does NOT create IMPORTS graph edges for Rust `use` declarations * even though extraction captures them. Each test below should FAIL (count=0) * until the edge-creation pipeline is fixed. * ═══════════════════════════════════════════════════════════════════════════ */ /* Rust: `mod a;` file inclusion + `use crate::a::f` cross-module use. */ TEST(ei_rust_mod_plus_use) { static const EILangFile f[] = { {"a.rs", "pub fn f(x: i32) -> i32 { x + 1 }\n"}, {"main.rs", "mod a;\nuse crate::a::f;\n\nfn main() { let _ = f(1); }\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* Rust: `use crate::util::helper` — top-level function in a sibling module. */ TEST(ei_rust_use_crate_path) { static const EILangFile f[] = { {"util.rs", "pub fn helper(x: i32) -> i32 { x * 2 }\n"}, {"main.rs", "mod util;\nuse crate::util::helper;\n\nfn main() { let _ = helper(3); }\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* Rust: grouped use `use crate::ops::{add, mul}`. */ TEST(ei_rust_grouped_use) { static const EILangFile f[] = { {"ops.rs", "pub fn add(a: i32, b: i32) -> i32 { a + b }\n" "pub fn mul(a: i32, b: i32) -> i32 { a * b }\n"}, {"main.rs", "mod ops;\nuse crate::ops::{add, mul};\n\n" "fn main() { let _ = add(mul(2, 3), 1); }\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* Rust: aliased use `use crate::util::helper as h`. */ TEST(ei_rust_aliased_use) { static const EILangFile f[] = { {"util.rs", "pub fn helper(x: i32) -> i32 { x + 1 }\n"}, {"main.rs", "mod util;\nuse crate::util::helper as h;\n\nfn run() -> i32 { h(5) }\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* Rust: pub re-export `pub use crate::util::helper`. */ TEST(ei_rust_pub_re_export) { static const EILangFile f[] = { {"util.rs", "pub fn helper(x: i32) -> i32 { x + 1 }\n"}, {"lib.rs", "mod util;\npub use crate::util::helper;\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* Rust: struct import `use crate::models::Config`. */ TEST(ei_rust_struct_use) { static const EILangFile f[] = { {"models.rs", "pub struct Config { pub value: i32 }\n"}, {"main.rs", "mod models;\nuse crate::models::Config;\n\n" "fn make() -> Config { Config { value: 1 } }\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* Rust: glob use `use crate::ops::*`. */ TEST(ei_rust_glob_use) { static const EILangFile f[] = { {"ops.rs", "pub fn add(a: i32, b: i32) -> i32 { a + b }\n" "pub fn sub(a: i32, b: i32) -> i32 { a - b }\n"}, {"main.rs", "mod ops;\nuse crate::ops::*;\n\nfn run() -> i32 { add(sub(5, 1), 2) }\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* Rust: trait import `use crate::traits::Compute`. */ TEST(ei_rust_trait_use) { static const EILangFile f[] = { {"traits.rs", "pub trait Compute { fn run(&self) -> i32; }\n"}, {"main.rs", "mod traits;\nuse crate::traits::Compute;\n\n" "struct Impl;\nimpl Compute for Impl { fn run(&self) -> i32 { 42 } }\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* ═══════════════════════════════════════════════════════════════════════════ * RED REPRODUCTION — Kotlin * * The pipeline does NOT create IMPORTS graph edges for Kotlin `import` * statements even though extraction captures them. Expected RED. * ═══════════════════════════════════════════════════════════════════════════ */ /* Kotlin: `import com.example.Util` — basic cross-file class import. */ TEST(ei_kotlin_basic_class_import) { static const EILangFile f[] = { {"Util.kt", "package com.example\n\nclass Util {\n fun greet() = \"hello\"\n}\n"}, {"Main.kt", "package com.example\n\nimport com.example.Util\n\n" "fun main() { val u = Util(); println(u.greet()) }\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* Kotlin: `import com.example.fn` — top-level function import. */ TEST(ei_kotlin_toplevel_function_import) { static const EILangFile f[] = { {"ops.kt", "package com.example\n\nfun add(a: Int, b: Int): Int = a + b\n"}, {"main.kt", "package com.example\n\nimport com.example.add\n\n" "fun run(): Int = add(1, 2)\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* Kotlin: aliased import `import com.example.Util as U`. */ TEST(ei_kotlin_aliased_import) { static const EILangFile f[] = { {"Util.kt", "package com.example\n\nclass Util {\n fun compute(x: Int) = x + 1\n}\n"}, {"Main.kt", "package com.example\n\nimport com.example.Util as U\n\n" "fun run(): Int { val u = U(); return u.compute(5) }\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* Kotlin: wildcard import `import com.example.*`. */ TEST(ei_kotlin_wildcard_import) { static const EILangFile f[] = { {"ops.kt", "package com.example\n\nfun add(a: Int, b: Int) = a + b\n" "fun mul(a: Int, b: Int) = a * b\n"}, {"main.kt", "package com.example\n\nimport com.example.*\n\n" "fun run() = add(1, mul(2, 3))\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* Kotlin: multiple imports in one file. */ TEST(ei_kotlin_multiple_imports) { static const EILangFile f[] = { {"A.kt", "package com.x\n\nclass A { fun a() = 1 }\n"}, {"B.kt", "package com.x\n\nclass B { fun b() = 2 }\n"}, {"Main.kt", "package com.x\n\nimport com.x.A\nimport com.x.B\n\n" "fun run(): Int { return A().a() + B().b() }\n"}}; ASSERT_TRUE(ei_edge_present(f, 3, "IMPORTS", 1)); PASS(); } /* Kotlin: object/companion import `import com.example.Config.DEFAULT`. */ TEST(ei_kotlin_object_member_import) { static const EILangFile f[] = { {"Config.kt", "package com.example\n\nobject Config {\n const val DEFAULT = 42\n}\n"}, {"Main.kt", "package com.example\n\nimport com.example.Config.DEFAULT\n\n" "fun run() = DEFAULT\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* Kotlin: data class import across packages. */ TEST(ei_kotlin_data_class_import) { static const EILangFile f[] = { {"model/User.kt", "package com.example.model\n\ndata class User(val name: String)\n"}, {"service/Svc.kt", "package com.example.service\n\nimport com.example.model.User\n\n" "fun greet(u: User) = \"Hello \" + u.name\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* ═══════════════════════════════════════════════════════════════════════════ * RED REPRODUCTION — Java * * The pipeline does NOT create IMPORTS graph edges for Java `import` * statements. Expected RED. * ═══════════════════════════════════════════════════════════════════════════ */ /* Java: `import com.example.Util` — basic class import. */ TEST(ei_java_basic_class_import) { static const EILangFile f[] = { {"Util.java", "package com.example;\npublic class Util {\n" " public int compute(int x) { return x + 1; }\n}\n"}, {"Main.java", "package com.example;\nimport com.example.Util;\n" "public class Main {\n" " public static void main(String[] args) {\n" " Util u = new Util();\n System.out.println(u.compute(1));\n" " }\n}\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* Java: `import com.example.util.MathOps` — utility class in sub-package. */ TEST(ei_java_subpackage_import) { static const EILangFile f[] = { {"util/MathOps.java", "package com.example.util;\n" "public class MathOps {\n" " public static int add(int a, int b) { return a + b; }\n}\n"}, {"Main.java", "package com.example;\nimport com.example.util.MathOps;\n" "public class Main {\n" " void run() { int x = MathOps.add(1, 2); }\n}\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* Java: wildcard import `import com.example.util.*`. */ TEST(ei_java_wildcard_import) { static const EILangFile f[] = { {"util/Ops.java", "package com.example.util;\n" "public class Ops { public static int add(int a,int b){return a+b;} }\n"}, {"Main.java", "package com.example;\nimport com.example.util.*;\n" "public class Main { void run() { int x = Ops.add(1, 2); } }\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* Java: static import `import static com.example.MathOps.add`. */ TEST(ei_java_static_import) { static const EILangFile f[] = { {"MathOps.java", "package com.example;\n" "public class MathOps { public static int add(int a,int b){return a+b;} }\n"}, {"Main.java", "package com.example;\nimport static com.example.MathOps.add;\n" "public class Main { void run() { int x = add(1, 2); } }\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* Java: multiple imports in one file. */ TEST(ei_java_multiple_imports) { static const EILangFile f[] = { {"A.java", "package com.x;\npublic class A { public int a() { return 1; } }\n"}, {"B.java", "package com.x;\npublic class B { public int b() { return 2; } }\n"}, {"Main.java", "package com.x;\nimport com.x.A;\nimport com.x.B;\n" "public class Main { void run() { new A().a(); new B().b(); } }\n"}}; ASSERT_TRUE(ei_edge_present(f, 3, "IMPORTS", 1)); PASS(); } /* Java: interface import across files. */ TEST(ei_java_interface_import) { static const EILangFile f[] = { {"Compute.java", "package com.example;\npublic interface Compute { int run(int x); }\n"}, {"Impl.java", "package com.example;\nimport com.example.Compute;\n" "public class Impl implements Compute {\n" " public int run(int x) { return x + 1; }\n}\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* Java: static wildcard import `import static com.example.Constants.*`. */ TEST(ei_java_static_wildcard_import) { static const EILangFile f[] = { {"Constants.java", "package com.example;\n" "public class Constants { public static final int MAX = 100; }\n"}, {"Main.java", "package com.example;\nimport static com.example.Constants.*;\n" "public class Main { void check() { int x = MAX; } }\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* ═══════════════════════════════════════════════════════════════════════════ * RED REPRODUCTION — C# * * The pipeline does NOT create IMPORTS graph edges for C# `using` directives. * Expected RED. * ═══════════════════════════════════════════════════════════════════════════ */ /* C#: `using App.Utils` — basic namespace import. */ TEST(ei_csharp_basic_using) { static const EILangFile f[] = { {"Utils.cs", "namespace App.Utils {\n" " public class Helper {\n" " public int Compute(int x) { return x + 1; }\n }\n}\n"}, {"Main.cs", "using App.Utils;\nnamespace App {\n" " class Main {\n" " void Run() { var h = new Helper(); _ = h.Compute(1); }\n }\n}\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* C#: aliased using `using H = App.Utils.Helper`. */ TEST(ei_csharp_aliased_using) { static const EILangFile f[] = { {"Utils.cs", "namespace App.Utils {\n" " public class Helper { public int Compute(int x) { return x + 1; } }\n}\n"}, {"Main.cs", "using H = App.Utils.Helper;\nnamespace App {\n" " class Main { void Run() { var h = new H(); } }\n}\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* C#: `using static App.MathOps` — static member access. */ TEST(ei_csharp_using_static) { static const EILangFile f[] = { {"MathOps.cs", "namespace App {\n" " public static class MathOps {\n" " public static int Add(int a, int b) { return a + b; }\n }\n}\n"}, {"Main.cs", "using static App.MathOps;\nnamespace App {\n" " class Main { void Run() { int x = Add(1, 2); } }\n}\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* C#: multiple using directives in one file. */ TEST(ei_csharp_multiple_usings) { static const EILangFile f[] = { {"A.cs", "namespace Com.X { public class A { public int a() { return 1; } } }\n"}, {"B.cs", "namespace Com.X { public class B { public int b() { return 2; } } }\n"}, {"Main.cs", "using Com.X;\nnamespace Com.X {\n" " class Main { void Run() { new A().a(); new B().b(); } }\n}\n"}}; ASSERT_TRUE(ei_edge_present(f, 3, "IMPORTS", 1)); PASS(); } /* C#: interface in a separate namespace imported via using. */ TEST(ei_csharp_interface_using) { static const EILangFile f[] = { {"Interfaces.cs", "namespace App.Contracts {\n" " public interface ICompute { int Run(int x); }\n}\n"}, {"Impl.cs", "using App.Contracts;\nnamespace App {\n" " public class Impl : ICompute {\n" " public int Run(int x) { return x + 1; }\n }\n}\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* C#: sub-namespace import `using App.Models.Domain`. */ TEST(ei_csharp_subnamespace_using) { static const EILangFile f[] = { {"models/User.cs", "namespace App.Models.Domain {\n" " public class User { public string Name { get; set; } }\n}\n"}, {"service/Svc.cs", "using App.Models.Domain;\nnamespace App.Service {\n" " public class UserService {\n" " public string Greet(User u) { return \"Hello \" + u.Name; }\n" " }\n}\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* C#: file-scoped namespace + using (C# 10 style). */ TEST(ei_csharp_file_scoped_namespace) { static const EILangFile f[] = { {"Ops.cs", "namespace App.Ops;\npublic static class Ops {\n" " public static int Add(int a, int b) => a + b;\n}\n"}, {"Main.cs", "using App.Ops;\nnamespace App.Main;\npublic class Main {\n" " public void Run() { int x = Ops.Add(1, 2); }\n}\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* ═══════════════════════════════════════════════════════════════════════════ * RED REPRODUCTION — PHP * * The pipeline does NOT create IMPORTS graph edges for PHP `use` statements. * Expected RED. * ═══════════════════════════════════════════════════════════════════════════ */ /* PHP: `use App\Utils\Helper` — basic namespace import. */ TEST(ei_php_basic_use) { static const EILangFile f[] = { {"Utils/Helper.php", "compute(1); }\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* PHP: aliased use `use App\Utils\Helper as H`. */ TEST(ei_php_aliased_use) { static const EILangFile f[] = { {"Utils/Helper.php", "compute(1); }\n"}}; ASSERT_TRUE(ei_edge_present(f, 2, "IMPORTS", 1)); PASS(); } /* PHP: grouped use `use App\Utils\{A, B}`. */ TEST(ei_php_grouped_use) { static const EILangFile f[] = { {"Utils/A.php", "a() + $b->b(); }\n"}}; ASSERT_TRUE(ei_edge_present(f, 3, "IMPORTS", 1)); PASS(); } /* PHP: `use function App\Utils\compute` — function import. */ TEST(ei_php_function_use) { static const EILangFile f[] = { {"Utils/funcs.php", "a() + $b->b(); }\n"}}; ASSERT_TRUE(ei_edge_present(f, 3, "IMPORTS", 1)); PASS(); } /* PHP: interface use across files. */ TEST(ei_php_interface_use) { static const EILangFile f[] = { {"Contracts/Computable.php", "