#![cfg(unix)]
mod common;
use std::os::unix::fs::PermissionsExt;
use std::path::Path;
struct RestoreMode(std::path::PathBuf, u32);
impl Drop for RestoreMode {
fn drop(&mut self) {
let _ = std::fs::set_permissions(&self.0, std::fs::Permissions::from_mode(self.1));
}
}
fn run(ws: &Path, args: &[&str]) -> std::process::Output {
common::atomwrite()
.args(["--workspace", ws.to_str().expect("utf8 workspace")])
.args(args)
.output()
.expect("run atomwrite")
}
fn terminal(events: &[serde_json::Value]) -> &serde_json::Value {
events
.iter()
.find(|e| e.get("ok").is_some())
.unwrap_or_else(|| panic!("no terminal record in {events:?}"))
}
#[test]
fn limit_stops_the_walker_early() {
let dir = tempfile::tempdir().expect("tempdir");
for i in 0..60 {
common::create_test_file(dir.path(), &format!("f{i}.txt"), "needle\n");
}
let total = run(dir.path(), &["search", "needle", "."]);
let total_events = common::parse_ndjson(&total.stdout);
assert_eq!(terminal(&total_events)["files_visited"], 60);
let limited = run(dir.path(), &["search", "needle", ".", "--limit", "1"]);
let limited_events = common::parse_ndjson(&limited.stdout);
let summary = terminal(&limited_events);
let visited = summary["files_visited"].as_u64().unwrap_or(0);
let stopped_early = summary["walker_stopped_early"].as_bool().unwrap_or(false);
assert!(
visited >= 60 || stopped_early,
"a walk that visited fewer files than the tree must report the quit: {summary}"
);
}
#[test]
fn filter_never_drops_the_failure_event() {
let dir = tempfile::tempdir().expect("tempdir");
common::create_test_file(dir.path(), "good.txt", "needle\n");
let locked = dir.path().join("locked");
std::fs::create_dir(&locked).expect("mkdir");
common::create_test_file(&locked, "x.txt", "needle\n");
std::fs::set_permissions(&locked, std::fs::Permissions::from_mode(0o000)).expect("chmod");
let _restore = RestoreMode(locked.clone(), 0o755);
if std::fs::read_dir(&locked).is_ok() {
eprintln!("skipped: running as root, mode 000 does not deny reads");
return;
}
let out = run(
dir.path(),
&["search", "needle", ".", "--filter", "ok=true"],
);
let events = common::parse_ndjson(&out.stdout);
let summary = terminal(&events);
assert_eq!(summary["files_failed"], 1, "{summary}");
assert!(
summary["errors"]
.as_array()
.is_some_and(|errors| !errors.is_empty()),
"the failure event survived the filter: {summary}"
);
assert_eq!(summary["ok"], true, "{summary}");
assert_eq!(out.status.code(), Some(0), "{events:?}");
}