@@ -76,6 +76,7 @@ func TestLoadSettingsMigratesCurrentOfficialAuthToActiveProfile(t *testing.T) {
7676 }
7777 settings .ActiveRelayID = "second"
7878 writeTestFile (t , filepath .Join (home , ".codex" , "auth.json" ), fakeChatGPTAuthJSON (t , "active@example.com" ))
79+ writeTestFile (t , filepath .Join (home , ".codex" , "config.toml" ), `model_provider = "openai"` + "\n " )
7980 if err := atomicWriteJSON (settingsPath (), settings ); err != nil {
8081 t .Fatalf ("failed to write settings: %v" , err )
8182 }
@@ -95,6 +96,12 @@ func TestLoadSettingsMigratesCurrentOfficialAuthToActiveProfile(t *testing.T) {
9596 if active .OfficialAuthContents == "" {
9697 t .Fatal ("official auth contents should be migrated" )
9798 }
99+ if active .AuthContents == "" {
100+ t .Fatal ("auth contents should be migrated" )
101+ }
102+ if active .ConfigContents == "" {
103+ t .Fatal ("config contents should be migrated for active profile" )
104+ }
98105}
99106
100107func TestRelayStatusDetectsBoundOfficialAuthWithoutCurrentAuthFile (t * testing.T ) {
@@ -388,7 +395,6 @@ func TestOfficialModeWritesBoundOfficialAuth(t *testing.T) {
388395 t .Fatalf ("failed to save settings: %v" , err )
389396 }
390397 writeTestFile (t , filepath .Join (home , ".codex" , "config.toml" ), `model_provider = "CodexPlusPlus"` + "\n \n [model_providers.CodexPlusPlus]\n base_url = \" https://api.example.com\" \n " )
391-
392398 result := (& server {}).clearRelayInjection ()
393399
394400 if result ["status" ] != "ok" {
@@ -404,6 +410,103 @@ func TestOfficialModeWritesBoundOfficialAuth(t *testing.T) {
404410 }
405411}
406412
413+ func TestActivateOfficialAuthWritesBoundOfficialAuth (t * testing.T ) {
414+ home := t .TempDir ()
415+ t .Setenv ("HOME" , home )
416+ officialAuth := fakeChatGPTAuthJSON (t , "bound@example.com" )
417+ currentAuth := fakeChatGPTAuthJSON (t , "current@example.com" )
418+ writeTestFile (t , filepath .Join (home , ".codex" , "auth.json" ), currentAuth )
419+ settings := defaultSettings ()
420+ settings .RelayProfiles = []relayProfile {{
421+ ID : "official" ,
422+ Name : "Official" ,
423+ RelayMode : "official" ,
424+ Protocol : "responses" ,
425+ OfficialAuthContents : officialAuth ,
426+ OfficialAccountLabel : "bound@example.com" ,
427+ }}
428+ settings .ActiveRelayID = "official"
429+ if err := saveSettings (settings ); err != nil {
430+ t .Fatalf ("failed to save settings: %v" , err )
431+ }
432+
433+ result := (& server {}).activateOfficialAuth (map [string ]any {
434+ "request" : map [string ]any {"profileId" : "official" },
435+ })
436+
437+ if result ["status" ] != "ok" {
438+ t .Fatalf ("activate official auth should succeed: %#v" , result )
439+ }
440+ status := chatGPTAuthStatus (filepath .Join (home , ".codex" ))
441+ if ! status .Authenticated || status .AccountLabel != "bound@example.com" {
442+ t .Fatalf ("bound official auth was not activated: %#v" , status )
443+ }
444+ }
445+
446+ func TestImportCurrentRelayFilesUpdatesTargetProfileSnapshot (t * testing.T ) {
447+ home := t .TempDir ()
448+ t .Setenv ("HOME" , home )
449+ writeTestFile (t , filepath .Join (home , ".codex" , "config.toml" ), "model_provider = \" openai\" \n " )
450+ writeTestFile (t , filepath .Join (home , ".codex" , "auth.json" ), fakeChatGPTAuthJSON (t , "import@example.com" ))
451+ settings := defaultSettings ()
452+ settings .RelayProfiles = []relayProfile {{ID : "one" , Name : "One" , RelayMode : "official" , Protocol : "responses" }}
453+ settings .ActiveRelayID = "one"
454+ if err := saveSettings (settings ); err != nil {
455+ t .Fatalf ("failed to save settings: %v" , err )
456+ }
457+
458+ result := (& server {}).importCurrentRelayFiles (map [string ]any {
459+ "request" : map [string ]any {"profileId" : "one" },
460+ })
461+
462+ if result ["status" ] != "ok" {
463+ t .Fatalf ("import current relay files should succeed: %#v" , result )
464+ }
465+ loaded := loadSettings ()
466+ profile := activeRelayProfile (loaded )
467+ if ! strings .Contains (profile .ConfigContents , `model_provider = "openai"` ) {
468+ t .Fatalf ("config snapshot mismatch:\n %s" , profile .ConfigContents )
469+ }
470+ if profile .AuthContents == "" || profile .OfficialAuthContents == "" {
471+ t .Fatalf ("auth snapshot should be imported: %#v" , profile )
472+ }
473+ if profile .OfficialAccountLabel != "import@example.com" {
474+ t .Fatalf ("official account label mismatch: %q" , profile .OfficialAccountLabel )
475+ }
476+ }
477+
478+ func TestClearRelayInjectionFallsBackToLegacyOfficialAuthContents (t * testing.T ) {
479+ home := t .TempDir ()
480+ t .Setenv ("HOME" , home )
481+ officialAuth := fakeChatGPTAuthJSON (t , "legacy@example.com" )
482+ settings := defaultSettings ()
483+ settings .RelayProfiles = []relayProfile {{
484+ ID : "legacy" ,
485+ Name : "Legacy" ,
486+ RelayMode : "official" ,
487+ Protocol : "responses" ,
488+ OfficialAuthContents : officialAuth ,
489+ OfficialAccountLabel : "legacy@example.com" ,
490+ AuthContents : "" ,
491+ ConfigContents : "" ,
492+ }}
493+ settings .ActiveRelayID = "legacy"
494+ if err := saveSettings (settings ); err != nil {
495+ t .Fatalf ("failed to save settings: %v" , err )
496+ }
497+ writeTestFile (t , filepath .Join (home , ".codex" , "config.toml" ), `model_provider = "CodexPlusPlus"` + "\n " )
498+
499+ result := (& server {}).clearRelayInjection ()
500+
501+ if result ["status" ] != "ok" {
502+ t .Fatalf ("official switch should succeed with legacy auth fallback: %#v" , result )
503+ }
504+ auth , _ := os .ReadFile (filepath .Join (home , ".codex" , "auth.json" ))
505+ if chatGPTAuthStatusFromContents (string (auth ), "auth" ).AccountLabel != "legacy@example.com" {
506+ t .Fatalf ("legacy official auth should be written to live auth.json, got:\n %s" , string (auth ))
507+ }
508+ }
509+
407510func TestMixedModeWritesBoundOfficialAuthAndRelayConfig (t * testing.T ) {
408511 home := t .TempDir ()
409512 t .Setenv ("HOME" , home )
@@ -437,9 +540,13 @@ func TestMixedModeWritesBoundOfficialAuthAndRelayConfig(t *testing.T) {
437540 if ! strings .Contains (string (config ), `experimental_bearer_token = "relay-key"` ) {
438541 t .Fatalf ("mixed relay config missing bearer token:\n %s" , string (config ))
439542 }
543+ auth , _ := os .ReadFile (filepath .Join (home , ".codex" , "auth.json" ))
544+ if chatGPTAuthStatusFromContents (string (auth ), "auth" ).AccountLabel != "mixed@example.com" {
545+ t .Fatalf ("mixed relay should use profile auth snapshot, got:\n %s" , string (auth ))
546+ }
440547}
441548
442- func TestPureAPIModeKeepsOfficialBindingInactive (t * testing.T ) {
549+ func TestPureAPIModeKeepsCurrentAuthWhenProfileSnapshotMissing (t * testing.T ) {
443550 home := t .TempDir ()
444551 t .Setenv ("HOME" , home )
445552 officialAuth := fakeChatGPTAuthJSON (t , "stored@example.com" )
@@ -467,7 +574,7 @@ func TestPureAPIModeKeepsOfficialBindingInactive(t *testing.T) {
467574 t .Fatalf ("pure API switch should succeed: %#v" , result )
468575 }
469576 auth , _ := os .ReadFile (filepath .Join (home , ".codex" , "auth.json" ))
470- if string (auth ) != currentAuth {
577+ if chatGPTAuthStatusFromContents ( string (auth ), "auth" ). AccountLabel != "current@example.com" {
471578 t .Fatalf ("pure API mode should preserve auth.json, got:\n %s" , string (auth ))
472579 }
473580 config , _ := os .ReadFile (filepath .Join (home , ".codex" , "config.toml" ))
@@ -480,6 +587,39 @@ func TestPureAPIModeKeepsOfficialBindingInactive(t *testing.T) {
480587 }
481588}
482589
590+ func TestPureAPIModeWritesProfileAuthSnapshotWhenPresent (t * testing.T ) {
591+ home := t .TempDir ()
592+ t .Setenv ("HOME" , home )
593+ currentAuth := fakeChatGPTAuthJSON (t , "current@example.com" )
594+ profileAuth := fakeChatGPTAuthJSON (t , "profile@example.com" )
595+ writeTestFile (t , filepath .Join (home , ".codex" , "auth.json" ), currentAuth )
596+ settings := defaultSettings ()
597+ settings .RelayProfiles = []relayProfile {{
598+ ID : "pure" ,
599+ Name : "Pure" ,
600+ BaseURL : "https://api.example.com" ,
601+ APIKey : "pure-key" ,
602+ RelayMode : "pureApi" ,
603+ Protocol : "responses" ,
604+ ConfigContents : buildTestRelayConfig ("https://api.example.com" , "pure-key" ),
605+ AuthContents : profileAuth ,
606+ }}
607+ settings .ActiveRelayID = "pure"
608+ if err := saveSettings (settings ); err != nil {
609+ t .Fatalf ("failed to save settings: %v" , err )
610+ }
611+
612+ result := (& server {}).applyRelayInjection (true )
613+
614+ if result ["status" ] != "ok" {
615+ t .Fatalf ("pure API switch should succeed: %#v" , result )
616+ }
617+ auth , _ := os .ReadFile (filepath .Join (home , ".codex" , "auth.json" ))
618+ if chatGPTAuthStatusFromContents (string (auth ), "auth" ).AccountLabel != "profile@example.com" {
619+ t .Fatalf ("pure API should restore saved auth snapshot, got:\n %s" , string (auth ))
620+ }
621+ }
622+
483623func TestPureAPIModeWritesImportedConfigWithoutAuthOverwrite (t * testing.T ) {
484624 home := t .TempDir ()
485625 t .Setenv ("HOME" , home )
@@ -513,7 +653,7 @@ func TestPureAPIModeWritesImportedConfigWithoutAuthOverwrite(t *testing.T) {
513653 t .Fatalf ("pure API switch should succeed: %#v" , result )
514654 }
515655 auth , _ := os .ReadFile (filepath .Join (home , ".codex" , "auth.json" ))
516- if string (auth ) != currentAuth {
656+ if chatGPTAuthStatusFromContents ( string (auth ), "auth" ). AccountLabel != "current@example.com" {
517657 t .Fatalf ("pure API mode should preserve auth.json, got:\n %s" , string (auth ))
518658 }
519659 config , _ := os .ReadFile (filepath .Join (home , ".codex" , "config.toml" ))
@@ -586,6 +726,20 @@ func writeTestFile(t *testing.T, path, contents string) {
586726 }
587727}
588728
729+ func buildTestRelayConfig (baseURL , apiKey string ) string {
730+ return strings .Join ([]string {
731+ `model_provider = "CodexPlusPlus"` ,
732+ `` ,
733+ `[model_providers.CodexPlusPlus]` ,
734+ `name = "CodexPlusPlus"` ,
735+ `wire_api = "responses"` ,
736+ `requires_openai_auth = true` ,
737+ `base_url = "` + baseURL + `"` ,
738+ `experimental_bearer_token = "` + apiKey + `"` ,
739+ `` ,
740+ }, "\n " )
741+ }
742+
589743func fakeChatGPTAuthJSON (t * testing.T , email string ) string {
590744 t .Helper ()
591745 payload , err := json .Marshal (map [string ]string {"email" : email })
0 commit comments