Files
kebab/crates/kebab-cli/tests/cli_readonly_quiet.rs

184 lines
5.9 KiB
Rust

//! Integration tests for `--readonly` and `--quiet` global flags (fb-28).
use std::io::Write;
use std::process::Command;
fn kebab_bin() -> std::path::PathBuf {
let manifest = env!("CARGO_MANIFEST_DIR");
std::path::PathBuf::from(manifest)
.parent()
.unwrap()
.parent()
.unwrap()
.join("target/debug/kebab")
}
fn fixture_workspace() -> (tempfile::TempDir, std::path::PathBuf) {
let tmp = tempfile::tempdir().unwrap();
let ws = tmp.path().join("workspace");
std::fs::create_dir_all(&ws).unwrap();
let mut a = std::fs::File::create(ws.join("a.md")).unwrap();
writeln!(a, "# Alpha\n\nfirst doc").unwrap();
(tmp, ws)
}
fn xdg_envs(tmp_path: &std::path::Path) -> [(&'static str, std::path::PathBuf); 4] {
[
("XDG_CONFIG_HOME", tmp_path.join("cfg")),
("XDG_DATA_HOME", tmp_path.join("data")),
("XDG_CACHE_HOME", tmp_path.join("cache")),
("XDG_STATE_HOME", tmp_path.join("state")),
]
}
#[test]
fn readonly_flag_blocks_ingest() {
let (tmp, ws) = fixture_workspace();
let out = Command::new(kebab_bin())
.args(["--readonly", "ingest", "--root", ws.to_str().unwrap()])
.envs(xdg_envs(tmp.path()))
.output()
.unwrap();
assert_eq!(out.status.code(), Some(1), "expected exit 1");
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(
stderr.contains("readonly mode"),
"expected 'readonly mode' in stderr, got: {stderr}"
);
}
#[test]
fn readonly_flag_blocks_ingest_file() {
let (tmp, ws) = fixture_workspace();
let file = ws.join("a.md");
let out = Command::new(kebab_bin())
.args(["--readonly", "ingest-file", file.to_str().unwrap()])
.envs(xdg_envs(tmp.path()))
.output()
.unwrap();
assert_eq!(out.status.code(), Some(1), "expected exit 1");
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(stderr.contains("readonly mode"), "stderr: {stderr}");
}
#[test]
fn readonly_flag_blocks_ingest_stdin() {
let (tmp, _ws) = fixture_workspace();
let out = Command::new(kebab_bin())
.args(["--readonly", "ingest-stdin", "--title", "test"])
.env("KEBAB_READONLY", "1")
.envs(xdg_envs(tmp.path()))
.stdin(std::process::Stdio::null())
.output()
.unwrap();
assert_eq!(out.status.code(), Some(1), "expected exit 1");
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(stderr.contains("readonly mode"), "stderr: {stderr}");
}
#[test]
fn readonly_flag_blocks_reset() {
let (tmp, _ws) = fixture_workspace();
let out = Command::new(kebab_bin())
.args(["--readonly", "reset", "--data-only", "--yes"])
.envs(xdg_envs(tmp.path()))
.output()
.unwrap();
assert_eq!(out.status.code(), Some(1), "expected exit 1");
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(stderr.contains("readonly mode"), "stderr: {stderr}");
}
#[test]
fn kebab_readonly_env_blocks_ingest() {
let (tmp, ws) = fixture_workspace();
let out = Command::new(kebab_bin())
.args(["ingest", "--root", ws.to_str().unwrap()])
.env("KEBAB_READONLY", "1")
.envs(xdg_envs(tmp.path()))
.output()
.unwrap();
assert_eq!(out.status.code(), Some(1), "expected exit 1");
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(stderr.contains("readonly mode"), "stderr: {stderr}");
}
#[test]
fn readonly_json_mode_emits_error_v1() {
let (tmp, ws) = fixture_workspace();
let out = Command::new(kebab_bin())
.args(["--readonly", "--json", "ingest", "--root", ws.to_str().unwrap()])
.envs(xdg_envs(tmp.path()))
.output()
.unwrap();
assert_eq!(out.status.code(), Some(1), "expected exit 1");
let stderr = String::from_utf8_lossy(&out.stderr);
let v: serde_json::Value = serde_json::from_str(stderr.trim())
.unwrap_or_else(|e| panic!("expected error.v1 JSON on stderr, got {stderr:?}: {e}"));
assert_eq!(
v.get("schema_version").and_then(|s| s.as_str()),
Some("error.v1"),
"expected schema_version=error.v1"
);
assert_eq!(
v.get("code").and_then(|s| s.as_str()),
Some("readonly_mode"),
"expected code=readonly_mode"
);
}
#[test]
fn quiet_flag_suppresses_progress_stderr() {
let (tmp, ws) = fixture_workspace();
let out = Command::new(kebab_bin())
.args(["--quiet", "ingest", "--root", ws.to_str().unwrap()])
.envs(xdg_envs(tmp.path()))
.output()
.unwrap();
assert!(
out.status.success(),
"exit: {:?}, stderr: {}",
out.status.code(),
String::from_utf8_lossy(&out.stderr)
);
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(
stderr.is_empty(),
"expected empty stderr with --quiet, got: {stderr}"
);
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stdout.contains("scanned"),
"expected report summary on stdout, got: {stdout}"
);
}
#[test]
fn quiet_with_json_stdout_has_report_stderr_is_empty() {
let (tmp, ws) = fixture_workspace();
let out = Command::new(kebab_bin())
.args(["--quiet", "--json", "ingest", "--root", ws.to_str().unwrap()])
.envs(xdg_envs(tmp.path()))
.output()
.unwrap();
assert!(out.status.success(), "stderr: {}", String::from_utf8_lossy(&out.stderr));
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(stderr.is_empty(), "expected empty stderr, got: {stderr}");
let stdout = String::from_utf8_lossy(&out.stdout);
let last_line = stdout.lines().last().unwrap_or("");
let v: serde_json::Value = serde_json::from_str(last_line)
.unwrap_or_else(|e| panic!("expected JSON on stdout last line, got {last_line:?}: {e}"));
assert_eq!(
v.get("schema_version").and_then(|s| s.as_str()),
Some("ingest_report.v1")
);
}