#[test]
fn test_save_load_roundtrip_v1_1() {
let temp_dir = tempfile::TempDir::new().unwrap();
let project_path = temp_dir.path();
std::fs::create_dir_all(project_path.join("src")).unwrap();
std::fs::write(
project_path.join("src/lib.rs"),
"fn hello() { world(); }\nfn world() { println!(\"hi\"); }\n",
)
.unwrap();
let index = AgentContextIndex::build(project_path).unwrap();
let index_path = project_path.join("idx");
index.save(&index_path).unwrap();
let loaded = AgentContextIndex::load(&index_path).unwrap();
assert!(
loaded.manifest.version == "2.0.0" || loaded.manifest.version == "1.4.0",
"expected v2.0.0 or v1.4.0, got {}",
loaded.manifest.version,
);
assert_eq!(loaded.functions.len(), index.functions.len());
if loaded.manifest.version == "2.0.0" {
assert!(loaded.corpus.is_empty(), "SQLite load should skip corpus");
} else {
assert_eq!(loaded.corpus.len(), index.corpus.len());
}
}
#[test]
fn test_load_prefers_sqlite_over_blob() {
let temp_dir = tempfile::TempDir::new().unwrap();
let project_path = temp_dir.path();
std::fs::create_dir_all(project_path.join("src")).unwrap();
std::fs::write(
project_path.join("src/lib.rs"),
"fn alpha() { beta(); }\nfn beta() {}\n",
)
.unwrap();
let index = AgentContextIndex::build(project_path).unwrap();
let index_path = project_path.join("idx");
index.save(&index_path).unwrap();
let db_path = index_path.with_extension("db");
assert!(db_path.exists(), "context.db should exist after save");
assert!(
index_path.join("manifest.json").exists(),
"manifest should exist"
);
assert!(
!index_path.join("functions.lz4").exists(),
"blob should NOT be written in Phase 3"
);
let loaded = AgentContextIndex::load(&index_path).unwrap();
assert_eq!(loaded.manifest.version, "2.0.0");
assert!(loaded.db_path.is_some());
assert_eq!(loaded.functions.len(), index.functions.len());
let has_call_data = (0..loaded.functions.len())
.any(|i| !loaded.get_calls(i).is_empty() || !loaded.get_called_by(i).is_empty());
assert!(
has_call_data,
"should have call graph data via SQLite query"
);
}
#[test]
fn test_load_fails_without_sqlite_or_blob() {
let temp_dir = tempfile::TempDir::new().unwrap();
let project_path = temp_dir.path();
std::fs::create_dir_all(project_path.join("src")).unwrap();
std::fs::write(project_path.join("src/lib.rs"), "fn gamma() {}\n").unwrap();
let index = AgentContextIndex::build(project_path).unwrap();
let index_path = project_path.join("idx");
index.save(&index_path).unwrap();
let db_path = index_path.with_extension("db");
std::fs::remove_file(&db_path).unwrap();
let result = AgentContextIndex::load(&index_path);
assert!(result.is_err());
}
#[test]
fn test_incremental_build_unchanged() {
let temp_dir = tempfile::TempDir::new().unwrap();
let project_path = temp_dir.path();
std::fs::create_dir_all(project_path.join("src")).unwrap();
std::fs::write(
project_path.join("src/lib.rs"),
"fn alpha() { }\nfn beta() { }\n",
)
.unwrap();
let original = AgentContextIndex::build(project_path).unwrap();
let incremental = AgentContextIndex::build_incremental(project_path, &original).unwrap();
assert_eq!(incremental.functions.len(), original.functions.len());
}
#[test]
fn test_incremental_build_with_change() {
let temp_dir = tempfile::TempDir::new().unwrap();
let project_path = temp_dir.path();
std::fs::create_dir_all(project_path.join("src")).unwrap();
std::fs::write(project_path.join("src/lib.rs"), "fn alpha() { }\n").unwrap();
let original = AgentContextIndex::build(project_path).unwrap();
assert_eq!(original.functions.len(), 1);
std::fs::write(
project_path.join("src/lib.rs"),
"fn alpha() { }\nfn gamma() { }\n",
)
.unwrap();
let incremental = AgentContextIndex::build_incremental(project_path, &original).unwrap();
assert_eq!(incremental.functions.len(), 2);
}
#[test]
fn test_parse_workspace_siblings() {
let toml = r#"siblings = ["../aprender", "../trueno", "../realizar"]"#;
let result = parse_workspace_siblings(toml);
assert_eq!(result, vec!["../aprender", "../trueno", "../realizar"]);
}
#[test]
fn test_parse_workspace_siblings_empty() {
let toml = "# no siblings configured\n";
let result = parse_workspace_siblings(toml);
assert!(result.is_empty());
}
#[test]
fn test_parse_workspace_siblings_single() {
let toml = r#"siblings = ["../trueno"]"#;
let result = parse_workspace_siblings(toml);
assert_eq!(result, vec!["../trueno"]);
}
#[test]
fn test_parse_workspace_siblings_with_spaces() {
let toml = r#"siblings = [ "../a" , "../b" ]"#;
let result = parse_workspace_siblings(toml);
assert_eq!(result, vec!["../a", "../b"]);
}
#[test]
fn test_discover_siblings_no_config() {
let temp_dir = tempfile::TempDir::new().unwrap();
let result = AgentContextIndex::discover_sibling_indexes(temp_dir.path());
assert!(result.is_empty());
}
#[test]
fn test_file_checksums_populated() {
let temp_dir = tempfile::TempDir::new().unwrap();
let project_path = temp_dir.path();
std::fs::create_dir_all(project_path.join("src")).unwrap();
std::fs::write(project_path.join("src/lib.rs"), "fn test_func() { }\n").unwrap();
let index = AgentContextIndex::build(project_path).unwrap();
assert!(!index.manifest.file_checksums.is_empty());
assert!(index.manifest.file_checksums.contains_key("src/lib.rs"));
}
#[test]
fn test_discover_siblings_with_config() {
let temp_dir = tempfile::TempDir::new().unwrap();
let project_path = temp_dir.path();
std::fs::create_dir_all(project_path.join(".pmat")).unwrap();
std::fs::write(
project_path.join(".pmat/workspace.toml"),
r#"siblings = ["../sibling_a"]"#,
)
.unwrap();
let result = AgentContextIndex::discover_sibling_indexes(project_path);
assert!(result.is_empty());
}
#[test]
fn test_discover_siblings_with_real_sibling() {
let temp_dir = tempfile::TempDir::new().unwrap();
let workspace = temp_dir.path();
let project_a = workspace.join("project_a");
std::fs::create_dir_all(project_a.join(".pmat")).unwrap();
let project_b = workspace.join("project_b");
std::fs::create_dir_all(project_b.join("src")).unwrap();
std::fs::write(project_b.join("src/lib.rs"), "fn sibling_func() {}\n").unwrap();
let b_index = AgentContextIndex::build(&project_b).unwrap();
let b_idx_path = project_b.join(".pmat/context.idx");
std::fs::create_dir_all(b_idx_path.parent().unwrap()).unwrap();
b_index.save(&b_idx_path).unwrap();
std::fs::write(
project_a.join(".pmat/workspace.toml"),
r#"siblings = ["../project_b"]"#,
)
.unwrap();
let siblings = AgentContextIndex::discover_sibling_indexes(&project_a);
assert_eq!(siblings.len(), 1);
assert_eq!(siblings[0].1, "project_b");
}
#[test]
fn test_save_and_load_preserves_calls() {
let temp_dir = tempfile::TempDir::new().unwrap();
let project_path = temp_dir.path();
std::fs::create_dir_all(project_path.join("src")).unwrap();
std::fs::write(
project_path.join("src/lib.rs"),
"fn caller() { callee(); }\nfn callee() { println!(\"hi\"); }\n",
)
.unwrap();
let index = AgentContextIndex::build(project_path).unwrap();
let idx_path = project_path.join("idx");
index.save(&idx_path).unwrap();
let loaded = AgentContextIndex::load(&idx_path).unwrap();
let original_calls: Vec<String> = index.get_calls(0).iter().map(|s| s.to_string()).collect();
let loaded_calls: Vec<String> = loaded.get_calls(0).iter().map(|s| s.to_string()).collect();
assert_eq!(
loaded_calls.len(),
original_calls.len(),
"call graph should be preserved"
);
}
#[test]
fn test_load_invalid_path() {
let result = AgentContextIndex::load(Path::new("/nonexistent/path"));
assert!(result.is_err());
}
#[test]
fn test_save_load_roundtrip_corpus_lower_lazy() {
let temp_dir = tempfile::TempDir::new().unwrap();
let project_path = temp_dir.path();
std::fs::create_dir_all(project_path.join("src")).unwrap();
std::fs::write(
project_path.join("src/lib.rs"),
"fn hello_world() { }\nfn goodbye_world() { }\n",
)
.unwrap();
let index = AgentContextIndex::build(project_path).unwrap();
let idx_path = project_path.join("idx");
index.save(&idx_path).unwrap();
let loaded = AgentContextIndex::load(&idx_path).unwrap();
assert_eq!(loaded.corpus_lower.len(), loaded.corpus.len());
for (orig, lower) in loaded.corpus.iter().zip(loaded.corpus_lower.iter()) {
assert_eq!(lower, &orig.to_lowercase());
}
}