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