use std::path::Path;
use std::process::Command;
use serde_json::Value;
use tempfile::TempDir;
fn bin() -> &'static str {
env!("CARGO_BIN_EXE_basemind")
}
fn git(repo: &Path, args: &[&str]) {
let status = Command::new("git")
.args(args)
.current_dir(repo)
.env("GIT_AUTHOR_NAME", "t")
.env("GIT_AUTHOR_EMAIL", "t@e.x")
.env("GIT_COMMITTER_NAME", "t")
.env("GIT_COMMITTER_EMAIL", "t@e.x")
.status()
.expect("git in PATH");
assert!(status.success(), "git {args:?} failed");
}
fn build_and_scan() -> TempDir {
let dir = tempfile::tempdir().expect("tempdir");
let root = dir.path();
git(root, &["init", "-q"]);
git(root, &["config", "commit.gpgsign", "false"]);
std::fs::write(
root.join("a.rs"),
b"pub fn alpha() {}\npub struct Beta { x: i32 }\n",
)
.unwrap();
std::fs::write(
root.join("c.rs"),
b"pub fn caller() { alpha(); alpha(); }\n",
)
.unwrap();
git(root, &["add", "-A"]);
git(root, &["commit", "-qm", "init"]);
let status = Command::new(bin())
.args(["--root", root.to_str().unwrap(), "scan", "--quiet"])
.status()
.expect("run basemind scan");
assert!(status.success(), "basemind scan failed");
dir
}
fn run(root: &Path, args: &[&str]) -> (String, bool) {
let mut full = vec!["--root", root.to_str().unwrap()];
full.extend_from_slice(args);
let output = Command::new(bin())
.args(&full)
.output()
.expect("run basemind");
(
String::from_utf8_lossy(&output.stdout).into_owned(),
output.status.success(),
)
}
fn assert_json_fields(root: &Path, args: &[&str], fields: &[&str]) -> Value {
let mut json_args = vec!["--json"];
json_args.extend_from_slice(args);
let (stdout, ok) = run(root, &json_args);
assert!(ok, "command {args:?} exited non-zero; stdout: {stdout}");
let value: Value = serde_json::from_str(stdout.trim())
.unwrap_or_else(|e| panic!("{args:?} not JSON: {e}\n{stdout}"));
for field in fields {
assert!(
value.get(field).is_some(),
"{args:?} JSON missing field `{field}`; got: {value}"
);
}
value
}
fn assert_human_contains(root: &Path, args: &[&str], needle: &str) {
let (stdout, ok) = run(root, args);
assert!(ok, "command {args:?} exited non-zero; stdout: {stdout}");
assert!(!stdout.trim().is_empty(), "{args:?} produced empty output");
assert!(
stdout.contains(needle),
"{args:?} output missing {needle:?}; got: {stdout}"
);
}
#[test]
fn query_outline_reports_symbols() {
let dir = build_and_scan();
let root = dir.path();
let v = assert_json_fields(
root,
&["query", "outline", "a.rs"],
&["path", "language", "symbols", "imports"],
);
let symbols = v["symbols"].as_array().expect("symbols array");
assert_eq!(symbols.len(), 2, "expected alpha + Beta");
assert_human_contains(root, &["query", "outline", "a.rs"], "alpha");
}
#[test]
fn query_search_finds_symbol() {
let dir = build_and_scan();
let root = dir.path();
let v = assert_json_fields(
root,
&["query", "search", "alpha"],
&["total", "truncated", "results"],
);
assert_eq!(v["total"], 1);
assert_human_contains(root, &["query", "search", "alpha"], "alpha");
}
#[test]
fn query_references_finds_call_sites() {
let dir = build_and_scan();
let root = dir.path();
let v = assert_json_fields(
root,
&["query", "references", "alpha"],
&["name", "total", "hits"],
);
assert_eq!(v["total"], 2, "alpha is called twice in c.rs");
assert_human_contains(root, &["query", "references", "alpha"], "c.rs");
}
#[test]
fn query_status_reports_file_count() {
let dir = build_and_scan();
let root = dir.path();
let v = assert_json_fields(
root,
&["query", "status"],
&[
"file_count",
"total_size_bytes",
"languages",
"schema_version",
],
);
assert_eq!(v["file_count"], 2);
assert_human_contains(root, &["query", "status"], "file_count");
}
#[test]
fn query_list_files_enumerates() {
let dir = build_and_scan();
let root = dir.path();
let v = assert_json_fields(
root,
&["query", "list-files"],
&["total", "returned", "files"],
);
assert_eq!(v["total"], 2);
assert_human_contains(root, &["query", "list-files"], "a.rs");
}
#[test]
fn git_working_tree_status_is_clean() {
let dir = build_and_scan();
let root = dir.path();
let v = assert_json_fields(
root,
&["git", "working-tree-status"],
&["staged_added", "modified", "untracked", "is_clean"],
);
assert_eq!(v["is_clean"], true, "repo should be clean after commit");
assert_human_contains(root, &["git", "working-tree-status"], "is_clean");
}
#[test]
fn cache_stats_reports_blob_accounting() {
let dir = build_and_scan();
let root = dir.path();
let v = assert_json_fields(
root,
&["cache", "stats"],
&["blobs_bytes", "blob_count", "orphan_blob_count"],
);
assert!(
v["blob_count"].as_u64().unwrap() >= 2,
"at least one blob per file"
);
assert_human_contains(root, &["cache", "stats"], "blob_count");
}