use claude_runner_core::{ ErrorKind, ExecutionOutput };
fn make_output( stdout : &str, stderr : &str, exit_code : i32 ) -> ExecutionOutput
{
ExecutionOutput
{
stdout : stdout.to_string(),
stderr : stderr.to_string(),
exit_code,
}
}
#[ test ]
fn classify_error_exit2_empty_is_rate_limit()
{
let out = make_output( "", "", 2 );
assert_eq!(
out.classify_error(),
Some( ErrorKind::RateLimit ),
"T01: exit_code=2 with empty output must yield RateLimit"
);
}
#[ test ]
fn classify_error_exit0_is_none()
{
let out = make_output( "", "", 0 );
assert_eq!(
out.classify_error(),
None,
"T02: exit_code=0 must yield None regardless of stderr/stdout"
);
}
#[ test ]
fn classify_error_quota_pattern_in_stderr()
{
let out = make_output( "", "You've hit your limit", 1 );
assert_eq!(
out.classify_error(),
Some( ErrorKind::QuotaExhausted ),
"T03: quota exhaustion pattern in stderr must yield QuotaExhausted"
);
}
#[ test ]
fn classify_error_auth_pattern_in_stdout()
{
let out = make_output(
"Your organization does not have access to Claude",
"",
1,
);
assert_eq!(
out.classify_error(),
Some( ErrorKind::AuthError ),
"T04: auth pattern in stdout must yield AuthError"
);
}
#[ test ]
fn classify_error_api_error_pattern_in_stderr()
{
let out = make_output( "", "API Error: 529 overloaded", 1 );
assert_eq!(
out.classify_error(),
Some( ErrorKind::ApiError ),
"T05: API Error pattern in stderr must yield ApiError"
);
}
#[ test ]
fn classify_error_exit130_is_signal()
{
let out = make_output( "", "", 130 );
assert_eq!(
out.classify_error(),
Some( ErrorKind::Signal ),
"T06: exit_code=130 must yield Signal"
);
}
#[ test ]
fn classify_error_exit143_is_signal()
{
let out = make_output( "", "", 143 );
assert_eq!(
out.classify_error(),
Some( ErrorKind::Signal ),
"T07: exit_code=143 must yield Signal"
);
}
#[ test ]
fn classify_error_exit1_empty_is_unknown()
{
let out = make_output( "", "", 1 );
assert_eq!(
out.classify_error(),
Some( ErrorKind::Unknown ),
"T08: exit_code=1 with no pattern and no signal code must yield Unknown"
);
}
#[ test ]
fn classify_error_api_error_not_unknown()
{
let out = make_output( "", "API Error: 500 internal server error", 1 );
let kind = out.classify_error();
assert_eq!(
kind,
Some( ErrorKind::ApiError ),
"T11: API Error pattern must yield ApiError, not Unknown; got {kind:?}"
);
}
#[ test ]
fn classify_error_auth_pattern_in_stderr()
{
let out = make_output(
"",
"Your organization does not have access to Claude",
1,
);
assert_eq!(
out.classify_error(),
Some( ErrorKind::AuthError ),
"T12: auth pattern in stderr must yield AuthError"
);
}
#[ test ]
fn classify_error_auth_before_api_error_priority()
{
let out = make_output(
"",
"Your organization does not have access to Claude\nAPI Error: 401 unauthorized",
1,
);
assert_eq!(
out.classify_error(),
Some( ErrorKind::AuthError ),
"Priority: auth pattern must take precedence over API Error pattern"
);
}
#[ test ]
fn classify_error_quota_pattern_in_stdout()
{
let out = make_output( "You've hit your limit", "", 1 );
assert_eq!(
out.classify_error(),
Some( ErrorKind::QuotaExhausted ),
"T13: quota pattern in stdout must yield QuotaExhausted"
);
}
#[ test ]
fn classify_error_exit0_with_quota_pattern_is_none()
{
let out = make_output( "You've hit your limit", "", 0 );
assert_eq!(
out.classify_error(),
None,
"T14: exit_code=0 must yield None even when quota pattern is present"
);
}
#[ test ]
fn classify_error_exit2_with_quota_pattern_is_quota()
{
let out = make_output( "", "You've hit your limit", 2 );
assert_eq!(
out.classify_error(),
Some( ErrorKind::QuotaExhausted ),
"T15: quota pattern must win over exit_code=2 RateLimit fallback"
);
}
#[ test ]
fn error_kind_derives_are_correct()
{
let variants = [
ErrorKind::RateLimit,
ErrorKind::QuotaExhausted,
ErrorKind::ApiError,
ErrorKind::AuthError,
ErrorKind::Signal,
ErrorKind::Unknown,
];
for v in &variants
{
let cloned = v.clone();
assert_eq!( v, &cloned, "ErrorKind::{v:?} must equal its clone" );
}
let debug = format!( "{:?}", ErrorKind::RateLimit );
assert!( debug.contains( "RateLimit" ), "Debug must show variant name" );
}
#[ test ]
fn classify_error_exit128_is_unknown_not_signal()
{
let out = make_output( "", "", 128 );
assert_eq!(
out.classify_error(),
Some( ErrorKind::Unknown ),
"T16: exit_code=128 must yield Unknown (boundary: > 128 is strict, not >=)"
);
}
#[ test ]
fn classify_error_exit129_is_signal()
{
let out = make_output( "", "", 129 );
assert_eq!(
out.classify_error(),
Some( ErrorKind::Signal ),
"T17: exit_code=129 must yield Signal (128+1 satisfies > 128)"
);
}
#[ test ]
fn classify_error_quota_pattern_case_sensitive()
{
let out = make_output( "", "YOU'VE HIT YOUR LIMIT", 1 );
assert_eq!(
out.classify_error(),
Some( ErrorKind::Unknown ),
"T18: uppercase quota pattern must NOT match; pattern matching is case-sensitive"
);
}