use std::path::{Path, PathBuf};
use std::process::{Command, Output};
fn bin() -> PathBuf {
PathBuf::from(env!("CARGO_BIN_EXE_supercode"))
}
fn fixtures() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../harness/tests/fixtures")
}
fn scratch(tag: &str) -> PathBuf {
static SEQUENCE: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
let dir = std::env::temp_dir().join(format!(
"supercode-orch12-{tag}-{}-{}-{}",
std::process::id(),
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos(),
SEQUENCE.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
));
std::fs::create_dir_all(&dir).unwrap();
dir
}
fn run(cwd: &Path, envs: &[(&str, PathBuf)], args: &[&str]) -> Output {
let home = scratch("home");
let mut command = Command::new(bin());
command
.current_dir(cwd)
.env("SUPERCODE_HOME", home.join("sessions"))
.env("HOME", &home)
.env("CLAUDE_CONFIG_DIR", home.join("absent-claude"))
.env("CODEX_HOME", home.join("absent-codex"))
.env("OPENCODE_DB", home.join("absent-opencode"))
.env("PI_CODING_AGENT_DIR", home.join("absent-pi"))
.env("HERMES_HOME", home.join("absent-hermes"))
.env("OPENCLAW_STATE_DIR", home.join("absent-openclaw"))
.env("NO_COLOR", "1")
.env_remove("OPENROUTER_API_KEY");
for (key, value) in envs {
command.env(key, value);
}
let output = command
.args(args)
.stdin(std::process::Stdio::null())
.output()
.expect("supercode binary runs");
let _ = std::fs::remove_dir_all(&home);
output
}
fn stdout(cwd: &Path, envs: &[(&str, PathBuf)], args: &[&str]) -> String {
let output = run(cwd, envs, args);
assert!(
output.status.success(),
"`supercode {}` failed: {}",
args.join(" "),
String::from_utf8_lossy(&output.stderr)
);
String::from_utf8(output.stdout).unwrap()
}
fn result(
cwd: &Path,
envs: &[(&str, PathBuf)],
args: &[&str],
key: &str,
) -> Vec<serde_json::Value> {
let text = stdout(cwd, envs, args);
let value: serde_json::Value =
serde_json::from_str(&text).expect("memory --json is one JSON object");
assert_eq!(value["schema"], "supercode.memory.v1", "{value:#}");
value[key].as_array().cloned().unwrap_or_default()
}
fn find<'a>(rows: &'a [serde_json::Value], profile: &str, name: &str) -> &'a serde_json::Value {
rows.iter()
.find(|row| row["profile"] == profile && row["name"] == name)
.unwrap_or_else(|| panic!("no `{profile}` document `{name}` in {rows:#?}"))
}
#[test]
fn memory_show_json_reads_the_hermes_fixture_home() {
let cwd = scratch("cwd");
let rows = result(
&cwd,
&[("HERMES_HOME", fixtures().join("hermes_home"))],
&["memory", "show", "--harness", "hermes", "--json"],
"documents",
);
let notes = find(&rows, "default", "MEMORY.md");
assert_eq!(notes["harness"], "hermes");
assert_eq!(notes["scope"], "user");
assert!(notes.get("content").is_none(), "{notes:#?}");
let user = find(&rows, "default", "USER.md");
assert!(user["size"].as_u64().unwrap() > 0);
find(&rows, "default", "memories/2026-09-01-notes.md");
let coder = find(&rows, "coder", "MEMORY.md");
assert_eq!(coder["scope"], "profile");
let _ = std::fs::remove_dir_all(&cwd);
}
#[test]
fn memory_show_full_returns_the_body_and_the_table_names_every_column() {
let cwd = scratch("cwd");
let homes = [("HERMES_HOME", fixtures().join("hermes_home"))];
let rows = result(
&cwd,
&homes,
&[
"memory",
"show",
"--harness",
"hermes",
"--profile",
"coder",
"--full",
"--json",
],
"documents",
);
assert!(
rows.iter().all(|row| row["profile"] == "coder"),
"{rows:#?}"
);
assert!(find(&rows, "coder", "MEMORY.md")["content"]
.as_str()
.expect("--full returns the body")
.contains("anthropic/claude-opus-4-8"));
let table = stdout(
&cwd,
&homes,
&[
"memory",
"show",
"--harness",
"hermes",
"--profile",
"coder",
],
);
let header = table
.lines()
.find(|line| line.contains("HARNESS"))
.unwrap_or_else(|| panic!("no header in:\n{table}"));
for column in ["SCOPE", "PROFILE", "NAME", "BYTES", "UPDATED"] {
assert!(header.contains(column), "{header}");
}
let row = table
.lines()
.find(|line| line.contains("MEMORY.md"))
.unwrap_or_else(|| panic!("no MEMORY.md row in:\n{table}"));
assert!(row.contains("hermes"), "{row}");
assert!(row.contains("profile"), "{row}");
assert!(row.contains("coder"), "{row}");
let _ = std::fs::remove_dir_all(&cwd);
}
#[test]
fn memory_show_reads_the_openclaw_agent_workspaces() {
let cwd = scratch("cwd");
let rows = result(
&cwd,
&[("OPENCLAW_STATE_DIR", fixtures().join("openclaw_home"))],
&["memory", "show", "--harness", "openclaw", "--json"],
"documents",
);
let main = find(&rows, "main", "MEMORY.md");
assert_eq!(main["scope"], "agent");
assert!(main["path"]
.as_str()
.unwrap()
.ends_with("openclaw_home/workspace/MEMORY.md"));
find(&rows, "main", "memory/2026-09-01-standup.md");
find(&rows, "design", "MEMORY.md");
let _ = std::fs::remove_dir_all(&cwd);
}
#[test]
fn memory_show_reads_a_claude_code_temp_project_directory() {
let claude_home = scratch("claude");
let project = scratch("project");
std::fs::create_dir_all(project.join(".git")).unwrap();
let resolved = std::fs::canonicalize(&project).unwrap();
let slug: String = resolved
.to_string_lossy()
.chars()
.map(|c| if c.is_ascii_alphanumeric() { c } else { '-' })
.collect();
let memory = claude_home.join("projects").join(&slug).join("memory");
std::fs::create_dir_all(&memory).unwrap();
std::fs::write(
memory.join("MEMORY.md"),
"# index\n- [pins](pins.md) — the pinned harnesses\n",
)
.unwrap();
std::fs::write(memory.join("pins.md"), "hermes 0.19.0 is the pin\n").unwrap();
let homes = [("CLAUDE_CONFIG_DIR", claude_home.clone())];
let rows = result(
&project,
&homes,
&["memory", "show", "--harness", "claude-code", "--json"],
"documents",
);
let index = find(&rows, &slug, "MEMORY.md");
assert_eq!(index["harness"], "claude-code");
assert_eq!(index["scope"], "project");
find(&rows, &slug, "pins.md");
let hits = result(
&project,
&homes,
&[
"memory",
"search",
"--harness",
"claude-code",
"0.19.0",
"--json",
],
"matches",
);
assert_eq!(hits.len(), 1, "{hits:#?}");
assert_eq!(hits[0]["name"], "pins.md");
assert_eq!(hits[0]["line"], 1);
let _ = std::fs::remove_dir_all(&claude_home);
let _ = std::fs::remove_dir_all(&project);
}
#[test]
fn memory_search_reports_a_hit_by_line_and_a_miss_as_no_rows() {
let cwd = scratch("cwd");
let homes = [("HERMES_HOME", fixtures().join("hermes_home"))];
let hits = result(
&cwd,
&homes,
&[
"memory",
"search",
"--harness",
"hermes",
"NEOVIM",
"--json",
],
"matches",
);
assert_eq!(hits.len(), 1, "{hits:#?}");
assert_eq!(hits[0]["name"], "USER.md");
assert_eq!(hits[0]["line"], 5);
let human = stdout(
&cwd,
&homes,
&["memory", "search", "--harness", "hermes", "neovim"],
);
assert!(human.contains("USER.md:5:"), "{human}");
let miss = result(
&cwd,
&homes,
&[
"memory",
"search",
"--harness",
"hermes",
"no-memory-line-says-this",
"--json",
],
"matches",
);
assert!(miss.is_empty(), "{miss:#?}");
let _ = std::fs::remove_dir_all(&cwd);
}
#[test]
fn memory_refuses_a_harness_without_a_store() {
let cwd = scratch("cwd");
for args in [
vec!["memory", "show", "--harness", "codex"],
vec!["memory", "search", "--harness", "opencode", "anything"],
] {
let output = run(&cwd, &[], &args);
assert!(!output.status.success(), "{args:?} must fail");
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(stderr.contains("unsupported action"), "{stderr}");
assert!(stderr.contains("claude-code, hermes, openclaw"), "{stderr}");
}
let _ = std::fs::remove_dir_all(&cwd);
}