#![cfg(test)]
use std::path::Path;
fn build_cluster_repo(dir: &Path) {
for m in 0..10 {
let sub = dir.join(format!("m{m}"));
std::fs::create_dir_all(&sub).unwrap();
for i in 0..8 {
let mut body = String::new();
body.push_str(&format!("pub fn f{m}_{i}(x: u32) -> u32 {{ "));
for j in 0..8 {
if j != i {
body.push_str(&format!("g{m}_{j}(x) + "));
}
}
body.push_str("x }\n");
body.push_str(&format!("pub fn g{m}_{i}(x: u32) -> u32 {{ x + {m} }}\n"));
if i == 0 && m < 9 {
body.push_str(&format!("pub fn cross{m}(x: u32) -> u32 {{ f{}_{0}(x) }}\n", m + 1));
}
std::fs::write(sub.join(format!("f{i}.rs")), body).unwrap();
}
}
}
#[test]
fn test_clustering_stable_across_runs() {
let dir = std::env::temp_dir().join(format!("code_repo_wiki_cluster_stab_{}", std::process::id()));
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
build_cluster_repo(&dir);
let root = code_repo_wiki::project::ProjectRoot::new(dir.clone());
let insights = code_repo_wiki::ingest::scan_and_parse_at(&root).unwrap().insights;
let graph = code_repo_wiki::analysis::build_graph(&insights).unwrap();
let modules_1 = code_repo_wiki::analysis::detect_modules(&graph).unwrap();
let modules_2 = code_repo_wiki::analysis::detect_modules(&graph).unwrap();
let features_1 = code_repo_wiki::analysis::feature::detect_features(&graph, None).unwrap();
let features_2 = code_repo_wiki::analysis::feature::detect_features(&graph, None).unwrap();
let mut names_1: Vec<&str> = modules_1.iter().map(|m| m.name.as_str()).collect();
let mut names_2: Vec<&str> = modules_2.iter().map(|m| m.name.as_str()).collect();
names_1.sort();
names_2.sort();
assert_eq!(names_1, names_2, "模块名称集合必须跨次一致");
assert!(!names_1.is_empty(), "合成仓库应检出模块");
for (m1, m2) in modules_1.iter().zip(modules_2.iter()) {
let mut ids_1: Vec<_> = m1.node_ids.to_vec();
let mut ids_2: Vec<_> = m2.node_ids.to_vec();
ids_1.sort();
ids_2.sort();
assert_eq!(ids_1, ids_2, "模块 {} 的节点归属必须跨次一致", m1.name);
}
assert_eq!(features_1.len(), features_2.len(), "特征数必须跨次一致");
for (f1, f2) in features_1.iter().zip(features_2.iter()) {
let mut ids_1: Vec<_> = f1.node_ids.to_vec();
let mut ids_2: Vec<_> = f2.node_ids.to_vec();
ids_1.sort();
ids_2.sort();
assert_eq!(ids_1, ids_2, "特征 {} 的节点归属必须跨次一致", f1.name);
}
let mut g_names: Vec<&str> = graph.modules.iter().map(|m| m.name.as_str()).collect();
g_names.sort();
assert_eq!(g_names, names_1, "build_graph 写回的 modules 必须与 detect 一致");
assert!(
(5..=15).contains(&modules_1.len()),
"模块数应接近 10,实际 {}",
modules_1.len()
);
let _ = std::fs::remove_dir_all(&dir);
}