Dev Intel Pipeline v2 — multi-language semantic graph extractor
Phase 1: extract.js — tree-sitter AST parser (TS/JS/Python/Go/Java/Bash) + config parsers (YAML/HCL) Phase 2: graph.js — in-memory directed graph store with build/query/diff CLI Phase 3: namespace.js — cross-repo namespace registry with 3-tier resolution Phase 4: semantic-diff.js — categorized diffs with impact scoring (0-100) Phase 5: pipeline.js — batch extraction, incremental diffing, benchmarking Benchmark: 4,325 files, 21,646 nodes, 133,979 edges in 67s (15ms/file) BMad SPA reviews: all phases GO
This commit is contained in:
92
test/ground-truth/bash-deploy.json
Normal file
92
test/ground-truth/bash-deploy.json
Normal file
@@ -0,0 +1,92 @@
|
||||
{
|
||||
"file": "/tmp/deploy.sh",
|
||||
"language": "bash",
|
||||
"entities": [
|
||||
{
|
||||
"id": "deploy.sh",
|
||||
"type": "Module",
|
||||
"name": "deploy.sh",
|
||||
"kind": "module",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
1,
|
||||
17
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "dep:./utils.sh",
|
||||
"type": "Dependency",
|
||||
"name": "./utils.sh",
|
||||
"kind": "import",
|
||||
"visibility": "internal",
|
||||
"line_range": [
|
||||
2,
|
||||
2
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "deploy.sh:build_image",
|
||||
"type": "Function",
|
||||
"name": "build_image",
|
||||
"kind": "function",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
4,
|
||||
8
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "deploy.sh:deploy_k8s",
|
||||
"type": "Function",
|
||||
"name": "deploy_k8s",
|
||||
"kind": "function",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
10,
|
||||
12
|
||||
]
|
||||
}
|
||||
],
|
||||
"relationships": [
|
||||
{
|
||||
"type": "IMPORTS",
|
||||
"source": "deploy.sh",
|
||||
"target": "dep:./utils.sh"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "deploy.sh",
|
||||
"target": "deploy.sh:build_image"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "deploy.sh:build_image",
|
||||
"target": "docker"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "deploy.sh",
|
||||
"target": "deploy.sh:deploy_k8s"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "deploy.sh:deploy_k8s",
|
||||
"target": "kubectl"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "deploy.sh",
|
||||
"target": "echo"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "deploy.sh",
|
||||
"target": "build_image"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "deploy.sh",
|
||||
"target": "deploy_k8s"
|
||||
}
|
||||
]
|
||||
}
|
||||
98
test/ground-truth/go-server.json
Normal file
98
test/ground-truth/go-server.json
Normal file
@@ -0,0 +1,98 @@
|
||||
{
|
||||
"file": "/tmp/test_go.go",
|
||||
"language": "go",
|
||||
"entities": [
|
||||
{
|
||||
"id": "test_go.go",
|
||||
"type": "Module",
|
||||
"name": "test_go.go",
|
||||
"kind": "module",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
1,
|
||||
21
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "dep:fmt",
|
||||
"type": "Dependency",
|
||||
"name": "fmt",
|
||||
"kind": "import",
|
||||
"visibility": "internal",
|
||||
"line_range": [
|
||||
4,
|
||||
4
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "dep:net/http",
|
||||
"type": "Dependency",
|
||||
"name": "net/http",
|
||||
"kind": "import",
|
||||
"visibility": "internal",
|
||||
"line_range": [
|
||||
5,
|
||||
5
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "test_go.go:Start",
|
||||
"type": "Function",
|
||||
"name": "Start",
|
||||
"kind": "method",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
12,
|
||||
15
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "test_go.go:main",
|
||||
"type": "Function",
|
||||
"name": "main",
|
||||
"kind": "function",
|
||||
"visibility": "internal",
|
||||
"line_range": [
|
||||
17,
|
||||
20
|
||||
]
|
||||
}
|
||||
],
|
||||
"relationships": [
|
||||
{
|
||||
"type": "IMPORTS",
|
||||
"source": "test_go.go",
|
||||
"target": "dep:fmt"
|
||||
},
|
||||
{
|
||||
"type": "IMPORTS",
|
||||
"source": "test_go.go",
|
||||
"target": "dep:net/http"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "test_go.go",
|
||||
"target": "test_go.go:Start"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "test_go.go:Start",
|
||||
"target": "fmt.Println"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "test_go.go:Start",
|
||||
"target": "http.ListenAndServe"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "test_go.go",
|
||||
"target": "test_go.go:main"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "test_go.go:main",
|
||||
"target": "s.Start"
|
||||
}
|
||||
]
|
||||
}
|
||||
130
test/ground-truth/java-service.json
Normal file
130
test/ground-truth/java-service.json
Normal file
@@ -0,0 +1,130 @@
|
||||
{
|
||||
"file": "/tmp/TestJava.java",
|
||||
"language": "java",
|
||||
"entities": [
|
||||
{
|
||||
"id": "TestJava.java",
|
||||
"type": "Module",
|
||||
"name": "TestJava.java",
|
||||
"kind": "module",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
1,
|
||||
22
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "dep:java.util.List",
|
||||
"type": "Dependency",
|
||||
"name": "java.util.List",
|
||||
"kind": "import",
|
||||
"visibility": "internal",
|
||||
"line_range": [
|
||||
3,
|
||||
3
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "dep:org.springframework.stereotype.Service",
|
||||
"type": "Dependency",
|
||||
"name": "org.springframework.stereotype.Service",
|
||||
"kind": "import",
|
||||
"visibility": "internal",
|
||||
"line_range": [
|
||||
4,
|
||||
4
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "TestJava.java:TenantService",
|
||||
"type": "Class",
|
||||
"name": "TenantService",
|
||||
"kind": "class",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
6,
|
||||
21
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "TestJava.java:TenantService:TenantService",
|
||||
"type": "Function",
|
||||
"name": "TenantService",
|
||||
"kind": "method",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
10,
|
||||
12
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "TestJava.java:TenantService:getTenants",
|
||||
"type": "Function",
|
||||
"name": "getTenants",
|
||||
"kind": "method",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
14,
|
||||
16
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "TestJava.java:TenantService:audit",
|
||||
"type": "Function",
|
||||
"name": "audit",
|
||||
"kind": "method",
|
||||
"visibility": "private",
|
||||
"line_range": [
|
||||
18,
|
||||
20
|
||||
]
|
||||
}
|
||||
],
|
||||
"relationships": [
|
||||
{
|
||||
"type": "IMPORTS",
|
||||
"source": "TestJava.java",
|
||||
"target": "dep:java.util.List"
|
||||
},
|
||||
{
|
||||
"type": "IMPORTS",
|
||||
"source": "TestJava.java",
|
||||
"target": "dep:org.springframework.stereotype.Service"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "TestJava.java",
|
||||
"target": "TestJava.java:TenantService"
|
||||
},
|
||||
{
|
||||
"type": "IMPLEMENTS",
|
||||
"source": "TestJava.java:TenantService",
|
||||
"target": "BaseService"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "TestJava.java:TenantService",
|
||||
"target": "TestJava.java:TenantService:TenantService"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "TestJava.java:TenantService",
|
||||
"target": "TestJava.java:TenantService:getTenants"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "TestJava.java:TenantService:getTenants",
|
||||
"target": "this.db.query"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "TestJava.java:TenantService",
|
||||
"target": "TestJava.java:TenantService:audit"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "TestJava.java:TenantService:audit",
|
||||
"target": "Logger.log"
|
||||
}
|
||||
]
|
||||
}
|
||||
45
test/ground-truth/mask-api-key.json
Normal file
45
test/ground-truth/mask-api-key.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"file": "/app/src/utils/mask-api-key.ts",
|
||||
"language": "typescript",
|
||||
"entities": [
|
||||
{
|
||||
"id": "utils/mask-api-key.ts",
|
||||
"type": "Module",
|
||||
"name": "utils/mask-api-key.ts",
|
||||
"kind": "module",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
1,
|
||||
14
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "utils/mask-api-key.ts:maskApiKey",
|
||||
"type": "Function",
|
||||
"name": "maskApiKey",
|
||||
"kind": "function",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
1,
|
||||
13
|
||||
]
|
||||
}
|
||||
],
|
||||
"relationships": [
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "utils/mask-api-key.ts",
|
||||
"target": "utils/mask-api-key.ts:maskApiKey"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "utils/mask-api-key.ts:maskApiKey",
|
||||
"target": "value.trim"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "utils/mask-api-key.ts:maskApiKey",
|
||||
"target": "trimmed.slice"
|
||||
}
|
||||
]
|
||||
}
|
||||
241
test/ground-truth/python-service.json
Normal file
241
test/ground-truth/python-service.json
Normal file
@@ -0,0 +1,241 @@
|
||||
{
|
||||
"file": "/tmp/test_service.py",
|
||||
"language": "python",
|
||||
"entities": [
|
||||
{
|
||||
"id": "test_service.py",
|
||||
"type": "Module",
|
||||
"name": "test_service.py",
|
||||
"kind": "module",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
1,
|
||||
34
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "dep:os",
|
||||
"type": "Dependency",
|
||||
"name": "os",
|
||||
"kind": "import",
|
||||
"visibility": "internal",
|
||||
"line_range": [
|
||||
1,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "dep:typing",
|
||||
"type": "Dependency",
|
||||
"name": "typing",
|
||||
"kind": "import",
|
||||
"visibility": "internal",
|
||||
"line_range": [
|
||||
2,
|
||||
2
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "dep:dataclasses",
|
||||
"type": "Dependency",
|
||||
"name": "dataclasses",
|
||||
"kind": "import",
|
||||
"visibility": "internal",
|
||||
"line_range": [
|
||||
3,
|
||||
3
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "dep:.config",
|
||||
"type": "Dependency",
|
||||
"name": ".config",
|
||||
"kind": "import",
|
||||
"visibility": "internal",
|
||||
"line_range": [
|
||||
4,
|
||||
4
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "dep:.database",
|
||||
"type": "Dependency",
|
||||
"name": ".database",
|
||||
"kind": "import",
|
||||
"visibility": "internal",
|
||||
"line_range": [
|
||||
5,
|
||||
5
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "test_service.py:TenantConfig",
|
||||
"type": "Class",
|
||||
"name": "TenantConfig",
|
||||
"kind": "class",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
8,
|
||||
11
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "test_service.py:TenantService",
|
||||
"type": "Class",
|
||||
"name": "TenantService",
|
||||
"kind": "class",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
13,
|
||||
30
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "test_service.py:TenantService:__init__",
|
||||
"type": "Function",
|
||||
"name": "__init__",
|
||||
"kind": "method",
|
||||
"visibility": "private",
|
||||
"line_range": [
|
||||
14,
|
||||
16
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "test_service.py:TenantService:get_tenant",
|
||||
"type": "Function",
|
||||
"name": "get_tenant",
|
||||
"kind": "method",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
18,
|
||||
22
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "test_service.py:TenantService:_enrich",
|
||||
"type": "Function",
|
||||
"name": "_enrich",
|
||||
"kind": "method",
|
||||
"visibility": "protected",
|
||||
"line_range": [
|
||||
24,
|
||||
26
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "test_service.py:TenantService:create_tenant",
|
||||
"type": "Function",
|
||||
"name": "create_tenant",
|
||||
"kind": "method",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
28,
|
||||
30
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "test_service.py:health_check",
|
||||
"type": "Function",
|
||||
"name": "health_check",
|
||||
"kind": "function",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
32,
|
||||
33
|
||||
]
|
||||
}
|
||||
],
|
||||
"relationships": [
|
||||
{
|
||||
"type": "IMPORTS",
|
||||
"source": "test_service.py",
|
||||
"target": "dep:os"
|
||||
},
|
||||
{
|
||||
"type": "IMPORTS",
|
||||
"source": "test_service.py",
|
||||
"target": "dep:typing"
|
||||
},
|
||||
{
|
||||
"type": "IMPORTS",
|
||||
"source": "test_service.py",
|
||||
"target": "dep:dataclasses"
|
||||
},
|
||||
{
|
||||
"type": "IMPORTS",
|
||||
"source": "test_service.py",
|
||||
"target": "dep:.config"
|
||||
},
|
||||
{
|
||||
"type": "IMPORTS",
|
||||
"source": "test_service.py",
|
||||
"target": "dep:.database"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "test_service.py",
|
||||
"target": "test_service.py:TenantConfig"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "test_service.py",
|
||||
"target": "test_service.py:TenantService"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "test_service.py:TenantService",
|
||||
"target": "test_service.py:TenantService:__init__"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "test_service.py:TenantService:__init__",
|
||||
"target": "load_config"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "test_service.py:TenantService",
|
||||
"target": "test_service.py:TenantService:get_tenant"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "test_service.py:TenantService:get_tenant",
|
||||
"target": "self.db.query"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "test_service.py:TenantService:get_tenant",
|
||||
"target": "self._enrich"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "test_service.py:TenantService",
|
||||
"target": "test_service.py:TenantService:_enrich"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "test_service.py:TenantService:_enrich",
|
||||
"target": "self.config.get"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "test_service.py:TenantService",
|
||||
"target": "test_service.py:TenantService:create_tenant"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "test_service.py:TenantService:create_tenant",
|
||||
"target": "TenantConfig"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "test_service.py:TenantService:create_tenant",
|
||||
"target": "self.db.insert"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "test_service.py",
|
||||
"target": "test_service.py:health_check"
|
||||
}
|
||||
]
|
||||
}
|
||||
234
test/ground-truth/route.json
Normal file
234
test/ground-truth/route.json
Normal file
@@ -0,0 +1,234 @@
|
||||
{
|
||||
"file": "/app/src/cli/route.ts",
|
||||
"language": "typescript",
|
||||
"entities": [
|
||||
{
|
||||
"id": "cli/route.ts",
|
||||
"type": "Module",
|
||||
"name": "cli/route.ts",
|
||||
"kind": "module",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
1,
|
||||
48
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "dep:infra/env.js",
|
||||
"type": "Dependency",
|
||||
"name": "infra/env.js",
|
||||
"kind": "import",
|
||||
"visibility": "internal",
|
||||
"line_range": [
|
||||
1,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "dep:runtime.js",
|
||||
"type": "Dependency",
|
||||
"name": "runtime.js",
|
||||
"kind": "import",
|
||||
"visibility": "internal",
|
||||
"line_range": [
|
||||
2,
|
||||
2
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "dep:version.js",
|
||||
"type": "Dependency",
|
||||
"name": "version.js",
|
||||
"kind": "import",
|
||||
"visibility": "internal",
|
||||
"line_range": [
|
||||
3,
|
||||
3
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "dep:cli/argv.js",
|
||||
"type": "Dependency",
|
||||
"name": "cli/argv.js",
|
||||
"kind": "import",
|
||||
"visibility": "internal",
|
||||
"line_range": [
|
||||
4,
|
||||
4
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "dep:cli/banner.js",
|
||||
"type": "Dependency",
|
||||
"name": "cli/banner.js",
|
||||
"kind": "import",
|
||||
"visibility": "internal",
|
||||
"line_range": [
|
||||
5,
|
||||
5
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "dep:cli/plugin-registry.js",
|
||||
"type": "Dependency",
|
||||
"name": "cli/plugin-registry.js",
|
||||
"kind": "import",
|
||||
"visibility": "internal",
|
||||
"line_range": [
|
||||
6,
|
||||
6
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "dep:cli/program/config-guard.js",
|
||||
"type": "Dependency",
|
||||
"name": "cli/program/config-guard.js",
|
||||
"kind": "import",
|
||||
"visibility": "internal",
|
||||
"line_range": [
|
||||
7,
|
||||
7
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "dep:cli/program/routes.js",
|
||||
"type": "Dependency",
|
||||
"name": "cli/program/routes.js",
|
||||
"kind": "import",
|
||||
"visibility": "internal",
|
||||
"line_range": [
|
||||
8,
|
||||
8
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "cli/route.ts:prepareRoutedCommand",
|
||||
"type": "Function",
|
||||
"name": "prepareRoutedCommand",
|
||||
"kind": "function",
|
||||
"visibility": "internal",
|
||||
"line_range": [
|
||||
10,
|
||||
27
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "cli/route.ts:tryRouteCli",
|
||||
"type": "Function",
|
||||
"name": "tryRouteCli",
|
||||
"kind": "function",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
29,
|
||||
47
|
||||
]
|
||||
}
|
||||
],
|
||||
"relationships": [
|
||||
{
|
||||
"type": "IMPORTS",
|
||||
"source": "cli/route.ts",
|
||||
"target": "dep:infra/env.js"
|
||||
},
|
||||
{
|
||||
"type": "IMPORTS",
|
||||
"source": "cli/route.ts",
|
||||
"target": "dep:runtime.js"
|
||||
},
|
||||
{
|
||||
"type": "IMPORTS",
|
||||
"source": "cli/route.ts",
|
||||
"target": "dep:version.js"
|
||||
},
|
||||
{
|
||||
"type": "IMPORTS",
|
||||
"source": "cli/route.ts",
|
||||
"target": "dep:cli/argv.js"
|
||||
},
|
||||
{
|
||||
"type": "IMPORTS",
|
||||
"source": "cli/route.ts",
|
||||
"target": "dep:cli/banner.js"
|
||||
},
|
||||
{
|
||||
"type": "IMPORTS",
|
||||
"source": "cli/route.ts",
|
||||
"target": "dep:cli/plugin-registry.js"
|
||||
},
|
||||
{
|
||||
"type": "IMPORTS",
|
||||
"source": "cli/route.ts",
|
||||
"target": "dep:cli/program/config-guard.js"
|
||||
},
|
||||
{
|
||||
"type": "IMPORTS",
|
||||
"source": "cli/route.ts",
|
||||
"target": "dep:cli/program/routes.js"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "cli/route.ts",
|
||||
"target": "cli/route.ts:prepareRoutedCommand"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "cli/route.ts:prepareRoutedCommand",
|
||||
"target": "hasFlag"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "cli/route.ts:prepareRoutedCommand",
|
||||
"target": "emitCliBanner"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "cli/route.ts:prepareRoutedCommand",
|
||||
"target": "ensureConfigReady"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "cli/route.ts:prepareRoutedCommand",
|
||||
"target": "params.loadPlugins"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "cli/route.ts:prepareRoutedCommand",
|
||||
"target": "ensurePluginRegistryLoaded"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "cli/route.ts",
|
||||
"target": "cli/route.ts:tryRouteCli"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "cli/route.ts:tryRouteCli",
|
||||
"target": "isTruthyEnvValue"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "cli/route.ts:tryRouteCli",
|
||||
"target": "hasHelpOrVersion"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "cli/route.ts:tryRouteCli",
|
||||
"target": "getCommandPathWithRootOptions"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "cli/route.ts:tryRouteCli",
|
||||
"target": "findRoutedCommand"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "cli/route.ts:tryRouteCli",
|
||||
"target": "prepareRoutedCommand"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "cli/route.ts:tryRouteCli",
|
||||
"target": "route.run"
|
||||
}
|
||||
]
|
||||
}
|
||||
579
test/ground-truth/session.json
Normal file
579
test/ground-truth/session.json
Normal file
@@ -0,0 +1,579 @@
|
||||
{
|
||||
"file": "/app/src/wizard/session.ts",
|
||||
"language": "typescript",
|
||||
"entities": [
|
||||
{
|
||||
"id": "wizard/session.ts",
|
||||
"type": "Module",
|
||||
"name": "wizard/session.ts",
|
||||
"kind": "module",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
1,
|
||||
265
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "dep:node:crypto",
|
||||
"type": "Dependency",
|
||||
"name": "node:crypto",
|
||||
"kind": "import",
|
||||
"visibility": "internal",
|
||||
"line_range": [
|
||||
1,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "dep:wizard/prompts.js",
|
||||
"type": "Dependency",
|
||||
"name": "wizard/prompts.js",
|
||||
"kind": "import",
|
||||
"visibility": "internal",
|
||||
"line_range": [
|
||||
2,
|
||||
2
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "wizard/session.ts:createDeferred",
|
||||
"type": "Function",
|
||||
"name": "createDeferred",
|
||||
"kind": "function",
|
||||
"visibility": "internal",
|
||||
"line_range": [
|
||||
37,
|
||||
45
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "wizard/session.ts:WizardSessionPrompter",
|
||||
"type": "Class",
|
||||
"name": "WizardSessionPrompter",
|
||||
"kind": "class",
|
||||
"visibility": "internal",
|
||||
"line_range": [
|
||||
47,
|
||||
161
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "wizard/session.ts:WizardSessionPrompter:constructor",
|
||||
"type": "Function",
|
||||
"name": "constructor",
|
||||
"kind": "method",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
48,
|
||||
48
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "wizard/session.ts:WizardSessionPrompter:intro",
|
||||
"type": "Function",
|
||||
"name": "intro",
|
||||
"kind": "method",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
50,
|
||||
57
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "wizard/session.ts:WizardSessionPrompter:outro",
|
||||
"type": "Function",
|
||||
"name": "outro",
|
||||
"kind": "method",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
59,
|
||||
66
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "wizard/session.ts:WizardSessionPrompter:note",
|
||||
"type": "Function",
|
||||
"name": "note",
|
||||
"kind": "method",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
68,
|
||||
70
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "wizard/session.ts:WizardSessionPrompter:select",
|
||||
"type": "Function",
|
||||
"name": "select",
|
||||
"kind": "method",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
72,
|
||||
89
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "wizard/session.ts:WizardSessionPrompter:multiselect",
|
||||
"type": "Function",
|
||||
"name": "multiselect",
|
||||
"kind": "method",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
91,
|
||||
108
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "wizard/session.ts:WizardSessionPrompter:text",
|
||||
"type": "Function",
|
||||
"name": "text",
|
||||
"kind": "method",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
110,
|
||||
136
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "wizard/session.ts:WizardSessionPrompter:confirm",
|
||||
"type": "Function",
|
||||
"name": "confirm",
|
||||
"kind": "method",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
138,
|
||||
146
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "wizard/session.ts:WizardSessionPrompter:progress",
|
||||
"type": "Function",
|
||||
"name": "progress",
|
||||
"kind": "method",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
148,
|
||||
153
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "wizard/session.ts:WizardSessionPrompter:prompt",
|
||||
"type": "Function",
|
||||
"name": "prompt",
|
||||
"kind": "method",
|
||||
"visibility": "private",
|
||||
"line_range": [
|
||||
155,
|
||||
160
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "wizard/session.ts:WizardSession",
|
||||
"type": "Class",
|
||||
"name": "WizardSession",
|
||||
"kind": "class",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
163,
|
||||
264
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "wizard/session.ts:WizardSession:constructor",
|
||||
"type": "Function",
|
||||
"name": "constructor",
|
||||
"kind": "method",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
170,
|
||||
173
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "wizard/session.ts:WizardSession:next",
|
||||
"type": "Function",
|
||||
"name": "next",
|
||||
"kind": "method",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
175,
|
||||
190
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "wizard/session.ts:WizardSession:answer",
|
||||
"type": "Function",
|
||||
"name": "answer",
|
||||
"kind": "method",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
192,
|
||||
200
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "wizard/session.ts:WizardSession:cancel",
|
||||
"type": "Function",
|
||||
"name": "cancel",
|
||||
"kind": "method",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
202,
|
||||
214
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "wizard/session.ts:WizardSession:pushStep",
|
||||
"type": "Function",
|
||||
"name": "pushStep",
|
||||
"kind": "method",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
216,
|
||||
219
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "wizard/session.ts:WizardSession:run",
|
||||
"type": "Function",
|
||||
"name": "run",
|
||||
"kind": "method",
|
||||
"visibility": "private",
|
||||
"line_range": [
|
||||
221,
|
||||
236
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "wizard/session.ts:WizardSession:awaitAnswer",
|
||||
"type": "Function",
|
||||
"name": "awaitAnswer",
|
||||
"kind": "method",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
238,
|
||||
246
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "wizard/session.ts:WizardSession:resolveStep",
|
||||
"type": "Function",
|
||||
"name": "resolveStep",
|
||||
"kind": "method",
|
||||
"visibility": "private",
|
||||
"line_range": [
|
||||
248,
|
||||
255
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "wizard/session.ts:WizardSession:getStatus",
|
||||
"type": "Function",
|
||||
"name": "getStatus",
|
||||
"kind": "method",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
257,
|
||||
259
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "wizard/session.ts:WizardSession:getError",
|
||||
"type": "Function",
|
||||
"name": "getError",
|
||||
"kind": "method",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
261,
|
||||
263
|
||||
]
|
||||
}
|
||||
],
|
||||
"relationships": [
|
||||
{
|
||||
"type": "IMPORTS",
|
||||
"source": "wizard/session.ts",
|
||||
"target": "dep:node:crypto"
|
||||
},
|
||||
{
|
||||
"type": "IMPORTS",
|
||||
"source": "wizard/session.ts",
|
||||
"target": "dep:wizard/prompts.js"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "wizard/session.ts",
|
||||
"target": "wizard/session.ts:createDeferred"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "wizard/session.ts",
|
||||
"target": "wizard/session.ts:WizardSessionPrompter"
|
||||
},
|
||||
{
|
||||
"type": "IMPLEMENTS",
|
||||
"source": "wizard/session.ts:WizardSessionPrompter",
|
||||
"target": "WizardPrompter"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "wizard/session.ts:WizardSessionPrompter",
|
||||
"target": "wizard/session.ts:WizardSessionPrompter:constructor"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "wizard/session.ts:WizardSessionPrompter",
|
||||
"target": "wizard/session.ts:WizardSessionPrompter:intro"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "wizard/session.ts:WizardSessionPrompter:intro",
|
||||
"target": "this.prompt"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "wizard/session.ts:WizardSessionPrompter",
|
||||
"target": "wizard/session.ts:WizardSessionPrompter:outro"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "wizard/session.ts:WizardSessionPrompter:outro",
|
||||
"target": "this.prompt"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "wizard/session.ts:WizardSessionPrompter",
|
||||
"target": "wizard/session.ts:WizardSessionPrompter:note"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "wizard/session.ts:WizardSessionPrompter:note",
|
||||
"target": "this.prompt"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "wizard/session.ts:WizardSessionPrompter",
|
||||
"target": "wizard/session.ts:WizardSessionPrompter:select"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "wizard/session.ts:WizardSessionPrompter:select",
|
||||
"target": "this.prompt"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "wizard/session.ts:WizardSessionPrompter:select",
|
||||
"target": "params.options.map"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "wizard/session.ts:WizardSessionPrompter",
|
||||
"target": "wizard/session.ts:WizardSessionPrompter:multiselect"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "wizard/session.ts:WizardSessionPrompter:multiselect",
|
||||
"target": "this.prompt"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "wizard/session.ts:WizardSessionPrompter:multiselect",
|
||||
"target": "params.options.map"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "wizard/session.ts:WizardSessionPrompter:multiselect",
|
||||
"target": "Array.isArray"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "wizard/session.ts:WizardSessionPrompter",
|
||||
"target": "wizard/session.ts:WizardSessionPrompter:text"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "wizard/session.ts:WizardSessionPrompter:text",
|
||||
"target": "this.prompt"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "wizard/session.ts:WizardSessionPrompter:text",
|
||||
"target": "String"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "wizard/session.ts:WizardSessionPrompter:text",
|
||||
"target": "params.validate"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "wizard/session.ts:WizardSessionPrompter",
|
||||
"target": "wizard/session.ts:WizardSessionPrompter:confirm"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "wizard/session.ts:WizardSessionPrompter:confirm",
|
||||
"target": "this.prompt"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "wizard/session.ts:WizardSessionPrompter:confirm",
|
||||
"target": "Boolean"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "wizard/session.ts:WizardSessionPrompter",
|
||||
"target": "wizard/session.ts:WizardSessionPrompter:progress"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "wizard/session.ts:WizardSessionPrompter",
|
||||
"target": "wizard/session.ts:WizardSessionPrompter:prompt"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "wizard/session.ts:WizardSessionPrompter:prompt",
|
||||
"target": "this.session.awaitAnswer"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "wizard/session.ts:WizardSessionPrompter:prompt",
|
||||
"target": "randomUUID"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "wizard/session.ts",
|
||||
"target": "wizard/session.ts:WizardSession"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "wizard/session.ts:WizardSession",
|
||||
"target": "wizard/session.ts:WizardSession:constructor"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "wizard/session.ts:WizardSession:constructor",
|
||||
"target": "this.run"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "wizard/session.ts:WizardSession",
|
||||
"target": "wizard/session.ts:WizardSession:next"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "wizard/session.ts:WizardSession:next",
|
||||
"target": "createDeferred"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "wizard/session.ts:WizardSession",
|
||||
"target": "wizard/session.ts:WizardSession:answer"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "wizard/session.ts:WizardSession:answer",
|
||||
"target": "this.answerDeferred.get"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "wizard/session.ts:WizardSession:answer",
|
||||
"target": "this.answerDeferred.delete"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "wizard/session.ts:WizardSession:answer",
|
||||
"target": "deferred.resolve"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "wizard/session.ts:WizardSession",
|
||||
"target": "wizard/session.ts:WizardSession:cancel"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "wizard/session.ts:WizardSession:cancel",
|
||||
"target": "deferred.reject"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "wizard/session.ts:WizardSession:cancel",
|
||||
"target": "this.answerDeferred.clear"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "wizard/session.ts:WizardSession:cancel",
|
||||
"target": "this.resolveStep"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "wizard/session.ts:WizardSession",
|
||||
"target": "wizard/session.ts:WizardSession:pushStep"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "wizard/session.ts:WizardSession:pushStep",
|
||||
"target": "this.resolveStep"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "wizard/session.ts:WizardSession",
|
||||
"target": "wizard/session.ts:WizardSession:run"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "wizard/session.ts:WizardSession:run",
|
||||
"target": "this.runner"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "wizard/session.ts:WizardSession:run",
|
||||
"target": "String"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "wizard/session.ts:WizardSession:run",
|
||||
"target": "this.resolveStep"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "wizard/session.ts:WizardSession",
|
||||
"target": "wizard/session.ts:WizardSession:awaitAnswer"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "wizard/session.ts:WizardSession:awaitAnswer",
|
||||
"target": "this.pushStep"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "wizard/session.ts:WizardSession:awaitAnswer",
|
||||
"target": "createDeferred"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "wizard/session.ts:WizardSession:awaitAnswer",
|
||||
"target": "this.answerDeferred.set"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "wizard/session.ts:WizardSession",
|
||||
"target": "wizard/session.ts:WizardSession:resolveStep"
|
||||
},
|
||||
{
|
||||
"type": "CALLS",
|
||||
"source": "wizard/session.ts:WizardSession:resolveStep",
|
||||
"target": "deferred.resolve"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "wizard/session.ts:WizardSession",
|
||||
"target": "wizard/session.ts:WizardSession:getStatus"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "wizard/session.ts:WizardSession",
|
||||
"target": "wizard/session.ts:WizardSession:getError"
|
||||
}
|
||||
]
|
||||
}
|
||||
83
test/ground-truth/terraform-main.json
Normal file
83
test/ground-truth/terraform-main.json
Normal file
@@ -0,0 +1,83 @@
|
||||
{
|
||||
"file": "/tmp/main.tf",
|
||||
"language": "hcl",
|
||||
"entities": [
|
||||
{
|
||||
"id": "main.tf",
|
||||
"type": "Config",
|
||||
"name": "main.tf",
|
||||
"kind": "terraform",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
1,
|
||||
18
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "main.tf:provider.aws",
|
||||
"type": "Config",
|
||||
"name": "provider.aws",
|
||||
"kind": "hcl-block",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
1,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "main.tf:resource.aws_s3_bucket.b",
|
||||
"type": "Config",
|
||||
"name": "resource.aws_s3_bucket.b",
|
||||
"kind": "hcl-block",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
5,
|
||||
5
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "main.tf:module.vpc",
|
||||
"type": "Config",
|
||||
"name": "module.vpc",
|
||||
"kind": "hcl-block",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
9,
|
||||
9
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "main.tf:data.aws_iam_policy_document.assume_role",
|
||||
"type": "Config",
|
||||
"name": "data.aws_iam_policy_document.assume_role",
|
||||
"kind": "hcl-block",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
13,
|
||||
13
|
||||
]
|
||||
}
|
||||
],
|
||||
"relationships": [
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "main.tf",
|
||||
"target": "main.tf:provider.aws"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "main.tf",
|
||||
"target": "main.tf:resource.aws_s3_bucket.b"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "main.tf",
|
||||
"target": "main.tf:module.vpc"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "main.tf",
|
||||
"target": "main.tf:data.aws_iam_policy_document.assume_role"
|
||||
}
|
||||
]
|
||||
}
|
||||
83
test/ground-truth/yaml-deployment.json
Normal file
83
test/ground-truth/yaml-deployment.json
Normal file
@@ -0,0 +1,83 @@
|
||||
{
|
||||
"file": "/tmp/test_deployment.yaml",
|
||||
"language": "yaml",
|
||||
"entities": [
|
||||
{
|
||||
"id": "test_deployment.yaml",
|
||||
"type": "Config",
|
||||
"name": "test_deployment.yaml",
|
||||
"kind": "yaml-config",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
1,
|
||||
12
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "test_deployment.yaml:apiVersion",
|
||||
"type": "Config",
|
||||
"name": "apiVersion",
|
||||
"kind": "yaml-key",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
1,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "test_deployment.yaml:kind",
|
||||
"type": "Config",
|
||||
"name": "kind",
|
||||
"kind": "yaml-key",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
1,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "test_deployment.yaml:metadata",
|
||||
"type": "Config",
|
||||
"name": "metadata",
|
||||
"kind": "yaml-key",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
1,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "test_deployment.yaml:spec",
|
||||
"type": "Config",
|
||||
"name": "spec",
|
||||
"kind": "yaml-key",
|
||||
"visibility": "public",
|
||||
"line_range": [
|
||||
1,
|
||||
1
|
||||
]
|
||||
}
|
||||
],
|
||||
"relationships": [
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "test_deployment.yaml",
|
||||
"target": "test_deployment.yaml:apiVersion"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "test_deployment.yaml",
|
||||
"target": "test_deployment.yaml:kind"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "test_deployment.yaml",
|
||||
"target": "test_deployment.yaml:metadata"
|
||||
},
|
||||
{
|
||||
"type": "CONTAINS",
|
||||
"source": "test_deployment.yaml",
|
||||
"target": "test_deployment.yaml:spec"
|
||||
}
|
||||
]
|
||||
}
|
||||
30
test/run-all.sh
Executable file
30
test/run-all.sh
Executable file
@@ -0,0 +1,30 @@
|
||||
#!/bin/bash
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
PASS=0
|
||||
FAIL=0
|
||||
TOTAL=0
|
||||
|
||||
echo "=== Dev Intel v2 — Ground Truth Benchmark Suite ==="
|
||||
echo ""
|
||||
|
||||
for gt in test/ground-truth/*.json; do
|
||||
name=$(basename "$gt" .json)
|
||||
TOTAL=$((TOTAL + 1))
|
||||
result=$(node validate-ground-truth.js "$gt" 2>&1)
|
||||
if echo "$result" | grep -q "^PASS"; then
|
||||
echo "✅ PASS $name"
|
||||
PASS=$((PASS + 1))
|
||||
else
|
||||
echo "❌ FAIL $name"
|
||||
echo "$result" | grep -E "^(Entities|Relationships|Missing)" | sed 's/^/ /'
|
||||
FAIL=$((FAIL + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "=== Results: $PASS/$TOTAL passed, $FAIL failed ==="
|
||||
|
||||
if [ $FAIL -gt 0 ]; then
|
||||
exit 1
|
||||
fi
|
||||
323
test/test-graph.js
Normal file
323
test/test-graph.js
Normal file
@@ -0,0 +1,323 @@
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { execSync } = require('child_process');
|
||||
const GraphStore = require('../graph');
|
||||
|
||||
let passed = 0;
|
||||
let total = 0;
|
||||
|
||||
function runTest(name, fn) {
|
||||
total++;
|
||||
try {
|
||||
fn();
|
||||
console.log(`✅ PASS ${name}`);
|
||||
passed++;
|
||||
} catch (err) {
|
||||
console.log(`❌ FAIL ${name}: ${err.message}`);
|
||||
// console.error(err.stack); // uncomment if debugging is needed
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to create mock extract results
|
||||
const mockFile1 = {
|
||||
file: 'file1.ts',
|
||||
language: 'typescript',
|
||||
entities: [
|
||||
{ id: 'file1.ts', type: 'Module', name: 'file1.ts', visibility: 'public' },
|
||||
{ id: 'func1', type: 'Function', name: 'func1', visibility: 'public' }
|
||||
],
|
||||
relationships: [
|
||||
{ type: 'CONTAINS', source: 'file1.ts', target: 'func1' },
|
||||
{ type: 'CALLS', source: 'func1', target: 'func2' }
|
||||
]
|
||||
};
|
||||
|
||||
const mockFile2 = {
|
||||
file: 'file2.ts',
|
||||
language: 'typescript',
|
||||
entities: [
|
||||
{ id: 'file2.ts', type: 'Module', name: 'file2.ts', visibility: 'public' },
|
||||
{ id: 'func2', type: 'Function', name: 'func2', visibility: 'private' }
|
||||
],
|
||||
relationships: [
|
||||
{ type: 'CONTAINS', source: 'file2.ts', target: 'func2' },
|
||||
{ type: 'IMPORTS', source: 'file2.ts', target: 'file1.ts' },
|
||||
{ type: 'CALLS', source: 'func2', target: 'func1' },
|
||||
// Duplicate edge from file1 to test deduplication
|
||||
{ type: 'CALLS', source: 'func1', target: 'func2' }
|
||||
]
|
||||
};
|
||||
|
||||
// --- 1. buildGraph() ---
|
||||
|
||||
runTest('buildGraph: Empty input (no results) -> empty graph', () => {
|
||||
const graph = GraphStore.buildGraph([]);
|
||||
assert.strictEqual(graph.nodes.size, 0);
|
||||
assert.strictEqual(graph.edges.length, 0);
|
||||
assert.strictEqual(graph.fileIndex.size, 0);
|
||||
});
|
||||
|
||||
runTest('buildGraph: Single file extraction -> correct node count, edge count', () => {
|
||||
const graph = GraphStore.buildGraph([mockFile1]);
|
||||
assert.strictEqual(graph.nodes.size, 2);
|
||||
assert.strictEqual(graph.edges.length, 2);
|
||||
});
|
||||
|
||||
runTest('buildGraph: Multiple file extractions -> merges correctly, no duplicate nodes', () => {
|
||||
// Pass mockFile1 twice to test node deduplication by ID Map
|
||||
const graph = GraphStore.buildGraph([mockFile1, mockFile2, mockFile1]);
|
||||
assert.strictEqual(graph.nodes.size, 4); // file1.ts, func1, file2.ts, func2
|
||||
});
|
||||
|
||||
runTest('buildGraph: Duplicate edges from multiple files are deduplicated', () => {
|
||||
const graph = GraphStore.buildGraph([mockFile1, mockFile2]);
|
||||
// Expected edges:
|
||||
// file1 CONTAINS func1
|
||||
// func1 CALLS func2
|
||||
// file2 CONTAINS func2
|
||||
// file2 IMPORTS file1
|
||||
// func2 CALLS func1
|
||||
// The second func1 CALLS func2 in mockFile2 should be ignored
|
||||
assert.strictEqual(graph.edges.length, 5);
|
||||
const callsFunc2 = graph.edges.filter(e => e.source === 'func1' && e.target === 'func2' && e.type === 'CALLS');
|
||||
assert.strictEqual(callsFunc2.length, 1);
|
||||
});
|
||||
|
||||
runTest('buildGraph: fileIndex is correctly populated', () => {
|
||||
const graph = GraphStore.buildGraph([mockFile1, mockFile2]);
|
||||
assert.strictEqual(graph.fileIndex.size, 2);
|
||||
assert.ok(graph.fileIndex.get('file1.ts').has('func1'));
|
||||
assert.ok(graph.fileIndex.get('file2.ts').has('func2'));
|
||||
});
|
||||
|
||||
// --- 2. saveSnapshot() / loadSnapshot() ---
|
||||
|
||||
const SNAPSHOT_PATH = path.join(__dirname, 'test-snapshot.json');
|
||||
|
||||
runTest('saveSnapshot/loadSnapshot: Round-trip -> verify nodes, edges, fileIndex match exactly', () => {
|
||||
const graph = GraphStore.buildGraph([mockFile1, mockFile2]);
|
||||
GraphStore.saveSnapshot(graph, SNAPSHOT_PATH);
|
||||
|
||||
const loaded = GraphStore.loadSnapshot(SNAPSHOT_PATH);
|
||||
assert.strictEqual(loaded.nodes.size, graph.nodes.size);
|
||||
assert.strictEqual(loaded.edges.length, graph.edges.length);
|
||||
assert.strictEqual(loaded.fileIndex.size, graph.fileIndex.size);
|
||||
|
||||
assert.deepStrictEqual(loaded.nodes.get('func1'), graph.nodes.get('func1'));
|
||||
assert.deepStrictEqual(loaded.edges, graph.edges);
|
||||
assert.deepStrictEqual(Array.from(loaded.fileIndex.get('file1.ts')), Array.from(graph.fileIndex.get('file1.ts')));
|
||||
|
||||
if (fs.existsSync(SNAPSHOT_PATH)) fs.unlinkSync(SNAPSHOT_PATH);
|
||||
});
|
||||
|
||||
runTest('saveSnapshot/loadSnapshot: Save creates valid JSON file', () => {
|
||||
const graph = GraphStore.buildGraph([mockFile1]);
|
||||
GraphStore.saveSnapshot(graph, SNAPSHOT_PATH);
|
||||
|
||||
const content = fs.readFileSync(SNAPSHOT_PATH, 'utf8');
|
||||
assert.doesNotThrow(() => JSON.parse(content));
|
||||
|
||||
if (fs.existsSync(SNAPSHOT_PATH)) fs.unlinkSync(SNAPSHOT_PATH);
|
||||
});
|
||||
|
||||
runTest('saveSnapshot/loadSnapshot: Load from non-existent file throws', () => {
|
||||
assert.throws(() => {
|
||||
GraphStore.loadSnapshot('does-not-exist.json');
|
||||
});
|
||||
});
|
||||
|
||||
// --- 3. query() ---
|
||||
|
||||
runTest('query: Query existing entity -> returns entity + correct incoming/outgoing edges', () => {
|
||||
const graph = GraphStore.buildGraph([mockFile1, mockFile2]);
|
||||
const result = GraphStore.query(graph, 'func1');
|
||||
|
||||
assert.ok(result);
|
||||
assert.strictEqual(result.entity.id, 'func1');
|
||||
|
||||
// Incoming: file1 CONTAINS func1, func2 CALLS func1
|
||||
assert.strictEqual(result.incoming.length, 2);
|
||||
|
||||
// Outgoing: func1 CALLS func2
|
||||
assert.strictEqual(result.outgoing.length, 1);
|
||||
});
|
||||
|
||||
runTest('query: Query non-existent entity -> returns null', () => {
|
||||
const graph = GraphStore.buildGraph([mockFile1]);
|
||||
const result = GraphStore.query(graph, 'non-existent');
|
||||
assert.strictEqual(result, null);
|
||||
});
|
||||
|
||||
runTest('query: Entity with no edges -> returns entity with empty incoming/outgoing arrays', () => {
|
||||
const isolatedEntity = {
|
||||
file: 'iso.ts',
|
||||
entities: [{ id: 'iso', type: 'Module' }],
|
||||
relationships: []
|
||||
};
|
||||
const graph = GraphStore.buildGraph([isolatedEntity]);
|
||||
const result = GraphStore.query(graph, 'iso');
|
||||
|
||||
assert.ok(result);
|
||||
assert.strictEqual(result.incoming.length, 0);
|
||||
assert.strictEqual(result.outgoing.length, 0);
|
||||
});
|
||||
|
||||
// --- 4. findCallers() ---
|
||||
|
||||
runTest('findCallers: Function with multiple callers -> returns all', () => {
|
||||
const multiCall = {
|
||||
file: 'multi.ts',
|
||||
entities: [{ id: 'target' }, { id: 'c1' }, { id: 'c2' }],
|
||||
relationships: [
|
||||
{ type: 'CALLS', source: 'c1', target: 'target' },
|
||||
{ type: 'CALLS', source: 'c2', target: 'target' }
|
||||
]
|
||||
};
|
||||
const graph = GraphStore.buildGraph([multiCall]);
|
||||
const callers = GraphStore.findCallers(graph, 'target');
|
||||
assert.strictEqual(callers.length, 2);
|
||||
const ids = callers.map(c => c.id);
|
||||
assert.ok(ids.includes('c1') && ids.includes('c2'));
|
||||
});
|
||||
|
||||
runTest('findCallers: Function with no callers -> returns empty array', () => {
|
||||
const graph = GraphStore.buildGraph([mockFile1]);
|
||||
const callers = GraphStore.findCallers(graph, 'func1');
|
||||
assert.strictEqual(callers.length, 0);
|
||||
});
|
||||
|
||||
runTest('findCallers: Only returns CALLS edges, not CONTAINS or IMPORTS', () => {
|
||||
const mixedEdges = {
|
||||
file: 'mixed.ts',
|
||||
entities: [{ id: 'target' }, { id: 'c1' }, { id: 'c2' }],
|
||||
relationships: [
|
||||
{ type: 'CALLS', source: 'c1', target: 'target' },
|
||||
{ type: 'CONTAINS', source: 'c2', target: 'target' },
|
||||
{ type: 'IMPORTS', source: 'c2', target: 'target' }
|
||||
]
|
||||
};
|
||||
const graph = GraphStore.buildGraph([mixedEdges]);
|
||||
const callers = GraphStore.findCallers(graph, 'target');
|
||||
assert.strictEqual(callers.length, 1);
|
||||
assert.strictEqual(callers[0].id, 'c1');
|
||||
});
|
||||
|
||||
// --- 5. findDependents() ---
|
||||
|
||||
runTest('findDependents: Module with dependents -> returns all', () => {
|
||||
const graph = GraphStore.buildGraph([mockFile1, mockFile2]);
|
||||
const dependents = GraphStore.findDependents(graph, 'file1.ts');
|
||||
assert.strictEqual(dependents.length, 1);
|
||||
assert.strictEqual(dependents[0].id, 'file2.ts');
|
||||
});
|
||||
|
||||
runTest('findDependents: Module with no dependents -> returns empty array', () => {
|
||||
const graph = GraphStore.buildGraph([mockFile1, mockFile2]);
|
||||
const dependents = GraphStore.findDependents(graph, 'file2.ts');
|
||||
assert.strictEqual(dependents.length, 0);
|
||||
});
|
||||
|
||||
// --- 6. getExports() ---
|
||||
|
||||
runTest('getExports: File with mix of public/private entities -> returns only public', () => {
|
||||
const graph = GraphStore.buildGraph([mockFile1, mockFile2]);
|
||||
const exportsFile1 = GraphStore.getExports(graph, 'file1.ts');
|
||||
assert.strictEqual(exportsFile1.length, 2); // Both file1.ts and func1 are public
|
||||
|
||||
const exportsFile2 = GraphStore.getExports(graph, 'file2.ts');
|
||||
assert.strictEqual(exportsFile2.length, 1); // Only file2.ts is public, func2 is private
|
||||
assert.strictEqual(exportsFile2[0].id, 'file2.ts');
|
||||
});
|
||||
|
||||
runTest('getExports: Non-existent file -> returns empty array', () => {
|
||||
const graph = GraphStore.buildGraph([mockFile1]);
|
||||
const exports = GraphStore.getExports(graph, 'missing.ts');
|
||||
assert.strictEqual(exports.length, 0);
|
||||
});
|
||||
|
||||
// --- 7. diffSnapshots() ---
|
||||
|
||||
runTest('diffSnapshots: Identical graphs -> empty diff', () => {
|
||||
const graph1 = GraphStore.buildGraph([mockFile1]);
|
||||
const graph2 = GraphStore.buildGraph([mockFile1]);
|
||||
const diff = GraphStore.diffSnapshots(graph1, graph2);
|
||||
|
||||
assert.strictEqual(diff.entities.added.length, 0);
|
||||
assert.strictEqual(diff.entities.removed.length, 0);
|
||||
assert.strictEqual(diff.entities.modified.length, 0);
|
||||
assert.strictEqual(diff.relationships.added.length, 0);
|
||||
assert.strictEqual(diff.relationships.removed.length, 0);
|
||||
});
|
||||
|
||||
runTest('diffSnapshots: Added entities -> appear in diff.entities.added', () => {
|
||||
const graph1 = GraphStore.buildGraph([mockFile1]);
|
||||
const graph2 = GraphStore.buildGraph([mockFile1, mockFile2]);
|
||||
const diff = GraphStore.diffSnapshots(graph1, graph2);
|
||||
|
||||
assert.strictEqual(diff.entities.added.length, 2); // file2.ts, func2
|
||||
const ids = diff.entities.added.map(e => e.id);
|
||||
assert.ok(ids.includes('file2.ts') && ids.includes('func2'));
|
||||
});
|
||||
|
||||
runTest('diffSnapshots: Removed entities -> appear in diff.entities.removed', () => {
|
||||
const graph1 = GraphStore.buildGraph([mockFile1, mockFile2]);
|
||||
const graph2 = GraphStore.buildGraph([mockFile1]);
|
||||
const diff = GraphStore.diffSnapshots(graph1, graph2);
|
||||
|
||||
assert.strictEqual(diff.entities.removed.length, 2);
|
||||
const ids = diff.entities.removed.map(e => e.id);
|
||||
assert.ok(ids.includes('file2.ts') && ids.includes('func2'));
|
||||
});
|
||||
|
||||
runTest('diffSnapshots: Modified entities (e.g. changed line_range) -> appear in diff.entities.modified', () => {
|
||||
const mockFile1Mod = JSON.parse(JSON.stringify(mockFile1));
|
||||
mockFile1Mod.entities[1].line_range = [10, 20];
|
||||
|
||||
const graph1 = GraphStore.buildGraph([mockFile1]);
|
||||
const graph2 = GraphStore.buildGraph([mockFile1Mod]);
|
||||
const diff = GraphStore.diffSnapshots(graph1, graph2);
|
||||
|
||||
assert.strictEqual(diff.entities.modified.length, 1);
|
||||
assert.strictEqual(diff.entities.modified[0].old.id, 'func1');
|
||||
});
|
||||
|
||||
runTest('diffSnapshots: Added relationships -> appear in diff.relationships.added', () => {
|
||||
const graph1 = GraphStore.buildGraph([mockFile1]);
|
||||
const graph2 = GraphStore.buildGraph([mockFile1, mockFile2]);
|
||||
const diff = GraphStore.diffSnapshots(graph1, graph2);
|
||||
|
||||
// file2 relationships will be added (3 of them: CONTAINS, IMPORTS, CALLS)
|
||||
assert.strictEqual(diff.relationships.added.length, 3);
|
||||
});
|
||||
|
||||
runTest('diffSnapshots: Removed relationships -> appear in diff.relationships.removed', () => {
|
||||
const graph1 = GraphStore.buildGraph([mockFile1, mockFile2]);
|
||||
const graph2 = GraphStore.buildGraph([mockFile1]);
|
||||
const diff = GraphStore.diffSnapshots(graph1, graph2);
|
||||
|
||||
assert.strictEqual(diff.relationships.removed.length, 3);
|
||||
});
|
||||
|
||||
// --- 8. Integration test ---
|
||||
|
||||
runTest('Integration test: extract.js on mask-api-key.ts -> buildGraph -> query', () => {
|
||||
const extractCmd = `node ${path.join(__dirname, '../extract.js')} /app/src/utils/mask-api-key.ts /app/src`;
|
||||
const output = execSync(extractCmd, { encoding: 'utf8' });
|
||||
const resultObj = JSON.parse(output);
|
||||
|
||||
const graph = GraphStore.buildGraph([resultObj]);
|
||||
const entityId = 'utils/mask-api-key.ts:maskApiKey';
|
||||
|
||||
const queryResult = GraphStore.query(graph, entityId);
|
||||
assert.ok(queryResult);
|
||||
assert.strictEqual(queryResult.entity.name, 'maskApiKey');
|
||||
assert.strictEqual(queryResult.outgoing.length, 2); // CALLS value.trim, CALLS trimmed.slice
|
||||
|
||||
const callTargets = queryResult.outgoing.map(e => e.target);
|
||||
assert.ok(callTargets.includes('value.trim'));
|
||||
assert.ok(callTargets.includes('trimmed.slice'));
|
||||
});
|
||||
|
||||
console.log(`\n${passed}/${total} tests passed.`);
|
||||
if (passed !== total) process.exit(1);
|
||||
Reference in New Issue
Block a user