use harn_vm::value::DictMap;
use harn_vm::{VmDictExt, VmValue};
use crate::error::HostlibError;
use crate::registry::{BuiltinRegistry, HostlibCapability};
use crate::tools::inspect_test_results::{get_run, AuthorizedTestPlanIdentity};
use crate::tools::payload::{optional_string, require_dict_arg, require_string};
pub const ISSUE_BUILTIN: &str = "__harness_verdict_issue";
pub struct VerdictCapability;
impl HostlibCapability for VerdictCapability {
fn module_name(&self) -> &'static str {
"verdict"
}
fn register_builtins(&self, registry: &mut BuiltinRegistry) {
registry.register_gated_fn("verdict", ISSUE_BUILTIN, "issue", verdict_issue_builtin);
}
}
fn verdict_issue_builtin(args: &[VmValue]) -> Result<VmValue, HostlibError> {
let map = require_dict_arg(ISSUE_BUILTIN, args)?;
let result_handle = require_string(ISSUE_BUILTIN, &map, "result_handle")?;
let subject = optional_string(ISSUE_BUILTIN, &map, "subject")?;
let Some(run) = get_run(&result_handle) else {
return Ok(outcome_dict(
"unavailable",
0,
0,
&result_handle,
"",
None,
None,
subject.as_deref(),
"no host execution recorded under this result handle; a positive verdict requires a real run_test execution",
));
};
let owner = run.execution_scope.as_deref();
let active = harn_vm::current_execution_scope();
let scope_ok = matches!((&active, &run.execution_scope), (Some(a), Some(o)) if a == o);
if !scope_ok {
return Ok(outcome_dict(
"unavailable",
0,
0,
&result_handle,
&run.content_hash,
owner,
run.artifacts.authorized_test_plan.as_ref(),
subject.as_deref(),
"result handle belongs to a different or ended execution; a positive verdict must be issued within the run that produced it",
));
}
if run.artifacts.exit_code != 0 {
return Ok(outcome_dict(
"fail",
0,
0,
&result_handle,
&run.content_hash,
owner,
run.artifacts.authorized_test_plan.as_ref(),
subject.as_deref(),
"host execution exited nonzero",
));
}
let Some(summary) = run.summary else {
return Ok(outcome_dict(
"unavailable",
0,
0,
&result_handle,
&run.content_hash,
owner,
run.artifacts.authorized_test_plan.as_ref(),
subject.as_deref(),
"host execution produced no recognized test results",
));
};
let total = summary.passed + summary.failed + summary.skipped;
if summary.failed > 0 {
return Ok(outcome_dict(
"fail",
summary.passed,
total,
&result_handle,
&run.content_hash,
owner,
run.artifacts.authorized_test_plan.as_ref(),
subject.as_deref(),
"host execution reported failing tests",
));
}
if summary.passed == 0 {
return Ok(outcome_dict(
"unavailable",
0,
total,
&result_handle,
&run.content_hash,
owner,
run.artifacts.authorized_test_plan.as_ref(),
subject.as_deref(),
"host execution reported zero passing tests",
));
}
let Some(plan) = run.artifacts.authorized_test_plan.as_ref() else {
return Ok(outcome_dict(
"unavailable",
0,
total,
&result_handle,
&run.content_hash,
owner,
None,
subject.as_deref(),
"execution was not produced by a host-discovered test plan bound to the active workspace",
));
};
Ok(outcome_dict(
"pass",
summary.passed,
total,
&result_handle,
&run.content_hash,
owner,
Some(plan),
subject.as_deref(),
"",
))
}
#[allow(clippy::too_many_arguments)]
fn outcome_dict(
outcome: &str,
passed: u32,
total: u32,
artifact_id: &str,
artifact_hash: &str,
execution_scope: Option<&str>,
plan: Option<&AuthorizedTestPlanIdentity>,
subject: Option<&str>,
detail: &str,
) -> VmValue {
let mut map = DictMap::new();
map.put_str("outcome", outcome);
map.put_int("passed", i64::from(passed));
map.put_int("total", i64::from(total));
map.put_str("artifact_id", artifact_id);
map.put_str("artifact_hash", artifact_hash);
map.put_opt_str("execution_scope", execution_scope);
map.put_opt_str("plan_id", plan.map(|identity| identity.plan_id.as_str()));
map.put_opt_str(
"workspace_hash",
plan.map(|identity| identity.workspace_hash.as_str()),
);
map.put_opt_str(
"command_hash",
plan.map(|identity| identity.command_hash.as_str()),
);
map.put_opt_str("subject", subject);
map.put_str("detail", detail);
VmValue::dict_map(map)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tools::inspect_test_results::{store_run, RawArtifacts, TestSummaryData};
use harn_lexer::Lexer;
use harn_parser::Parser;
use harn_vm::orchestration::RunExecutionRecord;
use harn_vm::{enter_execution_scope, mint_execution_scope, register_vm_stdlib, Compiler, Vm};
use std::path::Path;
use std::sync::Arc;
use tempfile::TempDir;
fn issue_arg(result_handle: &str) -> Vec<VmValue> {
let mut map = DictMap::new();
map.put_str("result_handle", result_handle);
vec![VmValue::dict_map(map)]
}
fn outcome_of(v: &VmValue) -> String {
v.as_dict()
.and_then(|d| d.get("outcome"))
.map(|o| o.as_str_cow().into_owned())
.unwrap_or_default()
}
fn artifacts(stdout: &str, exit_code: i32) -> RawArtifacts {
RawArtifacts {
stdout: stdout.to_string(),
stderr: String::new(),
exit_code,
junit_path: None,
ecosystem: None,
argv: Vec::new(),
authorized_test_plan: None,
}
}
fn cargo_fixture(parent: &Path, name: &str) -> TempDir {
let dir = tempfile::Builder::new()
.prefix(name)
.tempdir_in(parent)
.expect("fixture dir");
std::fs::write(
dir.path().join("Cargo.toml"),
format!(
"[package]\nname = \"{name}\"\nversion = \"0.0.0\"\nedition = \"2021\"\n\n[lib]\npath = \"lib.rs\"\n"
),
)
.expect("fixture manifest");
std::fs::write(
dir.path().join("lib.rs"),
"#[cfg(test)]\nmod tests {\n #[test]\n fn green() {}\n}\n",
)
.expect("fixture source");
dir
}
fn run_test_in(cwd: &Path, argv: Option<Vec<&str>>, filter: Option<&str>) -> VmValue {
let mut req = DictMap::new();
req.put_str("cwd", cwd.to_string_lossy());
if let Some(argv) = argv {
req.put(
"argv",
VmValue::List(Arc::new(argv.into_iter().map(VmValue::string).collect())),
);
}
req.put_opt_str("filter", filter);
crate::tools::run_test::handle(&[VmValue::dict_map(req)]).expect("run_test ok")
}
fn result_handle(result: &VmValue) -> String {
result
.as_dict()
.and_then(|dict| dict.get("result_handle"))
.map(|handle| handle.as_str_cow().into_owned())
.expect("run_test returns a result_handle")
}
fn compile(source: &str) -> harn_vm::Chunk {
let mut lexer = Lexer::new(source);
let tokens = lexer.tokenize().expect("tokenize");
let mut parser = Parser::new(tokens);
let program = parser.parse().expect("parse");
Compiler::new().compile(&program).expect("compile")
}
fn harn_string(value: &Path) -> String {
value
.to_string_lossy()
.replace('\\', "\\\\")
.replace('"', "\\\"")
}
fn overlap_vm(barrier: Arc<tokio::sync::Barrier>) -> Vm {
let mut vm = Vm::new();
register_vm_stdlib(&mut vm);
let _ = crate::install_default(&mut vm);
vm.register_async_builtin("__test_overlap", move |_ctx, _args| {
let barrier = barrier.clone();
async move {
barrier.wait().await;
Ok(VmValue::Nil)
}
});
vm
}
fn record_in_scope(
scope: &Arc<str>,
arts: RawArtifacts,
summary: Option<TestSummaryData>,
) -> String {
let _g = enter_execution_scope(scope.clone());
store_run(arts, summary)
}
#[cfg(unix)]
#[test]
fn arbitrary_passing_command_cannot_issue_pass() {
let _scope = enter_execution_scope(mint_execution_scope());
let cwd = std::env::current_dir().expect("cwd");
let res = run_test_in(
&cwd,
Some(vec![
"sh",
"-c",
"printf 'running 1 test\\ntest a ... ok\\n\\ntest result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\\n'",
]),
None,
);
let handle = result_handle(&res);
let out = verdict_issue_builtin(&issue_arg(&handle)).expect("issue ok");
assert_eq!(outcome_of(&out), "unavailable");
}
#[test]
fn host_discovered_test_plan_issues_pass() {
let workspace = TempDir::new().expect("workspace");
let fixture = cargo_fixture(workspace.path(), "verdict_positive");
harn_vm::stdlib::process::set_thread_execution_context(Some(RunExecutionRecord {
cwd: Some(fixture.path().to_string_lossy().into_owned()),
project_root: Some(fixture.path().to_string_lossy().into_owned()),
..RunExecutionRecord::default()
}));
let _scope = enter_execution_scope(mint_execution_scope());
let res = run_test_in(fixture.path(), None, None);
let out = verdict_issue_builtin(&issue_arg(&result_handle(&res))).expect("issue ok");
harn_vm::stdlib::process::set_thread_execution_context(None);
assert_eq!(outcome_of(&out), "pass");
}
#[test]
fn filtered_discovered_test_plan_cannot_issue_pass() {
let workspace = TempDir::new().expect("workspace");
let fixture = cargo_fixture(workspace.path(), "verdict_filtered");
harn_vm::stdlib::process::set_thread_execution_context(Some(RunExecutionRecord {
cwd: Some(fixture.path().to_string_lossy().into_owned()),
project_root: Some(fixture.path().to_string_lossy().into_owned()),
..RunExecutionRecord::default()
}));
let _scope = enter_execution_scope(mint_execution_scope());
let res = run_test_in(fixture.path(), None, Some("green"));
let out = verdict_issue_builtin(&issue_arg(&result_handle(&res))).expect("issue ok");
harn_vm::stdlib::process::set_thread_execution_context(None);
assert_eq!(outcome_of(&out), "unavailable");
}
#[tokio::test(flavor = "current_thread")]
async fn overlapping_top_level_vms_issue_only_their_own_receipts() {
tokio::task::LocalSet::new()
.run_until(async {
let workspace = TempDir::new().expect("workspace");
let fixture = cargo_fixture(workspace.path(), "verdict_overlap");
harn_vm::stdlib::process::set_thread_execution_context(Some(RunExecutionRecord {
cwd: Some(fixture.path().to_string_lossy().into_owned()),
project_root: Some(fixture.path().to_string_lossy().into_owned()),
..RunExecutionRecord::default()
}));
let source = |cwd: &Path| {
format!(
r#"
import {{ verdict_disposition, verdict_from_run }} from "std/agent/verdict"
pipeline default(_task) {{
hostlib_enable("tools:deterministic")
const run = hostlib_tools_run_test({{cwd: "{}", timeout_ms: 120000}})
__test_overlap()
return verdict_disposition(verdict_from_run(harness, run))
}}
"#,
harn_string(cwd)
)
};
let chunk_a = compile(&source(fixture.path()));
let chunk_b = compile(&source(fixture.path()));
let barrier = Arc::new(tokio::sync::Barrier::new(2));
let mut vm_a = overlap_vm(barrier.clone());
let mut vm_b = overlap_vm(barrier);
let (result_a, result_b) =
tokio::join!(vm_a.execute(&chunk_a), vm_b.execute(&chunk_b));
harn_vm::stdlib::process::set_thread_execution_context(None);
assert_eq!(result_a.expect("vm A").display(), "pass");
assert_eq!(result_b.expect("vm B").display(), "pass");
})
.await;
}
#[test]
fn unrecorded_handle_is_never_a_pass() {
let _scope = enter_execution_scope(mint_execution_scope());
let out = verdict_issue_builtin(&issue_arg("htr-deadbeef-999999")).expect("issue ok");
assert_eq!(outcome_of(&out), "unavailable");
}
#[test]
fn cross_scope_handle_is_rejected() {
let scope_a: Arc<str> = mint_execution_scope();
let handle = record_in_scope(
&scope_a,
artifacts("test result: ok. 2 passed; 0 failed", 0),
Some(TestSummaryData {
passed: 2,
failed: 0,
skipped: 0,
}),
);
let _scope_b = enter_execution_scope(mint_execution_scope());
let out = verdict_issue_builtin(&issue_arg(&handle)).expect("issue ok");
assert_eq!(outcome_of(&out), "unavailable");
}
#[test]
fn no_active_scope_is_rejected() {
let scope_a: Arc<str> = mint_execution_scope();
let handle = record_in_scope(
&scope_a,
artifacts("test result: ok. 1 passed; 0 failed", 0),
Some(TestSummaryData {
passed: 1,
failed: 0,
skipped: 0,
}),
);
let out = verdict_issue_builtin(&issue_arg(&handle)).expect("issue ok");
assert_eq!(outcome_of(&out), "unavailable");
}
#[test]
fn nonzero_exit_with_passing_text_is_not_a_pass() {
let _g = enter_execution_scope(mint_execution_scope());
let handle = store_run(
artifacts("test result: ok. 1 passed; 0 failed", 1),
Some(TestSummaryData {
passed: 1,
failed: 0,
skipped: 0,
}),
);
let out = verdict_issue_builtin(&issue_arg(&handle)).expect("issue ok");
assert_eq!(outcome_of(&out), "fail");
}
#[test]
fn red_host_execution_issues_fail() {
let _g = enter_execution_scope(mint_execution_scope());
let handle = store_run(
artifacts("test result: FAILED. 1 passed; 1 failed", 1),
Some(TestSummaryData {
passed: 1,
failed: 1,
skipped: 0,
}),
);
let out = verdict_issue_builtin(&issue_arg(&handle)).expect("issue ok");
assert_eq!(outcome_of(&out), "fail");
}
#[test]
fn recorded_but_unparsed_execution_is_unavailable() {
let _g = enter_execution_scope(mint_execution_scope());
let handle = store_run(artifacts("not test output", 0), None);
let out = verdict_issue_builtin(&issue_arg(&handle)).expect("issue ok");
assert_eq!(outcome_of(&out), "unavailable");
}
}