#![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 mode_zero_is_readable(path: &Path) -> bool {
std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o000)).expect("chmod 000");
let readable = std::fs::read(path).is_ok();
std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o644)).expect("chmod 644");
readable
}
struct Run {
code: Option<i32>,
events: Vec<serde_json::Value>,
}
fn run(ws: &Path, args: &[&str]) -> Run {
let output = common::atomwrite()
.args(["--workspace", ws.to_str().expect("utf8 workspace")])
.args(args)
.output()
.expect("run atomwrite");
Run {
code: output.status.code(),
events: common::parse_ndjson(&output.stdout),
}
}
fn terminal(run: &Run) -> &serde_json::Value {
run.events
.iter()
.find(|e| e.get("ok").is_some())
.unwrap_or_else(|| panic!("no terminal record in {:?}", run.events))
}
fn assert_hole_counted<'a>(run: &'a Run, what: &str, expected_path: &str) -> &'a serde_json::Value {
let summary = terminal(run);
assert_eq!(
summary["files_failed"], 1,
"{what}: the unreadable file is a failure: {summary}"
);
let errors = summary["errors"]
.as_array()
.unwrap_or_else(|| panic!("{what}: errors[] present: {summary}"));
assert_eq!(errors.len(), 1, "{what}: {summary}");
assert_eq!(errors[0]["code"], "PERMISSION_DENIED", "{what}: {summary}");
assert!(
errors[0]["path"]
.as_str()
.is_some_and(|p| p.ends_with(expected_path)),
"{what}: errors[0] names the file: {summary}"
);
summary
}
fn assert_one_failure(run: &Run, what: &str, expected_path: &str) {
let summary = assert_hole_counted(run, what, expected_path);
assert_eq!(summary["ok"], false, "{what}: {summary}");
assert_eq!(run.code, Some(13), "{what}: {:?}", run.events);
}
fn assert_hole_in_answer(run: &Run, what: &str, expected_path: &str) {
let summary = assert_hole_counted(run, what, expected_path);
assert_eq!(summary["partial"], true, "{what}: {summary}");
assert_eq!(summary["ok"], true, "{what}: {summary}");
assert_eq!(run.code, Some(0), "{what}: {:?}", run.events);
}
#[test]
fn count_reports_files_failed() {
let dir = tempfile::tempdir().expect("tempdir");
common::create_test_file(dir.path(), "good.txt", "hello\n");
let secret = common::create_test_file(dir.path(), "secret.txt", "hi\n");
if mode_zero_is_readable(&secret) {
eprintln!("skipped: running as root, mode 000 does not deny reads");
return;
}
std::fs::set_permissions(&secret, std::fs::Permissions::from_mode(0o000)).expect("chmod 000");
let _restore = RestoreMode(secret, 0o644);
let out = run(dir.path(), &["count", dir.path().to_str().expect("utf8")]);
assert_one_failure(&out, "count", "secret.txt");
}
#[test]
fn list_reports_files_failed() {
let dir = tempfile::tempdir().expect("tempdir");
let locked = dir.path().join("locked");
std::fs::create_dir(&locked).expect("mkdir");
common::create_test_file(&locked, "secret.txt", "hi\n");
if mode_zero_is_readable(&locked) {
eprintln!("skipped: running as root, mode 000 does not deny reads");
return;
}
std::fs::set_permissions(&locked, std::fs::Permissions::from_mode(0o000)).expect("chmod 000");
let _restore = RestoreMode(locked, 0o755);
let out = run(dir.path(), &["list", dir.path().to_str().expect("utf8")]);
assert_one_failure(&out, "list", "locked");
}
#[test]
fn sparse_reports_files_failed() {
let dir = tempfile::tempdir().expect("tempdir");
let good = common::create_test_file(dir.path(), "good.txt", "hello\n");
let secret = common::create_test_file(dir.path(), "secret.txt", "hi\n");
if mode_zero_is_readable(&secret) {
eprintln!("skipped: running as root, mode 000 does not deny reads");
return;
}
std::fs::set_permissions(&secret, std::fs::Permissions::from_mode(0o000)).expect("chmod 000");
let _restore = RestoreMode(secret.clone(), 0o644);
let list_file = dir.path().join("paths.txt");
std::fs::write(
&list_file,
format!(
"{}\n{}\n",
good.to_str().expect("utf8"),
secret.to_str().expect("utf8")
),
)
.expect("write paths file");
let out = run(
dir.path(),
&[
"sparse",
"read",
"--paths-file",
list_file.to_str().expect("utf8"),
],
);
assert_hole_in_answer(&out, "sparse", "secret.txt");
}
#[test]
fn semantic_search_reports_files_failed() {
let dir = tempfile::tempdir().expect("tempdir");
common::create_test_file(dir.path(), "good.txt", "hello world rust\n");
let secret = common::create_test_file(dir.path(), "secret.txt", "hello world rust\n");
if mode_zero_is_readable(&secret) {
eprintln!("skipped: running as root, mode 000 does not deny reads");
return;
}
std::fs::set_permissions(&secret, std::fs::Permissions::from_mode(0o000)).expect("chmod 000");
let _restore = RestoreMode(secret, 0o644);
let out = run(
dir.path(),
&[
"semantic-search",
"hello world",
dir.path().to_str().expect("utf8"),
],
);
assert_hole_in_answer(&out, "semantic-search", "secret.txt");
}