use std::path::{Path, PathBuf};
use std::process::{Command, Output};
fn bin() -> PathBuf {
PathBuf::from(env!("CARGO_BIN_EXE_supercode"))
}
fn fresh_home(tag: &str) -> PathBuf {
let nanos = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos();
let dir = std::env::temp_dir().join(format!(
"supercode-tr10-cli-{tag}-{}-{nanos}",
std::process::id()
));
std::fs::create_dir_all(&dir).unwrap();
dir
}
fn run(home: &Path, extra: &[&str]) -> Output {
let mut args = vec!["--api-key", "x", "--base-url", "http://127.0.0.1:1"];
args.extend_from_slice(extra);
Command::new(bin())
.env("SUPERCODE_HOME", home)
.env_remove("OPENROUTER_API_KEY")
.env_remove("OPENAI_API_KEY")
.env_remove("ANTHROPIC_API_KEY")
.args(&args)
.output()
.expect("failed to run the supercode binary")
}
fn stdout(out: &Output) -> String {
String::from_utf8_lossy(&out.stdout).into_owned()
}
fn stderr(out: &Output) -> String {
String::from_utf8_lossy(&out.stderr).into_owned()
}
fn write_big_write_fixture(dir: &Path) -> (PathBuf, usize) {
let sid = "77777777-8888-9999-aaaa-bbbbbbbbbbbb";
let big = "y".repeat(20_000);
let mut lines: Vec<String> = Vec::new();
lines.push(
serde_json::json!({
"type": "user",
"message": {"role": "user", "content": "please save the report to reports/out.txt"},
"uuid": "u0", "parentUuid": null,
"timestamp": "2026-07-07T18:38:00.000Z", "sessionId": sid,
"cwd": "/tmp/proj", "userType": "external",
})
.to_string(),
);
lines.push(
serde_json::json!({
"type": "assistant",
"message": {"role": "assistant", "content": [
{"type": "tool_use", "id": "toolu_w1", "name": "Write",
"input": {"file_path": "reports/out.txt", "content": big}}
]},
"uuid": "a0", "parentUuid": "u0",
"timestamp": "2026-07-07T18:38:01.000Z", "sessionId": sid,
})
.to_string(),
);
lines.push(
serde_json::json!({
"type": "user",
"message": {"role": "user", "content": [
{"tool_use_id": "toolu_w1", "type": "tool_result",
"content": [{"type": "text", "text": "File created successfully."}]}
]},
"uuid": "t0", "parentUuid": "a0",
"timestamp": "2026-07-07T18:38:02.000Z", "sessionId": sid,
"toolUseResult": {"status": "completed"},
})
.to_string(),
);
lines.push(
serde_json::json!({
"type": "assistant",
"message": {"role": "assistant", "content": "Done — I saved the report."},
"uuid": "a1", "parentUuid": "t0",
"timestamp": "2026-07-07T18:38:03.000Z", "sessionId": sid,
})
.to_string(),
);
let path = dir.join("claude_write_session.jsonl");
std::fs::write(&path, lines.join("\n") + "\n").unwrap();
(path, big.len())
}
fn mint_reduced_session(home: &Path, fixture: &Path) -> String {
let out = run(home, &["resume", fixture.to_str().unwrap(), "--reduced"]);
let err = stderr(&out);
let line = err
.lines()
.find(|l| l.contains("full copy:"))
.unwrap_or_else(|| panic!("no `full copy:` line in:\n{err}"));
let sidecar_path = line.split("full copy:").nth(1).unwrap().trim();
Path::new(sidecar_path)
.file_name()
.unwrap()
.to_str()
.unwrap()
.strip_suffix(".sidecar.jsonl")
.unwrap()
.to_string()
}
#[test]
fn dev06_imported_claude_write_session_shows_attributed_tool_input_savings() {
let home = fresh_home("demo");
let (fixture, content_len) = write_big_write_fixture(&home);
let name = mint_reduced_session(&home, &fixture);
let show = run(&home, &["sessions", "show-reductions", &name]);
assert!(
show.status.success(),
"show-reductions must exit 0: {}",
stderr(&show)
);
let table = stdout(&show);
assert!(
table.contains("tool-input"),
"show-reductions must list a tool-input row:\n{table}"
);
assert!(
table.contains("20,000"),
"the row must attribute the full elided payload size:\n{table}"
);
let json_show = run(&home, &["sessions", "show-reductions", &name, "--json"]);
assert!(json_show.status.success());
let log: serde_json::Value = serde_json::from_str(&stdout(&json_show)).unwrap();
let reductions = log["reductions"].as_array().unwrap();
assert_eq!(reductions.len(), 1, "exactly one reduction expected: {log}");
let kind = &reductions[0]["kind"];
assert!(
kind.get("ToolInputElided").is_some(),
"reduction kind must be ToolInputElided: {kind}"
);
assert_eq!(
kind["ToolInputElided"]["original_bytes"].as_u64().unwrap() as usize,
content_len,
"original_bytes must equal the elided Write content's true length"
);
assert_eq!(kind["ToolInputElided"]["field"], "content");
assert_eq!(kind["ToolInputElided"]["call_id"], "toolu_w1");
let insp = run(&home, &["inspect", &name]);
assert!(
insp.status.success(),
"inspect must exit 0: {}",
stderr(&insp)
);
let text = stdout(&insp);
assert!(text.contains("reduced"), "missing `reduced` row:\n{text}");
assert!(text.contains("1 stubs"), "missing stub count of 1:\n{text}");
assert!(
text.contains("⊟ reduced"),
"the Write assistant row must carry the reduced tag \
(arguments-side stub, not `m.content`):\n{text}"
);
let reduced_line = text
.lines()
.find(|l| l.contains("reduced") && l.contains("view"))
.unwrap_or_else(|| panic!("no reduced/view summary line:\n{text}"));
assert!(
reduced_line.contains('%'),
"expected a view/full percentage: {reduced_line}"
);
let pct: i64 = reduced_line
.rsplit('(')
.next()
.and_then(|s| s.strip_suffix("%)"))
.and_then(|s| s.trim().parse().ok())
.unwrap_or_else(|| panic!("could not parse pct from: {reduced_line}"));
assert!(
pct < 50,
"a 20,000-byte elided Write body out of a small session must show a \
large reduction: {reduced_line}"
);
std::fs::remove_dir_all(&home).ok();
}
#[test]
fn dev06_convert_of_the_same_session_leaks_nothing_and_restores_original_write() {
let home = fresh_home("demo-convert");
let (fixture, _content_len) = write_big_write_fixture(&home);
let name = mint_reduced_session(&home, &fixture);
let out_path = home.join("as-codex.jsonl");
let conv = run(
&home,
&[
"convert",
&name,
"--to",
"codex",
"-o",
out_path.to_str().unwrap(),
],
);
assert!(
conv.status.success(),
"convert must succeed: {}",
stderr(&conv)
);
let written = std::fs::read_to_string(&out_path).unwrap();
assert_eq!(
written.matches("sc-reduced").count(),
0,
"export must contain zero 'sc-reduced' occurrences (A11 leak guard)"
);
assert!(
written.contains(&"y".repeat(20_000)),
"export must contain the ORIGINAL Write content, not the stub"
);
std::fs::remove_dir_all(&home).ok();
}