use super::fixtures::*;
use crate::prompt::{Error, run};
use brazen::FinishReason;
use serde_json::json;
#[test]
fn loop_terminates_on_non_tool_use_finish() {
let repo = scaffold_repo(VALID_PER_REPO_PROVIDERS_YAML, Some("body"));
let harness = scaffold_harness_root();
let r1 = stream_of(FinishReason::Stop, &[Block::Text("done")]);
let adapter = StubAdapter::happy(&r1);
let git = StubGit::ok();
let (clock, id) = (FixedClock::default(), FixedIdGen);
let (sleeper, tool_executor) = (StubSleeper::default(), StubToolExecutor::ok());
run(
repo.path(),
"hi",
None,
None,
None,
crate::prompt::PinnedDocs::none(),
None,
None,
&valid_deps(
&adapter,
&sleeper,
&git,
&clock,
&id,
&tool_executor,
harness.path(),
),
)
.unwrap();
assert!(tool_executor.invocations.borrow().is_empty());
}
#[test]
fn loop_surfaces_tool_executor_failure_as_tool_exec_error() {
let repo = scaffold_repo(VALID_PER_REPO_PROVIDERS_YAML, Some("body"));
let harness = scaffold_harness_root();
let r1 = stream_of(
FinishReason::ToolUse,
&[Block::ToolUse {
id: "toolu_01",
name: "bash",
input: json!({"cmd": "ls"}),
}],
);
let adapter = StubAdapter::happy(&r1);
let git = StubGit::ok();
let (clock, id) = (FixedClock::default(), FixedIdGen);
let (sleeper, tool_executor) = (StubSleeper::default(), StubToolExecutor::failing_on("bash"));
let err = run(
repo.path(),
"hi",
None,
None,
None,
crate::prompt::PinnedDocs::none(),
None,
None,
&valid_deps(
&adapter,
&sleeper,
&git,
&clock,
&id,
&tool_executor,
harness.path(),
),
)
.unwrap_err();
match err {
Error::ToolExec { tool, .. } => assert_eq!(tool, "bash"),
other => panic!("expected ToolExec, got {other:?}"),
}
}
#[test]
fn a_response_cut_at_the_output_cap_is_refused_not_committed() {
let repo = scaffold_repo(VALID_PER_REPO_PROVIDERS_YAML, Some("body"));
let harness = scaffold_harness_root();
let cut = stream_of(
FinishReason::Length,
&[Block::ToolUse {
id: "toolu_01",
name: "bash",
input: json!({}),
}],
);
let adapter = StubAdapter::happy(&cut);
let git = StubGit::ok();
let (clock, id) = (FixedClock::default(), FixedIdGen);
let (sleeper, tool_executor) = (StubSleeper::default(), StubToolExecutor::ok());
let err = run(
repo.path(),
"hi",
None,
None,
None,
crate::prompt::PinnedDocs::none(),
None,
None,
&valid_deps(
&adapter,
&sleeper,
&git,
&clock,
&id,
&tool_executor,
harness.path(),
),
)
.unwrap_err();
assert!(matches!(err, Error::OutputTruncated), "{err:?}");
assert!(
err.to_string().contains("max_output_tokens"),
"the decline names the remedy: {err}"
);
assert!(
tool_executor.invocations.borrow().is_empty(),
"a truncated tool_use is never executed"
);
assert_eq!(
adapter.observed.borrow().len(),
2,
"the version guard and one model call โ no retry of a cut stream"
);
}