import assert from "node:assert/strict"; import { mkdtemp, mkdir, symlink, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import test from "node:test"; import { importSource } from "./index.js"; import { validateDiagramIR } from "../../model/diagram-ir.js"; const textInput = (sourceKind: Parameters[0]["sourceKind"], path: string, text: string) => importSource({ sourceKind, path, input: { type: "text", text } }); test("imports Python modules with exact line provenance", async () => { const result = await textInput("python", "src/app.py", "import os\nfrom pkg.db import Client\n"); assert.deepEqual(result.diagram.pages[0].nodes.map((n) => [n.label, n.kind]), [ ["src/app.py", "module"], ["os", "library"], ["pkg.db", "library"], ]); assert.equal(result.diagram.pages[0].edges[1].provenance?.line, 2); assert.equal(validateDiagramIR(result.diagram), result.diagram); }); test("imports JavaScript and TypeScript static and require dependencies", async () => { const js = await textInput("javascript", "src/app.js", "import express from 'express';\nconst x = require('./local.js');\n"); const ts = await textInput("typescript", "src/app.ts", "export { x } from '@scope/pkg';\nimport type { T } from './types.js';\n"); assert.deepEqual(js.diagram.pages[0].nodes.slice(1).map((n) => n.label), ["express", "./local.js"]); assert.deepEqual(ts.diagram.pages[0].nodes.slice(1).map((n) => n.label), ["@scope/pkg", "./types.js"]); assert.ok(js.diagram.pages[0].nodes.every((n) => ["module", "library"].includes(n.kind ?? ""))); }); test("JavaScript scanner ignores comments and strings while preserving real static imports", async () => { const source = [ "// import commented from 'commented';", "/* require('blocked');", " export { x } from 'also-blocked'; */", "const text = \"import fake from 'inside-string'\";", "const template = `require('inside-template')`;", "const pattern = /import regexFake from 'inside-regex'/;", "loader.require('member-call');", "const metadata = { import: 'property', from: 'property-from' };", "import real from 'real';", "export { thing } from './actual.js';", "const required = require(\"required\");", "import type {", " Value", "} from './types.js';", "// const dynamic = import(name);", ].join("\n"); const result = await textInput("typescript", "src/app.ts", source); assert.deepEqual(result.diagram.pages[0].nodes.slice(1).map((node) => node.label), ["real", "./actual.js", "required", "./types.js"]); assert.deepEqual(result.diagnostics, []); }); test("JavaScript scanner treats arrow-expression regex literals as inert without hiding division or imports", async () => { const source = [ "const matcher = () => /require('phantom')/;", "const ratio = numerator / denominator;", "import real from 'real';", "const actual = require('./actual.js');", ].join("\n"); const result = await textInput("typescript", "src/regex.ts", source); assert.deepEqual(result.diagram.pages[0].nodes.slice(1).map((node) => node.label), ["real", "./actual.js"]); assert.deepEqual(result.diagnostics, []); }); test("JavaScript scanner diagnoses unsupported dynamic and unterminated constructs", async () => { const result = await textInput("javascript", "src/app.js", "const dynamic = import(name);\nconst template = `${load(name)}`;\n/* unterminated"); assert.ok(result.diagnostics.length >= 3); assert.ok(result.diagnostics.every((diagnostic) => diagnostic.code === "unknown-construct")); }); test("imports Go single and grouped imports", async () => { const result = await textInput("go", "cmd/main.go", "package main\nimport \"fmt\"\nimport (\n alias \"example.com/acme/lib\"\n _ \"net/http/pprof\"\n)\n"); assert.deepEqual(result.diagram.pages[0].nodes.slice(1).map((n) => n.label), ["fmt", "example.com/acme/lib", "net/http/pprof"]); assert.equal(result.diagram.pages[0].edges[2].provenance?.line, 5); }); test("imports Rust mod declarations and use roots without guessing macros", async () => { const result = await textInput("rust", "src/lib.rs", "mod api;\nuse crate::db::Pool;\nuse serde::{Serialize, Deserialize};\ncustom!(unknown);\n"); assert.deepEqual(result.diagram.pages[0].nodes.slice(1).map((n) => n.label), ["api", "crate::db", "serde"]); assert.equal(result.diagnostics[0].code, "unknown-construct"); }); test("imports Terraform resources and explicit references", async () => { const result = await textInput("terraform", "main.tf", "resource \"aws_vpc\" \"main\" {}\nresource \"aws_subnet\" \"web\" {\n vpc_id = aws_vpc.main.id\n}\n"); assert.deepEqual(result.diagram.pages[0].nodes.map((n) => n.label), ["aws_subnet.web", "aws_vpc.main"]); assert.deepEqual(result.diagram.pages[0].edges.map((e) => [e.source, e.target, e.kind]), [["resource-6177735f7375626e65742e776562", "resource-6177735f7670632e6d61696e", "reference"]]); assert.equal(result.diagram.pages[0].edges[0].provenance?.line, 3); }); test("non-JavaScript scanners ignore inert constructs and diagnose unterminated regions", async () => { const [python, unterminatedPython, go, rust, sql, terraform] = await Promise.all([ textInput("python", "app.py", "'''\nimport phantom\n'''\nimport real\n"), textInput("python", "broken.py", "\"\"\"unterminated\nimport phantom\n"), textInput("go", "main.go", "package main\n/*\nimport \"phantom\"\n*/\nimport \"fmt\"\n/* unterminated\n"), textInput("rust", "lib.rs", "/*\nmod phantom;\nuse fake::Thing;\n*/\nmod api;\nuse serde::Serialize;\n/* unterminated\n"), textInput("sql", "schema.sql", "/*\nCREATE TABLE phantom (id INT REFERENCES ghost(id));\n*/\nCREATE TABLE real (id INT);\n/* unterminated\n"), textInput("terraform", "main.tf", [ "/*", "resource \"aws_vpc\" \"phantom\" {}", "*/", "resource \"aws_vpc\" \"main\" {}", "resource \"aws_subnet\" \"web\" {", " description = \"aws_vpc.phantom.id\"", " # fake = aws_vpc.phantom.id", " vpc_id = aws_vpc.main.id", "}", "/* unterminated", ].join("\n")), ]); assert.deepEqual(python.diagram.pages[0].nodes.map((node) => node.label), ["app.py", "real"]); assert.deepEqual(unterminatedPython.diagram.pages[0].nodes.map((node) => node.label), ["broken.py"]); assert.deepEqual(go.diagram.pages[0].nodes.map((node) => node.label), ["main.go", "fmt"]); assert.deepEqual(rust.diagram.pages[0].nodes.map((node) => node.label), ["lib.rs", "api", "serde"]); assert.deepEqual(sql.diagram.pages[0].nodes.map((node) => node.label), ["real"]); assert.deepEqual(terraform.diagram.pages[0].nodes.map((node) => node.label), ["aws_subnet.web", "aws_vpc.main"]); assert.equal(terraform.diagram.pages[0].edges.length, 1); for (const result of [unterminatedPython, go, rust, sql, terraform]) { assert.ok(result.diagnostics.some((diagnostic) => diagnostic.code === "unknown-construct")); } }); test("multiline literals mask phantom topology and diagnose only unterminated regions", async () => { const [go, rust, sql, terraform] = await Promise.all([ textInput("go", "main.go", [ "package main", "var first = `", "import \"phantom\"", "`", "import \"fmt\"", ].join("\n")), textInput("rust", "lib.rs", [ "const FIRST: &str = r\"", "mod phantom_one;", "\";", "const SECOND: &str = r##\"", "use phantom_two::Thing;", "\"##;", "mod api;", "use serde::Serialize;", ].join("\n")), textInput("sql", "schema.sql", [ "CREATE TABLE real (id INT);", "INSERT INTO real(note) VALUES ('", "CREATE TABLE phantom (id INT REFERENCES ghost(id));", "escaped '' quote", "');", ].join("\n")), textInput("terraform", "main.tf", [ "resource \"aws_vpc\" \"main\" {}", "resource \"aws_subnet\" \"web\" {", " user_data = <<-EOT", " resource \"aws_instance\" \"phantom\" {}", " aws_vpc.phantom.id", " EOT", " vpc_id = aws_vpc.main.id", "}", ].join("\n")), ]); assert.deepEqual(go.diagram.pages[0].nodes.map((node) => node.label), ["main.go", "fmt"]); assert.deepEqual(rust.diagram.pages[0].nodes.map((node) => node.label), ["lib.rs", "api", "serde"]); assert.deepEqual(sql.diagram.pages[0].nodes.map((node) => node.label), ["real"]); assert.deepEqual(terraform.diagram.pages[0].nodes.map((node) => node.label), ["aws_subnet.web", "aws_vpc.main"]); assert.equal(terraform.diagram.pages[0].edges.length, 1); for (const result of [go, rust, sql, terraform]) assert.deepEqual(result.diagnostics, []); const unterminated = await Promise.all([ textInput("go", "broken.go", "package main\nvar value = `unterminated\nimport \"phantom\""), textInput("rust", "broken.rs", "const VALUE: &str = r###\"unterminated\nmod phantom;"), textInput("sql", "broken.sql", "CREATE TABLE real (id INT);\nINSERT INTO real(note) VALUES ('unterminated\nCREATE TABLE phantom (id INT);"), textInput("terraform", "broken.tf", "resource \"aws_vpc\" \"main\" {}\nvalue = < node.label.includes("phantom")), false); assert.deepEqual(result.diagnostics.map((diagnostic) => diagnostic.code), ["unknown-construct"]); } }); test("imports Kubernetes relationships and redacts Secret payloads", async () => { const objects = [ { apiVersion: "v1", kind: "Secret", metadata: { name: "db-secret" }, data: { password: "c2VjcmV0" }, stringData: { token: "plain" } }, { apiVersion: "apps/v1", kind: "Deployment", metadata: { name: "api" }, spec: { selector: { matchLabels: { app: "api" } }, template: { metadata: { labels: { app: "api" } }, spec: { containers: [{ envFrom: [{ secretRef: { name: "db-secret" } }] }] } } } }, { apiVersion: "v1", kind: "Service", metadata: { name: "api-svc" }, spec: { selector: { app: "api" } } }, ]; const result = await importSource({ sourceKind: "kubernetes", path: "k8s.json", input: { type: "object", value: objects } }); assert.deepEqual(result.diagram.pages[0].nodes.map((n) => n.label), ["Deployment/default/api", "Secret/default/db-secret", "Service/default/api-svc"]); assert.deepEqual(result.diagram.pages[0].edges.map((e) => e.kind), ["uses-secret", "selects"]); assert.equal(JSON.stringify(result).includes("c2VjcmV0"), false); assert.equal(JSON.stringify(result).includes("plain"), false); assert.equal(result.diagnostics[0].code, "secret-redacted"); }); test("Kubernetes ignores secretRef-shaped data outside supported workload fields", async () => { const objects = [ { kind: "Secret", metadata: { name: "real" } }, { kind: "Secret", metadata: { name: "phantom" } }, { kind: "Deployment", metadata: { name: "api" }, status: { secretRef: { name: "phantom" } }, spec: { template: { spec: { containers: [{ envFrom: [{ secretRef: { name: "real" } }] }] } } }, }, ]; const result = await importSource({ sourceKind: "kubernetes", path: "k8s.json", input: { type: "object", value: objects } }); const labelById = new Map(result.diagram.pages[0].nodes.map((node) => [node.id, node.label])); assert.deepEqual(result.diagram.pages[0].edges.map((edge) => [edge.kind, labelById.get(edge.target)]), [ ["uses-secret", "Secret/default/real"], ]); }); test("scopes Kubernetes identity, secret references, and non-empty Service selectors by namespace", async () => { const objects = [ { kind: "Secret", metadata: { name: "shared" } }, { kind: "Secret", metadata: { namespace: "team-b", name: "shared" } }, { kind: "Deployment", metadata: { name: "api" }, spec: { selector: { matchLabels: { selectorOnly: "wrong" } }, template: { metadata: { labels: { app: "api" } }, spec: { containers: [{ envFrom: [{ secretRef: { name: "shared" } }] }] } } } }, { kind: "Deployment", metadata: { namespace: "team-b", name: "api" }, spec: { template: { metadata: { labels: { app: "api" } }, spec: { containers: [{ envFrom: [{ secretRef: { name: "shared" } }] }] } } } }, { kind: "Deployment", metadata: { name: "selector-only" }, spec: { selector: { matchLabels: { app: "api" } }, template: { metadata: { labels: { app: "other" } } } } }, { kind: "Service", metadata: { name: "api" }, spec: { selector: { app: "api" } } }, { kind: "Service", metadata: { name: "empty" }, spec: { selector: {} } }, ]; const result = await importSource({ sourceKind: "kubernetes", path: "k8s.json", input: { type: "object", value: objects } }); const nodes = result.diagram.pages[0].nodes; assert.equal(new Set(nodes.map((node) => node.id)).size, nodes.length); assert.ok(nodes.some((node) => node.label === "Secret/default/shared")); assert.ok(nodes.some((node) => node.label === "Secret/team-b/shared")); const labelById = new Map(nodes.map((node) => [node.id, node.label])); const relationships = result.diagram.pages[0].edges.map((edge) => [labelById.get(edge.source), edge.kind, labelById.get(edge.target)]); assert.deepEqual(relationships, [ ["Deployment/default/api", "uses-secret", "Secret/default/shared"], ["Deployment/team-b/api", "uses-secret", "Secret/team-b/shared"], ["Service/default/api", "selects", "Deployment/default/api"], ]); }); test("imports Docker Compose service dependencies and named volumes", async () => { const result = await importSource({ sourceKind: "docker-compose", path: "compose.json", input: { type: "object", value: { services: { api: { depends_on: ["db"], volumes: ["data:/var/lib/app"] }, db: { image: "postgres" } }, volumes: { data: {} }, } } }); assert.deepEqual(result.diagram.pages[0].nodes.map((n) => [n.label, n.kind]), [["api", "service"], ["db", "service"], ["data", "volume"]]); assert.deepEqual(result.diagram.pages[0].edges.map((e) => e.kind), ["depends-on", "mounts"]); }); test("imports SQL tables and foreign keys", async () => { const result = await textInput("sql", "schema.sql", "CREATE TABLE users (id INT PRIMARY KEY);\nCREATE TABLE orders (\n user_id INT,\n FOREIGN KEY (user_id) REFERENCES users(id)\n);\n"); assert.deepEqual(result.diagram.pages[0].nodes.map((n) => n.label), ["orders", "users"]); assert.deepEqual(result.diagram.pages[0].edges.map((e) => [e.source, e.target, e.kind]), [["table-6f7264657273", "table-7573657273", "foreign-key"]]); assert.equal(result.diagram.pages[0].edges[0].provenance?.line, 4); }); test("orders importer output by Unicode code point without locale-sensitive comparison", async () => { const sql = await textInput("sql", "schema.sql", "CREATE TABLE a (id INT);\nCREATE TABLE Z (id INT);\n"); assert.deepEqual(sql.diagram.pages[0].nodes.map((node) => node.label), ["Z", "a"]); const kubernetes = await importSource({ sourceKind: "kubernetes", path: "k8s.json", input: { type: "object", value: [ { kind: "Service", metadata: { name: "a" }, spec: { selector: {} } }, { kind: "Service", metadata: { name: "Z" }, spec: { selector: {} } }, { kind: "Service", metadata: { name: "😀" }, spec: { selector: {} } }, { kind: "Service", metadata: { name: "\uE000" }, spec: { selector: {} } }, ] } }); assert.deepEqual(kubernetes.diagram.pages[0].nodes.map((node) => node.label), ["Service/default/Z", "Service/default/a", "Service/default/\uE000", "Service/default/😀"]); }); test("imports OpenAPI operations and schema references", async () => { const result = await importSource({ sourceKind: "openapi", path: "openapi.json", input: { type: "object", value: { openapi: "3.1.0", paths: { "/pets": { get: { operationId: "listPets", responses: { "200": { content: { "application/json": { schema: { $ref: "#/components/schemas/Pet" } } } } } } } }, components: { schemas: { Pet: { type: "object" } } }, } } }); assert.deepEqual(result.diagram.pages[0].nodes.map((n) => [n.label, n.kind]), [["GET /pets", "command"], ["Pet", "library"]]); assert.equal(result.diagram.pages[0].edges[0].kind, "schema-reference"); }); test("OpenAPI inherited schema names remain unknown references", async () => { const result = await importSource({ sourceKind: "openapi", path: "openapi.json", input: { type: "object", value: { paths: { "/unsafe": { get: { responses: { "200": { content: { "application/json": { schema: { $ref: "#/components/schemas/toString" } } } } } } } }, components: { schemas: {} }, } } }); assert.deepEqual(result.diagram.pages[0].nodes.map((node) => node.label), ["GET /unsafe"]); assert.deepEqual(result.diagram.pages[0].edges, []); assert.deepEqual(result.diagnostics.map((diagnostic) => diagnostic.code), ["unknown-reference"]); }); test("imports CI job DAG needs", async () => { const result = await importSource({ sourceKind: "ci", path: ".github/workflows/ci.json", input: { type: "object", value: { jobs: { deploy: { needs: ["build", "test"] }, test: { needs: "build" }, build: { "runs-on": "ubuntu-latest" } }, } } }); assert.deepEqual(result.diagram.pages[0].nodes.map((n) => [n.label, n.kind]), [["build", "command"], ["deploy", "command"], ["test", "command"]]); assert.deepEqual(result.diagram.pages[0].edges.map((e) => [e.source, e.target]), [["command-6275696c64", "command-6465706c6f79"], ["command-74657374", "command-6465706c6f79"], ["command-6275696c64", "command-74657374"]]); }); test("keeps generated IDs unique for normalized names, ambiguous composites, and repeated relationships", async () => { const result = await importSource({ sourceKind: "ci", path: "ci.json", input: { type: "object", value: { jobs: { A: {}, "A": {}, a: {}, "a-needs-b": {}, "b-needs-c": { needs: ["a", "a"] }, c: { needs: "a-needs-b" }, }, } } }); const nodes = result.diagram.pages[0].nodes; const edges = result.diagram.pages[0].edges; assert.equal(new Set(nodes.map((node) => node.id)).size, nodes.length); assert.equal(new Set(edges.map((edge) => edge.id)).size, edges.length); assert.equal(edges.length, 3); assert.doesNotThrow(() => validateDiagramIR(result.diagram)); }); test("keeps sanitized IDs unique and reports duplicate logical definitions", async () => { const openapi = await importSource({ sourceKind: "openapi", path: "api.json", input: { type: "object", value: { paths: { "/x": { get: {} } }, components: { schemas: { "A/B": {}, "A-B": {} } } } } }); assert.equal(new Set(openapi.diagram.pages[0].nodes.map((n) => n.id)).size, openapi.diagram.pages[0].nodes.length); const sql = await textInput("sql", "schema.sql", "CREATE TABLE users (id INT);\nCREATE TABLE users (id INT);\n"); assert.equal(sql.diagnostics[0].code, "duplicate-id"); assert.doesNotThrow(() => validateDiagramIR(openapi.diagram)); }); test("disambiguates every repeated relationship occurrence deterministically", async () => { const sqlText = "CREATE TABLE users (id INT PRIMARY KEY);\nCREATE TABLE orders (\n a INT REFERENCES users(id),\n b INT REFERENCES users(id)\n);\n"; const terraformText = "resource \"aws_vpc\" \"main\" {}\nresource \"aws_subnet\" \"web\" {\n first = aws_vpc.main.id\n second = aws_vpc.main.id\n}\n"; const composeValue = { services: { api: { depends_on: ["db", "db"], volumes: ["data:/one", "data:/two"] }, db: {} }, volumes: { data: {} } }; const results = await Promise.all([ textInput("sql", "schema.sql", sqlText), textInput("terraform", "main.tf", terraformText), importSource({ sourceKind: "docker-compose", path: "compose.json", input: { type: "object", value: composeValue } }), ]); const expectedEdgeCounts = [2, 2, 4]; results.forEach((result, index) => { const edges = result.diagram.pages[0].edges; assert.equal(edges.length, expectedEdgeCounts[index]); assert.equal(new Set(edges.map((edge) => edge.id)).size, edges.length); assert.doesNotThrow(() => validateDiagramIR(result.diagram)); }); const again = await textInput("terraform", "main.tf", terraformText); assert.deepEqual(again.diagram.pages[0].edges.map((edge) => edge.id), results[1].diagram.pages[0].edges.map((edge) => edge.id)); }); test("rejects oversized and malformed inputs without executing object values", async () => { await assert.rejects(() => importSource({ sourceKind: "python", path: "x.py", maxBytes: 4, input: { type: "text", text: "import os" } }), /exceeds.*4/i); await assert.rejects(() => importSource({ sourceKind: "ci", path: "ci.json", input: { type: "object", value: { jobs: null } } }), /malformed CI/i); let accessed = false; const tagged = Object.create({ yamlTag: "!exec" }) as Record; Object.defineProperty(tagged, "jobs", { enumerable: true, get() { accessed = true; return {}; } }); await assert.rejects(() => importSource({ sourceKind: "ci", path: "ci.json", input: { type: "object", value: tagged } }), /plain JSON/i); assert.equal(accessed, false); }); test("rejects object input beyond the conservative nesting bound without stack overflow", async () => { const root: Record = {}; let cursor = root; for (let depth = 0; depth < 10_000; depth += 1) { const child: Record = {}; cursor.child = child; cursor = child; } await assert.rejects( () => importSource({ sourceKind: "ci", path: "ci.json", input: { type: "object", value: root } }), /nesting.*128/i, ); }); test("rejects file traversal and symlink escapes from the declared root", async () => { const base = await mkdtemp(join(tmpdir(), "source-import-")); const root = join(base, "root"); await mkdir(root); await writeFile(join(base, "outside.py"), "import os\n"); await symlink(join(base, "outside.py"), join(root, "escape.py")); await assert.rejects(() => importSource({ sourceKind: "python", path: "../outside.py", input: { type: "file", root } }), /outside.*root|escape/i); await assert.rejects(() => importSource({ sourceKind: "python", path: "escape.py", input: { type: "file", root } }), /outside.*root|symlink.*escape/i); }); test("refuses even root-confined symlinks so substitution cannot change an opened source", async () => { const root = await mkdtemp(join(tmpdir(), "source-no-follow-")); await writeFile(join(root, "real.py"), "import safe\n"); await symlink("real.py", join(root, "substituted.py")); await assert.rejects( () => importSource({ sourceKind: "python", path: "substituted.py", input: { type: "file", root } }), /symlink|symbolic|safe source/i, ); }); test("ingests directories in deterministic path order with confined provenance", async () => { const root = await mkdtemp(join(tmpdir(), "source-directory-")); await mkdir(join(root, "src")); await writeFile(join(root, "src", "b.py"), "import zlib\n"); await writeFile(join(root, "src", "a.py"), "from pkg import x\n"); const request = { sourceKind: "python" as const, path: "src", input: { type: "file" as const, root } }; const first = await importSource(request); const second = await importSource(request); assert.equal(JSON.stringify(first), JSON.stringify(second)); assert.deepEqual(first.diagram.pages[0].nodes.filter((n) => n.kind === "module").map((n) => n.label), ["src/a.py", "src/b.py"]); assert.deepEqual(first.diagram.pages[0].nodes.filter((n) => n.kind === "library").map((n) => n.label), ["pkg", "zlib"]); assert.equal(first.diagram.pages[0].edges[0].provenance?.path, "src/a.py"); assert.doesNotThrow(() => validateDiagramIR(first.diagram)); }); test("reports dynamic code imports as unknown constructs instead of guessing", async () => { const python = await textInput("python", "dynamic.py", "module = __import__(name)\n"); const javascript = await textInput("javascript", "dynamic.js", "const module = await import(name);\n"); assert.equal(python.diagnostics[0].code, "unknown-construct"); assert.equal(javascript.diagnostics[0].code, "unknown-construct"); assert.deepEqual(python.diagram.pages[0].nodes.map((n) => n.label), ["dynamic.py"]); }); test("loads YAML data without permitting custom tags", async () => { const root = await mkdtemp(join(tmpdir(), "source-yaml-")); await writeFile(join(root, "objects.yaml"), "apiVersion: v1\nkind: Service\nmetadata:\n name: api\nspec:\n selector:\n app: api\n"); await writeFile(join(root, "tagged.yaml"), "jobs: !exec dangerous\n"); const result = await importSource({ sourceKind: "kubernetes", path: "objects.yaml", input: { type: "file", root } }); assert.deepEqual(result.diagram.pages[0].nodes.map((n) => n.label), ["Service/default/api"]); await assert.rejects(() => importSource({ sourceKind: "ci", path: "tagged.yaml", input: { type: "file", root } }), /tag|malformed YAML/i); });