use crate::domain::{
CodeRepositoryRegistration, RepositoryCodeRange, RepositoryCodeReferenceRecord,
RepositoryCodeSymbolRecord,
};
use super::*;
#[test]
fn nested_symbols_receive_stable_canonical_qualified_names() {
let mut build = snapshot_build();
let source = br#"
class RetryPolicy {
void run() {
backoff();
}
void backoff() {}
}
"#;
parse_indexed_file(&mut build, "src/RetryPolicy.java", source).expect("file should parse");
let snapshot = build.finish();
let method = snapshot
.symbols
.iter()
.find(|symbol| symbol.name == "run")
.expect("method should be extracted");
let call = snapshot
.references
.iter()
.find(|reference| reference.name == "backoff")
.expect("call should be extracted");
assert_eq!(method.qualified_name, "src::RetryPolicy::RetryPolicy.run");
assert_eq!(
method.canonical_symbol_id,
"repo://repo/src::RetryPolicy::RetryPolicy.run"
);
assert_eq!(call.resolution_state, "resolved");
assert_eq!(call.confidence_tier, "inferred");
}
#[test]
fn nested_functions_are_canonical_symbol_containers() {
let mut build = snapshot_build();
let source = br#"
def outer_a():
def inner():
return 1
return inner()
def outer_b():
def inner():
return 2
return inner()
"#;
parse_indexed_file(&mut build, "src/nested.py", source).expect("file should parse");
let snapshot = build.finish();
let inner_names = snapshot
.symbols
.iter()
.filter(|symbol| symbol.name == "inner")
.map(|symbol| symbol.canonical_symbol_id.as_str())
.collect::<Vec<_>>();
assert_eq!(inner_names.len(), 2);
assert!(inner_names.contains(&"repo://repo/src::nested::outer_a.inner"));
assert!(inner_names.contains(&"repo://repo/src::nested::outer_b.inner"));
}
#[test]
fn rust_tag_and_manual_symbols_share_one_snapshot_identity() {
let snapshot = parse_snapshot(
"src/auth.rs",
br#"
trait S3Auth { async fn get_secret_key(&self); }
struct IAMAuth;
impl S3Auth for IAMAuth { async fn get_secret_key(&self) {} }
impl IAMAuth { fn new() -> Self { IAMAuth } }
"#,
);
for (index, symbol) in snapshot.symbols.iter().enumerate() {
assert!(
snapshot
.symbols
.iter()
.skip(index + 1)
.all(|other| other.symbol_snapshot_id != symbol.symbol_snapshot_id),
"duplicate symbol_snapshot_id for {} at {:?}",
symbol.name,
symbol.line_range
);
}
assert!(
snapshot
.symbols
.iter()
.any(|symbol| symbol.name == "get_secret_key")
);
}
#[test]
fn duplicate_symbol_snapshot_identity_keeps_single_symbol_record() {
let mut output = FileParseOutput::new();
records::upsert_symbol(&mut output, duplicate_identity_symbol("class"));
records::upsert_symbol(&mut output, duplicate_identity_symbol("module"));
assert_eq!(output.symbols.len(), 1);
assert_eq!(output.symbols[0].symbol_snapshot_id, "symbol:Session");
}
#[test]
fn duplicate_reference_identity_keeps_single_reference_record() {
let mut output = FileParseOutput::new();
records::upsert_reference(&mut output, duplicate_reference("call"));
records::upsert_reference(&mut output, duplicate_reference("implementation"));
assert_eq!(output.references.len(), 1);
assert_eq!(output.references[0].reference_id, "reference:run");
}
#[test]
fn extracted_unresolved_references_start_with_low_confidence() {
let mut build = snapshot_build();
parse_indexed_file(&mut build, "src/app.rs", b"fn run() { missing(); }\n")
.expect("file should parse");
let reference = build
.references
.iter()
.find(|reference| reference.name == "missing")
.expect("call reference should be extracted before finalization");
assert_eq!(reference.resolution_state, "unresolved");
assert_eq!(reference.confidence_basis_points, 2_500);
assert_eq!(reference.confidence_tier, "ambiguous");
}
#[test]
fn typescript_function_factory_members_are_call_containers() {
let snapshot = parse_snapshot(
"src/agent.ts",
br#"
export const layer = Service.of({
generate: Effect.fn("Agent.generate")(function* (input: Input) {
const params = buildParams(input)
return yield* Effect.promise(() => generateObject(params).then((r) => r.object))
}),
})
"#,
);
let generate = snapshot
.symbols
.iter()
.find(|symbol| symbol.name == "generate")
.expect("function factory object member should be indexed as a symbol");
let call = snapshot
.calls
.iter()
.find(|call| call.callee_name == "generateObject")
.expect("generateObject call should be indexed");
let chunk = snapshot
.chunks
.iter()
.find(|chunk| chunk.symbol_snapshot_id.as_deref() == Some(&generate.symbol_snapshot_id))
.expect("function factory member should have a retrievable chunk");
assert_eq!(call.caller_name.as_deref(), Some("generate"));
assert!(chunk.content.contains("generateObject(params)"));
}
#[test]
fn typescript_data_transform_members_are_not_function_factory_symbols() {
let snapshot = parse_snapshot(
"src/converter.ts",
br#"
const converted = {
tool_calls: content.filter((c) => c.type === "tool_use").map((c) => ({ id: c.id })),
}
"#,
);
assert!(
snapshot
.symbols
.iter()
.all(|symbol| symbol.name != "tool_calls")
);
}
#[test]
fn java_object_creation_expressions_are_constructor_call_edges() {
let snapshot = parse_snapshot(
"src/app/Worker.java",
br#"
class Box<T> {}
class Session {}
class Worker {
Box<Session> create() {
return new Box<Session>();
}
}
"#,
);
let call = snapshot
.calls
.iter()
.find(|call| call.callee_name == "Box")
.expect("generic object construction should be indexed as a call to the constructed type");
assert_eq!(call.caller_name.as_deref(), Some("create"));
assert_eq!(call.resolution_state, "resolved");
assert!(
!snapshot
.calls
.iter()
.any(|call| call.callee_name == "Session"),
"generic type arguments must not become constructor callees",
);
}
#[test]
fn csharp_target_typed_new_does_not_index_argument_as_callee() {
let snapshot = parse_snapshot(
"src/app/Worker.cs",
br#"
class Foo {
public Foo(object value) {}
}
class Worker {
Foo Create(object bar) {
Foo inferred = new(bar);
return new Foo(bar);
}
}
"#,
);
assert!(
!snapshot.calls.iter().any(|call| call.callee_name == "bar"),
"target-typed object creation must not fall back to argument identifiers"
);
}
fn parse_snapshot(path: &str, source: &[u8]) -> crate::domain::CodeIndexSnapshot {
let mut build = snapshot_build();
parse_indexed_file(&mut build, path, source).expect("file should parse");
build.finish()
}
fn snapshot_build() -> SnapshotBuild {
let registration =
CodeRepositoryRegistration::new("repo", "alias", "/tmp/repo", Vec::new(), Vec::new())
.expect("registration should validate");
SnapshotBuild::new(
®istration,
"commit".to_owned(),
"tree".to_owned(),
true,
1,
0,
)
}
fn duplicate_identity_symbol(kind: &str) -> RepositoryCodeSymbolRecord {
RepositoryCodeSymbolRecord {
repository_id: "repo".to_owned(),
source_scope: "scope".to_owned(),
symbol_snapshot_id: "symbol:Session".to_owned(),
canonical_symbol_id: "src::Session".to_owned(),
file_id: "file:Session".to_owned(),
path: "src/Session.swift".to_owned(),
language_id: "swift".to_owned(),
name: "Session".to_owned(),
qualified_name: "src::Session".to_owned(),
kind: kind.to_owned(),
signature: "class Session".to_owned(),
doc_comment: None,
byte_range: RepositoryCodeRange::new("byte_range", 0, 14)
.expect("byte range should validate"),
line_range: RepositoryCodeRange::new("line_range", 1, 1)
.expect("line range should validate"),
symbol_role: None,
}
}
fn duplicate_reference(kind: &str) -> RepositoryCodeReferenceRecord {
RepositoryCodeReferenceRecord {
repository_id: "repo".to_owned(),
source_scope: "scope".to_owned(),
reference_id: "reference:run".to_owned(),
file_id: "file:run".to_owned(),
path: "src/lib.rs".to_owned(),
name: "run".to_owned(),
kind: kind.to_owned(),
target_symbol_snapshot_id: None,
target_hint: Some("run".to_owned()),
resolution_state: "unresolved".to_owned(),
confidence_basis_points: 5_000,
confidence_tier: "ambiguous".to_owned(),
byte_range: RepositoryCodeRange::new("byte_range", 10, 13)
.expect("byte range should validate"),
line_range: RepositoryCodeRange::new("line_range", 2, 2)
.expect("line range should validate"),
}
}