11import { mkdtemp , rm } from "node:fs/promises" ;
22import { tmpdir } from "node:os" ;
33import { join } from "node:path" ;
4- import { afterEach , describe , expect , test } from "bun:test" ;
4+ import { describe , expect , test } from "bun:test" ;
55
66import {
77 listCodexProfiles ,
@@ -13,21 +13,16 @@ import {
1313 loadXaiProfile ,
1414 saveXaiProfile ,
1515} from "../config/oauth-stores.js" ;
16- import {
17- clearSourceCredentials ,
18- registerSourceCredentialRecord ,
19- } from "../config/source-credentials.js" ;
2016import { oauthStoreForProvider } from "./remove-provider.js" ;
2117
22- function registerOAuthProvider (
18+ function oauthProvider (
2319 providerName : string ,
2420 provider : "codex" | "xai" ,
2521 profile : string ,
26- ) : void {
27- registerSourceCredentialRecord ( providerName , {
28- provenance : { kind : "oauth" , provider, profile } ,
29- material : { secret : "token" } ,
30- } ) ;
22+ ) : { name : string ; codexProfile ?: string ; xaiProfile ?: string } {
23+ return provider === "codex"
24+ ? { name : providerName , codexProfile : profile }
25+ : { name : providerName , xaiProfile : profile } ;
3126}
3227
3328async function withHome < T > ( fn : ( home : string ) => Promise < T > ) : Promise < T > {
@@ -40,50 +35,47 @@ async function withHome<T>(fn: (home: string) => Promise<T>): Promise<T> {
4035}
4136
4237describe ( "oauthStoreForProvider" , ( ) => {
43- afterEach ( ( ) => {
44- clearSourceCredentials ( ) ;
45- } ) ;
46-
47- test ( "maps xai/ and codex/ provider names to their store and profile" , ( ) => {
48- clearSourceCredentials ( ) ;
49- registerOAuthProvider ( "xai/work" , "xai" , "work" ) ;
50- registerOAuthProvider ( "codex/personal" , "codex" , "personal" ) ;
51- expect ( oauthStoreForProvider ( "xai/work" ) ?. profile ) . toBe ( "work" ) ;
52- expect ( oauthStoreForProvider ( "codex/personal" ) ?. profile ) . toBe ( "personal" ) ;
38+ test ( "maps marked xai/ and codex/ catalog entries to their stores" , ( ) => {
39+ expect (
40+ oauthStoreForProvider ( oauthProvider ( "xai/work" , "xai" , "work" ) ) ?. profile ,
41+ ) . toBe ( "work" ) ;
42+ expect (
43+ oauthStoreForProvider (
44+ oauthProvider ( "codex/personal" , "codex" , "personal" ) ,
45+ ) ?. profile ,
46+ ) . toBe ( "personal" ) ;
5347 } ) ;
5448
55- test ( "rejects namespaced custom providers without OAuth ownership" , ( ) => {
56- clearSourceCredentials ( ) ;
57- registerSourceCredentialRecord ( "codex/manual" , {
58- provenance : { kind : "api-key" } ,
59- material : { secret : "manual-key" } ,
60- } ) ;
61- registerSourceCredentialRecord ( "xai/manual" , {
62- provenance : { kind : "api-key" } ,
63- material : { secret : "manual-key" } ,
64- } ) ;
65-
66- expect ( oauthStoreForProvider ( "codex/manual" ) ) . toBeNull ( ) ;
67- expect ( oauthStoreForProvider ( "xai/manual" ) ) . toBeNull ( ) ;
49+ test ( "rejects unmarked and mismatched namespaced catalog entries" , ( ) => {
50+ expect ( oauthStoreForProvider ( { name : "codex/manual" } ) ) . toBeNull ( ) ;
51+ expect ( oauthStoreForProvider ( { name : "xai/manual" } ) ) . toBeNull ( ) ;
52+ expect (
53+ oauthStoreForProvider ( { name : "xai/work" , xaiProfile : "personal" } ) ,
54+ ) . toBeNull ( ) ;
55+ expect (
56+ oauthStoreForProvider ( { name : "codex/work" , codexProfile : "personal" } ) ,
57+ ) . toBeNull ( ) ;
6858 } ) ;
6959
7060 test ( "returns null for API-key, keyless, and degenerate names" , ( ) => {
71- expect ( oauthStoreForProvider ( "openai" ) ) . toBeNull ( ) ;
72- expect ( oauthStoreForProvider ( "ollama" ) ) . toBeNull ( ) ;
73- expect ( oauthStoreForProvider ( "my-custom" ) ) . toBeNull ( ) ;
74- expect ( oauthStoreForProvider ( "xai/" ) ) . toBeNull ( ) ;
75- expect ( oauthStoreForProvider ( "codex/" ) ) . toBeNull ( ) ;
61+ expect ( oauthStoreForProvider ( { name : "openai" } ) ) . toBeNull ( ) ;
62+ expect ( oauthStoreForProvider ( { name : "ollama" } ) ) . toBeNull ( ) ;
63+ expect ( oauthStoreForProvider ( { name : "my-custom" } ) ) . toBeNull ( ) ;
64+ expect ( oauthStoreForProvider ( { name : "xai/" , xaiProfile : "" } ) ) . toBeNull ( ) ;
65+ expect (
66+ oauthStoreForProvider ( { name : "codex/" , codexProfile : "" } ) ,
67+ ) . toBeNull ( ) ;
7668 } ) ;
7769
7870 test ( "removing an xai profile leaves sibling profiles untouched" , async ( ) => {
7971 await withHome ( async ( home ) => {
80- clearSourceCredentials ( ) ;
81- registerOAuthProvider ( "xai/work" , "xai" , "work" ) ;
8272 const tokens = { access : "a" , refresh : "r" , expiresAt : 10_000_000 } ;
8373 await saveXaiProfile ( { name : "work" , createdAt : 0 , tokens } , home ) ;
8474 await saveXaiProfile ( { name : "personal" , createdAt : 0 , tokens } , home ) ;
8575
86- const target = oauthStoreForProvider ( "xai/work" ) ;
76+ const target = oauthStoreForProvider (
77+ oauthProvider ( "xai/work" , "xai" , "work" ) ,
78+ ) ;
8779 expect ( target ?. profile ) . toBe ( "work" ) ;
8880 expect ( await target ?. removeProfile ( target . profile , home ) ) . toEqual ( [
8981 "work" ,
@@ -97,13 +89,13 @@ describe("oauthStoreForProvider", () => {
9789
9890 test ( "removing a codex profile leaves sibling profiles untouched" , async ( ) => {
9991 await withHome ( async ( home ) => {
100- clearSourceCredentials ( ) ;
101- registerOAuthProvider ( "codex/night" , "codex" , "night" ) ;
10292 const tokens = { access : "a" , refresh : "r" , expiresAt : 10_000_000 } ;
10393 await saveCodexProfile ( { name : "day" , createdAt : 0 , tokens } , home ) ;
10494 await saveCodexProfile ( { name : "night" , createdAt : 0 , tokens } , home ) ;
10595
106- const target = oauthStoreForProvider ( "codex/night" ) ;
96+ const target = oauthStoreForProvider (
97+ oauthProvider ( "codex/night" , "codex" , "night" ) ,
98+ ) ;
10799 expect ( await target ?. removeProfile ( target . profile , home ) ) . toEqual ( [
108100 "night" ,
109101 ] ) ;
@@ -116,22 +108,23 @@ describe("oauthStoreForProvider", () => {
116108
117109 test ( "removing an already-absent profile is a no-op" , async ( ) => {
118110 await withHome ( async ( home ) => {
119- clearSourceCredentials ( ) ;
120- registerOAuthProvider ( "xai/ghost" , "xai" , "ghost" ) ;
121- const target = oauthStoreForProvider ( "xai/ghost" ) ;
111+ const target = oauthStoreForProvider (
112+ oauthProvider ( "xai/ghost" , "xai" , "ghost" ) ,
113+ ) ;
122114 expect ( await target ?. removeProfile ( target . profile , home ) ) . toEqual ( [ ] ) ;
123115 } ) ;
124116 } ) ;
125117
126118 test ( "missing auth files are tolerated" , async ( ) => {
127119 await withHome ( async ( home ) => {
128- clearSourceCredentials ( ) ;
129- registerOAuthProvider ( "xai/work" , "xai" , "work" ) ;
130- registerOAuthProvider ( "codex/work" , "codex" , "work" ) ;
131120 // No profiles ever saved: the store treats a missing file as empty.
132- const xai = oauthStoreForProvider ( "xai/work" ) ;
121+ const xai = oauthStoreForProvider (
122+ oauthProvider ( "xai/work" , "xai" , "work" ) ,
123+ ) ;
133124 expect ( await xai ?. removeProfile ( xai . profile , home ) ) . toEqual ( [ ] ) ;
134- const codex = oauthStoreForProvider ( "codex/work" ) ;
125+ const codex = oauthStoreForProvider (
126+ oauthProvider ( "codex/work" , "codex" , "work" ) ,
127+ ) ;
135128 expect ( await codex ?. removeProfile ( codex . profile , home ) ) . toEqual ( [ ] ) ;
136129 } ) ;
137130 } ) ;
0 commit comments