use std::path::PathBuf;
use std::process::{Command, Output};
use serde_json::Value;
fn skilltest() -> PathBuf {
PathBuf::from(env!("CARGO_BIN_EXE_skilltest"))
}
fn fixtures() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../tests/fixtures/oneharness")
}
fn shim() -> PathBuf {
fixtures().join("fake-claude.sh")
}
fn oneharness_bin() -> String {
std::env::var("SKILLTEST_ONEHARNESS_BIN").unwrap_or_else(|_| "oneharness".into())
}
fn run_case(case: &str, extra: &[&str]) -> Output {
run_platform(case, "claude-code", extra)
}
fn run_platform(case: &str, platform: &str, extra: &[&str]) -> Output {
Command::new(skilltest())
.arg("run")
.arg(fixtures().join("cases").join(case))
.args(["--oneharness-bin", &oneharness_bin()])
.args(["--platform", platform])
.args(["--model", "fake-model"])
.args(["--judge-harness", "claude-code"])
.args(["--judge-model", "fake-model"])
.args(["--timeout", "60"])
.args(extra)
.env("ONEHARNESS_BIN_CLAUDE_CODE", shim())
.output()
.expect("skilltest run executes")
}
fn report(output: &Output) -> Value {
assert!(
output.status.code() == Some(0) || output.status.code() == Some(1),
"expected a completed run (exit 0/1), got {:?}; stderr: {}",
output.status.code(),
String::from_utf8_lossy(&output.stderr)
);
serde_json::from_slice(&output.stdout).expect("stdout is a JSON report")
}
#[test]
#[ignore = "needs oneharness on PATH (just install-oneharness); run via just test-oneharness"]
fn stub_and_spy_round_trip_through_real_oneharness() {
let out = run_case("mock_stub.yaml", &["--format", "json"]);
assert_eq!(
out.status.code(),
Some(0),
"stderr: {}",
String::from_utf8_lossy(&out.stderr)
);
let report = report(&out);
assert_eq!(report["passed"], Value::Bool(true), "report: {report:#}");
let run = &report["runs"][0];
let records = run["mock_calls"].as_array().expect("mock_calls present");
assert_eq!(records.len(), 3, "records: {records:?}");
assert_eq!(records[0]["action"], "stub");
assert_eq!(records[0]["mock"], "push");
assert_eq!(records[0]["input"]["command"], "git push origin main");
assert_eq!(records[1]["action"], "allow");
let events = run["transcript"]["messages"][1]["events"]
.as_array()
.expect("assistant turn carries events");
let stub_call = events[0]["input"]["command"].as_str().unwrap();
assert!(stub_call.starts_with("printf"), "post-rewrite: {stub_call}");
let stub_result = events
.iter()
.find(|e| e["kind"] == "tool_result")
.expect("a tool_result event");
assert_eq!(
stub_result["output"].as_str().unwrap().trim(),
"Everything up-to-date"
);
let content = run["transcript"]["messages"][1]["content"]
.as_str()
.unwrap();
assert!(content.contains("Everything up-to-date"), "{content}");
let evals = run["evals"].as_array().unwrap();
assert_eq!(evals[0]["detail"]["kind"], "calls");
assert_eq!(evals[3]["detail"]["kind"], "boolean");
assert_eq!(evals[3]["reason"], "fake-claude judge");
assert!(
!out_text(&out).contains("JUDGE_SAW_SETTINGS"),
"mocks must never be delivered to the judge"
);
assert!(run["usage"]["input_tokens"].as_u64().unwrap_or(0) > 0);
}
fn out_text(out: &Output) -> String {
String::from_utf8_lossy(&out.stdout).into_owned()
}
#[test]
#[ignore = "needs oneharness on PATH (just install-oneharness); run via just test-oneharness"]
fn deny_violation_fails_not_called_through_real_oneharness() {
let out = run_case("mock_violation.yaml", &["--format", "json"]);
assert_eq!(out.status.code(), Some(1), "a violated expectation exits 1");
let report = report(&out);
let eval = &report["runs"][0]["evals"][0];
assert_eq!(eval["passed"], Value::Bool(false));
assert_eq!(eval["detail"]["kind"], "calls");
assert_eq!(eval["detail"]["negated"], true);
let reason = eval["reason"].as_str().unwrap();
assert!(
reason.contains("rm -rf") && reason.contains("[deny]"),
"reason: {reason}"
);
let content = report["runs"][0]["transcript"]["messages"][1]["content"]
.as_str()
.unwrap();
assert!(
content.contains("destructive commands are blocked"),
"{content}"
);
}
#[test]
#[ignore = "needs oneharness on PATH (just install-oneharness); run via just test-oneharness"]
fn rewrite_substitutes_input_through_real_oneharness() {
let out = run_case("mock_rewrite.yaml", &["--format", "json"]);
assert_eq!(
out.status.code(),
Some(0),
"stderr: {}",
String::from_utf8_lossy(&out.stderr)
);
let report = report(&out);
let run = &report["runs"][0];
let record = run["mock_calls"]
.as_array()
.unwrap()
.iter()
.find(|r| r["action"] == "rewrite")
.expect("a rewrite record");
assert_eq!(record["mock"], "redirect");
assert_eq!(record["input"]["command"], "git status");
let content = run["transcript"]["messages"][1]["content"]
.as_str()
.unwrap();
assert!(content.contains("REDIRECTED-OUTPUT"), "{content}");
}
#[test]
#[ignore = "needs oneharness on PATH (just install-oneharness); run via just test-oneharness"]
fn spy_flag_records_all_calls_through_real_oneharness() {
let out = run_case("spy_plain.yaml", &["--spy", "--format", "json"]);
assert_eq!(
out.status.code(),
Some(0),
"stderr: {}",
String::from_utf8_lossy(&out.stderr)
);
let report = report(&out);
let records = report["runs"][0]["mock_calls"].as_array().unwrap();
assert_eq!(records.len(), 3);
assert!(records.iter().all(|r| r["action"] == "allow"));
assert!(records.iter().all(|r| r["mock"].is_null()));
let content = report["runs"][0]["transcript"]["messages"][1]["content"]
.as_str()
.unwrap();
assert!(
!content.contains("[bash]"),
"no intercepts surfaced: {content}"
);
}
#[test]
#[ignore = "needs oneharness on PATH (just install-oneharness); run via just test-oneharness"]
fn cli_mocks_file_flows_through_real_oneharness() {
let dir = std::env::temp_dir().join(format!("skilltest-ohit-{}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();
let mocks = dir.join("mocks.json");
std::fs::write(
&mocks,
r#"[{"name":"push","match":{"tool":"bash","pattern":"git push( --force)?\\b"},"stub":"Everything up-to-date"}]"#,
)
.unwrap();
let out = run_case(
"spy_plain.yaml",
&["--mocks", mocks.to_str().unwrap(), "--format", "json"],
);
assert_eq!(
out.status.code(),
Some(0),
"stderr: {}",
String::from_utf8_lossy(&out.stderr)
);
let report = report(&out);
let records = report["runs"][0]["mock_calls"].as_array().unwrap();
assert_eq!(records[0]["action"], "stub");
assert_eq!(records[0]["mock"], "push");
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
#[ignore = "needs oneharness on PATH (just install-oneharness); run via just test-oneharness"]
fn unsupported_verb_is_refused_loudly_by_real_oneharness() {
let out = run_platform("mock_stub.yaml", "goose", &["--format", "json"]);
assert_eq!(
out.status.code(),
Some(3),
"an inexpressible verb is a provider error; stderr: {}",
String::from_utf8_lossy(&out.stderr)
);
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(
stderr.contains("cannot express") || stderr.contains("mock action"),
"the oneharness refusal reaches the user: {stderr}"
);
}
#[test]
#[ignore = "needs oneharness on PATH (just install-oneharness); run via just test-oneharness"]
fn streaming_with_mocks_through_real_oneharness() {
let out = run_case("mock_stub.yaml", &["--format", "json-stream"]);
assert_eq!(
out.status.code(),
Some(0),
"stderr: {}",
String::from_utf8_lossy(&out.stderr)
);
let text = String::from_utf8(out.stdout).expect("stdout is utf8");
let lines: Vec<Value> = text
.lines()
.filter(|l| !l.trim().is_empty())
.map(|l| serde_json::from_str(l).expect("one JSON object per line"))
.collect();
let (events, results): (Vec<&Value>, Vec<&Value>) =
lines.iter().partition(|l| l["type"] == "event");
assert!(!events.is_empty(), "tool events streamed live");
assert_eq!(results.len(), 1, "exactly one terminal result");
let report = &results[0]["report"];
assert_eq!(report["passed"], Value::Bool(true), "report: {report:#}");
let records = report["runs"][0]["mock_calls"].as_array().unwrap();
assert_eq!(records[0]["action"], "stub");
assert_eq!(records[0]["mock"], "push");
}