mod support;
use std::path::Path;
use serde_json::Value;
use support::git_repo::GitRepo;
use support::shore;
fn modified_repo() -> GitRepo {
let repo = GitRepo::new();
repo.write("README.md", "base\n");
repo.commit_all("base");
repo.write("README.md", "changed\n");
repo
}
fn parse_json(stdout: &[u8]) -> Value {
serde_json::from_slice(stdout).expect("stdout is json")
}
fn assert_success(output: &std::process::Output, what: &str) {
assert!(
output.status.success(),
"{what} stderr:\n{}",
String::from_utf8_lossy(&output.stderr)
);
}
fn capture_with_externalized_observation_body(repo: &Path) -> (String, String) {
let arg = repo.to_str().unwrap();
let capture = shore(["capture", "--repo", arg]);
assert_success(&capture, "capture");
let capture = parse_json(&capture.stdout);
let revision_id = capture["revision"]["id"].as_str().unwrap().to_owned();
let body = "x".repeat(5000);
let observation = shore([
"observation",
"add",
"--repo",
arg,
"--track",
"agent:codex",
"--title",
"a large observation",
"--body",
&body,
]);
assert_success(&observation, "observation add");
let observation = parse_json(&observation.stdout);
let body_hash = observation["bodyContentHash"]
.as_str()
.expect("a >4096-byte body is stored as a note artifact")
.to_owned();
(revision_id, body_hash)
}
fn remove_revision(repo: &Path, revision_id: &str) {
let output = shore([
"store",
"remove",
"--repo",
repo.to_str().unwrap(),
"--revision",
revision_id,
]);
assert_success(&output, "store remove");
}
fn compact(repo: &Path) {
let output = shore([
"store",
"compact",
"--repo",
repo.to_str().unwrap(),
"--yes",
]);
assert_success(&output, "store compact");
}
fn observation_entry(document: &Value) -> &Value {
document["observations"]
.as_array()
.expect("observations array")
.first()
.expect("one observation")
}
fn has_diagnostic(document: &Value, code: &str, hash: &str) -> bool {
document["diagnostics"]
.as_array()
.expect("diagnostics array")
.iter()
.any(|d| d["code"] == code && d["message"].as_str().unwrap_or_default().contains(hash))
}
#[test]
fn swept_body_renders_physically_removed_across_read_surfaces() {
let repo = modified_repo();
let arg = repo.path().to_str().unwrap();
let (revision_id, body_hash) = capture_with_externalized_observation_body(repo.path());
remove_revision(repo.path(), &revision_id);
compact(repo.path());
let show = shore(["revision", "show", "--repo", arg, "--include-body"]);
assert_success(&show, "revision show");
let show = parse_json(&show.stdout);
let observation = observation_entry(&show);
assert!(observation.get("body").is_none(), "no fabricated body");
assert_eq!(observation["bodyContentState"], "physically_removed");
assert_eq!(observation["bodyContentHash"], body_hash.as_str());
assert!(has_diagnostic(
&show,
"body_content_physically_removed",
&body_hash
));
let list = shore(["observation", "list", "--repo", arg, "--include-body"]);
assert_success(&list, "observation list");
let list = parse_json(&list.stdout);
let listed = observation_entry(&list);
assert_eq!(listed["bodyContentState"], "physically_removed");
assert!(has_diagnostic(
&list,
"body_content_physically_removed",
&body_hash
));
let history = shore(["history", "--repo", arg, "--include-body"]);
assert_success(&history, "review history");
let history = parse_json(&history.stdout);
let entry = history["entries"]
.as_array()
.expect("entries")
.iter()
.find(|entry| entry["summary"]["kind"] == "review_observation_recorded")
.expect("observation history entry");
assert!(entry["summary"].get("body").is_none());
assert_eq!(entry["summary"]["bodyContentState"], "physically_removed");
assert!(has_diagnostic(
&history,
"body_content_physically_removed",
&body_hash
));
}
#[test]
fn unswept_removal_renders_suppressed_present() {
let repo = modified_repo();
let arg = repo.path().to_str().unwrap();
let (revision_id, body_hash) = capture_with_externalized_observation_body(repo.path());
remove_revision(repo.path(), &revision_id);
let show = shore(["revision", "show", "--repo", arg, "--include-body"]);
assert_success(&show, "revision show");
let show = parse_json(&show.stdout);
let observation = observation_entry(&show);
assert!(observation.get("body").is_none());
assert_eq!(observation["bodyContentState"], "suppressed_present");
assert!(has_diagnostic(
&show,
"body_content_suppressed_present",
&body_hash
));
}
#[test]
fn missing_blob_without_removal_still_hard_errors() {
let repo = modified_repo();
let arg = repo.path().to_str().unwrap();
let (_revision_id, body_hash) = capture_with_externalized_observation_body(repo.path());
let hex = body_hash.strip_prefix("sha256:").unwrap();
std::fs::remove_file(
support::common_dir_store(repo.path())
.join("artifacts")
.join("notes")
.join(format!("{hex}.json")),
)
.expect("delete the note blob without a removal event");
let show = shore(["revision", "show", "--repo", arg, "--include-body"]);
assert!(
!show.status.success(),
"absent bytes without an operative removal must keep the hard error"
);
assert!(
String::from_utf8_lossy(&show.stderr).contains("import referenced artifacts"),
"stderr:\n{}",
String::from_utf8_lossy(&show.stderr)
);
}
#[test]
fn wire_stays_silent_without_removal() {
let repo = modified_repo();
let arg = repo.path().to_str().unwrap();
let (_revision_id, _body_hash) = capture_with_externalized_observation_body(repo.path());
let show = shore(["revision", "show", "--repo", arg, "--include-body"]);
assert_success(&show, "revision show");
let list = shore(["observation", "list", "--repo", arg, "--include-body"]);
assert_success(&list, "observation list");
for (name, output) in [("show", &show.stdout), ("list", &list.stdout)] {
let text = String::from_utf8_lossy(output);
assert!(
!text.contains("ContentState"),
"{name} output must omit state fields without a removal:\n{text}"
);
}
}