@@ -171,6 +171,60 @@ describe("loginOpenAICodexDeviceCode", () => {
|
171 | 171 | expect(credentials.expires).toBe(expectedExpiry); |
172 | 172 | }); |
173 | 173 | |
| 174 | +it("falls back when device-code intervals and token lifetimes overflow safe milliseconds", async () => { |
| 175 | +vi.useFakeTimers(); |
| 176 | +try { |
| 177 | +const accessToken = createJwt({ |
| 178 | +exp: Math.floor(Date.now() / 1000) + 600, |
| 179 | +"https://api.openai.com/auth": { |
| 180 | +chatgpt_account_id: "acct_123", |
| 181 | +}, |
| 182 | +}); |
| 183 | +const expectedExpiry = resolveCodexAccessTokenExpiry(accessToken); |
| 184 | +const fetchMock = vi |
| 185 | +.fn<typeof fetch>() |
| 186 | +.mockResolvedValueOnce( |
| 187 | +createJsonResponse({ |
| 188 | +device_auth_id: "device-auth-123", |
| 189 | +user_code: "CODE-12345", |
| 190 | +interval: Number.MAX_SAFE_INTEGER, |
| 191 | +}), |
| 192 | +) |
| 193 | +.mockResolvedValueOnce(new Response(null, { status: 404 })) |
| 194 | +.mockResolvedValueOnce( |
| 195 | +createJsonResponse({ |
| 196 | +authorization_code: "authorization-code-123", |
| 197 | +code_verifier: "code-verifier-123", |
| 198 | +}), |
| 199 | +) |
| 200 | +.mockResolvedValueOnce( |
| 201 | +createJsonResponse({ |
| 202 | +access_token: accessToken, |
| 203 | +refresh_token: "refresh-token-123", |
| 204 | +expires_in: Number.MAX_SAFE_INTEGER, |
| 205 | +}), |
| 206 | +); |
| 207 | + |
| 208 | +const credentialsPromise = loginOpenAICodexDeviceCode({ |
| 209 | +fetchFn: fetchMock as typeof fetch, |
| 210 | +onVerification: async () => {}, |
| 211 | +}); |
| 212 | +await vi.advanceTimersByTimeAsync(0); |
| 213 | +expect(fetchMock).toHaveBeenCalledTimes(2); |
| 214 | +await vi.advanceTimersByTimeAsync(4_999); |
| 215 | +expect(fetchMock).toHaveBeenCalledTimes(2); |
| 216 | +await vi.advanceTimersByTimeAsync(1); |
| 217 | +const credentials = await credentialsPromise; |
| 218 | + |
| 219 | +if (expectedExpiry === undefined) { |
| 220 | +throw new Error("expected device-code expiry to be calculated"); |
| 221 | +} |
| 222 | +expect(credentials.expires).toBe(expectedExpiry); |
| 223 | +} finally { |
| 224 | +vi.useRealTimers(); |
| 225 | +} |
| 226 | +}); |
| 227 | + |
174 | 228 | it("surfaces user-code request failures", async () => { |
175 | 229 | const fetchMock = vi.fn().mockResolvedValueOnce( |
176 | 230 | new Response(`down\r\n\u001B[31mnow\u001B[0m`, { |
|