use std::process::Command;
fn cli() -> Command {
Command::new(env!("CARGO_BIN_EXE_fslite"))
}
#[test]
fn create_workspace_then_mkdir_write_cat_via_local_mode() {
let db = tempfile::NamedTempFile::new().unwrap();
let db_path = db.path().to_str().unwrap();
let create = cli()
.args(["--db", db_path, "--create-workspace"])
.output()
.unwrap();
assert!(
create.status.success(),
"stderr: {}",
String::from_utf8_lossy(&create.stderr)
);
let stdout = String::from_utf8(create.stdout).unwrap();
let workspace_id = stdout.trim();
assert!(!workspace_id.is_empty());
let mkdir = cli()
.args([
"--db",
db_path,
"--workspace",
workspace_id,
"mkdir",
"/docs",
])
.output()
.unwrap();
assert!(
mkdir.status.success(),
"stderr: {}",
String::from_utf8_lossy(&mkdir.stderr)
);
let write = cli()
.args([
"--db",
db_path,
"--workspace",
workspace_id,
"write",
"/docs/a.txt",
"--text=hello cli",
])
.output()
.unwrap();
assert!(
write.status.success(),
"stderr: {}",
String::from_utf8_lossy(&write.stderr)
);
let cat = cli()
.args([
"--db",
db_path,
"--workspace",
workspace_id,
"cat",
"/docs/a.txt",
])
.output()
.unwrap();
assert!(cat.status.success());
assert_eq!(String::from_utf8(cat.stdout).unwrap().trim(), "hello cli");
let rm = cli()
.args([
"--db",
db_path,
"--workspace",
workspace_id,
"rm",
"/docs/a.txt",
])
.output()
.unwrap();
assert!(rm.status.success());
let stat_after_rm = cli()
.args([
"--db",
db_path,
"--workspace",
workspace_id,
"stat",
"/docs/a.txt",
])
.output()
.unwrap();
assert!(!stat_after_rm.status.success());
}
#[test]
fn one_shot_mode_never_writes_a_raw_escape_byte_to_stderr_on_error() {
let db = tempfile::NamedTempFile::new().unwrap();
let db_path = db.path().to_str().unwrap();
let create = cli()
.args(["--db", db_path, "--create-workspace"])
.output()
.unwrap();
assert!(create.status.success());
let workspace_id = String::from_utf8(create.stdout).unwrap().trim().to_string();
let hostile_path = "/evil\x1b[31mFAKE\x1b[0m.txt";
let first = cli()
.args([
"--db",
db_path,
"--workspace",
&workspace_id,
"mkdir",
hostile_path,
])
.output()
.unwrap();
assert!(
first.status.success(),
"stderr: {}",
String::from_utf8_lossy(&first.stderr)
);
let second = cli()
.args([
"--db",
db_path,
"--workspace",
&workspace_id,
"mkdir",
hostile_path,
])
.output()
.unwrap();
assert!(
!second.status.success(),
"expected the duplicate mkdir to fail"
);
assert!(
!second.stderr.contains(&0x1b),
"raw ESC byte reached stderr: {:?}",
String::from_utf8_lossy(&second.stderr)
);
let stderr_text = String::from_utf8_lossy(&second.stderr);
assert!(
stderr_text.contains("evil") && stderr_text.contains("FAKE"),
"expected the sanitized message to retain the benign surrounding text, got: {stderr_text}"
);
}
#[test]
fn ls_does_not_forge_an_extra_row_from_a_newline_in_a_node_name() {
let db = tempfile::NamedTempFile::new().unwrap();
let db_path = db.path().to_str().unwrap();
let create = cli()
.args(["--db", db_path, "--create-workspace"])
.output()
.unwrap();
assert!(create.status.success());
let workspace_id = String::from_utf8(create.stdout).unwrap().trim().to_string();
cli()
.args([
"--db",
db_path,
"--workspace",
&workspace_id,
"write",
"/legit.txt",
"--text=x",
])
.output()
.unwrap();
let hostile_name = "/a.txt\nfile 999 IMPORTANT-SYSTEM-FILE.txt";
let write_hostile = cli()
.args([
"--db",
db_path,
"--workspace",
&workspace_id,
"write",
hostile_name,
"--text=y",
])
.output()
.unwrap();
assert!(
write_hostile.status.success(),
"stderr: {}",
String::from_utf8_lossy(&write_hostile.stderr)
);
let ls = cli()
.args(["--db", db_path, "--workspace", &workspace_id, "ls", "/"])
.output()
.unwrap();
assert!(
ls.status.success(),
"stderr: {}",
String::from_utf8_lossy(&ls.stderr)
);
let stdout = String::from_utf8(ls.stdout).unwrap();
let lines: Vec<&str> = stdout.lines().collect();
assert_eq!(
lines.len(),
2,
"ls output had a forged extra row: {stdout:?}"
);
}
#[test]
fn json_flag_prints_machine_readable_output() {
let db = tempfile::NamedTempFile::new().unwrap();
let db_path = db.path().to_str().unwrap();
let create = cli()
.args(["--db", db_path, "--create-workspace"])
.output()
.unwrap();
assert!(create.status.success());
let workspace_id = String::from_utf8(create.stdout).unwrap().trim().to_string();
let usage = cli()
.args([
"--db",
db_path,
"--workspace",
&workspace_id,
"--json",
"usage",
])
.output()
.unwrap();
assert!(usage.status.success());
let parsed: serde_json::Value = serde_json::from_slice(&usage.stdout).unwrap();
assert!(parsed["usage"]["active_nodes"].is_number());
}
#[test]
fn help_does_not_print_the_fslite_token_value() {
let output = cli()
.env("FSLITE_TOKEN", "s3cr3t-do-not-print-me")
.arg("--help")
.output()
.unwrap();
assert!(output.status.success());
let stdout = String::from_utf8(output.stdout).unwrap();
assert!(
!stdout.contains("s3cr3t-do-not-print-me"),
"--help printed the FSLITE_TOKEN value: {stdout}"
);
assert!(
stdout.contains("--token") && stdout.contains("FSLITE_TOKEN"),
"expected --help to still document the --token flag and its env var, got: {stdout}"
);
}
#[test]
fn one_shot_mode_does_not_forge_an_extra_stderr_line_from_a_newline_in_an_error_message() {
let db = tempfile::NamedTempFile::new().unwrap();
let db_path = db.path().to_str().unwrap();
let create = cli()
.args(["--db", db_path, "--create-workspace"])
.output()
.unwrap();
assert!(create.status.success());
let workspace_id = String::from_utf8(create.stdout).unwrap().trim().to_string();
let hostile_path =
"/evil\nerror: workspace quota exceeded, contact admin (InternalStorageFailure).txt";
let first = cli()
.args([
"--db",
db_path,
"--workspace",
&workspace_id,
"mkdir",
hostile_path,
])
.output()
.unwrap();
assert!(
first.status.success(),
"stderr: {}",
String::from_utf8_lossy(&first.stderr)
);
let second = cli()
.args([
"--db",
db_path,
"--workspace",
&workspace_id,
"mkdir",
hostile_path,
])
.output()
.unwrap();
assert!(
!second.status.success(),
"expected the duplicate mkdir to fail"
);
let stderr_text = String::from_utf8_lossy(&second.stderr);
assert_eq!(
stderr_text.lines().count(),
1,
"expected exactly one stderr line (no forged extra line), got: {stderr_text}"
);
assert!(
stderr_text.contains("evil") && stderr_text.contains("quota exceeded"),
"expected the sanitized message to retain the benign surrounding text, got: {stderr_text}"
);
}