11import fs from "node:fs/promises" ;
22import os from "node:os" ;
33import path from "node:path" ;
4- import { afterAll , beforeAll , beforeEach , describe , expect , it , vi } from "vitest" ;
4+ import { afterAll , afterEach , beforeAll , beforeEach , describe , expect , it , vi } from "vitest" ;
5+ import { clearRuntimeAuthProfileStoreSnapshots } from "../agents/auth-profiles.js" ;
56import { NON_ENV_SECRETREF_MARKER } from "../agents/model-auth-markers.js" ;
7+ import { clearConfigCache , type OpenClawConfig } from "../config/config.js" ;
68
79const resolveProviderUsageAuthWithPluginMock = vi . fn ( async ( ..._args : unknown [ ] ) => null ) ;
810
911vi . mock ( "../plugins/provider-runtime.js" , ( ) => ( {
1012 resolveProviderUsageAuthWithPlugin : resolveProviderUsageAuthWithPluginMock ,
1113} ) ) ;
1214
15+ vi . mock ( "../agents/cli-credentials.js" , ( ) => ( {
16+ readCodexCliCredentialsCached : ( ) => null ,
17+ readMiniMaxCliCredentialsCached : ( ) => null ,
18+ readQwenCliCredentialsCached : ( ) => null ,
19+ } ) ) ;
20+
1321let resolveProviderAuths : typeof import ( "./provider-usage.auth.js" ) . resolveProviderAuths ;
1422type ProviderAuth = import ( "./provider-usage.auth.js" ) . ProviderAuth ;
1523
@@ -36,65 +44,59 @@ describe("resolveProviderAuths key normalization", () => {
3644 } ) ;
3745
3846 beforeEach ( ( ) => {
47+ clearConfigCache ( ) ;
48+ clearRuntimeAuthProfileStoreSnapshots ( ) ;
3949 resolveProviderUsageAuthWithPluginMock . mockReset ( ) ;
4050 resolveProviderUsageAuthWithPluginMock . mockResolvedValue ( null ) ;
4151 } ) ;
4252
43- async function withSuiteHome < T > (
44- fn : ( home : string ) => Promise < T > ,
45- env : Record < string , string | undefined > ,
46- ) : Promise < T > {
53+ afterEach ( ( ) => {
54+ clearConfigCache ( ) ;
55+ clearRuntimeAuthProfileStoreSnapshots ( ) ;
56+ } ) ;
57+
58+ async function withSuiteHome < T > ( fn : ( home : string ) => Promise < T > ) : Promise < T > {
4759 const base = path . join ( suiteRoot , `case-${ ++ suiteCase } ` ) ;
4860 await fs . mkdir ( base , { recursive : true } ) ;
4961 await fs . mkdir ( path . join ( base , ".openclaw" , "agents" , "main" , "sessions" ) , { recursive : true } ) ;
62+ return await fn ( base ) ;
63+ }
5064
51- const keysToRestore = new Set < string > ( [
52- "HOME" ,
53- "USERPROFILE" ,
54- "HOMEDRIVE" ,
55- "HOMEPATH" ,
56- "OPENCLAW_HOME" ,
57- "OPENCLAW_STATE_DIR" ,
58- ...Object . keys ( env ) ,
59- ] ) ;
60- const snapshot : Record < string , string | undefined > = { } ;
61- for ( const key of keysToRestore ) {
62- snapshot [ key ] = process . env [ key ] ;
63- }
65+ function agentDirForHome ( home : string ) : string {
66+ return path . join ( home , ".openclaw" , "agents" , "main" , "agent" ) ;
67+ }
6468
65- process . env . HOME = base ;
66- process . env . USERPROFILE = base ;
67- if ( process . platform === "win32" ) {
68- const match = base . match ( / ^ ( [ A - Z a - z ] : ) ( .* ) $ / ) ;
69- if ( match ) {
70- process . env . HOMEDRIVE = match [ 1 ] ;
71- process . env . HOMEPATH = match [ 2 ] || "\\" ;
72- }
73- }
74- delete process . env . OPENCLAW_HOME ;
75- process . env . OPENCLAW_STATE_DIR = path . join ( base , ".openclaw" ) ;
76- for ( const [ key , value ] of Object . entries ( env ) ) {
77- if ( value === undefined ) {
78- delete process . env [ key ] ;
79- } else {
80- process . env [ key ] = value ;
81- }
69+ function buildSuiteEnv (
70+ home : string ,
71+ env : Record < string , string | undefined > = { } ,
72+ ) : NodeJS . ProcessEnv {
73+ const suiteEnv : NodeJS . ProcessEnv = {
74+ ...EMPTY_PROVIDER_ENV ,
75+ HOME : home ,
76+ USERPROFILE : home ,
77+ OPENCLAW_STATE_DIR : path . join ( home , ".openclaw" ) ,
78+ ...env ,
79+ } ;
80+ const match = home . match ( / ^ ( [ A - Z a - z ] : ) ( .* ) $ / ) ;
81+ if ( match ) {
82+ suiteEnv . HOMEDRIVE = match [ 1 ] ;
83+ suiteEnv . HOMEPATH = match [ 2 ] || "\\" ;
8284 }
85+ return suiteEnv ;
86+ }
87+
88+ async function readConfigForHome ( home : string ) : Promise < Record < string , unknown > > {
8389 try {
84- return await fn ( base ) ;
85- } finally {
86- for ( const [ key , value ] of Object . entries ( snapshot ) ) {
87- if ( value === undefined ) {
88- delete process . env [ key ] ;
89- } else {
90- process . env [ key ] = value ;
91- }
92- }
90+ return JSON . parse (
91+ await fs . readFile ( path . join ( home , ".openclaw" , "openclaw.json" ) , "utf8" ) ,
92+ ) as Record < string , unknown > ;
93+ } catch {
94+ return { } ;
9395 }
9496 }
9597
9698 async function writeAuthProfiles ( home : string , profiles : Record < string , unknown > ) {
97- const agentDir = path . join ( home , ".openclaw" , "agents" , "main" , "agent" ) ;
99+ const agentDir = agentDirForHome ( home ) ;
98100 await fs . mkdir ( agentDir , { recursive : true } ) ;
99101 await fs . writeFile (
100102 path . join ( agentDir , "auth-profiles.json" ) ,
@@ -114,7 +116,7 @@ describe("resolveProviderAuths key normalization", () => {
114116 }
115117
116118 async function writeProfileOrder ( home : string , provider : string , profileIds : string [ ] ) {
117- const agentDir = path . join ( home , ".openclaw" , "agents" , "main" , "agent" ) ;
119+ const agentDir = agentDirForHome ( home ) ;
118120 const parsed = JSON . parse (
119121 await fs . readFile ( path . join ( agentDir , "auth-profiles.json" ) , "utf8" ) ,
120122 ) as Record < string , unknown > ;
@@ -149,28 +151,26 @@ describe("resolveProviderAuths key normalization", () => {
149151 }
150152
151153 async function resolveMinimaxAuthFromConfiguredKey ( apiKey : string ) {
152- return await withSuiteHome (
153- async ( home ) => {
154- await writeConfig ( home , {
155- models : {
156- providers : {
157- minimax : {
158- baseUrl : "https://api.minimaxi.com" ,
159- models : [ createTestModelDefinition ( ) ] ,
160- apiKey,
161- } ,
154+ return await withSuiteHome ( async ( home ) => {
155+ await writeConfig ( home , {
156+ models : {
157+ providers : {
158+ minimax : {
159+ baseUrl : "https://api.minimaxi.com" ,
160+ models : [ createTestModelDefinition ( ) ] ,
161+ apiKey,
162162 } ,
163163 } ,
164- } ) ;
164+ } ,
165+ } ) ;
165166
166- return await resolveProviderAuths ( {
167- providers : [ "minimax" ] ,
168- } ) ;
169- } ,
170- {
171- ...EMPTY_PROVIDER_ENV ,
172- } ,
173- ) ;
167+ return await resolveProviderAuths ( {
168+ providers : [ "minimax" ] ,
169+ agentDir : agentDirForHome ( home ) ,
170+ config : ( await readConfigForHome ( home ) ) as OpenClawConfig ,
171+ env : buildSuiteEnv ( home ) ,
172+ } ) ;
173+ } ) ;
174174 }
175175
176176 async function expectResolvedAuthsFromSuiteHome ( params : {
@@ -179,19 +179,16 @@ describe("resolveProviderAuths key normalization", () => {
179179 env ?: Record < string , string | undefined > ;
180180 setup ?: ( home : string ) => Promise < void > ;
181181 } ) {
182- await withSuiteHome (
183- async ( home ) => {
184- await params . setup ?.( home ) ;
185- const auths = await resolveProviderAuths ( {
186- providers : params . providers ,
187- } ) ;
188- expect ( auths ) . toEqual ( params . expected ) ;
189- } ,
190- {
191- ...EMPTY_PROVIDER_ENV ,
192- ...params . env ,
193- } ,
194- ) ;
182+ await withSuiteHome ( async ( home ) => {
183+ await params . setup ?.( home ) ;
184+ const auths = await resolveProviderAuths ( {
185+ providers : params . providers ,
186+ agentDir : agentDirForHome ( home ) ,
187+ config : ( await readConfigForHome ( home ) ) as OpenClawConfig ,
188+ env : buildSuiteEnv ( home , params . env ) ,
189+ } ) ;
190+ expect ( auths ) . toEqual ( params . expected ) ;
191+ } ) ;
195192 }
196193
197194 it . each ( [
@@ -401,18 +398,24 @@ describe("resolveProviderAuths key normalization", () => {
401398
402399 const auths = await resolveProviderAuths ( {
403400 providers : [ "anthropic" ] ,
401+ agentDir : agentDirForHome ( home ) ,
402+ config : ( await readConfigForHome ( home ) ) as OpenClawConfig ,
403+ env : buildSuiteEnv ( home ) ,
404404 } ) ;
405405 expect ( auths ) . toEqual ( [ ] ) ;
406- } , { } ) ;
406+ } ) ;
407407 } ) ;
408408
409409 it ( "skips providers without oauth-compatible profiles" , async ( ) => {
410- await withSuiteHome ( async ( ) => {
410+ await withSuiteHome ( async ( home ) => {
411411 const auths = await resolveProviderAuths ( {
412412 providers : [ "anthropic" ] ,
413+ agentDir : agentDirForHome ( home ) ,
414+ config : ( await readConfigForHome ( home ) ) as OpenClawConfig ,
415+ env : buildSuiteEnv ( home ) ,
413416 } ) ;
414417 expect ( auths ) . toEqual ( [ ] ) ;
415- } , { } ) ;
418+ } ) ;
416419 } ) ;
417420
418421 it ( "skips oauth profiles that resolve without an api key and uses later profiles" , async ( ) => {
@@ -430,9 +433,12 @@ describe("resolveProviderAuths key normalization", () => {
430433
431434 const auths = await resolveProviderAuths ( {
432435 providers : [ "anthropic" ] ,
436+ agentDir : agentDirForHome ( home ) ,
437+ config : ( await readConfigForHome ( home ) ) as OpenClawConfig ,
438+ env : buildSuiteEnv ( home ) ,
433439 } ) ;
434440 expect ( auths ) . toEqual ( [ { provider : "anthropic" , token : "anthropic-token" } ] ) ;
435- } , { } ) ;
441+ } ) ;
436442 } ) ;
437443
438444 it ( "skips api_key entries in oauth token resolution order" , async ( ) => {
@@ -445,9 +451,12 @@ describe("resolveProviderAuths key normalization", () => {
445451
446452 const auths = await resolveProviderAuths ( {
447453 providers : [ "anthropic" ] ,
454+ agentDir : agentDirForHome ( home ) ,
455+ config : ( await readConfigForHome ( home ) ) as OpenClawConfig ,
456+ env : buildSuiteEnv ( home ) ,
448457 } ) ;
449458 expect ( auths ) . toEqual ( [ { provider : "anthropic" , token : "token-1" } ] ) ;
450- } , { } ) ;
459+ } ) ;
451460 } ) ;
452461
453462 it ( "ignores marker-backed config keys for provider usage auth resolution" , async ( ) => {
0 commit comments