@@ -135,19 +135,6 @@ async function loadModelFallbackProbeModules() {
135135
136136beforeAll ( loadModelFallbackProbeModules ) ;
137137
138- function expectFallbackUsed (
139- result : { result : unknown ; attempts : Array < { reason ?: string } > } ,
140- run : {
141- ( ...args : unknown [ ] ) : unknown ;
142- mock : { calls : unknown [ ] [ ] } ;
143- } ,
144- ) {
145- expect ( result . result ) . toBe ( "ok" ) ;
146- expect ( run ) . toHaveBeenCalledTimes ( 1 ) ;
147- expect ( run ) . toHaveBeenCalledWith ( "anthropic" , "claude-haiku-3-5" ) ;
148- expect ( result . attempts [ 0 ] ?. reason ) . toBe ( "rate_limit" ) ;
149- }
150-
151138function expectPrimarySkippedForReason (
152139 result : { result : unknown ; attempts : Array < { reason ?: string } > } ,
153140 run : {
@@ -259,9 +246,14 @@ describe("runWithModelFallback – probe logic", () => {
259246 hasFallbackCandidates ?: boolean ;
260247 requestedModel ?: boolean ;
261248 throttleKey ?: string ;
249+ usageStats ?: AuthProfileStore [ "usageStats" ] ;
262250 } ) {
263251 mockedGetSoonestCooldownExpiry . mockReturnValue ( params . soonest ) ;
264252 mockedResolveProfilesUnavailableReason . mockReturnValue ( params . reason ) ;
253+ const authStore : AuthProfileStore = { version : 1 , profiles : { } } ;
254+ if ( params . usageStats ) {
255+ authStore . usageStats = params . usageStats ;
256+ }
265257 return modelFallbackTesting . resolveCooldownDecision ( {
266258 candidate : OPENAI_PROBE_CANDIDATE ,
267259 isPrimary : params . isPrimary ?? true ,
@@ -275,7 +267,7 @@ describe("runWithModelFallback – probe logic", () => {
275267 } as unknown as Parameters <
276268 typeof modelFallbackTesting . resolveCooldownDecision
277269 > [ 0 ] [ "authRuntime" ] ,
278- authStore : { version : 1 , profiles : { } } ,
270+ authStore,
279271 profileIds : [ "openai-profile-1" ] ,
280272 } ) ;
281273 }
@@ -291,7 +283,7 @@ describe("runWithModelFallback – probe logic", () => {
291283 } ) ;
292284 }
293285
294- async function expectPrimarySkippedAfterLongCooldown ( reason : "billing" | "rate_limit" ) {
286+ async function expectPrimarySkippedAfterLongCooldown ( reason : "billing" ) {
295287 const cfg = makeCfg ( ) ;
296288 const expiresIn30Min = NOW + 30 * 60 * 1000 ;
297289 mockedGetSoonestCooldownExpiry . mockReturnValue ( expiresIn30Min ) ;
@@ -348,25 +340,43 @@ describe("runWithModelFallback – probe logic", () => {
348340 vi . restoreAllMocks ( ) ;
349341 } ) ;
350342
351- it ( "skips primary model when far from cooldown expiry (30 min remaining) " , async ( ) => {
343+ it ( "probes rate-limited primary model when far from cooldown expiry" , async ( ) => {
352344 const cfg = makeCfg ( ) ;
353- // Cooldown expires in 30 min — well beyond the 2-min margin
354345 const expiresIn30Min = NOW + 30 * 60 * 1000 ;
355346 mockedGetSoonestCooldownExpiry . mockReturnValue ( expiresIn30Min ) ;
356347
357348 const run = vi . fn ( ) . mockResolvedValue ( "ok" ) ;
358349
359350 const result = await runPrimaryCandidate ( cfg , run ) ;
360351
361- // Should skip primary and use fallback
362- expectFallbackUsed ( result , run ) ;
352+ expectPrimaryProbeSuccess ( result , run , "ok" ) ;
363353 } ) ;
364354
365355 it ( "uses inferred unavailable reason when skipping a cooldowned primary model" , async ( ) => {
366356 await expectPrimarySkippedAfterLongCooldown ( "billing" ) ;
367357 } ) ;
368358
369359 it ( "decides when cooldowned primary probes are allowed" , ( ) => {
360+ expect (
361+ resolveOpenAiCooldownDecision ( {
362+ reason : "rate_limit" ,
363+ soonest : NOW + 30 * 60 * 1000 ,
364+ } ) ,
365+ ) . toEqual ( { type : "attempt" , reason : "rate_limit" , markProbe : true } ) ;
366+ expectOpenAiProbeSuspension (
367+ resolveOpenAiCooldownDecision ( {
368+ reason : "rate_limit" ,
369+ soonest : NOW + 30 * 60 * 1000 ,
370+ usageStats : {
371+ "openai-profile-1" : {
372+ blockedUntil : NOW + 30 * 60 * 1000 ,
373+ blockedReason : "subscription_limit" ,
374+ blockedSource : "wham" ,
375+ } ,
376+ } ,
377+ } ) ,
378+ "rate_limit" ,
379+ ) ;
370380 expect (
371381 resolveOpenAiCooldownDecision ( {
372382 reason : "rate_limit" ,
0 commit comments