| 1 | use super::*; |
| 2 | |
| 3 | #[test] |
| 4 | fn retryable_infra_categories_exit_with_ex_tempfail() { |
| 5 | // Provider/transport failures after all in-session retries: the task |
| 6 | // neither passed nor failed, and the harness may safely retry. |
| 7 | assert_eq!(exec_failure_exit_code(Some("network")), 75); |
| 8 | assert_eq!(exec_failure_exit_code(Some("timeout")), 75); |
| 9 | assert_eq!(EXEC_EXIT_RETRYABLE_INFRA, 75, "EX_TEMPFAIL from sysexits.h"); |
| 10 | } |
| 11 | |
| 12 | #[test] |
| 13 | fn genuine_failures_keep_exit_1() { |
| 14 | // Task-side failures and unknown categories keep the historical |
| 15 | // exit-1 contract — no masking, no forced zero exits. |
| 16 | assert_eq!(exec_failure_exit_code(Some("tool")), 1); |
| 17 | assert_eq!(exec_failure_exit_code(Some("authentication")), 1); |
| 18 | assert_eq!(exec_failure_exit_code(Some("invalid_input")), 1); |
| 19 | assert_eq!(exec_failure_exit_code(None), 1); |
| 20 | // rate_limit is deliberately exit 1: the same category also covers |
| 21 | // quota exhaustion, which a blind harness retry would hammer. |
| 22 | assert_eq!(exec_failure_exit_code(Some("rate_limit")), 1); |
| 23 | } |
| 24 | |
| 25 | #[test] |
| 26 | fn recoverable_error_events_do_not_fail_the_run_summary() { |
| 27 | // A recoverable warning (e.g. a stream-stall notice mid-turn) must |
| 28 | // not force the exec summary into failure; the terminal TurnComplete |
| 29 | // carries the authoritative outcome. |
| 30 | let warning = crate::error_taxonomy::ErrorEnvelope::network( |
| 31 | "Stream stalled: no data received for 120s, closing stream", |
| 32 | ); |
| 33 | assert!( |
| 34 | !exec_error_event_is_fatal(&warning), |
| 35 | "recoverable envelopes must not poison the exec summary" |
| 36 | ); |
| 37 | let fatal = crate::error_taxonomy::ErrorEnvelope::fatal("engine exploded"); |
| 38 | assert!(exec_error_event_is_fatal(&fatal)); |
| 39 | } |
| 40 |