use std::fs;
use std::path::Path;
use std::process::Command;
use serde_json::Value;
use tempfile::TempDir;
fn bin() -> &'static str {
env!("CARGO_BIN_EXE_writestead")
}
fn run(args: &[&str], config_file: &Path) -> (bool, String, String) {
let output = Command::new(bin())
.args(args)
.env("WRITESTEAD_CONFIG_FILE", config_file)
.output()
.expect("run command");
(
output.status.success(),
String::from_utf8_lossy(&output.stdout).to_string(),
String::from_utf8_lossy(&output.stderr).to_string(),
)
}
fn init_vault(config_file: &Path, vault_path: &Path) {
let (ok, _out, err) = run(
&[
"init",
"--vault-path",
vault_path.to_str().expect("vault path"),
"--sync-backend",
"none",
"--force",
],
config_file,
);
assert!(ok, "init failed: {err}");
}
#[test]
fn raw_add_local_file_smoke() {
let dir = TempDir::new().expect("tempdir");
let config_file = dir.path().join("config.json");
let vault_path = dir.path().join("vault");
init_vault(&config_file, &vault_path);
let source = dir.path().join("source.txt");
fs::write(&source, "alpha\n").expect("source file");
let (ok, out, err) = run(
&["raw", "add", source.to_str().expect("source path")],
&config_file,
);
assert!(ok, "raw add failed: {err}");
let payload: Value = serde_json::from_str(&out).expect("raw add json");
assert_eq!(payload["ok"], Value::Bool(true));
assert_eq!(payload["path"], Value::from("raw/source.txt"));
}
#[test]
fn raw_add_rejects_path_traversal() {
let dir = TempDir::new().expect("tempdir");
let config_file = dir.path().join("config.json");
let vault_path = dir.path().join("vault");
init_vault(&config_file, &vault_path);
let (ok, _out, err) = run(&["raw", "add", "../etc/passwd"], &config_file);
assert!(!ok, "raw add should fail");
assert!(err.contains("path traversal"), "unexpected err: {err}");
}
#[test]
fn raw_add_rejects_overwrite_without_force() {
let dir = TempDir::new().expect("tempdir");
let config_file = dir.path().join("config.json");
let vault_path = dir.path().join("vault");
init_vault(&config_file, &vault_path);
let source = dir.path().join("source.txt");
fs::write(&source, "alpha\n").expect("source file");
let (ok, _out, err) = run(
&["raw", "add", source.to_str().expect("source path")],
&config_file,
);
assert!(ok, "first raw add failed: {err}");
let (ok, _out, err) = run(
&["raw", "add", source.to_str().expect("source path")],
&config_file,
);
assert!(!ok, "second raw add should fail");
assert!(
err.contains("destination already exists"),
"unexpected err: {err}"
);
}
#[test]
fn raw_add_size_cap_enforced() {
let dir = TempDir::new().expect("tempdir");
let config_file = dir.path().join("config.json");
let vault_path = dir.path().join("vault");
init_vault(&config_file, &vault_path);
let (ok, _out, err) = run(
&["config", "set", "raw.upload_max_bytes", "4"],
&config_file,
);
assert!(ok, "config set failed: {err}");
let source = dir.path().join("big.txt");
fs::write(&source, "12345").expect("big source");
let (ok, _out, err) = run(
&["raw", "add", source.to_str().expect("source path")],
&config_file,
);
assert!(!ok, "raw add should fail by size cap");
assert!(err.contains("file too large"), "unexpected err: {err}");
}