use assert_cmd::Command;
use tempfile::TempDir;
fn memstead() -> Command {
Command::cargo_bin("memstead").expect("memstead binary must be built by cargo")
}
fn stdout_of(assert: assert_cmd::assert::Assert) -> String {
String::from_utf8(assert.get_output().stdout.clone()).expect("stdout is UTF-8")
}
const CONFLICTED: &str = "---\ntype: spec\n---\n# Torn\n\n## Identity\n\n\
<<<<<<< HEAD\nours line\n=======\ntheirs line\n>>>>>>> feature\n\n## Purpose\n\nshared\n";
#[test]
fn conflicts_list_and_resolve_roundtrip() {
let tmp = TempDir::new().unwrap();
let root = tmp.path().join("ws");
memstead()
.args(["quickstart", "--agent", "claude-code"])
.arg(&root)
.assert()
.success();
std::fs::write(root.join("torn.md"), CONFLICTED).unwrap();
let out = stdout_of(
memstead()
.current_dir(&root)
.args(["--json", "conflicts", "list"])
.assert()
.success(),
);
let listed: serde_json::Value = serde_json::from_str(&out).unwrap();
assert_eq!(listed["count"], 1, "got: {listed}");
assert_eq!(listed["conflicts"][0]["id"], "ws--torn");
assert_eq!(listed["conflicts"][0]["file_path"], "torn.md");
let out = stdout_of(
memstead()
.current_dir(&root)
.args(["--json", "health"])
.assert()
.success(),
);
let health: serde_json::Value = serde_json::from_str(&out).unwrap();
let load_errors = health["load_errors"]
.as_array()
.expect("health carries load_errors for the conflicted file");
assert!(
load_errors.iter().any(|e| {
e["file"].as_str().unwrap_or("").ends_with("torn.md")
&& e["error"]
.as_str()
.unwrap_or("")
.contains("memstead conflicts resolve")
}),
"the load failure names the remedy: {load_errors:?}"
);
let out = stdout_of(
memstead()
.current_dir(&root)
.args([
"--json",
"conflicts",
"resolve",
"ws--torn",
"--side",
"theirs",
"--note",
"keeping upstream wording",
])
.assert()
.success(),
);
let resolved: serde_json::Value = serde_json::from_str(&out).unwrap();
assert_eq!(resolved["id"], "ws--torn");
assert_eq!(resolved["side"], "theirs");
let out = stdout_of(
memstead()
.current_dir(&root)
.args(["--json", "entity", "ws--torn"])
.assert()
.success(),
);
assert!(out.contains("theirs line") && !out.contains("ours line"));
let on_disk = std::fs::read_to_string(root.join("torn.md")).unwrap();
assert!(!on_disk.contains("<<<<<<<"));
let ledger = std::fs::read_to_string(root.join(".memstead").join("changes.jsonl")).unwrap();
assert!(
ledger.contains("ws--torn") && ledger.contains("keeping upstream wording"),
"ledger records the resolution: {ledger}"
);
let out = stdout_of(
memstead()
.current_dir(&root)
.args(["--json", "health"])
.assert()
.success(),
);
let health: serde_json::Value = serde_json::from_str(&out).unwrap();
assert!(
health.get("load_errors").is_none(),
"clean workspace omits load_errors: {health}"
);
let out = stdout_of(
memstead()
.current_dir(&root)
.args(["--json", "conflicts", "list"])
.assert()
.success(),
);
let listed: serde_json::Value = serde_json::from_str(&out).unwrap();
assert_eq!(listed["count"], 0);
let assert = memstead()
.current_dir(&root)
.args([
"--json",
"conflicts",
"resolve",
"ws--torn",
"--side",
"ours",
])
.assert()
.failure();
let err = stdout_of(assert);
assert!(err.contains("NOT_CONFLICTED"), "got: {err}");
let assert = memstead()
.current_dir(&root)
.args([
"--json",
"conflicts",
"resolve",
"ws--torn",
"--side",
"both",
])
.assert()
.failure();
let err = stdout_of(assert);
assert!(err.contains("INVALID_INPUT"), "got: {err}");
}