


























@@ -18,6 +18,28 @@ function createJsonResponse(body: unknown, init?: { status?: number }) {
1818});
1919}
202021+function cancelTrackedResponse(
22+text: string,
23+init: ResponseInit,
24+): {
25+response: Response;
26+wasCanceled: () => boolean;
27+} {
28+let canceled = false;
29+const stream = new ReadableStream<Uint8Array>({
30+start(controller) {
31+controller.enqueue(new TextEncoder().encode(text));
32+},
33+cancel() {
34+canceled = true;
35+},
36+});
37+return {
38+response: new Response(stream, init),
39+wasCanceled: () => canceled,
40+};
41+}
42+2143function fetchCall(fetchMock: ReturnType<typeof vi.fn<typeof fetch>>, index: number) {
2244const call = fetchMock.mock.calls[index];
2345if (!call) {
@@ -172,6 +194,44 @@ describe("loginOpenAICodexDeviceCode", () => {
172194expect(credentials.expires).toBe(expectedExpiry);
173195});
174196197+it("accepts token exchange JSON above the diagnostic preview limit", async () => {
198+const accessToken = createJwt({
199+exp: Math.floor(Date.now() / 1000) + 600,
200+"https://api.openai.com/auth": {
201+chatgpt_account_id: "acct_123",
202+},
203+});
204+const fetchMock = vi
205+.fn()
206+.mockResolvedValueOnce(
207+createJsonResponse({
208+device_auth_id: "device-auth-123",
209+user_code: "CODE-12345",
210+interval: "0",
211+}),
212+)
213+.mockResolvedValueOnce(
214+createJsonResponse({
215+authorization_code: "authorization-code-123",
216+code_verifier: "code-verifier-123",
217+}),
218+)
219+.mockResolvedValueOnce(
220+createJsonResponse({
221+access_token: accessToken,
222+refresh_token: "refresh-token-123",
223+id_token: "x".repeat(10_000),
224+}),
225+);
226+227+const credentials = await loginOpenAICodexDeviceCode({
228+fetchFn: fetchMock as typeof fetch,
229+onVerification: async () => {},
230+});
231+232+expect(credentials.refresh).toBe("refresh-token-123");
233+});
234+175235it("falls back when device-code intervals and token lifetimes overflow safe milliseconds", async () => {
176236vi.useFakeTimers();
177237try {
@@ -241,6 +301,28 @@ describe("loginOpenAICodexDeviceCode", () => {
241301).rejects.toThrow("OpenAI device code request failed: HTTP 503 down now");
242302});
243303304+it("bounds user-code error bodies without using response.text()", async () => {
305+const tracked = cancelTrackedResponse(`${"device code unavailable ".repeat(1024)}tail`, {
306+status: 503,
307+headers: { "Content-Type": "text/plain" },
308+});
309+const textSpy = vi.spyOn(tracked.response, "text").mockRejectedValue(new Error("unbounded"));
310+const fetchMock = vi.fn().mockResolvedValueOnce(tracked.response);
311+312+const error = await loginOpenAICodexDeviceCode({
313+fetchFn: fetchMock as typeof fetch,
314+onVerification: async () => {},
315+}).catch((cause: unknown) => cause);
316+317+expect(error).toBeInstanceOf(Error);
318+expect((error as Error).message).toMatch(
319+/OpenAI device code request failed: HTTP 503 device code unavailable/,
320+);
321+expect((error as Error).message).not.toContain("tail");
322+expect(tracked.wasCanceled()).toBe(true);
323+expect(textSpy).not.toHaveBeenCalled();
324+});
325+244326it("surfaces device authorization failures with sanitized payload details", async () => {
245327const fetchMock = vi
246328.fn()
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。