22 * @vitest -environment node
33 */
44import { setupGlobalFetchMock } from '@sim/testing/mocks'
5+ import { setEnv } from '@sim/testing/mocks/env.mock'
6+ import { resetEnvFlagsMock , setEnvFlags } from '@sim/testing/mocks/env-flags.mock'
57import { afterEach , beforeEach , describe , expect , it , vi } from 'vitest'
68import type {
79 AtomicAdmissionOptions ,
@@ -13,6 +15,8 @@ const admission = vi.hoisted(() => ({
1315 setCooldown : vi . fn ( ) ,
1416 cooldowns : new Map < string , Date > ( ) ,
1517} ) )
18+ const { getBYOKKey } = vi . hoisted ( ( ) => ( { getBYOKKey : vi . fn ( ) } ) )
19+ vi . mock ( '@/lib/api-key/byok' , ( ) => ( { getBYOKKey } ) )
1620vi . mock ( '@/lib/core/rate-limiter/storage/factory' , ( ) => ( {
1721 createStorageAdapter : ( ) => ( {
1822 consumeTokensAtomically : admission . consume ,
@@ -31,6 +35,15 @@ const envSnapshot = { ...env }
3135describe ( 'Knowledge reranker model boundary' , ( ) => {
3236 beforeEach ( ( ) => {
3337 vi . clearAllMocks ( )
38+ setEnvFlags ( { isHosted : true } )
39+ getBYOKKey . mockResolvedValue ( null )
40+ setEnv ( {
41+ KB_CONFIG_RERANK_REQUESTS_PER_MINUTE : undefined ,
42+ KB_CONFIG_HOSTED_RERANK_REQUESTS_PER_MINUTE : undefined ,
43+ COHERE_API_KEY_1 : undefined ,
44+ COHERE_API_KEY_2 : undefined ,
45+ COHERE_API_KEY_3 : undefined ,
46+ } )
3447 admission . cooldowns . clear ( )
3548 admission . consume . mockImplementation (
3649 async ( _reservations : readonly TokenBucketReservation [ ] , options : AtomicAdmissionOptions ) => {
@@ -55,11 +68,59 @@ describe('Knowledge reranker model boundary', () => {
5568
5669 afterEach ( ( ) => {
5770 vi . useRealTimers ( )
71+ resetEnvFlagsMock ( )
5872 vi . unstubAllGlobals ( )
5973 for ( const key of Object . keys ( env ) ) delete ( env as Record < string , unknown > ) [ key ]
6074 Object . assign ( env , envSnapshot )
6175 } )
6276
77+ it . each ( [
78+ { hosted : true , source : 'env' , expectedKey : 'cohere-key' , burst : 16 , refill : 10 } ,
79+ { hosted : true , source : 'rotation' , expectedKey : 'rotating-key' , burst : 16 , refill : 10 } ,
80+ { hosted : true , source : 'workspace' , expectedKey : 'byok-key' , burst : 2 , refill : 1 } ,
81+ { hosted : true , source : 'organization' , expectedKey : 'byok-key' , burst : 2 , refill : 1 } ,
82+ { hosted : false , source : 'user' , expectedKey : 'user-key' , burst : 2 , refill : 1 } ,
83+ { hosted : false , source : 'env' , expectedKey : 'cohere-key' , burst : 2 , refill : 1 } ,
84+ { hosted : false , source : 'rotation' , expectedKey : 'rotating-key' , burst : 2 , refill : 1 } ,
85+ { hosted : false , source : 'workspace' , expectedKey : 'byok-key' , burst : 2 , refill : 1 } ,
86+ { hosted : false , source : 'organization' , expectedKey : 'byok-key' , burst : 2 , refill : 1 } ,
87+ ] ) ( 'uses the $source credential budget on hosted=$hosted' , async ( fixture ) => {
88+ setEnvFlags ( { isHosted : fixture . hosted } )
89+ const isBYOK = fixture . source === 'workspace' || fixture . source === 'organization'
90+ if ( isBYOK ) {
91+ getBYOKKey . mockResolvedValue ( { apiKey : 'byok-key' , scope : fixture . source , isBYOK : true } )
92+ }
93+ if ( fixture . source === 'rotation' ) {
94+ setEnv ( { COHERE_API_KEY : undefined , COHERE_API_KEY_1 : 'rotating-key' } )
95+ }
96+ const result = await rerank ( 'query' , [ { id : 'one' , text : 'content' } ] , {
97+ model : 'rerank-v4.0-fast' ,
98+ workspaceId : 'fixture-workspace' ,
99+ apiKey : fixture . hosted || fixture . source === 'user' ? 'user-key' : undefined ,
100+ } )
101+ expect ( result . isBYOK ) . toBe ( isBYOK )
102+ expect ( fetch ) . toHaveBeenCalledWith (
103+ 'https://api.cohere.com/v2/rerank' ,
104+ expect . objectContaining ( {
105+ headers : expect . objectContaining ( { Authorization : `Bearer ${ fixture . expectedKey } ` } ) ,
106+ } )
107+ )
108+ expect ( admission . consume . mock . calls [ 0 ] [ 0 ] ) . toMatchObject ( [
109+ { config : { maxTokens : fixture . burst , refillRate : fixture . refill , refillIntervalMs : 1000 } } ,
110+ ] )
111+ if ( fixture . source === 'user' ) expect ( getBYOKKey ) . not . toHaveBeenCalled ( )
112+ else expect ( getBYOKKey ) . toHaveBeenCalledWith ( 'fixture-workspace' , 'cohere' )
113+ } )
114+
115+ it ( 'fails before admission when no credential is configured' , async ( ) => {
116+ setEnv ( { COHERE_API_KEY : undefined } )
117+ await expect (
118+ rerank ( 'query' , [ { id : 'one' , text : 'content' } ] , { model : 'rerank-v4.0-fast' } )
119+ ) . rejects . toThrow ( 'No Cohere API key configured' )
120+ expect ( admission . consume ) . not . toHaveBeenCalled ( )
121+ expect ( fetch ) . not . toHaveBeenCalled ( )
122+ } )
123+
63124 it ( 'projects query and documents at egress while returning the original item' , async ( ) => {
64125 const registry = new ResolvedSecretTraceRegistry ( [
65126 { name : 'TOKEN' , plaintext : 'secret-value' , encryptedValue : 'encrypted-token' } ,
@@ -132,10 +193,16 @@ describe('Knowledge reranker model boundary', () => {
132193 vi . mocked ( fetch ) . mockResolvedValueOnce (
133194 new Response ( '{}' , { status : 429 , headers : { 'Retry-After' : '2' } } )
134195 )
135- const first = rerank ( 'first' , [ { id : 'one' , text : 'content' } ] , { model : 'rerank-v4.0-fast' } )
196+ const first = rerank ( 'first' , [ { id : 'one' , text : 'content' } ] , {
197+ model : 'rerank-v4.0-fast' ,
198+ workspaceId : 'fixture-workspace-one' ,
199+ } )
136200 await vi . advanceTimersByTimeAsync ( 0 )
137201 expect ( admission . setCooldown ) . toHaveBeenCalledOnce ( )
138- const second = rerank ( 'second' , [ { id : 'two' , text : 'content' } ] , { model : 'rerank-v4.0-fast' } )
202+ const second = rerank ( 'second' , [ { id : 'two' , text : 'content' } ] , {
203+ model : 'rerank-v4.0-fast' ,
204+ workspaceId : 'fixture-workspace-two' ,
205+ } )
139206 await vi . advanceTimersByTimeAsync ( 1999 )
140207 expect ( fetch ) . toHaveBeenCalledTimes ( 1 )
141208 await vi . advanceTimersByTimeAsync ( 1 )
@@ -147,7 +214,7 @@ describe('Knowledge reranker model boundary', () => {
147214 expect ( new Set ( reservations . map ( ( item ) => item . key ) ) . size ) . toBe ( 1 )
148215 expect ( reservations [ 0 ] . key ) . toMatch ( / ^ p r o v i d e r : r e r a n k : c o h e r e : [ a - f 0 - 9 ] { 64 } : r e q u e s t s $ / )
149216 expect ( reservations [ 0 ] . key ) . not . toContain ( 'cohere-key' )
150- expect ( reservations [ 0 ] . config . refillRate ) . toBe ( 1 )
217+ expect ( reservations [ 0 ] . config ) . toMatchObject ( { maxTokens : 16 , refillRate : 10 } )
151218 } )
152219
153220 it ( 'bounds repeated 429s to four attempts with no timer left behind' , async ( ) => {
0 commit comments