@@ -3,12 +3,14 @@ import { describe, expect, test } from "bun:test";
33import type { CodexProfile } from "../auth/codex/store.js" ;
44import type { XaiProfile } from "../auth/xai/store.js" ;
55import { codexProfilesToCatalogEntries } from "./codex-providers.js" ;
6+ import { createOAuthProviderProjection } from "./oauth-providers.js" ;
67import { xaiProfilesToCatalogEntries } from "./xai-providers.js" ;
78
89// Characterization tests pinning the projection behavior both provider
910// wrappers must preserve. Name round-trips and settings projection are
1011// characterized in codex-providers.test.ts and xai-providers.test.ts — only
11- // cross-provider marker isolation lives here.
12+ // cross-provider marker isolation and the per-profile models contract live
13+ // here.
1214
1315const codexProfile : CodexProfile = {
1416 name : "work" ,
@@ -50,3 +52,45 @@ describe("catalog projection", () => {
5052 expect ( entries [ 0 ] ?. codexAccountId ) . toBeUndefined ( ) ;
5153 } ) ;
5254} ) ;
55+
56+ describe ( "per-profile models" , ( ) => {
57+ const profiles = [
58+ { name : "a" , tokens : { access : "ta" } } ,
59+ { name : "b" , tokens : { access : "tb" } } ,
60+ ] ;
61+ const perProfile : Record < string , readonly [ string , ...string [ ] ] > = {
62+ a : [ "m-a1" , "m-a2" ] ,
63+ b : [ "m-b1" ] ,
64+ } ;
65+ const projection = createOAuthProviderProjection ( {
66+ prefix : "p/" ,
67+ baseURL : "https://example.invalid" ,
68+ models : ( profile ) => {
69+ const list = perProfile [ profile . name ] ;
70+ if ( list === undefined ) throw new Error ( `no models for ${ profile . name } ` ) ;
71+ return list ;
72+ } ,
73+ catalogExtras : ( ) => ( { } ) ,
74+ } ) ;
75+
76+ test ( "the settings projection lists each profile's models with the first as default" , ( ) => {
77+ const settings = projection . providersAsSettings ( profiles ) ;
78+ expect ( settings [ "p/a" ] ?. models ) . toEqual ( [ "m-a1" , "m-a2" ] ) ;
79+ expect ( settings [ "p/a" ] ?. defaultModel ) . toBe ( "m-a1" ) ;
80+ expect ( settings [ "p/b" ] ?. models ) . toEqual ( [ "m-b1" ] ) ;
81+ expect ( settings [ "p/b" ] ?. defaultModel ) . toBe ( "m-b1" ) ;
82+ } ) ;
83+
84+ test ( "the catalog projection lists each profile's models with the first as default" , ( ) => {
85+ const entries = projection . profilesToCatalogEntries ( profiles ) ;
86+ expect ( entries . map ( ( e ) => [ e . name , e . models , e . defaultModel ] ) ) . toEqual ( [
87+ [ "p/a" , [ "m-a1" , "m-a2" ] , "m-a1" ] ,
88+ [ "p/b" , [ "m-b1" ] , "m-b1" ] ,
89+ ] ) ;
90+ } ) ;
91+
92+ test ( "projected lists are copies the caller can mutate without touching the source" , ( ) => {
93+ projection . providersAsSettings ( profiles ) [ "p/a" ] ?. models . push ( "mutated" ) ;
94+ expect ( perProfile [ "a" ] ) . toEqual ( [ "m-a1" , "m-a2" ] ) ;
95+ } ) ;
96+ } ) ;
0 commit comments