use std::{fs, path::Path};
use assert_cmd::Command;
use predicates::prelude::*;
fn git(repo: &Path, args: &[&str]) {
let status = std::process::Command::new("git")
.args(args)
.current_dir(repo)
.status()
.unwrap();
assert!(status.success(), "git {args:?} failed");
}
fn git_stdout(repo: &Path, args: &[&str]) -> String {
let output = std::process::Command::new("git")
.args(args)
.current_dir(repo)
.output()
.unwrap();
assert!(output.status.success(), "git {args:?} failed");
String::from_utf8(output.stdout).unwrap().trim().to_owned()
}
#[cfg(unix)]
fn make_executable(path: &Path) {
use std::os::unix::fs::PermissionsExt;
let mut permissions = fs::metadata(path).unwrap().permissions();
permissions.set_mode(0o755);
fs::set_permissions(path, permissions).unwrap();
}
#[cfg(not(unix))]
fn make_executable(_path: &Path) {}
#[test]
fn watch_once_drains_queued_commit_through_mocked_reviewer() {
let temp = tempfile::tempdir().unwrap();
let repo = temp.path().join("repo");
let bin = temp.path().join("bin");
fs::create_dir(&repo).unwrap();
fs::create_dir(&bin).unwrap();
git(&repo, &["init"]);
git(&repo, &["config", "user.email", "truth@example.invalid"]);
git(&repo, &["config", "user.name", "Truth Mirror Test"]);
fs::write(repo.join("file.txt"), "hello\n").unwrap();
git(&repo, &["add", "file.txt"]);
git(
&repo,
&[
"commit",
"-m",
"feat: add file",
"-m",
"CLAIM: add file | verified: cargo test | evidence: tests:watch-e2e",
],
);
let sha = git_stdout(&repo, &["rev-parse", "HEAD"]);
let state = repo.join(".truth-mirror");
fs::create_dir_all(&state).unwrap();
fs::write(
state.join("review-queue.jsonl"),
format!("{{\"commit_sha\":\"{sha}\",\"enqueued_at_unix\":100}}\n"),
)
.unwrap();
let fake_codex = bin.join("codex");
fs::write(
&fake_codex,
r#"#!/usr/bin/env python3
import json
print(json.dumps({
"verdict": "REJECT",
"summary": "The claim is not substantiated.",
"findings": [{
"severity": "high",
"title": "claim not substantiated",
"body": "The cited evidence does not prove the claim.",
"file": "file.txt",
"line_start": 1,
"line_end": 1,
"confidence": 95,
"recommendation": "Add evidence that directly proves the claim."
}],
"next_steps": ["Re-run the verification command."]
}))
"#,
)
.unwrap();
make_executable(&fake_codex);
let path = format!(
"{}:{}",
bin.display(),
std::env::var("PATH").unwrap_or_default()
);
Command::cargo_bin("truth-mirror")
.unwrap()
.current_dir(&repo)
.env("PATH", path)
.args([
"--state-dir",
".truth-mirror",
"watch",
"--once",
"--watched-agent",
"claude",
"--watched-model",
"model-a",
"--reviewer-harness",
"codex",
"--reviewer-model",
"model-b",
])
.assert()
.success()
.stdout(predicate::str::contains("reviewed 1 commit"));
let ledger = fs::read_to_string(state.join("ledger.jsonl")).unwrap();
assert!(ledger.contains("\"verdict\":\"REJECT\""));
assert!(ledger.contains(&sha));
let queue = state.join("review-queue.jsonl");
let remaining = fs::read_to_string(&queue).unwrap_or_default();
assert!(
remaining.trim().is_empty(),
"queue should be empty after drain, got: {remaining}"
);
Command::cargo_bin("truth-mirror")
.unwrap()
.current_dir(&repo)
.args(["--state-dir", ".truth-mirror", "ledger", "stats"])
.assert()
.success()
.stdout(predicate::str::contains("reject=1"))
.stdout(predicate::str::contains("unresolved=1"));
}
#[test]
fn watch_once_is_quiet_with_empty_queue() {
let temp = tempfile::tempdir().unwrap();
let repo = temp.path().join("repo");
fs::create_dir(&repo).unwrap();
git(&repo, &["init"]);
Command::cargo_bin("truth-mirror")
.unwrap()
.current_dir(&repo)
.args(["--state-dir", ".truth-mirror", "watch", "--once"])
.assert()
.success()
.stdout(predicate::str::contains("reviewed 0 commit"));
}