Files
searchi/crates/searchi-cli/tests/commands.rs
T

129 lines
4.1 KiB
Rust
Raw Normal View History

//! Exercise the executable boundary: exit status, durable indexing, and JSON.
use std::{
fs,
path::PathBuf,
process::{Command, Output},
sync::atomic::{AtomicU64, Ordering},
};
static NEXT_ID: AtomicU64 = AtomicU64::new(0);
struct Fixture(PathBuf);
impl Fixture {
fn new() -> Self {
let path = std::env::temp_dir().join(format!(
"searchi-cli-test-{}-{}",
std::process::id(),
NEXT_ID.fetch_add(1, Ordering::Relaxed)
));
fs::create_dir(&path).unwrap();
Self(path)
}
fn run(&self, args: &[&str]) -> Output {
Command::new(env!("CARGO_BIN_EXE_searchi"))
.current_dir(&self.0)
.args(args)
.output()
.unwrap()
}
}
impl Drop for Fixture {
fn drop(&mut self) {
let _ = fs::remove_dir_all(&self.0);
}
}
#[test]
fn indexes_persists_and_returns_search_and_stats_json() {
let fixture = Fixture::new();
fs::create_dir(fixture.0.join("documents")).unwrap();
fs::write(
fixture.0.join("documents/contract.txt"),
"Supplier termination clause requires sixty days notice.",
)
.unwrap();
let indexed = fixture.run(&["index", "--db", "index.db", "--root", "documents"]);
assert!(
indexed.status.success(),
"{}",
String::from_utf8_lossy(&indexed.stderr)
);
// Each command is a new process, so retrieval proves the index survived exit.
let searched = fixture.run(&["search", "--db", "index.db", "termination", "--json"]);
assert!(
searched.status.success(),
"{}",
String::from_utf8_lossy(&searched.stderr)
);
let results: serde_json::Value = serde_json::from_slice(&searched.stdout).unwrap();
let results = results.as_array().unwrap();
assert_eq!(results.len(), 1);
assert_eq!(results[0]["filename"], "contract.txt");
assert!(results[0]["snippet"]
.as_str()
.unwrap()
.contains("termination"));
let stats = fixture.run(&["stats", "--db", "index.db", "--json"]);
assert!(stats.status.success());
let stats: serde_json::Value = serde_json::from_slice(&stats.stdout).unwrap();
assert_eq!(stats["files"], 1);
assert_eq!(stats["content_files"], 1);
}
#[test]
fn errors_are_nonzero_and_missing_database_is_not_created() {
let fixture = Fixture::new();
let missing = fixture.run(&["search", "--db", "missing.db", "invoice", "--json"]);
assert!(!missing.status.success());
assert!(String::from_utf8_lossy(&missing.stderr).contains("does not exist"));
assert!(!fixture.0.join("missing.db").exists());
assert!(missing.stdout.is_empty());
let bad_root = fixture.run(&["index", "--db", "index.db", "--root", "missing-folder"]);
assert!(!bad_root.status.success());
assert!(String::from_utf8_lossy(&bad_root.stderr).contains("missing-folder"));
}
#[test]
fn exclusions_and_filters_change_real_results() {
let fixture = Fixture::new();
fs::create_dir_all(fixture.0.join("documents/private")).unwrap();
fs::write(fixture.0.join("documents/report.txt"), "quarterly invoice").unwrap();
fs::write(fixture.0.join("documents/report.md"), "quarterly invoice").unwrap();
fs::write(fixture.0.join("documents/private/secret.txt"), "invoice").unwrap();
let indexed = fixture.run(&[
"index",
"--db",
"index.db",
"--root",
"documents",
"--exclude",
"private",
]);
assert!(indexed.status.success());
let searched = fixture.run(&[
"search",
"--db",
"index.db",
"invoice",
"--extension",
"md",
"--json",
]);
assert!(
searched.status.success(),
"{}",
String::from_utf8_lossy(&searched.stderr)
);
let results: serde_json::Value = serde_json::from_slice(&searched.stdout).unwrap();
assert_eq!(results.as_array().unwrap().len(), 1);
assert_eq!(results[0]["filename"], "report.md");
let stats = fixture.run(&["stats", "--db", "index.db", "--json"]);
let stats: serde_json::Value = serde_json::from_slice(&stats.stdout).unwrap();
assert_eq!(stats["files"], 2);
}