#![cfg(feature = "viz")]
use std::fs;
use std::path::Path;
use nornir::viz::UrdrThreadsApp;
use serde_json::Value;
fn make_crate(root: &Path, name: &str, body: &str) {
let dir = root.join(name);
fs::create_dir_all(dir.join("src")).unwrap();
fs::write(
dir.join("Cargo.toml"),
format!("[package]\nname = \"{name}\"\nversion = \"0.0.0\"\nedition = \"2021\"\n"),
)
.unwrap();
fs::write(dir.join("src/lib.rs"), body).unwrap();
}
#[test]
fn knowledge_scan_counts_known_repo() {
let tmp = tempfile::tempdir().unwrap();
let ws_root = tmp.path();
make_crate(
ws_root,
"alpha",
r#"
pub struct Foo { pub n: u32 }
pub fn one() {
two();
helper();
}
pub fn two() {}
fn helper() {}
"#,
);
make_crate(
ws_root,
"beta",
r#"
pub fn solo() {
work();
}
fn work() {}
"#,
);
let wh = tmp.path().join("warehouse");
fs::create_dir_all(&wh).unwrap();
let mut app = UrdrThreadsApp::with_repos(
wh,
"testws".to_string(),
ws_root.to_path_buf(),
vec!["alpha".to_string(), "beta".to_string()],
);
app.knowledge_scan_blocking_for_test();
let kn = &app.state_json()["knowledge"];
eprintln!("knowledge state = {}", serde_json::to_string_pretty(kn).unwrap());
assert_eq!(kn["scanned_repos"], 2, "both repos scanned");
assert_eq!(kn["scanning"], Value::Bool(false), "scan finished (not in flight)");
let totals = kn["repo_totals"].as_array().unwrap();
let alpha = totals.iter().find(|r| r["repo"] == "alpha").expect("alpha row");
assert_eq!(alpha["ok"], Value::Bool(true));
assert_eq!(alpha["symbols"], 4, "alpha: struct Foo + fn one + fn two + fn helper");
assert_eq!(alpha["calls"], 2, "alpha: two() + helper() inside one()");
let beta = totals.iter().find(|r| r["repo"] == "beta").expect("beta row");
assert_eq!(beta["symbols"], 2, "beta: fn solo + fn work");
assert_eq!(beta["calls"], 1, "beta: work() inside solo()");
let rows = kn["rows"].as_array().unwrap();
assert_eq!(kn["crates"], 2, "two crates → two bubble rows");
assert_eq!(rows.len(), 2);
let alpha_row = rows.iter().find(|r| r["krate"] == "alpha").expect("alpha bubble");
assert_eq!(alpha_row["symbols"], 4);
assert_eq!(alpha_row["calls"], 2);
}
#[test]
fn knowledge_switch_workspace_resets_and_rescopes() {
let tmp = tempfile::tempdir().unwrap();
let ws_root = tmp.path();
make_crate(ws_root, "alpha", "pub fn a() {}\n");
make_crate(ws_root, "gamma", "pub fn g() {} pub struct G;\n");
let wh = tmp.path().join("warehouse");
fs::create_dir_all(&wh).unwrap();
let mut app = UrdrThreadsApp::with_repos(
wh,
"testws".to_string(),
ws_root.to_path_buf(),
vec!["alpha".to_string()],
);
app.knowledge_scan_blocking_for_test();
let kn = &app.state_json()["knowledge"];
assert_eq!(kn["scanned_repos"], 1, "first workspace: 1 repo");
assert_eq!(kn["configured_repos"], serde_json::json!(["alpha"]));
app.knowledge_set_workspace_for_test(ws_root.to_path_buf(), vec!["gamma".to_string()]);
let kn = &app.state_json()["knowledge"];
assert_eq!(kn["scanned_repos"], 0, "switch drops stale scans");
assert_eq!(kn["crates"], 0, "no rows until the rescan runs");
assert_eq!(kn["configured_repos"], serde_json::json!(["gamma"]), "re-scoped to new repos");
app.knowledge_scan_blocking_for_test();
let kn = &app.state_json()["knowledge"];
assert_eq!(kn["scanned_repos"], 1);
let totals = kn["repo_totals"].as_array().unwrap();
let gamma = totals.iter().find(|r| r["repo"] == "gamma").expect("gamma row");
assert_eq!(gamma["symbols"], 2, "gamma: fn g + struct G");
assert!(totals.iter().all(|r| r["repo"] != "alpha"), "alpha no longer scanned");
}