use super::*;
use crate::index::scip::{ScipExternal, ScipReadResult, ScipReference, ScipRelationship};
fn make_def(
scip_symbol: &str,
qualified_name: &str,
file_path: &str,
kind: NodeKind,
line_start: u32,
line_end: u32,
) -> ScipDefinition {
ScipDefinition {
scip_symbol: scip_symbol.to_string(),
qualified_name: qualified_name.to_string(),
file_path: file_path.to_string(),
line_start,
line_end,
col_start: 0,
col_end: 10,
kind,
documentation: vec![],
relationships: vec![],
is_test: false,
is_generated: false,
}
}
fn make_ref(scip_symbol: &str, file_path: &str, line: u32, role_bitmask: i32) -> ScipReference {
ScipReference {
scip_symbol: scip_symbol.to_string(),
file_path: file_path.to_string(),
line,
col_start: 0,
col_end: 5,
role_bitmask,
}
}
#[test]
fn test_build_creates_sym_nodes() {
let scip = ScipReadResult {
project_root: String::new(),
definitions: vec![make_def(
"rust-analyzer cargo foo 1.0 bar/baz().",
"bar::baz",
"src/bar.rs",
NodeKind::Function,
10,
20,
)],
references: vec![],
externals: vec![],
covered_files: vec!["src/bar.rs".to_string()],
};
let result = build_graph(&scip, Some("test-ns"), &ScipConfig::default());
let def_nodes: Vec<_> = result
.nodes
.iter()
.filter(|n| {
n.id.starts_with("sym:")
&& n.payload.get("source").and_then(|v| v.as_str()) == Some("scip")
})
.collect();
assert_eq!(def_nodes.len(), 1);
assert_eq!(def_nodes[0].id, "sym:bar::baz");
assert_eq!(def_nodes[0].kind, NodeKind::Function);
assert_eq!(def_nodes[0].namespace, Some("test-ns".to_string()));
}
#[test]
fn test_build_creates_flat_contains_edges() {
let config = ScipConfig {
hierarchical_containment: false,
..ScipConfig::default()
};
let scip = ScipReadResult {
project_root: String::new(),
definitions: vec![make_def(
"rust-analyzer cargo foo 1.0 bar/baz().",
"bar::baz",
"src/bar.rs",
NodeKind::Function,
10,
20,
)],
references: vec![],
externals: vec![],
covered_files: vec!["src/bar.rs".to_string()],
};
let result = build_graph(&scip, None, &config);
let contains: Vec<_> = result
.edges
.iter()
.filter(|e| e.relationship == RelationshipType::Contains)
.collect();
assert_eq!(contains.len(), 1);
assert_eq!(contains[0].src, "file:src/bar.rs");
assert_eq!(contains[0].dst, "sym:bar::baz");
}
#[test]
fn test_hierarchical_containment_chain() {
let scip = ScipReadResult {
project_root: String::new(),
definitions: vec![make_def(
"rust-analyzer cargo foo 1.0 auth/middleware/validate_token().",
"auth::middleware::validate_token",
"src/auth.rs",
NodeKind::Function,
10,
20,
)],
references: vec![],
externals: vec![],
covered_files: vec!["src/auth.rs".to_string()],
};
let result = build_graph(&scip, Some("test-ns"), &ScipConfig::default());
let contains: Vec<_> = result
.edges
.iter()
.filter(|e| e.relationship == RelationshipType::Contains)
.collect();
assert_eq!(contains.len(), 3, "should have 3 CONTAINS edges in chain");
assert!(contains
.iter()
.any(|e| e.src == "file:src/auth.rs" && e.dst == "sym:auth"));
assert!(contains
.iter()
.any(|e| e.src == "sym:auth" && e.dst == "sym:auth::middleware"));
assert!(contains.iter().any(
|e| e.src == "sym:auth::middleware" && e.dst == "sym:auth::middleware::validate_token"
));
let auth_node = result.nodes.iter().find(|n| n.id == "sym:auth");
assert!(auth_node.is_some(), "should create synthetic auth node");
assert_eq!(auth_node.unwrap().kind, NodeKind::Module);
assert_eq!(
auth_node
.unwrap()
.payload
.get("source")
.and_then(|v| v.as_str()),
Some("scip-synthetic")
);
let mw_node = result.nodes.iter().find(|n| n.id == "sym:auth::middleware");
assert!(mw_node.is_some(), "should create synthetic middleware node");
assert_eq!(mw_node.unwrap().kind, NodeKind::Module);
}
#[test]
fn test_hierarchical_deduplicates_intermediate_nodes() {
let scip = ScipReadResult {
project_root: String::new(),
definitions: vec![
make_def(
"rust-analyzer cargo foo 1.0 auth/login().",
"auth::login",
"src/auth.rs",
NodeKind::Function,
1,
10,
),
make_def(
"rust-analyzer cargo foo 1.0 auth/logout().",
"auth::logout",
"src/auth.rs",
NodeKind::Function,
20,
30,
),
],
references: vec![],
externals: vec![],
covered_files: vec!["src/auth.rs".to_string()],
};
let result = build_graph(&scip, None, &ScipConfig::default());
let auth_nodes: Vec<_> = result.nodes.iter().filter(|n| n.id == "sym:auth").collect();
assert_eq!(auth_nodes.len(), 1, "should deduplicate intermediate nodes");
let contains: Vec<_> = result
.edges
.iter()
.filter(|e| e.relationship == RelationshipType::Contains)
.collect();
assert_eq!(contains.len(), 3, "file→auth, auth→login, auth→logout");
}
#[test]
fn test_build_creates_pkg_nodes() {
let scip = ScipReadResult {
project_root: String::new(),
definitions: vec![],
references: vec![],
externals: vec![
ScipExternal {
scip_symbol: "rust-analyzer cargo serde 1.0.0 Serialize#serialize().".to_string(),
package_manager: "cargo".to_string(),
package_name: "serde".to_string(),
package_version: "1.0.0".to_string(),
kind: NodeKind::Method,
documentation: vec!["Serialize this value".to_string()],
},
ScipExternal {
scip_symbol: "rust-analyzer cargo serde 1.0.0 Deserialize#deserialize()."
.to_string(),
package_manager: "cargo".to_string(),
package_name: "serde".to_string(),
package_version: "1.0.0".to_string(),
kind: NodeKind::Method,
documentation: vec![],
},
],
covered_files: vec![],
};
let result = build_graph(&scip, None, &ScipConfig::default());
assert_eq!(result.ext_nodes_created, 1);
assert_eq!(result.nodes.len(), 1);
assert_eq!(result.nodes[0].kind, NodeKind::External);
assert_eq!(result.nodes[0].id, "pkg:cargo:serde");
assert_eq!(result.nodes[0].label, "serde");
assert_eq!(result.doc_memories_created, 0);
}
#[test]
fn test_build_creates_call_edges_from_refs() {
let scip = ScipReadResult {
project_root: String::new(),
definitions: vec![
make_def(
"rust-analyzer cargo foo 1.0 bar/caller().",
"bar::caller",
"src/bar.rs",
NodeKind::Function,
1,
50,
),
make_def(
"rust-analyzer cargo foo 1.0 bar/callee().",
"bar::callee",
"src/bar.rs",
NodeKind::Function,
60,
80,
),
],
references: vec![make_ref(
"rust-analyzer cargo foo 1.0 bar/callee().",
"src/bar.rs",
25, 0, )],
externals: vec![],
covered_files: vec!["src/bar.rs".to_string()],
};
let result = build_graph(&scip, None, &ScipConfig::default());
let calls: Vec<_> = result
.edges
.iter()
.filter(|e| e.relationship == RelationshipType::Calls)
.collect();
assert_eq!(calls.len(), 1);
assert_eq!(calls[0].src, "sym:bar::caller");
assert_eq!(calls[0].dst, "sym:bar::callee");
}
#[test]
fn test_build_skips_high_fanout_refs() {
let mut refs = Vec::new();
for i in 0..101 {
refs.push(make_ref(
"rust-analyzer cargo foo 1.0 bar/utility().",
"src/bar.rs",
i,
0,
));
}
let scip = ScipReadResult {
project_root: String::new(),
definitions: vec![
make_def(
"rust-analyzer cargo foo 1.0 bar/caller().",
"bar::caller",
"src/bar.rs",
NodeKind::Function,
0,
200,
),
make_def(
"rust-analyzer cargo foo 1.0 bar/utility().",
"bar::utility",
"src/bar.rs",
NodeKind::Function,
300,
310,
),
],
references: refs,
externals: vec![],
covered_files: vec!["src/bar.rs".to_string()],
};
let result = build_graph(&scip, None, &ScipConfig::default());
let calls: Vec<_> = result
.edges
.iter()
.filter(|e| e.relationship == RelationshipType::Calls)
.collect();
assert_eq!(calls.len(), 0);
}
#[test]
fn test_build_import_edges() {
let scip = ScipReadResult {
project_root: String::new(),
definitions: vec![
make_def(
"rust-analyzer cargo foo 1.0 bar/func().",
"bar::func",
"src/bar.rs",
NodeKind::Function,
1,
50,
),
make_def(
"rust-analyzer cargo foo 1.0 baz/helper().",
"baz::helper",
"src/baz.rs",
NodeKind::Function,
1,
30,
),
],
references: vec![make_ref(
"rust-analyzer cargo foo 1.0 baz/helper().",
"src/bar.rs",
5,
0x2, )],
externals: vec![],
covered_files: vec!["src/bar.rs".to_string(), "src/baz.rs".to_string()],
};
let result = build_graph(&scip, None, &ScipConfig::default());
let imports: Vec<_> = result
.edges
.iter()
.filter(|e| e.relationship == RelationshipType::Imports)
.collect();
assert_eq!(imports.len(), 1);
assert_eq!(imports[0].dst, "sym:baz::helper");
}
#[test]
fn test_build_implements_edges() {
let mut def = make_def(
"rust-analyzer cargo foo 1.0 MyStruct#.",
"MyStruct",
"src/lib.rs",
NodeKind::Class,
1,
50,
);
def.relationships.push(ScipRelationship {
target_symbol: "rust-analyzer cargo foo 1.0 MyTrait#.".to_string(),
is_implementation: true,
is_type_definition: false,
is_reference: false,
is_definition: false,
});
let target_def = make_def(
"rust-analyzer cargo foo 1.0 MyTrait#.",
"MyTrait",
"src/lib.rs",
NodeKind::Trait,
60,
80,
);
let scip = ScipReadResult {
project_root: String::new(),
definitions: vec![def, target_def],
references: vec![],
externals: vec![],
covered_files: vec!["src/lib.rs".to_string()],
};
let result = build_graph(&scip, None, &ScipConfig::default());
let impls: Vec<_> = result
.edges
.iter()
.filter(|e| e.relationship == RelationshipType::Implements)
.collect();
assert_eq!(impls.len(), 1);
assert_eq!(impls[0].src, "sym:MyStruct");
assert_eq!(impls[0].dst, "sym:MyTrait");
}
#[test]
fn test_build_doc_memories() {
let mut def = make_def(
"rust-analyzer cargo foo 1.0 bar/baz().",
"bar::baz",
"src/bar.rs",
NodeKind::Function,
10,
20,
);
def.documentation = vec![
"fn baz() -> Result<(), Error>".to_string(),
"Does something useful.".to_string(),
];
let scip = ScipReadResult {
project_root: String::new(),
definitions: vec![def],
references: vec![],
externals: vec![],
covered_files: vec!["src/bar.rs".to_string()],
};
let result = build_graph(&scip, None, &ScipConfig::default());
assert_eq!(result.doc_memories_created, 1);
let (mem, related_node) = &result.memories[0];
assert_eq!(related_node, "sym:bar::baz");
assert!(mem.content.contains("fn baz()"));
assert_eq!(mem.memory_type, MemoryType::Context);
assert!(mem.tags.contains(&"scip-doc".to_string()));
assert!(mem.tags.contains(&"auto-generated".to_string()));
}
#[test]
fn test_build_test_nodes() {
let mut def = make_def(
"rust-analyzer cargo foo 1.0 tests/test_bar().",
"tests::test_bar",
"src/tests.rs",
NodeKind::Function,
1,
10,
);
def.is_test = true;
let scip = ScipReadResult {
project_root: String::new(),
definitions: vec![def],
references: vec![],
externals: vec![],
covered_files: vec!["src/tests.rs".to_string()],
};
let result = build_graph(&scip, None, &ScipConfig::default());
assert_eq!(result.nodes[0].kind, NodeKind::Test);
}
#[test]
fn test_scip_edge_properties() {
let props = scip_edge_properties();
assert_eq!(props.get("source").unwrap(), "scip");
assert_eq!(props.get("confidence").unwrap(), &serde_json::json!(0.15));
}
#[test]
fn test_edge_deduplication() {
let scip = ScipReadResult {
project_root: String::new(),
definitions: vec![
make_def(
"rust-analyzer cargo foo 1.0 bar/caller().",
"bar::caller",
"src/bar.rs",
NodeKind::Function,
1,
100,
),
make_def(
"rust-analyzer cargo foo 1.0 bar/callee().",
"bar::callee",
"src/bar.rs",
NodeKind::Function,
110,
120,
),
],
references: vec![
make_ref(
"rust-analyzer cargo foo 1.0 bar/callee().",
"src/bar.rs",
10,
0,
),
make_ref(
"rust-analyzer cargo foo 1.0 bar/callee().",
"src/bar.rs",
20,
0,
),
],
externals: vec![],
covered_files: vec!["src/bar.rs".to_string()],
};
let result = build_graph(&scip, None, &ScipConfig::default());
let calls: Vec<_> = result
.edges
.iter()
.filter(|e| e.relationship == RelationshipType::Calls)
.collect();
assert_eq!(calls.len(), 2);
}
#[test]
fn test_read_only_role_treated_as_calls() {
let scip = ScipReadResult {
project_root: String::new(),
definitions: vec![
make_def(
"scip-go gomod foo 1.0 bar/caller().",
"bar.caller",
"src/bar.go",
NodeKind::Function,
1,
50,
),
make_def(
"scip-go gomod foo 1.0 bar/callee().",
"bar.callee",
"src/bar.go",
NodeKind::Function,
60,
80,
),
],
references: vec![make_ref(
"scip-go gomod foo 1.0 bar/callee().",
"src/bar.go",
25,
0x8, )],
externals: vec![],
covered_files: vec!["src/bar.go".to_string()],
};
let result = build_graph(&scip, None, &ScipConfig::default());
let calls: Vec<_> = result
.edges
.iter()
.filter(|e| e.relationship == RelationshipType::Calls)
.collect();
let reads: Vec<_> = result
.edges
.iter()
.filter(|e| e.relationship == RelationshipType::Reads)
.collect();
assert_eq!(calls.len(), 1, "READ_ACCESS-only should become CALLS");
assert_eq!(reads.len(), 0, "should not create READS edges");
}
#[test]
fn test_read_with_import_stays_import() {
let scip = ScipReadResult {
project_root: String::new(),
definitions: vec![
make_def(
"rust-analyzer cargo foo 1.0 bar/func().",
"bar::func",
"src/bar.rs",
NodeKind::Function,
1,
50,
),
make_def(
"rust-analyzer cargo foo 1.0 baz/helper().",
"baz::helper",
"src/baz.rs",
NodeKind::Function,
1,
30,
),
],
references: vec![make_ref(
"rust-analyzer cargo foo 1.0 baz/helper().",
"src/bar.rs",
5,
0x2 | 0x8, )],
externals: vec![],
covered_files: vec!["src/bar.rs".to_string(), "src/baz.rs".to_string()],
};
let result = build_graph(&scip, None, &ScipConfig::default());
let imports: Vec<_> = result
.edges
.iter()
.filter(|e| e.relationship == RelationshipType::Imports)
.collect();
assert_eq!(imports.len(), 1, "IMPORT+READ should stay IMPORTS");
}
#[test]
fn test_stub_pkg_nodes_created_for_missing_targets() {
let scip = ScipReadResult {
project_root: String::new(),
definitions: vec![make_def(
"scip-go gomod foo 1.0 bar/caller().",
"bar.caller",
"src/bar.go",
NodeKind::Function,
1,
50,
)],
references: vec![make_ref(
"scip-go gomod golang.org/x/net 0.1.0 http/Get().",
"src/bar.go",
10,
0, )],
externals: vec![], covered_files: vec!["src/bar.go".to_string()],
};
let result = build_graph(&scip, None, &ScipConfig::default());
let pkg_nodes: Vec<_> = result
.nodes
.iter()
.filter(|n| n.id.starts_with("pkg:"))
.collect();
assert_eq!(pkg_nodes.len(), 1, "should create stub pkg: node");
assert_eq!(pkg_nodes[0].kind, NodeKind::External);
assert_eq!(pkg_nodes[0].id, "pkg:gomod:golang.org/x/net");
assert_eq!(
pkg_nodes[0].payload.get("source").and_then(|v| v.as_str()),
Some("scip")
);
let calls: Vec<_> = result
.edges
.iter()
.filter(|e| e.relationship == RelationshipType::Calls)
.collect();
assert_eq!(calls.len(), 1);
assert_eq!(calls[0].dst, "pkg:gomod:golang.org/x/net");
}
#[test]
fn test_wildcard_modules_filtered() {
let scip = ScipReadResult {
project_root: String::new(),
definitions: vec![
make_def(
"scip-typescript npm foo 1.0 src/types/global.d.ts/'*.css'.",
"src.types.global.d.ts.'*.css'",
"src/types/global.d.ts",
NodeKind::Module,
86,
86,
),
make_def(
"scip-typescript npm foo 1.0 src/App#.",
"src.App",
"src/App.tsx",
NodeKind::Class,
1,
100,
),
],
references: vec![],
externals: vec![],
covered_files: vec![
"src/types/global.d.ts".to_string(),
"src/App.tsx".to_string(),
],
};
let result = build_graph(&scip, None, &ScipConfig::default());
let sym_nodes: Vec<_> = result
.nodes
.iter()
.filter(|n| n.id.starts_with("sym:"))
.collect();
assert_eq!(sym_nodes.len(), 1, "wildcard module should be filtered");
assert_eq!(sym_nodes[0].id, "sym:src.App");
}
#[test]
fn test_intra_class_edge_collapsing() {
let scip = ScipReadResult {
project_root: String::new(),
definitions: vec![
make_def(
"rust-analyzer cargo foo 1.0 MyClass#method_a().",
"MyClass::method_a",
"src/lib.rs",
NodeKind::Method,
10,
30,
),
make_def(
"rust-analyzer cargo foo 1.0 MyClass#method_b().",
"MyClass::method_b",
"src/lib.rs",
NodeKind::Method,
40,
60,
),
make_def(
"rust-analyzer cargo foo 1.0 MyClass#.",
"MyClass",
"src/lib.rs",
NodeKind::Class,
1,
100,
),
],
references: vec![
make_ref(
"rust-analyzer cargo foo 1.0 MyClass#method_b().",
"src/lib.rs",
20, 0,
),
make_ref(
"rust-analyzer cargo foo 1.0 MyClass#method_a().",
"src/lib.rs",
50, 0,
),
],
externals: vec![],
covered_files: vec!["src/lib.rs".to_string()],
};
let config = ScipConfig {
collapse_intra_class_edges: true,
..ScipConfig::default()
};
let result = build_graph(&scip, None, &config);
let calls: Vec<_> = result
.edges
.iter()
.filter(|e| e.relationship == RelationshipType::Calls)
.collect();
assert_eq!(calls.len(), 0, "intra-class calls should be collapsed");
let class_node = result
.nodes
.iter()
.find(|n| n.id == "sym:MyClass")
.expect("class node should exist");
let intra_calls = class_node
.payload
.get("intra_class_calls")
.expect("should have intra_class_calls metadata");
let entries = intra_calls.as_array().expect("should be array");
assert_eq!(entries.len(), 2, "should have 2 intra-class call pairs");
}
#[test]
fn test_blocklist_filters_builtin_calls() {
let scip = ScipReadResult {
project_root: String::new(),
definitions: vec![
make_def(
"rust-analyzer cargo foo 1.0 bar/caller().",
"bar::caller",
"src/bar.rs",
NodeKind::Function,
1,
50,
),
make_def(
"rust-analyzer cargo foo 1.0 bar/callee().",
"bar::callee",
"src/bar.rs",
NodeKind::Function,
60,
80,
),
],
references: vec![
make_ref(
"rust-analyzer cargo foo 1.0 bar/callee().",
"src/bar.rs",
10,
0,
),
make_ref(
"scip-cargo std 1.0 core/Clone#clone().",
"src/bar.rs",
20,
0,
),
],
externals: vec![],
covered_files: vec!["src/bar.rs".to_string()],
};
let result = build_graph(&scip, None, &ScipConfig::default());
let calls: Vec<_> = result
.edges
.iter()
.filter(|e| e.relationship == RelationshipType::Calls)
.collect();
assert_eq!(calls.len(), 1, "blocked builtin call should be filtered");
assert_eq!(
calls[0].dst, "sym:bar::callee",
"only the user-defined callee should have an edge"
);
}
#[test]
fn test_intra_module_edges_not_collapsed() {
let scip = ScipReadResult {
project_root: String::new(),
definitions: vec![
make_def(
"rust-analyzer cargo foo 1.0 mymod/func_a().",
"mymod::func_a",
"src/lib.rs",
NodeKind::Function,
10,
30,
),
make_def(
"rust-analyzer cargo foo 1.0 mymod/func_b().",
"mymod::func_b",
"src/lib.rs",
NodeKind::Function,
40,
60,
),
],
references: vec![make_ref(
"rust-analyzer cargo foo 1.0 mymod/func_b().",
"src/lib.rs",
20,
0,
)],
externals: vec![],
covered_files: vec!["src/lib.rs".to_string()],
};
let config = ScipConfig {
collapse_intra_class_edges: true,
..ScipConfig::default()
};
let result = build_graph(&scip, None, &config);
let calls: Vec<_> = result
.edges
.iter()
.filter(|e| e.relationship == RelationshipType::Calls)
.collect();
assert_eq!(
calls.len(),
1,
"inter-function calls in a module should NOT be collapsed"
);
}
fn check_noise(scip_symbol: &str, file_path: &str, is_generated: bool) -> bool {
let mut def = make_def(scip_symbol, "qn", file_path, NodeKind::Function, 1, 1);
def.is_generated = is_generated;
let parsed = scip::symbol::parse_symbol(scip_symbol).unwrap_or_default();
is_noise_symbol(&def, &parsed)
}
#[test]
fn noise_filter_skips_generated_code() {
assert!(!is_source_path("src/__generated__/graphql.ts"));
assert!(check_noise(
"scip-typescript npm homepage 1.0.0 src/types#Query.",
"src/types.ts",
true
));
}
#[test]
fn noise_filter_skips_bundle_files() {
assert!(!is_source_path(
"frontend/static/webpack_bundles/partnerPortal.bundle.js"
));
assert!(!is_source_path("dist/app.min.js"));
assert!(!is_source_path("build/output.js"));
}
#[test]
fn noise_filter_skips_type_literals() {
assert!(check_noise(
"scip-typescript npm homepage 1.0.0 src/graphql#QueryArgs.typeLiteral103.first.",
"src/graphql.ts",
false
));
}
#[test]
fn noise_filter_skips_parameters() {
assert!(check_noise(
"scip-typescript npm homepage 1.0.0 src/auth#validate().(token)",
"src/auth.ts",
false
));
}
#[test]
fn noise_filter_skips_type_parameters() {
assert!(check_noise(
"scip-typescript npm homepage 1.0.0 src/utils#identity().[T]",
"src/utils.ts",
false
));
}
#[test]
fn noise_filter_skips_locals_inside_methods() {
assert!(check_noise(
"scip-typescript npm homepage 1.0.0 src/auth#validate().token.",
"src/auth.ts",
false
));
}
#[test]
fn noise_filter_skips_positional_terms() {
assert!(check_noise(
"scip-typescript npm homepage 1.0.0 src/component#Component.name0.",
"src/component.ts",
false
));
assert!(check_noise(
"scip-typescript npm homepage 1.0.0 src/component#Component.key21.",
"src/component.ts",
false
));
}
#[test]
fn noise_filter_keeps_real_functions() {
assert!(!check_noise(
"scip-typescript npm homepage 1.0.0 src/auth#validateToken().",
"src/auth.ts",
false
));
assert!(!check_noise(
"scip-typescript npm homepage 1.0.0 src/hooks#useCustomCompareMemo().",
"src/hooks.ts",
false
));
}
#[test]
fn noise_filter_keeps_class_fields() {
assert!(!check_noise(
"scip-typescript npm homepage 1.0.0 src/types#Track#title.",
"src/types.ts",
false
));
}
#[test]
fn noise_filter_end_to_end_build_graph() {
let scip = ScipReadResult {
project_root: String::new(),
definitions: vec![
make_def(
"scip-typescript npm pkg 1.0.0 src/auth#validate().",
"auth.validate",
"src/auth.ts",
NodeKind::Function,
1,
10,
),
make_def(
"scip-typescript npm pkg 1.0.0 src/graphql#Query.typeLiteral5.first.",
"graphql.Query.typeLiteral5.first",
"src/graphql.ts",
NodeKind::Function,
1,
1,
),
make_def(
"scip-typescript npm pkg 1.0.0 src/auth#validate().(token)",
"auth.validate.token",
"src/auth.ts",
NodeKind::Function,
1,
1,
),
make_def(
"scip-typescript npm pkg 1.0.0 src/__generated__/types#Query.",
"gen.Query",
"src/__generated__/types.ts",
NodeKind::Class,
1,
5,
),
make_def(
"scip-typescript npm pkg 1.0.0 static/app.bundle#init().",
"app.init",
"static/app.bundle.js",
NodeKind::Function,
1,
1,
),
make_def(
"scip-typescript npm pkg 1.0.0 src/payments#PaymentProcessor#",
"payments.PaymentProcessor",
"src/payments.ts",
NodeKind::Class,
1,
20,
),
],
references: vec![],
externals: vec![],
covered_files: vec!["src/auth.ts".to_string(), "src/payments.ts".to_string()],
};
let result = build_graph(&scip, Some("test-ns"), &ScipConfig::default());
let sym_nodes: Vec<_> = result
.nodes
.iter()
.filter(|n| n.payload.get("source").and_then(|v| v.as_str()) == Some("scip"))
.collect();
let node_ids: Vec<&str> = sym_nodes.iter().map(|n| n.id.as_str()).collect();
assert!(
node_ids.contains(&"sym:auth.validate"),
"real function should survive, got: {node_ids:?}"
);
assert!(
node_ids.contains(&"sym:payments.PaymentProcessor"),
"real class should survive, got: {node_ids:?}"
);
assert!(
!node_ids.iter().any(|id| id.contains("typeLiteral")),
"typeLiteral should be filtered"
);
assert!(
!node_ids.iter().any(|id| id.contains("token")),
"parameter should be filtered"
);
assert!(
!node_ids.iter().any(|id| id.contains("gen.Query")),
"generated file should be filtered"
);
assert!(
!node_ids.iter().any(|id| id.contains("app.init")),
"bundle file should be filtered"
);
}