use aion_core::{ContentType, Payload};
use serde_json::json;
use super::HarnessWorkspace;
type TestResult = Result<(), Box<dyn std::error::Error>>;
fn input(value: &serde_json::Value) -> Result<Payload, Box<dyn std::error::Error>> {
Ok(Payload::from_json(value)?)
}
#[test]
fn a_job_that_names_its_tree_resolves_to_that_tree() -> TestResult {
let workspace = HarnessWorkspace::PerRun("repo".to_owned());
let resolved = workspace.for_attempt(&input(&json!({ "repo": "/srv/lane-3" }))?)?;
assert_eq!(resolved, std::path::PathBuf::from("/srv/lane-3"));
Ok(())
}
#[test]
fn a_fixed_tree_ignores_what_the_job_carries() -> TestResult {
let workspace = HarnessWorkspace::Fixed(std::path::PathBuf::from("/srv/one-tree"));
let resolved = workspace.for_attempt(&input(&json!({ "repo": "/srv/other" }))?)?;
assert_eq!(resolved, std::path::PathBuf::from("/srv/one-tree"));
Ok(())
}
#[test]
fn a_job_missing_the_parameter_is_refused_and_never_defaulted() -> TestResult {
let workspace = HarnessWorkspace::PerRun("repo".to_owned());
let error = workspace
.for_attempt(&input(&json!({ "branch": "main" }))?)
.err()
.ok_or("a job that does not name its tree cannot launch an agent")?;
assert!(
error.to_string().contains("repo"),
"the refusal names the parameter the job omitted: {error}"
);
assert!(
error.is_deterministic(),
"the same input meets the same wall, so retrying spends an attempt to learn \
nothing: {error}"
);
Ok(())
}
#[test]
fn a_blank_directory_is_refused_rather_than_read_as_here() -> TestResult {
let workspace = HarnessWorkspace::PerRun("repo".to_owned());
let error = workspace
.for_attempt(&input(&json!({ "repo": " " }))?)
.err()
.ok_or("a blank directory is a job that did not say where to work")?;
assert!(
error.is_deterministic(),
"a blank value is deterministic: {error}"
);
Ok(())
}
#[test]
fn a_parameter_that_is_not_a_string_says_what_it_was() -> TestResult {
let workspace = HarnessWorkspace::PerRun("repo".to_owned());
let error = workspace
.for_attempt(&input(&json!({ "repo": 7 }))?)
.err()
.ok_or("a number is not a path")?;
assert!(
error.to_string().contains("a number"),
"the refusal says what arrived instead of a path: {error}"
);
Ok(())
}
#[test]
fn an_input_that_is_not_an_object_says_it_carries_no_parameters() -> TestResult {
let workspace = HarnessWorkspace::PerRun("repo".to_owned());
let error = workspace
.for_attempt(&input(&json!(["/srv/lane-3"]))?)
.err()
.ok_or("a list carries no named parameters")?;
assert!(
error.to_string().contains("carries no parameters"),
"the refusal distinguishes a shapeless input from a missing field: {error}"
);
Ok(())
}
#[test]
fn input_that_is_not_json_is_refused_terminally() -> TestResult {
let workspace = HarnessWorkspace::PerRun("repo".to_owned());
let payload = Payload::new(ContentType::Json, b"not json".to_vec());
let error = workspace
.for_attempt(&payload)
.err()
.ok_or("undecodable bytes cannot name a directory")?;
assert!(
error.is_deterministic(),
"undecodable input is deterministic: {error}"
);
Ok(())
}
#[test]
fn each_form_answers_only_for_itself() {
let fixed = HarnessWorkspace::Fixed(std::path::PathBuf::from("/srv/one-tree"));
let per_run = HarnessWorkspace::PerRun("repo".to_owned());
assert_eq!(fixed.fixed(), Some(std::path::Path::new("/srv/one-tree")));
assert_eq!(fixed.per_run(), None);
assert_eq!(per_run.per_run(), Some("repo"));
assert_eq!(per_run.fixed(), None);
}
#[test]
fn a_one_field_object_asks_the_agent_the_field_and_not_the_json() -> TestResult {
let workspace = HarnessWorkspace::Fixed(std::path::PathBuf::from("/srv/one-tree"));
let prompt = workspace.prompt_for_attempt(&input(&json!({ "prompt": "say hi" }))?, &[])?;
assert_eq!(prompt, "say hi");
Ok(())
}
#[test]
fn a_bound_directory_is_subtracted_and_what_remains_is_the_prompt() -> TestResult {
let workspace = HarnessWorkspace::PerRun("repo".to_owned());
let carried = input(&json!({ "repo": "/srv/lane-3", "prompt": "review the diff" }))?;
assert_eq!(
workspace.for_attempt(&carried)?,
std::path::PathBuf::from("/srv/lane-3")
);
assert_eq!(
workspace.prompt_for_attempt(&carried, &[])?,
"review the diff"
);
Ok(())
}
#[test]
fn the_prompt_is_found_wherever_the_directory_sits_in_the_object() -> TestResult {
let workspace = HarnessWorkspace::PerRun("repo".to_owned());
let prompt =
workspace.prompt_for_attempt(&input(&json!({ "prompt": "go", "repo": "/srv/x" }))?, &[])?;
assert_eq!(prompt, "go");
Ok(())
}
#[test]
fn a_json_string_input_is_the_prompt_verbatim() -> TestResult {
let workspace = HarnessWorkspace::Fixed(std::path::PathBuf::from("/srv/one-tree"));
let encoded = serde_json::to_vec(&json!("line one\nsay \"hi\"\nline three"))?;
let payload = Payload::new(ContentType::Json, encoded);
assert_eq!(
workspace.prompt_for_attempt(&payload, &[])?,
"line one\nsay \"hi\"\nline three"
);
Ok(())
}
#[test]
fn json_tagged_text_that_is_not_json_passes_through_as_the_prompt() -> TestResult {
let workspace = HarnessWorkspace::Fixed(std::path::PathBuf::from("/srv/one-tree"));
let payload = Payload::new(ContentType::Json, b"plain prompt, not json".to_vec());
assert_eq!(
workspace.prompt_for_attempt(&payload, &[])?,
"plain prompt, not json"
);
Ok(())
}
#[test]
fn a_reserved_parameter_is_subtracted_and_what_remains_is_the_prompt() -> TestResult {
let workspace = HarnessWorkspace::PerRun("repo".to_owned());
let carried = input(&json!({
"repo": "/srv/lane-3",
"session": "sess-42",
"prompt": "carry on",
}))?;
assert_eq!(
workspace.prompt_for_attempt(&carried, &["session"])?,
"carry on"
);
Ok(())
}
#[test]
fn an_absent_reserved_parameter_changes_nothing() -> TestResult {
let workspace = HarnessWorkspace::PerRun("repo".to_owned());
let carried = input(&json!({ "repo": "/srv/lane-3", "prompt": "fresh start" }))?;
assert_eq!(
workspace.prompt_for_attempt(&carried, &["session"])?,
"fresh start"
);
Ok(())
}
#[test]
fn an_object_carrying_two_possible_prompts_is_refused_by_name() -> TestResult {
let workspace = HarnessWorkspace::PerRun("repo".to_owned());
let carried = input(&json!({ "repo": "/srv/x", "prompt": "go", "note": "also go" }))?;
let error = workspace
.prompt_for_attempt(&carried, &[])
.err()
.ok_or("two candidate prompts is a choice no adapter may make")?;
assert!(
error.to_string().contains("prompt") && error.to_string().contains("note"),
"the refusal names the fields it found: {error}"
);
assert!(
error.is_deterministic(),
"the same input meets the same wall on every attempt: {error}"
);
Ok(())
}
#[test]
fn an_object_carrying_only_the_directory_is_refused() -> TestResult {
let workspace = HarnessWorkspace::PerRun("repo".to_owned());
let error = workspace
.prompt_for_attempt(&input(&json!({ "repo": "/srv/x" }))?, &[])
.err()
.ok_or("a job with no prompt asks the agent nothing")?;
assert!(
error.is_deterministic(),
"a missing prompt is missing on every attempt: {error}"
);
Ok(())
}
#[test]
fn a_prompt_field_that_is_not_text_is_refused_naming_what_arrived() -> TestResult {
let workspace = HarnessWorkspace::Fixed(std::path::PathBuf::from("/srv/one-tree"));
let error = workspace
.prompt_for_attempt(&input(&json!({ "prompt": { "task": "build" } }))?, &[])
.err()
.ok_or("a structured prompt is not words")?;
assert!(
error.to_string().contains("an object"),
"the refusal says what arrived instead of text: {error}"
);
Ok(())
}
#[test]
fn non_utf8_input_is_refused_as_a_protocol_fault() -> TestResult {
let workspace = HarnessWorkspace::Fixed(std::path::PathBuf::from("/srv/one-tree"));
let payload = Payload::new(ContentType::Json, vec![0xff, 0xfe, 0xfd]);
let error = workspace
.prompt_for_attempt(&payload, &[])
.err()
.ok_or("non-UTF-8 input carries no prompt")?;
assert!(
error.to_string().contains("not valid UTF-8"),
"the refusal names the UTF-8 mismatch: {error}"
);
Ok(())
}