use super::*;
use std::path::PathBuf;
#[test]
fn test_severity_unknown_is_default_not_warning() {
let default_res = CheckResult::default();
assert_eq!(default_res.severity, Severity::Unknown);
}
#[test]
fn check_state_accepts_state_json_above_generic_cap() {
let nanos = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos())
.unwrap_or(0);
let base = std::env::temp_dir().join(format!(
"aicx-doctor-state-cap-{}-{}",
std::process::id(),
nanos
));
std::fs::create_dir_all(&base).expect("create base");
let state_path = base.join("state.json");
let padding = "A".repeat(9 * 1024 * 1024);
let body = format!(r#"{{"pad":"{}","seen_hashes":{{}},"runs":[]}}"#, padding);
std::fs::write(&state_path, &body).expect("write state.json");
let result = check_state(&base);
let _ = std::fs::remove_dir_all(&base);
assert_eq!(
result.severity,
Severity::Green,
"≥9 MiB state.json must pass under the dedicated 128 MiB cap; got: {:?}",
result
);
assert!(
result.detail.contains("parses cleanly"),
"expected 'parses cleanly' detail, got: {}",
result.detail
);
}
#[test]
fn test_aggregation_unknown_does_not_inflate_warning() {
assert_eq!(
max_severity(&[
Severity::Green,
Severity::Unknown,
Severity::Skipped,
Severity::NotConfigured
]),
Severity::Green
);
assert_eq!(
max_severity(&[Severity::Green, Severity::Unknown, Severity::Warning]),
Severity::Warning
);
}
#[test]
fn test_legacy_report_deserialization_maps_missing_fields_to_unknown() {
let legacy_json = r#"{
"canonical_store": {"name":"canonical","severity":"green","detail":"ok"},
"steer_lance": {"name":"lance","severity":"green","detail":"ok"},
"steer_bm25": {"name":"bm25","severity":"green","detail":"ok"},
"state": {"name":"state","severity":"green","detail":"ok"},
"sidecars": {"name":"sidecars","severity":"green","detail":"ok"},
"corpus_buckets": {"name":"buckets","severity":"green","detail":"ok"},
"noise_health": {"name":"noise","severity":"green","detail":"ok"},
"fixes_applied": [],
"overall": "green"
}"#;
let report: DoctorReport = serde_json::from_str(legacy_json).unwrap();
assert_eq!(report.semantic_health.severity, Severity::Unknown);
assert_eq!(report.embedder_warmth.severity, Severity::Unknown);
assert_eq!(report.schema_version, 2); }
#[test]
fn test_check_embedder_warmth_without_smoke_returns_skipped() {
let opts = DoctorOptions {
rebuild_steer_index: false,
fix_buckets: false,
dry_run: false,
rebuild_sidecars: false,
prune_empty_bodies: false,
apply_prune_empty_bodies: false,
check_dedup: false,
verbose: false,
smoke: false,
};
let check = check_embedder_warmth(&opts);
#[cfg(any(feature = "native-embedder", feature = "cloud-embedder"))]
assert_eq!(check.severity, Severity::Skipped);
#[cfg(not(any(feature = "native-embedder", feature = "cloud-embedder")))]
assert_eq!(check.severity, Severity::NotConfigured);
}
#[test]
fn test_max_severity_promotes_critical() {
assert_eq!(
max_severity(&[Severity::Green, Severity::Warning]),
Severity::Warning
);
assert_eq!(max_severity(&[Severity::Green]), Severity::Green);
}
#[test]
fn oracle_readiness_is_ready_when_semantic_and_freshness_are_green() {
let report = DoctorReport {
schema_version: 2,
canonical_store: CheckResult {
name: "canonical".to_string(),
severity: Severity::Green,
detail: "ok".to_string(),
recommendation: None,
},
steer_lance: CheckResult {
name: "metadata_steer_index lance".to_string(),
severity: Severity::Green,
detail: "ok".to_string(),
recommendation: None,
},
steer_bm25: CheckResult {
name: "metadata_steer_index bm25".to_string(),
severity: Severity::Green,
detail: "ok".to_string(),
recommendation: None,
},
state: CheckResult {
name: "state".to_string(),
severity: Severity::Green,
detail: "ok".to_string(),
recommendation: None,
},
sidecars: CheckResult {
name: "sidecars".to_string(),
severity: Severity::Green,
detail: "ok".to_string(),
recommendation: None,
},
corpus_buckets: CheckResult {
name: "buckets".to_string(),
severity: Severity::Green,
detail: "ok".to_string(),
recommendation: None,
},
noise_health: CheckResult {
name: "noise".to_string(),
severity: Severity::Green,
detail: "ok".to_string(),
recommendation: None,
},
semantic_health: CheckResult {
name: "semantic".to_string(),
severity: Severity::Green,
detail: "ok".to_string(),
recommendation: None,
},
index_freshness: CheckResult {
name: "freshness".to_string(),
severity: Severity::Green,
detail: "ok".to_string(),
recommendation: None,
},
index_consistency: CheckResult {
name: "index_consistency".to_string(),
severity: Severity::Green,
detail: "ok".to_string(),
recommendation: None,
},
sidecar_coverage: CheckResult {
name: "sidecar_coverage".to_string(),
severity: Severity::Green,
detail: "ok".to_string(),
recommendation: None,
},
embedder_warmth: CheckResult {
name: "warmth".to_string(),
severity: Severity::Green,
detail: "ok".to_string(),
recommendation: None,
},
empty_body_chunks: CheckResult {
name: "empty_body_chunks".to_string(),
severity: Severity::Green,
detail: "ok".to_string(),
recommendation: None,
},
content_dedup: CheckResult {
name: "content_dedup".to_string(),
severity: Severity::Green,
detail: "ok".to_string(),
recommendation: None,
},
context_corpus: CheckResult {
name: "context_corpus".to_string(),
severity: Severity::Green,
detail: "ok".to_string(),
recommendation: None,
},
rebuild_sidecars_script: None,
prune_empty_bodies_script: None,
fixes_applied: Vec::new(),
overall: Severity::Green,
};
let readiness = oracle_readiness(&report);
assert_eq!(readiness.readiness_label, "ready");
assert_eq!(readiness.content_semantic_index_health, Severity::Green);
assert_eq!(
readiness.dashboard_semantic_route_health,
Severity::NotConfigured
);
}
#[test]
fn index_consistency_flags_orphaned_and_missing_tuples() {
let tmp = unique_test_dir("index-consistency");
let dir = tmp
.join("store")
.join("VetCoders")
.join("aicx")
.join("2026_0506")
.join("conversations")
.join("codex");
std::fs::create_dir_all(&dir).unwrap();
std::fs::write(dir.join("2026_0506_codex_sess-a_001.md"), "real chunk").unwrap();
std::fs::write(
tmp.join("index.json"),
serde_json::json!({
"projects": {
"VetCoders/aicx": {
"agents": {
"codex": {
"dates": ["2026_0505"],
"total_entries": 1,
"last_updated": "2026-05-06T00:00:00Z"
}
}
}
},
"last_updated": "2026-05-06T00:00:00Z"
})
.to_string(),
)
.unwrap();
let check = check_index_consistency(&tmp);
assert_eq!(check.severity, Severity::Warning);
assert!(check.detail.contains("1 orphaned index tuple"));
assert!(check.detail.contains("1 missing index tuple"));
let _ = std::fs::remove_dir_all(&tmp);
}
#[test]
fn check_canonical_store_warns_when_missing() {
let tmp = std::env::temp_dir().join(format!("aicx-doctor-test-{}", std::process::id()));
let _ = std::fs::create_dir_all(&tmp);
let result = check_canonical_store(&tmp);
assert_eq!(result.severity, Severity::Warning);
let _ = std::fs::remove_dir_all(&tmp);
}
#[test]
fn check_corpus_buckets_green_when_only_valid_names() {
let tmp = unique_test_dir("valid-buckets");
let store = tmp.join("store");
std::fs::create_dir_all(store.join("vetcoders").join("aicx")).unwrap();
std::fs::create_dir_all(store.join("libraxisai").join("vista")).unwrap();
std::fs::create_dir_all(store.join("local")).unwrap();
let result = check_corpus_buckets(&tmp);
assert_eq!(result.severity, Severity::Green);
let _ = std::fs::remove_dir_all(&tmp);
}
#[test]
fn check_corpus_buckets_flags_template_literals() {
let tmp = unique_test_dir("bad-buckets");
let store = tmp.join("store");
std::fs::create_dir_all(store.join("vetcoders")).unwrap();
std::fs::create_dir_all(store.join("vetcoders").join("vibecrafted.git`")).unwrap();
std::fs::create_dir_all(store.join("vetcoders").join("loctree\n\n**AICX")).unwrap();
std::fs::create_dir_all(store.join("{target_owner}")).unwrap();
std::fs::create_dir_all(store.join("<owner>")).unwrap();
std::fs::create_dir_all(store.join("$RELEASE_REPO")).unwrap();
let result = check_corpus_buckets(&tmp);
assert_eq!(result.severity, Severity::Warning);
assert!(result.detail.contains("{target_owner}"));
assert!(result.detail.contains("<owner>"));
assert!(result.detail.contains("$RELEASE_REPO"));
assert!(result.detail.contains("vetcoders/vibecrafted.git`"));
assert!(result.detail.contains("vetcoders/loctree"));
let _ = std::fs::remove_dir_all(&tmp);
}
#[test]
fn quarantine_moves_bucket_atomically() {
let tmp = unique_test_dir("quarantine-move");
let store = tmp.join("store");
let bad = store.join("{x}");
std::fs::create_dir_all(&bad).unwrap();
std::fs::write(bad.join("test.md"), "content").unwrap();
let dest = quarantine_bucket(&store, "{x}").unwrap();
assert!(dest.exists());
assert!(!bad.exists());
assert!(dest.join("test.md").exists());
let _ = std::fs::remove_dir_all(&tmp);
}
#[test]
fn quarantine_moves_nested_repo_bucket_atomically() {
let tmp = unique_test_dir("quarantine-nested-move");
let store = tmp.join("store");
let bad = store.join("vetcoders").join("vc-skills.git\"><span");
std::fs::create_dir_all(&bad).unwrap();
std::fs::write(bad.join("test.md"), "content").unwrap();
let dest = quarantine_bucket(&store, "vetcoders/vc-skills.git\"><span").unwrap();
assert!(dest.exists());
assert!(!bad.exists());
assert!(dest.join("test.md").exists());
let _ = std::fs::remove_dir_all(&tmp);
}
#[test]
fn quarantine_skips_when_no_buckets_match() {
let tmp = unique_test_dir("quarantine-noop");
let store = tmp.join("store");
std::fs::create_dir_all(store.join("vetcoders").join("aicx")).unwrap();
std::fs::create_dir_all(store.join("local")).unwrap();
let suspicious = scan_corpus_buckets(&store).unwrap();
assert!(suspicious.is_empty());
let _ = std::fs::remove_dir_all(&tmp);
}
#[test]
fn scan_passes_camelcase_dotfile_underscore_buckets_through() {
let tmp = unique_test_dir("scan-passthrough");
let store = tmp.join("store");
std::fs::create_dir_all(store.join("LibraxisAI").join("vista")).unwrap();
std::fs::create_dir_all(store.join("VetCoders").join("Vista")).unwrap();
std::fs::create_dir_all(store.join("Loctree").join("aicx")).unwrap();
std::fs::create_dir_all(store.join("Szowesgad").join("family-onko-portal")).unwrap();
std::fs::create_dir_all(store.join("vetcoders").join("aicx")).unwrap();
std::fs::create_dir_all(store.join("local")).unwrap();
std::fs::create_dir_all(store.join(".scripts")).unwrap();
std::fs::create_dir_all(store.join(".aicx")).unwrap();
std::fs::create_dir_all(store.join(".github")).unwrap();
std::fs::create_dir_all(store.join("_internal")).unwrap();
std::fs::create_dir_all(store.join("{target_owner}").join("repo")).unwrap();
std::fs::create_dir_all(store.join("<owner>")).unwrap();
std::fs::create_dir_all(store.join("$RELEASE_REPO")).unwrap();
let suspicious = scan_corpus_buckets(&store).unwrap();
for legit in [
"LibraxisAI",
"VetCoders",
"Loctree",
"Szowesgad",
"vetcoders",
"local",
".scripts",
".aicx",
".github",
"_internal",
] {
assert!(
suspicious.iter().all(|n| n != legit),
"{legit} should not be in suspicious"
);
}
assert!(suspicious.iter().any(|n| n == "{target_owner}"));
assert!(suspicious.iter().any(|n| n == "<owner>"));
assert!(suspicious.iter().any(|n| n == "$RELEASE_REPO"));
let _ = std::fs::remove_dir_all(&tmp);
}
#[test]
fn empty_body_chunks_red_when_over_threshold_and_script_is_reviewable() {
let tmp = unique_test_dir("empty-bodies");
let dir = tmp
.join("store")
.join("VetCoders")
.join("aicx")
.join("2026_0506")
.join("conversations")
.join("claude");
std::fs::create_dir_all(&dir).unwrap();
let empty = dir.join("2026_0506_claude_sess-empty_001.md");
let full = dir.join("2026_0506_claude_sess-full_001.md");
std::fs::write(
&empty,
"[project: VetCoders/aicx | agent: claude | date: 2026-05-06 | frame_kind: internal_thought]\n\n",
)
.unwrap();
std::fs::write(
&full,
"[project: VetCoders/aicx | agent: claude | date: 2026-05-06]\n\nThis chunk carries enough real body content to avoid the empty-body threshold.",
)
.unwrap();
let check = check_empty_body_chunks(&tmp);
assert_eq!(check.severity, Severity::Critical);
assert!(check.detail.contains("1 empty-body"));
let script = render_prune_empty_bodies_script(&tmp).unwrap();
assert!(script.starts_with("#!/usr/bin/env bash"));
assert!(script.contains("mv -n --"));
assert!(!script.contains("rm -f --"));
assert!(script.contains("sess-empty"));
assert!(!script.contains("sess-full"));
assert!(empty.exists(), "script generation must not delete files");
let _ = std::fs::remove_dir_all(&tmp);
}
#[test]
fn apply_prune_empty_bodies_moves_chunks_to_quarantine_and_rechecks() {
let tmp = unique_test_dir("apply-empty-bodies");
let dir = tmp
.join("store")
.join("VetCoders")
.join("aicx")
.join("2026_0506")
.join("conversations")
.join("claude");
std::fs::create_dir_all(&dir).unwrap();
let empty = dir.join("2026_0506_claude_sess-empty_001.md");
let full = dir.join("2026_0506_claude_sess-full_001.md");
let empty_sidecar = empty.with_extension("meta.json");
std::fs::write(
&empty,
"[project: VetCoders/aicx | agent: claude | date: 2026-05-06 | frame_kind: internal_thought]\n\n",
)
.unwrap();
std::fs::write(&empty_sidecar, "{}").unwrap();
std::fs::write(
&full,
"[project: VetCoders/aicx | agent: claude | date: 2026-05-06]\n\nThis chunk carries enough real body content to avoid the empty-body threshold.",
)
.unwrap();
let opts = DoctorOptions {
rebuild_steer_index: false,
fix_buckets: false,
dry_run: false,
rebuild_sidecars: false,
prune_empty_bodies: true,
apply_prune_empty_bodies: true,
check_dedup: false,
verbose: false,
smoke: false,
};
let rt = tokio::runtime::Runtime::new().unwrap();
let report = rt.block_on(run_at(&tmp, &opts)).unwrap();
assert_eq!(
report.empty_body_chunks.severity,
Severity::Green,
"detail: {}; fixes: {:?}",
report.empty_body_chunks.detail,
report.fixes_applied
);
assert!(report.empty_body_chunks.detail.contains("0 empty-body"));
assert!(report.prune_empty_bodies_script.is_none());
assert!(
report
.fixes_applied
.iter()
.any(|entry| entry.contains("quarantined 1 empty-body chunk(s) and 1 sidecar(s)"))
);
assert!(!empty.exists());
assert!(!empty_sidecar.exists());
assert!(full.exists());
let quarantine_root = std::fs::read_dir(tmp.join("quarantine"))
.unwrap()
.map(|entry| entry.unwrap().path())
.find(|path| {
path.file_name()
.and_then(|name| name.to_str())
.is_some_and(|name| name.starts_with("empty-bodies-"))
})
.expect("empty-body quarantine root should exist");
let moved = quarantine_root
.join("store")
.join("VetCoders")
.join("aicx")
.join("2026_0506")
.join("conversations")
.join("claude")
.join("2026_0506_claude_sess-empty_001.md");
assert!(moved.exists());
assert!(moved.with_extension("meta.json").exists());
let _ = std::fs::remove_dir_all(&tmp);
}
#[test]
fn test_doctor_sidecars_and_coverage_share_check_result() {
let tmp = unique_test_dir("sidecars-shared-result");
let dir = tmp
.join("store")
.join("VetCoders")
.join("aicx")
.join("2026_0506")
.join("conversations")
.join("codex");
std::fs::create_dir_all(&dir).unwrap();
std::fs::write(
dir.join("2026_0506_codex_sess-sidecars_001.md"),
"chunk without a sidecar",
)
.unwrap();
let opts = DoctorOptions {
rebuild_steer_index: false,
fix_buckets: false,
dry_run: false,
rebuild_sidecars: false,
prune_empty_bodies: false,
apply_prune_empty_bodies: false,
check_dedup: false,
verbose: false,
smoke: false,
};
let rt = tokio::runtime::Runtime::new().unwrap();
let report = rt.block_on(run_at(&tmp, &opts)).unwrap();
assert_eq!(report.sidecars, report.sidecar_coverage);
assert_eq!(report.sidecars.name, "sidecars");
assert_eq!(report.sidecars.severity, Severity::Critical);
let _ = std::fs::remove_dir_all(&tmp);
}
#[test]
fn index_freshness_reports_missing_when_chunks_exist_but_no_indexed_dir() {
let tmp = unique_test_dir("freshness-missing");
let chunk_dir = tmp
.join("store")
.join("VetCoders")
.join("aicx")
.join("2026_0506")
.join("conversations")
.join("claude");
std::fs::create_dir_all(&chunk_dir).unwrap();
std::fs::write(chunk_dir.join("2026_0506_claude_sess-a_001.md"), "chunk").unwrap();
let result = check_index_freshness(&tmp);
assert_eq!(
result.severity,
Severity::Critical,
"missing index buckets must be Critical"
);
assert!(
result.detail.contains("no semantic index buckets"),
"detail must mention missing buckets, got: {}",
result.detail
);
let rec = result
.recommendation
.expect("recovery hint required when missing");
assert!(
rec.contains("aicx index"),
"recovery must invoke `aicx index`, got: {rec}"
);
assert!(
rec.contains("--full-rescan"),
"recovery must reference canonical `--full-rescan` flag, got: {rec}"
);
assert!(
!rec.contains(&format!("--{}", "fresh ")),
"recovery must NOT use legacy fresh flag, got: {rec}"
);
let _ = std::fs::remove_dir_all(&tmp);
}
#[test]
fn index_freshness_reports_missing_when_bucket_dir_lacks_embeddings_file() {
let tmp = unique_test_dir("freshness-bucket-empty");
let chunk_dir = tmp.join("store").join("Acme").join("svc");
std::fs::create_dir_all(&chunk_dir).unwrap();
std::fs::write(chunk_dir.join("chunk.md"), "chunk").unwrap();
let bucket_dir = tmp.join("indexed").join("_all");
std::fs::create_dir_all(&bucket_dir).unwrap();
std::fs::write(bucket_dir.join("embeddings.ndjson.tmp"), "partial").unwrap();
let result = check_index_freshness(&tmp);
assert_eq!(result.severity, Severity::Critical);
assert!(
result.detail.contains("missing"),
"detail must say missing, got: {}",
result.detail
);
assert!(
result
.recommendation
.as_ref()
.is_some_and(|r| r.contains("--full-rescan"))
);
let _ = std::fs::remove_dir_all(&tmp);
}
#[test]
fn index_freshness_reports_stale_when_chunk_mtime_exceeds_index_mtime() {
use filetime::{FileTime, set_file_mtime};
let tmp = unique_test_dir("freshness-stale");
let chunk_dir = tmp.join("store").join("VetCoders").join("aicx");
std::fs::create_dir_all(&chunk_dir).unwrap();
let chunk_path = chunk_dir.join("chunk.md");
std::fs::write(&chunk_path, "chunk body").unwrap();
let bucket_dir = tmp.join("indexed").join("_all");
std::fs::create_dir_all(&bucket_dir).unwrap();
let index_path = bucket_dir.join("embeddings.ndjson");
std::fs::write(&index_path, "{\"id\":\"a\"}\n").unwrap();
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs() as i64;
set_file_mtime(&chunk_path, FileTime::from_unix_time(now, 0)).unwrap();
set_file_mtime(&index_path, FileTime::from_unix_time(now - 7_200, 0)).unwrap();
let result = check_index_freshness(&tmp);
assert_eq!(result.severity, Severity::Warning, "<72h lag is Warning");
assert!(
result.detail.contains("stale"),
"detail must say stale, got: {}",
result.detail
);
let rec = result
.recommendation
.expect("stale must carry recovery hint");
assert!(rec.contains("aicx index"), "got: {rec}");
assert!(rec.contains("--full-rescan"), "got: {rec}");
assert!(
!rec.contains(&format!("--{}", "fresh ")),
"no legacy fresh-flag literal, got: {rec}"
);
let _ = std::fs::remove_dir_all(&tmp);
}
#[test]
fn index_freshness_reports_fresh_when_index_mtime_meets_or_exceeds_chunks() {
use filetime::{FileTime, set_file_mtime};
let tmp = unique_test_dir("freshness-fresh");
let chunk_dir = tmp.join("store").join("VetCoders").join("aicx");
std::fs::create_dir_all(&chunk_dir).unwrap();
let chunk_path = chunk_dir.join("chunk.md");
std::fs::write(&chunk_path, "chunk body").unwrap();
let bucket_dir = tmp.join("indexed").join("_all");
std::fs::create_dir_all(&bucket_dir).unwrap();
let index_path = bucket_dir.join("embeddings.ndjson");
std::fs::write(&index_path, "{\"id\":\"a\"}\n").unwrap();
let t0 = 1_700_000_000i64;
set_file_mtime(&chunk_path, FileTime::from_unix_time(t0, 0)).unwrap();
set_file_mtime(&index_path, FileTime::from_unix_time(t0 + 60, 0)).unwrap();
set_file_mtime(&chunk_dir, FileTime::from_unix_time(t0, 0)).unwrap();
set_file_mtime(tmp.join("store"), FileTime::from_unix_time(t0, 0)).unwrap();
set_file_mtime(
tmp.join("store").join("VetCoders"),
FileTime::from_unix_time(t0, 0),
)
.unwrap();
let result = check_index_freshness(&tmp);
assert_eq!(
result.severity,
Severity::Green,
"got detail: {}",
result.detail
);
assert!(
result.detail.contains("fresh"),
"detail must say fresh, got: {}",
result.detail
);
assert!(
result.recommendation.is_none(),
"fresh state needs no recovery hint"
);
let _ = std::fs::remove_dir_all(&tmp);
}
fn unique_test_dir(label: &str) -> PathBuf {
let tmp = std::env::temp_dir().join(format!(
"aicx-doctor-{label}-{}-{}",
std::process::id(),
chrono::Utc::now().timestamp_nanos_opt().unwrap_or_default()
));
let _ = std::fs::remove_dir_all(&tmp);
std::fs::create_dir_all(&tmp).unwrap();
tmp
}