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(),
)
}
#[test]
fn cli_smoke_temp_vault() {
let dir = TempDir::new().expect("tempdir");
let config_file = dir.path().join("config.json");
let vault_path = dir.path().join("vault");
let page_file = dir.path().join("page.md");
fs::write(
&page_file,
"---\ntitle: Cli Demo\ntype: entity\ncreated: 2026-04-23\nupdated: 2026-04-23\ntags: [demo]\n---\n\n# Cli Demo\n\nalpha\nbeta\n",
)
.expect("write page file");
let (ok, out, err) = run(
&[
"init",
"--vault-path",
vault_path.to_str().expect("vault path"),
"--name",
"cli-test",
"--sync-backend",
"none",
"--force",
],
&config_file,
);
assert!(ok, "init failed: {err}");
assert!(out.contains("initialized vault"));
let (ok, out, err) = run(
&[
"write",
"wiki/entities/cli-demo.md",
"--content-file",
page_file.to_str().expect("page file"),
"--log-action",
"create",
"--log-description",
"create cli demo",
],
&config_file,
);
assert!(ok, "write failed: {err}");
let payload: Value = serde_json::from_str(&out).expect("write json");
assert_eq!(payload["ok"], Value::Bool(true));
let raw_source = dir.path().join("source.txt");
fs::write(&raw_source, "raw alpha\nraw beta\n").expect("raw source");
let (ok, _out, err) = run(
&["raw", "add", raw_source.to_str().expect("raw source path")],
&config_file,
);
assert!(ok, "raw add failed: {err}");
let (ok, out, err) = run(
&["raw", "list", "--offset", "0", "--limit", "20"],
&config_file,
);
assert!(ok, "raw list failed: {err}");
let payload: Value = serde_json::from_str(&out).expect("raw list json");
assert_eq!(payload["files"], Value::from(vec!["source.txt"]));
let (ok, out, err) = run(&["raw", "read", "source.txt"], &config_file);
assert!(ok, "raw read failed: {err}");
let payload: Value = serde_json::from_str(&out).expect("raw read json");
assert_eq!(payload["extractor"], Value::from("direct"));
let (ok, out, err) = run(&["list", "--offset", "0", "--limit", "2"], &config_file);
assert!(ok, "list failed: {err}");
let payload: Value = serde_json::from_str(&out).expect("list json");
assert!(payload.get("pages").is_some());
assert_eq!(payload["offset"], Value::from(0));
assert_eq!(payload["limit"], Value::from(2));
let (ok, out, err) = run(
&[
"edit",
"wiki/entities/cli-demo.md",
"--old-text",
"beta",
"--new-text",
"beta2",
"--log-action",
"update",
"--log-description",
"rename beta",
],
&config_file,
);
assert!(ok, "edit failed: {err}");
let payload: Value = serde_json::from_str(&out).expect("edit json");
assert_eq!(payload["updated"], Value::Bool(true));
let (ok, out, err) = run(
&["read", "wiki/log.md", "--offset", "1", "--limit", "50"],
&config_file,
);
assert!(ok, "read log failed: {err}");
let payload: Value = serde_json::from_str(&out).expect("read log json");
let content = payload["content"].as_str().unwrap_or_default();
assert!(content.contains("create cli demo"));
assert!(content.contains("rename beta"));
let (ok, _out, _err) = run(&["status", "--json"], &config_file);
assert!(ok, "status --json failed");
let (ok, out, err) = run(&["help-wiki"], &config_file);
assert!(ok, "help-wiki failed: {err}");
assert!(out.contains("Writestead workflow guide"));
}