use crate::brain::provider::ProviderError;
#[test]
fn a_thinking_loop_timeout_is_still_retryable() {
assert!(
ProviderError::ThinkingLoopTimeout(120).is_retryable(),
"reclassifying this as fatal would skip the nudge retries that fix \
the transient case"
);
}
#[test]
fn a_thinking_loop_timeout_is_not_quota_exhaustion() {
assert!(
!ProviderError::ThinkingLoopTimeout(120).is_quota_exhausted(),
"marking it exhausted would blacklist a provider that is merely slow \
to emit a tool call"
);
}
#[test]
fn the_nudge_arm_is_gated_on_the_retry_budget() {
let src = std::fs::read_to_string("src/brain/agent/service/tool_loop.rs")
.expect("tool_loop.rs must be readable");
assert!(
src.contains("&& phantom_retries_used < MAX_PHANTOM_RETRIES"),
"the ThinkingLoopTimeout arm must stop matching once the budget is \
spent, or the error can never fall through to the fallback walk"
);
}
#[test]
fn the_fallback_walk_admits_a_thinking_loop_timeout() {
let src = std::fs::read_to_string("src/brain/agent/service/tool_loop.rs")
.expect("tool_loop.rs must be readable");
let walk = src
.find("|| matches!(&e, crate::brain::provider::ProviderError::InvalidApiKey)")
.expect("the fallback-walk guard must still exist");
let tail = &src[walk..walk + 600];
assert!(
tail.contains("ThinkingLoopTimeout"),
"the walk's allow-list must include ThinkingLoopTimeout, or a spent \
budget dead-ends with a healthy chain untried"
);
}
#[test]
fn the_retry_cap_no_longer_returns_to_the_user() {
let src = std::fs::read_to_string("src/brain/agent/service/tool_loop.rs")
.expect("tool_loop.rs must be readable");
assert!(
!src.contains("Thinking-loop timeout retry cap reached"),
"the cap must hand off to the fallback chain, not surface the error \
while fallbacks remain untried"
);
}