@@ -203,6 +203,67 @@ describe('ACP server test', { timeout: 40_000 }, () => {
203203 expect ( accountLoginSpy ) . not . toHaveBeenCalled ( ) ;
204204 } ) ;
205205
206+
207+ it ( 'should request ACP URL elicitation for device code auth' , async ( ) => {
208+ const deviceCodeFixture = createCodexMockTestFixture ( ) ;
209+ vi . spyOn ( deviceCodeFixture . getCodexAppServerClient ( ) , "accountRead" ) . mockResolvedValue ( {
210+ account : null ,
211+ requiresOpenaiAuth : true ,
212+ } ) ;
213+ deviceCodeFixture . setElicitationResponse ( { action : "accept" } ) ;
214+ deviceCodeFixture . setAccountLoginResponse ( {
215+ type : "chatgptDeviceCode" ,
216+ loginId : "login-id" ,
217+ verificationUrl : "https://openai.example/device" ,
218+ userCode : "ABCD-EFGH" ,
219+ } ) ;
220+ vi . stubEnv ( "NO_BROWSER" , "1" ) ;
221+
222+ const authPromise = deviceCodeFixture . getCodexAcpAgent ( ) . authenticate ( { methodId : "chat-gpt" } ) ;
223+
224+ await vi . waitFor ( ( ) => {
225+ expect ( deviceCodeFixture . getCodexAppServerClient ( ) . accountLogin )
226+ . toHaveBeenCalledWith ( { type : "chatgptDeviceCode" } ) ;
227+ } ) ;
228+
229+ await vi . waitFor ( ( ) => {
230+ const elicitationEvent = deviceCodeFixture . getAcpConnectionEvents ( [ ] )
231+ . find ( event => event . method === "createElicitation" ) ;
232+ expect ( elicitationEvent ?. args [ 0 ] ) . toEqual ( expect . objectContaining ( {
233+ mode : "url" ,
234+ requestId : null ,
235+ elicitationId : expect . stringMatching ( / ^ c h a t g p t - l o g i n - / ) ,
236+ url : "https://openai.example/device" ,
237+ message : expect . stringContaining ( "ABCD-EFGH" ) ,
238+ } ) ) ;
239+ } ) ;
240+
241+ deviceCodeFixture . sendAccountLoginCompleted ( { loginId : "login-id" , success : true , error : null } ) ;
242+
243+ await expect ( authPromise ) . resolves . toEqual ( { } ) ;
244+ } ) ;
245+
246+ it ( 'should fail device code auth when ACP URL elicitation is not accepted' , async ( ) => {
247+ const deviceCodeFixture = createCodexMockTestFixture ( ) ;
248+ vi . spyOn ( deviceCodeFixture . getCodexAppServerClient ( ) , "accountRead" ) . mockResolvedValue ( {
249+ account : null ,
250+ requiresOpenaiAuth : true ,
251+ } ) ;
252+ deviceCodeFixture . setElicitationResponse ( { action : "cancel" } ) ;
253+ deviceCodeFixture . setAccountLoginResponse ( {
254+ type : "chatgptDeviceCode" ,
255+ loginId : "login-id" ,
256+ verificationUrl : "https://openai.example/device" ,
257+ userCode : "ABCD-EFGH" ,
258+ } ) ;
259+ vi . stubEnv ( "NO_BROWSER" , "1" ) ;
260+
261+ await expect ( deviceCodeFixture . getCodexAcpAgent ( ) . authenticate ( { methodId : "chat-gpt" } ) )
262+ . rejects . toThrow ( "Authentication required" ) ;
263+ expect ( deviceCodeFixture . getCodexAppServerClient ( ) . accountLogin )
264+ . toHaveBeenCalledWith ( { type : "chatgptDeviceCode" } ) ;
265+ } ) ;
266+
206267 it ( 'should authenticate with a gateway' , async ( ) => {
207268 const gatewayFixture = createTestFixture ( ) ;
208269 const codexAcpAgent = gatewayFixture . getCodexAcpAgent ( ) ;
@@ -2856,7 +2917,7 @@ describe('ACP server test', { timeout: 40_000 }, () => {
28562917
28572918 await codexAcpAgent . authenticate ( { methodId : "api-key" } ) ;
28582919
2859- expect ( authenticateSpy ) . toHaveBeenCalledWith ( { methodId : "api-key" } ) ;
2920+ expect ( authenticateSpy ) . toHaveBeenCalledWith ( expect . anything ( ) , { methodId : "api-key" } ) ;
28602921 expect ( getAccountSpy ) . toHaveBeenCalledTimes ( 4 ) ;
28612922 expect ( codexAcpAgent . getSessionState ( session1 . sessionId ) ) . toMatchObject ( {
28622923 account : { type : "apiKey" } ,
@@ -2912,7 +2973,7 @@ describe('ACP server test', { timeout: 40_000 }, () => {
29122973 } ;
29132974 await codexAcpAgent . authenticate ( gatewayAuthRequest ) ;
29142975
2915- expect ( authenticateSpy ) . toHaveBeenCalledWith ( gatewayAuthRequest ) ;
2976+ expect ( authenticateSpy ) . toHaveBeenCalledWith ( expect . anything ( ) , gatewayAuthRequest ) ;
29162977 expect ( getAccountSpy ) . toHaveBeenCalledTimes ( 1 ) ;
29172978 expect ( codexAcpAgent . getSessionState ( session . sessionId ) ) . toMatchObject ( {
29182979 account : { type : "apiKey" } ,
0 commit comments