//! End-to-end: a Codex transcript through the real ledger and footer. //! //! The unit tests prove the adapter builds the right observations. These prove //! the observations still obey every decision 0012 rule once they reach //! `lumbridge-core`, which is the property that actually matters to a user //! reading the footer. //! //! Every transcript below is hand-written from the documented wire shape in //! openai/codex (Apache-2.0), commit //! `17e8101699c5062117d0d37f504313e8af53b043`, `codex-rs/app-server/README.md` //! section "7) Rate limits (`ChatGPT`)". No real account, transcript, or //! credential is used, and all timestamps are small synthetic integers. use lumbridge_core::{ AccountProfileId, FooterUsage, UsageConfidence, UsageLedger, UsageProvenance, }; use lumbridge_harness::codex::replay_transcript; const HOUR_MS: u64 = 3_600_000; /// A synthetic reset five hours after the epoch, in seconds. const RESETS_AT_SECONDS: u64 = 5 * 3_600; const RESETS_AT_MS: u64 = 5 * HOUR_MS; fn initialize_response() -> &'static str { r#"{"jsonrpc":"2.0","id":1,"result":{"userAgent":"codex"}}"# } fn read_response(id: u64, percent: u64) -> String { format!( r#"{{"jsonrpc":"2.0","id":{id},"result":{{"rateLimits":{{"primary":{{"usedPercent":{percent},"windowDurationMins":300,"resetsAt":{RESETS_AT_SECONDS}}},"secondary":null,"rateLimitReachedType":null}}}}}}"# ) } fn primary() -> AccountProfileId { AccountProfileId::new("codex-app-server-primary").expect("the probe's static ID is valid") } fn ledger_from(frames: &[(&str, u64)]) -> UsageLedger { let mut ledger = UsageLedger::new(); for observation in replay_transcript(frames) { ledger .record(observation) .expect("the adapter emits observations in time order"); } ledger } #[test] fn a_two_reading_transcript_produces_a_provider_reported_footer() { let first = read_response(2, 20); let second = read_response(3, 35); let ledger = ledger_from(&[ (initialize_response(), 0), (first.as_str(), HOUR_MS), (second.as_str(), 2 * HOUR_MS), ]); let projection = ledger.project(&primary(), 2 * HOUR_MS); assert_eq!(projection.provenance(), UsageProvenance::ProviderReported); assert_eq!(projection.confidence(), UsageConfidence::Exact); assert_eq!(projection.consumed_permille(), Some(350)); assert_eq!(projection.resets_in_ms(), Some(3 * HOUR_MS)); // 200 permille burned over one hour, derived rather than reported. assert_eq!(projection.burn_per_hour(), Some(150)); assert_eq!(projection.burn_provenance(), UsageProvenance::Estimated); } #[test] fn a_single_reading_yields_a_percentage_but_no_rate() { let only = read_response(2, 20); let ledger = ledger_from(&[(initialize_response(), 0), (only.as_str(), HOUR_MS)]); let projection = ledger.project(&primary(), HOUR_MS); assert_eq!(projection.consumed_permille(), Some(200)); assert_eq!( projection.burn_per_hour(), None, "one provider reading is not a rate" ); assert_eq!(projection.exhaustion_in_ms(), None); } #[test] fn an_api_key_account_renders_the_unavailable_path_rather_than_a_zero() { // Codex answers -32600 with "chatgpt authentication required to read rate // limits" for an account that has no subscription quota. let denied = r#"{"jsonrpc":"2.0","id":2,"error":{"code":-32600,"message":"chatgpt authentication required to read rate limits"}}"#; let ledger = ledger_from(&[(initialize_response(), 0), (denied, HOUR_MS)]); let projection = ledger.project(&primary(), HOUR_MS); assert!(!projection.is_available()); assert_eq!(projection.consumed_permille(), None); let profile = lumbridge_core::AccountProfile::new( "codex-app-server-primary", "Codex", "ChatGPT", "primary window", "subscription", ) .expect("a valid profile"); let footer = FooterUsage::new(&profile, &projection); assert_eq!(footer.window(), "usage unavailable"); assert_eq!(footer.burn(), "burn rate unavailable"); assert_eq!(footer.trust(), "no usage source"); assert!( !footer.to_string().contains('%'), "an account with no quota must not render a percentage" ); } #[test] fn a_reading_taken_after_the_reset_never_renders_a_frozen_percentage() { let stale = read_response(2, 90); let ledger = ledger_from(&[ (initialize_response(), 0), // Observed one millisecond after the window it describes ended. (stale.as_str(), RESETS_AT_MS + 1), ]); let projection = ledger.project(&primary(), RESETS_AT_MS + 1); assert!(!projection.is_available()); assert_eq!( projection.consumed_permille(), None, "an ended window must not keep rendering its last percentage" ); } #[test] fn a_credential_refresh_request_produces_no_observation_at_all() { let refresh = r#"{"jsonrpc":"2.0","id":77,"method":"account/chatgptAuthTokens/refresh"}"#; let reading = read_response(2, 20); let observations = replay_transcript(&[ (initialize_response(), 0), (refresh, 1_000), (reading.as_str(), HOUR_MS), ]); assert_eq!( observations.len(), 1, "the refused credential request must not enter the usage stream" ); } #[test] fn a_repeated_identical_reading_does_not_flood_the_ledger() { let a = read_response(2, 20); let b = read_response(3, 20); let c = read_response(4, 20); let ledger = ledger_from(&[ (initialize_response(), 0), (a.as_str(), HOUR_MS), (b.as_str(), HOUR_MS + 60_000), (c.as_str(), HOUR_MS + 120_000), ]); assert_eq!( ledger.observation_count(&primary()), 1, "identical readings inside the re-emit floor must not evict history" ); } #[test] fn a_malformed_transcript_records_nothing_rather_than_guessing() { let observations = replay_transcript(&[ (initialize_response(), 0), ("}{ not json", 1_000), ( r#"{"jsonrpc":"2.0","id":2,"result":{"rateLimits":"a string"}}"#, 2_000, ), ]); assert!(observations.is_empty()); }