11// noinspection ES6RedundantAwait
22
33import { describe , expect , it , vi , beforeEach } from 'vitest' ;
4- import fs from "node:fs" ;
5- import os from "node:os" ;
6- import path from "node:path" ;
74import type { CodexAuthRequest } from "../../CodexAuthMethod" ;
85import type * as acp from "@agentclientprotocol/sdk" ;
96import { createTestFixture , createCodexMockTestFixture , createTestSessionState , type TestFixture } from "../acp-test-utils" ;
@@ -14,46 +11,6 @@ import type {ListMcpServerStatusResponse, Model, SkillsListResponse} from "../..
1411import type { RateLimitsMap } from "../../RateLimitsMap" ;
1512import { ModelId } from "../../ModelId" ;
1613
17- const CODEX_HOME_ENV = "CODEX_HOME" ;
18-
19- async function overrideCodexHome < T > ( configToml : string , run : ( ) => Promise < T > ) : Promise < T > {
20- const previousCodexHome = process . env [ CODEX_HOME_ENV ] ;
21- const codexHome = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , "codex-acp-codex-home-" ) ) ;
22- fs . writeFileSync ( path . join ( codexHome , "config.toml" ) , configToml , "utf8" ) ;
23- process . env [ CODEX_HOME_ENV ] = codexHome ;
24-
25- try {
26- return await run ( ) ;
27- } finally {
28- if ( previousCodexHome === undefined ) {
29- delete process . env [ CODEX_HOME_ENV ] ;
30- } else {
31- process . env [ CODEX_HOME_ENV ] = previousCodexHome ;
32- }
33- await removeDirectoryWithRetry ( codexHome ) ;
34- }
35- }
36-
37- async function removeDirectoryWithRetry ( directory : string ) : Promise < void > {
38- let lastError : NodeJS . ErrnoException | null = null ;
39- for ( let attempt = 0 ; attempt < 5 ; attempt += 1 ) {
40- try {
41- fs . rmSync ( directory , { recursive : true , force : true } ) ;
42- return ;
43- } catch ( error ) {
44- const err = error as NodeJS . ErrnoException ;
45- if ( err . code !== "ENOTEMPTY" && err . code !== "EBUSY" ) {
46- throw err ;
47- }
48- lastError = err ;
49- await new Promise ( ( resolve ) => setTimeout ( resolve , 50 * ( attempt + 1 ) ) ) ;
50- }
51- }
52- if ( lastError ) {
53- throw lastError ;
54- }
55- }
56-
5714describe ( 'ACP server test' , { timeout : 40_000 } , ( ) => {
5815
5916 let fixture : TestFixture ;
@@ -65,126 +22,119 @@ describe('ACP server test', { timeout: 40_000 }, () => {
6522 const ignoredFields = [ "thread" , "cwd" , "id" , "createdAt" , "path" , "threadId" , "userAgent" , "sandbox" , "conversationId" , "origins" , "supportedReasoningEfforts" , "reasoningEffort" , "model" , "readOnlyAccess" , "approvalsReviewer" ] ;
6623
6724 it ( 'should throw error without authentication' , async ( ) => {
68- await overrideCodexHome ( 'cli_auth_credentials_store = "file"' , async ( ) => {
69- const authFixture = createTestFixture ( ) ;
70- const codexAcpAgent = authFixture . getCodexAcpAgent ( ) ;
25+ const authFixture = createTestFixture ( ) ;
26+ const codexAcpAgent = authFixture . getCodexAcpAgent ( ) ;
7127
72- await codexAcpAgent . initialize ( { protocolVersion : 1 } ) ;
73- await authFixture . getCodexAcpClient ( ) . logout ( ) ;
74- authFixture . clearCodexConnectionDump ( ) ;
28+ await codexAcpAgent . initialize ( { protocolVersion : 1 } ) ;
29+ await authFixture . getCodexAcpClient ( ) . logout ( ) ;
30+ authFixture . clearCodexConnectionDump ( ) ;
7531
76- await expect (
77- codexAcpAgent . newSession ( { cwd : "" , mcpServers : [ ] } )
78- ) . rejects . toThrow ( "Authentication required" ) ;
32+ await expect (
33+ codexAcpAgent . newSession ( { cwd : "" , mcpServers : [ ] } )
34+ ) . rejects . toThrow ( "Authentication required" ) ;
7935
80- const transportDump = authFixture . getCodexConnectionDump ( ignoredFields ) ;
81- await expect ( transportDump ) . toMatchFileSnapshot ( "data/auth-failed.json" ) ;
82- } ) ;
36+ const transportDump = authFixture . getCodexConnectionDump ( ignoredFields ) ;
37+ await expect ( transportDump ) . toMatchFileSnapshot ( "data/auth-failed.json" ) ;
8338 } ) ;
8439
8540 it ( 'should authenticate with key' , async ( ) => {
86- // In sandboxed environments Codex may fail when trying to write to the OS keychain (`Operation not permitted`).
87- await overrideCodexHome ( 'cli_auth_credentials_store = "file"' , async ( ) => {
88- const keyFixture = createTestFixture ( ) ;
89- const codexAcpAgent = keyFixture . getCodexAcpAgent ( ) ;
90-
91- await codexAcpAgent . initialize ( { protocolVersion : 1 } ) ;
92- await keyFixture . getCodexAcpClient ( ) . logout ( ) ;
93-
94-
95- const unauthenticatedResponse = await keyFixture . getCodexAcpAgent ( ) . extMethod ( "authentication/status" , { } ) ;
96- expect ( unauthenticatedResponse ) . toEqual ( { type : "unauthenticated" } ) ;
97-
98- keyFixture . clearCodexConnectionDump ( ) ;
99-
100- const authRequest : CodexAuthRequest = { methodId : "api-key" , _meta : { "api-key" : { apiKey : "TOKEN" } } }
101- await codexAcpAgent . authenticate ( authRequest ) ;
102- const newSessionResponse = await codexAcpAgent . newSession ( { cwd : "" , mcpServers : [ ] } ) ;
103- expect ( newSessionResponse . sessionId ) . toBeDefined ( )
104-
105- const transportEvents = keyFixture . getCodexConnectionEvents ( [ ...ignoredFields , "upgrade" ] ) ;
106- const transportMethods = transportEvents . flatMap ( event => "method" in event ? [ event . method ] : [ ] ) ;
107- const loginRequest = transportEvents . find ( event =>
108- event . eventType === "request" &&
109- "method" in event &&
110- event . method === "account/login/start"
111- ) ;
112- const loginResponse = transportEvents . find ( event =>
113- event . eventType === "response" &&
114- "type" in event &&
115- event . type === "apiKey"
116- ) ;
117- const threadStartResponse = transportEvents . find ( event =>
118- event . eventType === "response" &&
119- "modelProvider" in event &&
120- "approvalPolicy" in event
121- ) ;
122- expect ( transportMethods ) . toEqual ( [
123- "account/login/start" ,
124- "account/read" ,
125- "account/updated" ,
126- "thread/start" ,
127- "model/list" ,
128- "thread/started" ,
129- "account/read" ,
130- "skills/list" ,
131- ] ) ;
132- expect ( loginRequest ) . toEqual ( {
133- eventType : "request" ,
134- method : "account/login/start" ,
135- params : {
136- type : "apiKey" ,
137- apiKey : "TOKEN" ,
138- }
139- } ) ;
140- expect ( loginResponse ) . toEqual ( {
141- eventType : "response" ,
41+ const keyFixture = createTestFixture ( ) ;
42+ const codexAcpAgent = keyFixture . getCodexAcpAgent ( ) ;
43+
44+ await codexAcpAgent . initialize ( { protocolVersion : 1 } ) ;
45+ await keyFixture . getCodexAcpClient ( ) . logout ( ) ;
46+
47+
48+ const unauthenticatedResponse = await keyFixture . getCodexAcpAgent ( ) . extMethod ( "authentication/status" , { } ) ;
49+ expect ( unauthenticatedResponse ) . toEqual ( { type : "unauthenticated" } ) ;
50+
51+ keyFixture . clearCodexConnectionDump ( ) ;
52+
53+ const authRequest : CodexAuthRequest = { methodId : "api-key" , _meta : { "api-key" : { apiKey : "TOKEN" } } }
54+ await codexAcpAgent . authenticate ( authRequest ) ;
55+ const newSessionResponse = await codexAcpAgent . newSession ( { cwd : "" , mcpServers : [ ] } ) ;
56+ expect ( newSessionResponse . sessionId ) . toBeDefined ( )
57+
58+ const transportEvents = keyFixture . getCodexConnectionEvents ( [ ...ignoredFields , "upgrade" ] ) ;
59+ const transportMethods = transportEvents . flatMap ( event => "method" in event ? [ event . method ] : [ ] ) ;
60+ const loginRequest = transportEvents . find ( event =>
61+ event . eventType === "request" &&
62+ "method" in event &&
63+ event . method === "account/login/start"
64+ ) ;
65+ const loginResponse = transportEvents . find ( event =>
66+ event . eventType === "response" &&
67+ "type" in event &&
68+ event . type === "apiKey"
69+ ) ;
70+ const threadStartResponse = transportEvents . find ( event =>
71+ event . eventType === "response" &&
72+ "modelProvider" in event &&
73+ "approvalPolicy" in event
74+ ) ;
75+ expect ( transportMethods ) . toEqual ( [
76+ "account/login/start" ,
77+ "account/read" ,
78+ "account/updated" ,
79+ "thread/start" ,
80+ "model/list" ,
81+ "thread/started" ,
82+ "account/read" ,
83+ "skills/list" ,
84+ ] ) ;
85+ expect ( loginRequest ) . toEqual ( {
86+ eventType : "request" ,
87+ method : "account/login/start" ,
88+ params : {
14289 type : "apiKey" ,
143- } ) ;
144- expect ( threadStartResponse ) . toMatchObject ( {
145- eventType : "response" ,
146- modelProvider : "openai" ,
147- approvalPolicy : "on-request" ,
148- approvalsReviewer : "approvalsReviewer" ,
149- } ) ;
150- const authenticatedResponse = await keyFixture . getCodexAcpAgent ( ) . extMethod ( "authentication/status" , { } ) ;
151- expect ( authenticatedResponse ) . toEqual ( { type : "api-key" } ) ;
152-
153- await keyFixture . getCodexAcpAgent ( ) . extMethod ( "authentication/logout" , { } ) ;
154- const logoutResponse = await keyFixture . getCodexAcpAgent ( ) . extMethod ( "authentication/status" , { } ) ;
155- expect ( logoutResponse ) . toEqual ( { type : "unauthenticated" } ) ;
90+ apiKey : "TOKEN" ,
91+ }
15692 } ) ;
93+ expect ( loginResponse ) . toEqual ( {
94+ eventType : "response" ,
95+ type : "apiKey" ,
96+ } ) ;
97+ expect ( threadStartResponse ) . toMatchObject ( {
98+ eventType : "response" ,
99+ modelProvider : "openai" ,
100+ approvalPolicy : "on-request" ,
101+ approvalsReviewer : "approvalsReviewer" ,
102+ } ) ;
103+ const authenticatedResponse = await keyFixture . getCodexAcpAgent ( ) . extMethod ( "authentication/status" , { } ) ;
104+ expect ( authenticatedResponse ) . toEqual ( { type : "api-key" } ) ;
105+
106+ await keyFixture . getCodexAcpAgent ( ) . extMethod ( "authentication/logout" , { } ) ;
107+ const logoutResponse = await keyFixture . getCodexAcpAgent ( ) . extMethod ( "authentication/status" , { } ) ;
108+ expect ( logoutResponse ) . toEqual ( { type : "unauthenticated" } ) ;
157109 } ) ;
158110
159111 it ( 'should authenticate with a gateway' , async ( ) => {
160- await overrideCodexHome ( 'cli_auth_credentials_store = "file"' , async ( ) => {
161- const gatewayFixture = createTestFixture ( ) ;
162- const codexAcpAgent = gatewayFixture . getCodexAcpAgent ( ) ;
163-
164- await codexAcpAgent . initialize ( { protocolVersion : 1 } ) ;
165- await gatewayFixture . getCodexAcpClient ( ) . logout ( ) ;
166-
167- const authRequest : CodexAuthRequest = {
168- methodId : "gateway" ,
169- _meta : {
170- "gateway" : {
171- baseUrl : "https://www.example.com" ,
172- headers : {
173- "Custom-Auth-Header" : "TOKEN"
174- }
112+ const gatewayFixture = createTestFixture ( ) ;
113+ const codexAcpAgent = gatewayFixture . getCodexAcpAgent ( ) ;
114+
115+ await codexAcpAgent . initialize ( { protocolVersion : 1 } ) ;
116+ await gatewayFixture . getCodexAcpClient ( ) . logout ( ) ;
117+
118+ const authRequest : CodexAuthRequest = {
119+ methodId : "gateway" ,
120+ _meta : {
121+ "gateway" : {
122+ baseUrl : "https://www.example.com" ,
123+ headers : {
124+ "Custom-Auth-Header" : "TOKEN"
175125 }
176126 }
177- } ;
127+ }
128+ } ;
178129
179- await codexAcpAgent . authenticate ( authRequest ) ;
180- expect ( await gatewayFixture . getCodexAcpClient ( ) . authRequired ( ) ) . toBe ( false ) ;
130+ await codexAcpAgent . authenticate ( authRequest ) ;
131+ expect ( await gatewayFixture . getCodexAcpClient ( ) . authRequired ( ) ) . toBe ( false ) ;
181132
182- const authenticatedResponse = await gatewayFixture . getCodexAcpAgent ( ) . extMethod ( "authentication/status" , { } ) ;
183- expect ( authenticatedResponse ) . toEqual ( { type : "gateway" , name : "custom-gateway" } ) ;
133+ const authenticatedResponse = await gatewayFixture . getCodexAcpAgent ( ) . extMethod ( "authentication/status" , { } ) ;
134+ expect ( authenticatedResponse ) . toEqual ( { type : "gateway" , name : "custom-gateway" } ) ;
184135
185- const newSessionResponse = await codexAcpAgent . newSession ( { cwd : "" , mcpServers : [ ] } ) ;
186- expect ( newSessionResponse . sessionId ) . toBeDefined ( )
187- } ) ;
136+ const newSessionResponse = await codexAcpAgent . newSession ( { cwd : "" , mcpServers : [ ] } ) ;
137+ expect ( newSessionResponse . sessionId ) . toBeDefined ( )
188138 } )
189139
190140 it ( 'prefetches session additional skill roots before thread start' , async ( ) => {
0 commit comments