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");
}
#[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 review_command_writes_ledger_with_mocked_reviewer_cli() {
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:review-e2e",
],
);
let fake_codex = bin.join("codex");
fs::write(
&fake_codex,
r#"#!/usr/bin/env python3
import json
print(json.dumps({
"verdict": "PASS",
"summary": "The claim is substantiated.",
"findings": [],
"next_steps": []
}))
"#,
)
.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",
"review",
"HEAD",
"--watched-agent",
"claude",
"--watched-model",
"model-a",
"--reviewer-harness",
"codex",
"--reviewer-model",
"model-b",
])
.assert()
.success()
.stdout(predicate::str::contains("truth-mirror review: run"));
let ledger = fs::read_to_string(repo.join(".truth-mirror/ledger.jsonl")).unwrap();
assert!(ledger.contains("\"verdict\":\"PASS\""));
Command::cargo_bin("truth-mirror")
.unwrap()
.current_dir(&repo)
.args(["--state-dir", ".truth-mirror", "review", "status"])
.assert()
.success()
.stdout(predicate::str::contains("completed"))
.stdout(predicate::str::contains("HEAD"));
Command::cargo_bin("truth-mirror")
.unwrap()
.current_dir(&repo)
.args(["--state-dir", ".truth-mirror", "review", "result"])
.assert()
.success()
.stdout(predicate::str::contains("status: completed"))
.stdout(predicate::str::contains("ledger_entries:"));
Command::cargo_bin("truth-mirror")
.unwrap()
.current_dir(&repo)
.args(["--state-dir", ".truth-mirror", "ledger", "stats"])
.assert()
.success()
.stdout(predicate::str::contains("pass=1"));
}
#[test]
fn review_strict_goal_loops_until_lie_threshold() {
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:strict-goal-e2e",
],
);
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": "fabricated evidence",
"body": "The cited evidence does not prove the claim.",
"file": "file.txt",
"line_start": 1,
"line_end": 1,
"confidence": 95,
"recommendation": "Replace the claim with one supported by executable evidence."
}],
"next_steps": ["Run the real 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",
"review",
"HEAD",
"--watched-agent",
"claude",
"--watched-model",
"model-a",
"--reviewer-harness",
"codex",
"--reviewer-model",
"model-b",
"--strict-goal",
"--stop-after-lies",
"1",
"--max-passes",
"5",
])
.assert()
.success()
.stdout(predicate::str::contains("1 pass"))
.stdout(predicate::str::contains("1 lie"))
.stdout(predicate::str::contains("stopped: lies exposed"));
let ledger = fs::read_to_string(repo.join(".truth-mirror/ledger.jsonl")).unwrap();
assert!(ledger.contains("\"verdict\":\"REJECT\""));
}
#[test]
fn review_working_tree_scope_uses_claim_file_and_worktree_context() {
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: base",
"-m",
"CLAIM: base | verified: cargo test | evidence: tests:base",
],
);
fs::write(repo.join("file.txt"), "hello\nworld\n").unwrap();
fs::write(repo.join("new.txt"), "untracked\n").unwrap();
fs::create_dir(repo.join(".truth-mirror")).unwrap();
fs::write(
repo.join(".truth-mirror/claim.txt"),
"CLAIM: working tree change | verified: cargo test | evidence: tests:working-tree\n",
)
.unwrap();
let fake_codex = bin.join("codex");
fs::write(
&fake_codex,
r#"#!/usr/bin/env python3
import json
import sys
prompt = sys.argv[-1]
assert "WORKING TREE STATUS:" in prompt, prompt
assert "TRACKED DIFF AGAINST HEAD:" in prompt, prompt
assert "UNTRACKED FILES:" in prompt, prompt
print(json.dumps({
"verdict": "PASS",
"summary": "The working tree claim is substantiated.",
"findings": [],
"next_steps": []
}))
"#,
)
.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",
"review",
"--scope",
"working-tree",
"--watched-agent",
"claude",
"--watched-model",
"model-a",
"--reviewer-harness",
"codex",
"--reviewer-model",
"model-b",
])
.assert()
.success();
let run = fs::read_dir(repo.join(".truth-mirror/runs"))
.unwrap()
.next()
.unwrap()
.unwrap()
.path();
let run_json = fs::read_to_string(run).unwrap();
assert!(run_json.contains("\"target\": \"working-tree\""));
}
#[test]
fn review_auto_scope_uses_working_tree_when_repo_is_dirty() {
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: base",
"-m",
"CLAIM: base | verified: cargo test | evidence: tests:base",
],
);
fs::write(repo.join("file.txt"), "hello\nauto\n").unwrap();
fs::create_dir(repo.join(".truth-mirror")).unwrap();
fs::write(
repo.join(".truth-mirror/claim.txt"),
"CLAIM: auto working tree change | verified: cargo test | evidence: tests:auto\n",
)
.unwrap();
let fake_codex = bin.join("codex");
fs::write(
&fake_codex,
r#"#!/usr/bin/env python3
import json
import sys
prompt = sys.argv[-1]
assert "WORKING TREE STATUS:" in prompt, prompt
assert "TRACKED DIFF AGAINST HEAD:" in prompt, prompt
print(json.dumps({
"verdict": "PASS",
"summary": "The auto-selected working tree claim is substantiated.",
"findings": [],
"next_steps": []
}))
"#,
)
.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",
"review",
"--scope",
"auto",
"--watched-agent",
"claude",
"--watched-model",
"model-a",
"--reviewer-harness",
"codex",
"--reviewer-model",
"model-b",
])
.assert()
.success();
let run = fs::read_dir(repo.join(".truth-mirror/runs"))
.unwrap()
.next()
.unwrap()
.unwrap()
.path();
let run_json = fs::read_to_string(run).unwrap();
assert!(run_json.contains("\"target\": \"working-tree\""));
}
#[test]
fn review_branch_scope_uses_explicit_base_and_branch_context() {
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: base",
"-m",
"CLAIM: base | verified: cargo test | evidence: tests:base",
],
);
fs::write(repo.join("file.txt"), "hello\nbranch\n").unwrap();
git(&repo, &["add", "file.txt"]);
git(
&repo,
&[
"commit",
"-m",
"feat: branch",
"-m",
"CLAIM: branch change | verified: cargo test | evidence: tests:branch",
],
);
let fake_codex = bin.join("codex");
fs::write(
&fake_codex,
r#"#!/usr/bin/env python3
import json
import sys
prompt = sys.argv[-1]
assert "BRANCH BASE: HEAD~1" in prompt, prompt
assert "MERGE BASE:" in prompt, prompt
assert "COMMITS:" in prompt, prompt
print(json.dumps({
"verdict": "PASS",
"summary": "The branch claim is substantiated.",
"findings": [],
"next_steps": []
}))
"#,
)
.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",
"review",
"--scope",
"branch",
"--base",
"HEAD~1",
"--watched-agent",
"claude",
"--watched-model",
"model-a",
"--reviewer-harness",
"codex",
"--reviewer-model",
"model-b",
])
.assert()
.success();
let run = fs::read_dir(repo.join(".truth-mirror/runs"))
.unwrap()
.next()
.unwrap()
.unwrap()
.path();
let run_json = fs::read_to_string(run).unwrap();
assert!(run_json.contains("\"target\": \"branch:HEAD~1\""));
}