mirror of
https://github.com/openai/codex.git
synced 2026-11-06 20:20:34 +00:00
## Why Sparse and out-of-order rate-limit updates can omit account metadata or let an older full read overwrite a newer workspace hard stop. Credit availability also does not always include a displayable balance. ## What changed - Propagate the backend's `spendControlReached` state through rate-limit snapshots and the app-server protocol, preserving it across sparse updates. - Keep workspace hard-stop classifications for error routing and ignore stale full-read results after a newer hard-stop notification. - Base TUI warnings and model-switch prompts on credit availability and hard-stop state, and show `Credits: Available` when a finite balance is hidden. ## Testing Added coverage for spend-control mapping, snapshot merging, out-of-order reads, workspace error routing, warning and prompt behavior, and credit status display. GitOrigin-RevId: 18b09d493d5a620a7c079fb721ec50b38dd5b04c
81 lines
2.2 KiB
Rust
81 lines
2.2 KiB
Rust
use super::*;
|
|
use codex_protocol::protocol::RateLimitReachedType;
|
|
|
|
fn snapshot(
|
|
primary_used_percent: Option<f64>,
|
|
secondary_used_percent: Option<f64>,
|
|
) -> RateLimitSnapshot {
|
|
RateLimitSnapshot {
|
|
limit_id: Some(crate::guard_limits::CODEX_LIMIT_ID.to_string()),
|
|
limit_name: None,
|
|
primary: primary_used_percent.map(window),
|
|
secondary: secondary_used_percent.map(window),
|
|
credits: None,
|
|
individual_limit: None,
|
|
spend_control_reached: None,
|
|
plan_type: None,
|
|
rate_limit_reached_type: None,
|
|
}
|
|
}
|
|
|
|
fn window(used_percent: f64) -> RateLimitWindow {
|
|
RateLimitWindow {
|
|
used_percent,
|
|
window_minutes: None,
|
|
resets_at: None,
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn startup_check_uses_configured_remaining_threshold() {
|
|
let snapshot = snapshot(
|
|
/*primary_used_percent*/ Some(89.9),
|
|
/*secondary_used_percent*/ Some(50.0),
|
|
);
|
|
|
|
assert!(snapshot_allows_startup(
|
|
&snapshot, /*min_remaining_percent*/ 10
|
|
));
|
|
assert!(!snapshot_allows_startup(
|
|
&snapshot, /*min_remaining_percent*/ 11
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn startup_check_skips_when_primary_or_secondary_is_too_low() {
|
|
assert!(!snapshot_allows_startup(
|
|
&snapshot(
|
|
/*primary_used_percent*/ Some(75.1),
|
|
/*secondary_used_percent*/ Some(10.0),
|
|
),
|
|
/*min_remaining_percent*/ 25,
|
|
));
|
|
assert!(!snapshot_allows_startup(
|
|
&snapshot(
|
|
/*primary_used_percent*/ Some(10.0),
|
|
/*secondary_used_percent*/ Some(75.1),
|
|
),
|
|
/*min_remaining_percent*/ 25,
|
|
));
|
|
assert!(snapshot_allows_startup(
|
|
&snapshot(
|
|
/*primary_used_percent*/ Some(74.9),
|
|
/*secondary_used_percent*/ Some(74.9),
|
|
),
|
|
/*min_remaining_percent*/ 25,
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn startup_check_skips_when_limit_is_reached() {
|
|
let mut snapshot = snapshot(
|
|
/*primary_used_percent*/ Some(10.0),
|
|
/*secondary_used_percent*/ Some(10.0),
|
|
);
|
|
snapshot.rate_limit_reached_type = Some(RateLimitReachedType::RateLimitReached);
|
|
|
|
assert!(!snapshot_allows_startup(
|
|
&snapshot, /*min_remaining_percent*/ 25,
|
|
));
|
|
}
|