#![cfg(feature = "mem-repo")]
use assert_cmd::Command;
use tempfile::TempDir;
fn memstead() -> Command {
Command::cargo_bin("memstead").expect("memstead binary must be built by cargo")
}
fn parse_envelope(stdout_bytes: &[u8]) -> serde_json::Value {
let body = std::str::from_utf8(stdout_bytes).expect("stdout must be UTF-8");
serde_json::from_str(body.trim()).unwrap_or_else(|e| {
panic!("--json error must parse as JSON: {e}\n--- stdout ---\n{body}\n--- end stdout ---")
})
}
fn assert_typed_code(envelope: &serde_json::Value, ctx: &str) {
let code = envelope["code"]
.as_str()
.unwrap_or_else(|| panic!("{ctx}: envelope must carry a string `code`, got: {envelope}"));
assert_ne!(
code, "INTERNAL",
"{ctx}: recoverable path must not leak code=INTERNAL — got envelope: {envelope}",
);
}
#[test]
fn update_missing_entity_returns_typed_code() {
let tmp = TempDir::new().unwrap();
let workspace = tmp.path().join("ws");
memstead()
.args([
"mem-repo",
"init",
workspace.to_str().unwrap(),
"--no-gitignore",
])
.assert()
.success();
let output = memstead()
.current_dir(&workspace)
.args([
"--json",
"update",
"nope--does-not-exist",
"--section",
"Body=hi",
"--auto-hash",
])
.assert()
.failure()
.get_output()
.stdout
.clone();
let env = parse_envelope(&output);
assert_typed_code(&env, "update missing entity");
}
#[test]
fn update_missing_hash_flag_returns_typed_code() {
let tmp = TempDir::new().unwrap();
let workspace = tmp.path().join("ws");
memstead()
.args([
"mem-repo",
"init",
workspace.to_str().unwrap(),
"--no-gitignore",
])
.assert()
.success();
let output = memstead()
.current_dir(&workspace)
.args(["--json", "update", "anything--here", "--section", "Body=hi"])
.assert()
.failure()
.get_output()
.stdout
.clone();
let env = parse_envelope(&output);
assert_typed_code(&env, "update missing hash flag");
assert_eq!(
env["code"], "HASH_FLAG_REQUIRED",
"expected HASH_FLAG_REQUIRED, got: {env}",
);
}
#[test]
fn overview_chunk_past_end_returns_typed_code() {
let tmp = TempDir::new().unwrap();
let workspace = tmp.path().join("ws");
memstead()
.args([
"mem-repo",
"init",
workspace.to_str().unwrap(),
"--no-gitignore",
])
.assert()
.success();
let output = memstead()
.current_dir(&workspace)
.args(["--json", "overview", "--chunk", "99"])
.assert()
.failure()
.get_output()
.stdout
.clone();
let env = parse_envelope(&output);
assert_typed_code(&env, "overview chunk past end");
assert_eq!(
env["code"], "CHUNK_OUT_OF_RANGE",
"expected CHUNK_OUT_OF_RANGE, got: {env}",
);
}
#[test]
fn init_non_empty_returns_typed_code() {
let tmp = TempDir::new().unwrap();
std::fs::write(tmp.path().join("preexisting.md"), "hi").unwrap();
let output = memstead()
.args([
"--json",
"init",
"--name",
"tmp",
"--schema",
"default@1.0.0",
tmp.path().to_str().unwrap(),
])
.assert()
.failure()
.get_output()
.stdout
.clone();
let env = parse_envelope(&output);
assert_typed_code(&env, "init non-empty target");
assert_eq!(
env["code"], "TARGET_NOT_EMPTY",
"expected TARGET_NOT_EMPTY, got: {env}",
);
}
#[test]
fn type_unknown_returns_typed_code() {
let tmp = TempDir::new().unwrap();
let workspace = tmp.path().join("ws");
memstead()
.args([
"mem-repo",
"init",
workspace.to_str().unwrap(),
"--no-gitignore",
])
.assert()
.success();
let output = memstead()
.current_dir(&workspace)
.args(["--json", "type", "nonexistent-type"])
.assert()
.failure()
.get_output()
.stdout
.clone();
let env = parse_envelope(&output);
assert_typed_code(&env, "type unknown");
}
#[test]
fn cold_start_returns_typed_code() {
let tmp = TempDir::new().unwrap();
let output = memstead()
.current_dir(tmp.path())
.args(["--json", "stats"])
.assert()
.failure()
.get_output()
.stdout
.clone();
let env = parse_envelope(&output);
assert_typed_code(&env, "stats outside workspace");
}