From 586282618460f4be9ec8e6c841bddd15c81c7811 Mon Sep 17 00:00:00 2001 From: ndycode Date: Sat, 21 Mar 2026 04:31:19 +0800 Subject: [PATCH 1/4] test: cover list and status cli output --- test/codex-manager-cli.test.ts | 632 +++++++++++++++++++++++---------- 1 file changed, 436 insertions(+), 196 deletions(-) diff --git a/test/codex-manager-cli.test.ts b/test/codex-manager-cli.test.ts index 930cf8fb..6a6a824d 100644 --- a/test/codex-manager-cli.test.ts +++ b/test/codex-manager-cli.test.ts @@ -96,7 +96,10 @@ vi.mock("../lib/accounts.js", () => ({ tokenId: string | undefined, ) => { if (!storedAccountId) return tokenId; - if (currentAccountIdSource === "org" || currentAccountIdSource === "manual") { + if ( + currentAccountIdSource === "org" || + currentAccountIdSource === "manual" + ) { return storedAccountId; } return tokenId ?? storedAccountId; @@ -107,10 +110,16 @@ vi.mock("../lib/accounts.js", () => ({ ), selectBestAccountCandidate: vi.fn(() => null), shouldUpdateAccountIdFromToken: vi.fn( - (currentAccountIdSource: string | undefined, currentAccountId: string | undefined) => { + ( + currentAccountIdSource: string | undefined, + currentAccountId: string | undefined, + ) => { if (!currentAccountId) return true; if (!currentAccountIdSource) return true; - return currentAccountIdSource === "token" || currentAccountIdSource === "id_token"; + return ( + currentAccountIdSource === "token" || + currentAccountIdSource === "id_token" + ); }, ), })); @@ -499,33 +508,31 @@ describe("codex manager cli commands", () => { version: 1, accounts: [], }); - withAccountStorageTransactionMock.mockImplementation( - async (handler) => { - const current = await loadAccountsMock(); - return handler( - current == null - ? { + withAccountStorageTransactionMock.mockImplementation(async (handler) => { + const current = await loadAccountsMock(); + return handler( + current == null + ? { version: 3, accounts: [], activeIndex: 0, activeIndexByFamily: {}, } - : structuredClone(current), - async (storage: unknown) => saveAccountsMock(storage), - ); - }, - ); + : structuredClone(current), + async (storage: unknown) => saveAccountsMock(storage), + ); + }); withAccountAndFlaggedStorageTransactionMock.mockImplementation( async (handler) => { const current = await loadAccountsMock(); let snapshot = current == null ? { - version: 3, - accounts: [], - activeIndex: 0, - activeIndexByFamily: {}, - } + version: 3, + accounts: [], + activeIndex: 0, + activeIndexByFamily: {}, + } : structuredClone(current); return handler( structuredClone(snapshot), @@ -576,7 +583,9 @@ describe("codex manager cli commands", () => { const { formatBackupSavedAt } = await import("../lib/codex-manager.js"); try { - expect(formatBackupSavedAt(1_710_000_000_000)).toBe("Localized Saved Time"); + expect(formatBackupSavedAt(1_710_000_000_000)).toBe( + "Localized Saved Time", + ); expect(localeSpy).toHaveBeenCalledWith(undefined, { month: "short", day: "numeric", @@ -589,6 +598,62 @@ describe("codex manager cli commands", () => { } }); + it("prints empty account status for auth list", async () => { + loadAccountsMock.mockResolvedValueOnce(null); + const logSpy = vi.spyOn(console, "log").mockImplementation(() => {}); + const { runCodexMultiAuthCli } = await import("../lib/codex-manager.js"); + + const exitCode = await runCodexMultiAuthCli(["auth", "list"]); + + expect(exitCode).toBe(0); + expect(logSpy).toHaveBeenCalledWith("No accounts configured."); + expect(logSpy).toHaveBeenCalledWith( + "Storage: /mock/openai-codex-accounts.json", + ); + expect(setStoragePathMock).toHaveBeenCalledWith(null); + }); + + it("prints populated account status for auth status", async () => { + const now = Date.now(); + loadAccountsMock.mockResolvedValueOnce({ + version: 3, + activeIndex: 0, + activeIndexByFamily: { codex: 0 }, + accounts: [ + { + email: "active@example.com", + refreshToken: "refresh-active", + addedAt: now - 2_000, + lastUsed: now - 1_000, + coolingDownUntil: now + 60_000, + }, + { + email: "disabled@example.com", + refreshToken: "refresh-disabled", + addedAt: now - 2_000, + lastUsed: now - 500, + enabled: false, + rateLimits: { + codex_rpm: { remaining: 0, resetAt: now + 60_000 }, + }, + }, + ], + }); + const logSpy = vi.spyOn(console, "log").mockImplementation(() => {}); + const { runCodexMultiAuthCli } = await import("../lib/codex-manager.js"); + + const exitCode = await runCodexMultiAuthCli(["auth", "status"]); + + expect(exitCode).toBe(0); + expect(logSpy).toHaveBeenCalledWith("Accounts (2)"); + expect(logSpy).toHaveBeenCalledWith( + expect.stringContaining("1. 1. active@example.com [current]"), + ); + expect(logSpy).toHaveBeenCalledWith( + expect.stringContaining("2. 2. disabled@example.com [disabled]"), + ); + }); + it("runs forecast in json mode", async () => { const now = Date.now(); loadAccountsMock.mockResolvedValueOnce({ @@ -859,9 +924,7 @@ describe("codex manager cli commands", () => { expect(logSpy.mock.calls[0]?.[0]).toBe("Implemented features (41)"); expect( logSpy.mock.calls.some((call) => - String(call[0]).includes( - "41. Auto-switch to best account command", - ), + String(call[0]).includes("41. Auto-switch to best account command"), ), ).toBe(true); }); @@ -930,10 +993,17 @@ describe("codex manager cli commands", () => { const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {}); const { runCodexMultiAuthCli } = await import("../lib/codex-manager.js"); - const exitCode = await runCodexMultiAuthCli(["auth", "best", "--model", "gpt-5.1"]); + const exitCode = await runCodexMultiAuthCli([ + "auth", + "best", + "--model", + "gpt-5.1", + ]); expect(exitCode).toBe(1); - expect(errorSpy).toHaveBeenCalledWith("--model requires --live for codex auth best"); + expect(errorSpy).toHaveBeenCalledWith( + "--model requires --live for codex auth best", + ); expect(loadAccountsMock).not.toHaveBeenCalled(); expect(saveAccountsMock).not.toHaveBeenCalled(); expect(fetchCodexQuotaSnapshotMock).not.toHaveBeenCalled(); @@ -1074,7 +1144,9 @@ describe("codex manager cli commands", () => { ]); expect(exitCode).toBe(0); - expect(withAccountAndFlaggedStorageTransactionMock).toHaveBeenCalledTimes(1); + expect(withAccountAndFlaggedStorageTransactionMock).toHaveBeenCalledTimes( + 1, + ); expect(saveAccountsMock).toHaveBeenCalledWith( expect.objectContaining({ accounts: expect.arrayContaining([ @@ -1131,7 +1203,9 @@ describe("codex manager cli commands", () => { ]); expect(exitCode).toBe(0); - expect(withAccountAndFlaggedStorageTransactionMock).toHaveBeenCalledTimes(1); + expect(withAccountAndFlaggedStorageTransactionMock).toHaveBeenCalledTimes( + 1, + ); const savedStorage = saveAccountsMock.mock.calls.at(-1)?.[0]; expect(savedStorage).toEqual( expect.objectContaining({ @@ -1199,7 +1273,11 @@ describe("codex manager cli commands", () => { expect(exitCode).toBe(0); const savedStorage = saveAccountsMock.mock.calls.at(-1)?.[0] as { - accounts: Array<{ accountId?: string; accountIdSource?: string; refreshToken?: string }>; + accounts: Array<{ + accountId?: string; + accountIdSource?: string; + refreshToken?: string; + }>; }; expect(savedStorage.accounts[0]).toEqual( expect.objectContaining({ @@ -1556,21 +1634,19 @@ describe("codex manager cli commands", () => { }); const accountsModule = await import("../lib/accounts.js"); const extractAccountIdMock = vi.mocked(accountsModule.extractAccountId); - const extractAccountEmailMock = vi.mocked(accountsModule.extractAccountEmail); - extractAccountIdMock.mockImplementation( - (accessToken?: string) => { - if (accessToken === "access-alpha-stale") return "shared-workspace"; - if (accessToken === "access-alpha-refreshed") return "shared-workspace"; - if (accessToken === "access-beta") return "shared-workspace"; - return "acc_test"; - }, - ); - extractAccountEmailMock.mockImplementation( - (accessToken?: string) => { - if (accessToken === "access-alpha-refreshed") return "owner@example.com"; - return undefined; - }, + const extractAccountEmailMock = vi.mocked( + accountsModule.extractAccountEmail, ); + extractAccountIdMock.mockImplementation((accessToken?: string) => { + if (accessToken === "access-alpha-stale") return "shared-workspace"; + if (accessToken === "access-alpha-refreshed") return "shared-workspace"; + if (accessToken === "access-beta") return "shared-workspace"; + return "acc_test"; + }); + extractAccountEmailMock.mockImplementation((accessToken?: string) => { + if (accessToken === "access-alpha-refreshed") return "owner@example.com"; + return undefined; + }); fetchCodexQuotaSnapshotMock .mockResolvedValueOnce({ status: 200, @@ -1690,21 +1766,19 @@ describe("codex manager cli commands", () => { }); const accountsModule = await import("../lib/accounts.js"); const extractAccountIdMock = vi.mocked(accountsModule.extractAccountId); - const extractAccountEmailMock = vi.mocked(accountsModule.extractAccountEmail); - extractAccountIdMock.mockImplementation( - (accessToken?: string) => { - if (accessToken === "access-alpha-stale") return "shared-workspace"; - if (accessToken === "access-alpha-refreshed") return "shared-workspace"; - if (accessToken === "access-beta") return "shared-workspace"; - return "acc_test"; - }, - ); - extractAccountEmailMock.mockImplementation( - (accessToken?: string) => { - if (accessToken === "access-alpha-refreshed") return "owner@example.com"; - return undefined; - }, + const extractAccountEmailMock = vi.mocked( + accountsModule.extractAccountEmail, ); + extractAccountIdMock.mockImplementation((accessToken?: string) => { + if (accessToken === "access-alpha-stale") return "shared-workspace"; + if (accessToken === "access-alpha-refreshed") return "shared-workspace"; + if (accessToken === "access-beta") return "shared-workspace"; + return "acc_test"; + }); + extractAccountEmailMock.mockImplementation((accessToken?: string) => { + if (accessToken === "access-alpha-refreshed") return "owner@example.com"; + return undefined; + }); fetchCodexQuotaSnapshotMock .mockResolvedValueOnce({ status: 200, @@ -1850,10 +1924,12 @@ describe("codex manager cli commands", () => { ); const { runCodexMultiAuthCli } = await import("../lib/codex-manager.js"); - await expect(runCodexMultiAuthCli(["auth", "check"])).rejects.toMatchObject({ - code: "EBUSY", - message: "save failed", - }); + await expect(runCodexMultiAuthCli(["auth", "check"])).rejects.toMatchObject( + { + code: "EBUSY", + message: "save failed", + }, + ); expect(originalQuotaCache).toEqual({ byAccountId: {}, byEmail: {}, @@ -1959,7 +2035,9 @@ describe("codex manager cli commands", () => { }, ], }; - loadAccountsMock.mockImplementation(async () => structuredClone(storageState)); + loadAccountsMock.mockImplementation(async () => + structuredClone(storageState), + ); saveAccountsMock.mockImplementation(async (nextStorage) => { storageState = structuredClone(nextStorage); }); @@ -2080,7 +2158,12 @@ describe("codex manager cli commands", () => { const logSpy = vi.spyOn(console, "log").mockImplementation(() => {}); const { runCodexMultiAuthCli } = await import("../lib/codex-manager.js"); - const exitCode = await runCodexMultiAuthCli(["auth", "best", "--live", "--json"]); + const exitCode = await runCodexMultiAuthCli([ + "auth", + "best", + "--live", + "--json", + ]); expect(exitCode).toBe(0); expect(setCodexCliActiveSelectionMock).toHaveBeenCalledTimes(1); @@ -2128,7 +2211,9 @@ describe("codex manager cli commands", () => { }, ], }; - loadAccountsMock.mockImplementation(async () => structuredClone(storageState)); + loadAccountsMock.mockImplementation(async () => + structuredClone(storageState), + ); saveAccountsMock.mockImplementation(async (nextStorage) => { storageState = structuredClone(nextStorage); }); @@ -2282,9 +2367,11 @@ describe("codex manager cli commands", () => { ); expect(saveAccountsMock).not.toHaveBeenCalled(); expect(setCodexCliActiveSelectionMock).not.toHaveBeenCalled(); - expect(logSpy.mock.calls.some((call) => - String(call[0]).includes("Already on best account 1"), - )).toBe(true); + expect( + logSpy.mock.calls.some((call) => + String(call[0]).includes("Already on best account 1"), + ), + ).toBe(true); }); it("syncs refreshed current best account during live best check", async () => { @@ -2304,7 +2391,9 @@ describe("codex manager cli commands", () => { }, ], }; - loadAccountsMock.mockImplementation(async () => structuredClone(storageState)); + loadAccountsMock.mockImplementation(async () => + structuredClone(storageState), + ); saveAccountsMock.mockImplementation(async (nextStorage) => { storageState = structuredClone(nextStorage); }); @@ -2350,9 +2439,11 @@ describe("codex manager cli commands", () => { idToken: "id-best-next", }), ); - expect(logSpy.mock.calls.some((call) => - String(call[0]).includes("Already on best account 1"), - )).toBe(true); + expect( + logSpy.mock.calls.some((call) => + String(call[0]).includes("Already on best account 1"), + ), + ).toBe(true); }); it("reports synced=false in already-best json output when live sync fails", async () => { @@ -2372,7 +2463,9 @@ describe("codex manager cli commands", () => { }, ], }; - loadAccountsMock.mockImplementation(async () => structuredClone(storageState)); + loadAccountsMock.mockImplementation(async () => + structuredClone(storageState), + ); saveAccountsMock.mockImplementation(async (nextStorage) => { storageState = structuredClone(nextStorage); }); @@ -2402,7 +2495,12 @@ describe("codex manager cli commands", () => { const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {}); const { runCodexMultiAuthCli } = await import("../lib/codex-manager.js"); - const exitCode = await runCodexMultiAuthCli(["auth", "best", "--live", "--json"]); + const exitCode = await runCodexMultiAuthCli([ + "auth", + "best", + "--live", + "--json", + ]); expect(exitCode).toBe(0); expect(saveAccountsMock).toHaveBeenCalledTimes(1); @@ -2506,7 +2604,9 @@ describe("codex manager cli commands", () => { }, ], }); - fetchCodexQuotaSnapshotMock.mockRejectedValueOnce(new Error("network timeout")); + fetchCodexQuotaSnapshotMock.mockRejectedValueOnce( + new Error("network timeout"), + ); const logSpy = vi.spyOn(console, "log").mockImplementation(() => {}); const { runCodexMultiAuthCli } = await import("../lib/codex-manager.js"); @@ -2515,15 +2615,21 @@ describe("codex manager cli commands", () => { expect(exitCode).toBe(0); expect(saveAccountsMock).not.toHaveBeenCalled(); expect(setCodexCliActiveSelectionMock).not.toHaveBeenCalled(); - expect(logSpy.mock.calls.some((call) => - String(call[0]).includes("Already on best account 1"), - )).toBe(true); - expect(logSpy.mock.calls.some((call) => - String(call[0]).includes("Live check notes (1)"), - )).toBe(true); - expect(logSpy.mock.calls.some((call) => - String(call[0]).includes("network timeout"), - )).toBe(true); + expect( + logSpy.mock.calls.some((call) => + String(call[0]).includes("Already on best account 1"), + ), + ).toBe(true); + expect( + logSpy.mock.calls.some((call) => + String(call[0]).includes("Live check notes (1)"), + ), + ).toBe(true); + expect( + logSpy.mock.calls.some((call) => + String(call[0]).includes("network timeout"), + ), + ).toBe(true); }); it("reuses the queued refresh result across concurrent live best runs", async () => { @@ -2543,7 +2649,9 @@ describe("codex manager cli commands", () => { }, ], }; - loadAccountsMock.mockImplementation(async () => structuredClone(storageState)); + loadAccountsMock.mockImplementation(async () => + structuredClone(storageState), + ); saveAccountsMock.mockImplementation(async (nextStorage) => { storageState = structuredClone(nextStorage); }); @@ -2591,7 +2699,12 @@ describe("codex manager cli commands", () => { const { runCodexMultiAuthCli } = await import("../lib/codex-manager.js"); const firstRun = runCodexMultiAuthCli(["auth", "best", "--live", "--json"]); - const secondRun = runCodexMultiAuthCli(["auth", "best", "--live", "--json"]); + const secondRun = runCodexMultiAuthCli([ + "auth", + "best", + "--live", + "--json", + ]); refreshDeferred.resolve({ type: "success", @@ -2601,7 +2714,10 @@ describe("codex manager cli commands", () => { idToken: "id-best-next", }); - const [firstExitCode, secondExitCode] = await Promise.all([firstRun, secondRun]); + const [firstExitCode, secondExitCode] = await Promise.all([ + firstRun, + secondRun, + ]); expect(firstExitCode).toBe(0); expect(secondExitCode).toBe(0); @@ -2612,14 +2728,16 @@ describe("codex manager cli commands", () => { expect(storageState.accounts[0]?.refreshToken).toBe("refresh-best-next"); expect(setCodexCliActiveSelectionMock).toHaveBeenCalledTimes(2); for (const call of setCodexCliActiveSelectionMock.mock.calls) { - expect(call[0]).toEqual(expect.objectContaining({ - accountId: "acc_test", - email: "best@example.com", - accessToken: "access-best-next", - refreshToken: "refresh-best-next", - expiresAt: now + 3_600_000, - idToken: "id-best-next", - })); + expect(call[0]).toEqual( + expect.objectContaining({ + accountId: "acc_test", + email: "best@example.com", + accessToken: "access-best-next", + refreshToken: "refresh-best-next", + expiresAt: now + 3_600_000, + idToken: "id-best-next", + }), + ); } expect(logSpy.mock.calls).toHaveLength(2); for (const call of logSpy.mock.calls) { @@ -3022,7 +3140,9 @@ describe("codex manager cli commands", () => { activeIndexByFamily: { codex: 0 }, accounts: [] as Array>, }; - loadAccountsMock.mockImplementation(async () => structuredClone(storageState)); + loadAccountsMock.mockImplementation(async () => + structuredClone(storageState), + ); saveAccountsMock.mockImplementation(async (nextStorage) => { storageState = structuredClone(nextStorage); }); @@ -3064,12 +3184,16 @@ describe("codex manager cli commands", () => { expect(exitCode).toBe(0); expect(openBrowserUrlMock).not.toHaveBeenCalled(); - expect(vi.mocked(serverModule.startLocalOAuthServer)).not.toHaveBeenCalled(); + expect( + vi.mocked(serverModule.startLocalOAuthServer), + ).not.toHaveBeenCalled(); expect(waitForCodeMock).not.toHaveBeenCalled(); - expect(renderedLogs.some((entry) => entry.includes("Manual mode active"))).toBe( - true, - ); - expect(renderedLogs.some((entry) => entry.includes("No callback received"))).toBe(false); + expect( + renderedLogs.some((entry) => entry.includes("Manual mode active")), + ).toBe(true); + expect( + renderedLogs.some((entry) => entry.includes("No callback received")), + ).toBe(false); expect(storageState.accounts).toHaveLength(1); }); @@ -3138,7 +3262,9 @@ describe("codex manager cli commands", () => { mtimeMs: now - 60_000, }, ]); - restoreAccountsFromBackupMock.mockResolvedValue(structuredClone(restoredStorage)); + restoreAccountsFromBackupMock.mockResolvedValue( + structuredClone(restoredStorage), + ); setCodexCliActiveSelectionMock.mockResolvedValueOnce(true); selectMock .mockResolvedValueOnce("restore-backup") @@ -3163,9 +3289,10 @@ describe("codex manager cli commands", () => { expect(signInItems.map((item) => item.label)).toContain( "Recover saved accounts", ); - expect(signInItems.find((item) => item.label === "Recover saved accounts")?.kind).toBe( - "heading", - ); + expect( + signInItems.find((item) => item.label === "Recover saved accounts") + ?.kind, + ).toBe("heading"); expect( signInItems.find((item) => item.label === "Restore Saved Backup")?.hint, ).toBe("last-good.json | 2 accounts | saved Localized Saved Time"); @@ -3180,7 +3307,9 @@ describe("codex manager cli commands", () => { "/mock/backups/last-good.json", { persist: false }, ); - expect(confirmMock).toHaveBeenCalledWith("Load last-good.json (2 accounts)?"); + expect(confirmMock).toHaveBeenCalledWith( + "Load last-good.json (2 accounts)?", + ); expect(saveAccountsMock).toHaveBeenCalledTimes(1); expect(saveAccountsMock.mock.calls[0]?.[0]).toEqual( expect.objectContaining({ @@ -3280,7 +3409,9 @@ describe("codex manager cli commands", () => { expect(exitCode).toBe(0); expect(restoreAccountsFromBackupMock).not.toHaveBeenCalled(); - expect(confirmMock).not.toHaveBeenCalledWith("Load manual-choice.json (1 account)?"); + expect(confirmMock).not.toHaveBeenCalledWith( + "Load manual-choice.json (1 account)?", + ); expect(saveAccountsMock).not.toHaveBeenCalled(); expect(promptLoginModeMock).not.toHaveBeenCalled(); }); @@ -3356,7 +3487,9 @@ describe("codex manager cli commands", () => { expect.any(String), ]); expect(restoreAccountsFromBackupMock).not.toHaveBeenCalled(); - expect(confirmMock).toHaveBeenCalledWith("Load replacement.json (2 accounts)?"); + expect(confirmMock).toHaveBeenCalledWith( + "Load replacement.json (2 accounts)?", + ); }); it("does not offer backup restore on onboarding when accounts already exist", async () => { @@ -3494,7 +3627,9 @@ describe("codex manager cli commands", () => { mtimeMs: now, }, ]); - restoreAccountsFromBackupMock.mockResolvedValue(structuredClone(restoredStorage)); + restoreAccountsFromBackupMock.mockResolvedValue( + structuredClone(restoredStorage), + ); setCodexCliActiveSelectionMock.mockResolvedValueOnce(false); selectMock .mockResolvedValueOnce("restore-backup") @@ -3556,7 +3691,9 @@ describe("codex manager cli commands", () => { expect(saveAccountsMock).not.toHaveBeenCalled(); expect(promptLoginModeMock).not.toHaveBeenCalled(); expect(selectMock).toHaveBeenCalledTimes(3); - expect(errorSpy).toHaveBeenCalledWith("Backup restore failed: File is busy"); + expect(errorSpy).toHaveBeenCalledWith( + "Backup restore failed: File is busy", + ); }); it("prints the storage hint only once when restore fails with StorageError", async () => { @@ -3627,8 +3764,12 @@ describe("codex manager cli commands", () => { mtimeMs: now, }, ]); - restoreAccountsFromBackupMock.mockResolvedValue(structuredClone(restoredStorage)); - saveAccountsMock.mockRejectedValueOnce(new Error("save selected account failed")); + restoreAccountsFromBackupMock.mockResolvedValue( + structuredClone(restoredStorage), + ); + saveAccountsMock.mockRejectedValueOnce( + new Error("save selected account failed"), + ); selectMock .mockResolvedValueOnce("restore-backup") .mockResolvedValueOnce("latest") @@ -3688,7 +3829,9 @@ describe("codex manager cli commands", () => { mtimeMs: now, }, ]); - restoreAccountsFromBackupMock.mockResolvedValue(structuredClone(restoredStorage)); + restoreAccountsFromBackupMock.mockResolvedValue( + structuredClone(restoredStorage), + ); selectMock .mockResolvedValueOnce("restore-backup") .mockResolvedValueOnce("latest"); @@ -3763,7 +3906,9 @@ describe("codex manager cli commands", () => { const exitCode = await runCodexMultiAuthCli(["auth", "login"]); expect(exitCode).toBe(0); - expect(confirmMock).toHaveBeenCalledWith("Load manual-choice.json (1 account)?"); + expect(confirmMock).toHaveBeenCalledWith( + "Load manual-choice.json (1 account)?", + ); expect(restoreAccountsFromBackupMock).not.toHaveBeenCalled(); expect(setCodexCliActiveSelectionMock).not.toHaveBeenCalled(); }); @@ -3830,17 +3975,21 @@ describe("codex manager cli commands", () => { expect(exitCode).toBe(0); expect(selectMock).toHaveBeenCalled(); expect(openBrowserUrlMock).not.toHaveBeenCalled(); - expect(vi.mocked(serverModule.startLocalOAuthServer)).not.toHaveBeenCalled(); + expect( + vi.mocked(serverModule.startLocalOAuthServer), + ).not.toHaveBeenCalled(); expect(waitForCodeMock).not.toHaveBeenCalled(); const signInItems = selectMock.mock.calls[0]?.[0] as Array<{ label: string; value?: string; }>; expect(signInItems.some((item) => item.value === "manual")).toBe(true); - expect(renderedLogs.some((entry) => entry.includes("Manual mode active"))).toBe( - true, - ); - expect(renderedLogs.some((entry) => entry.includes("No callback received"))).toBe(false); + expect( + renderedLogs.some((entry) => entry.includes("Manual mode active")), + ).toBe(true); + expect( + renderedLogs.some((entry) => entry.includes("No callback received")), + ).toBe(false); expect(logSpy).toHaveBeenCalledWith("Refreshed account 1."); }); @@ -3853,7 +4002,9 @@ describe("codex manager cli commands", () => { activeIndexByFamily: { codex: 0 }, accounts: [] as Array>, }; - loadAccountsMock.mockImplementation(async () => structuredClone(storageState)); + loadAccountsMock.mockImplementation(async () => + structuredClone(storageState), + ); saveAccountsMock.mockImplementation(async (nextStorage) => { storageState = structuredClone(nextStorage); }); @@ -3881,9 +4032,13 @@ describe("codex manager cli commands", () => { const browserModule = await import("../lib/auth/browser.js"); const openBrowserUrlMock = vi.mocked(browserModule.openBrowserUrl); - vi.mocked(browserModule.isBrowserLaunchSuppressed).mockReturnValueOnce(true); + vi.mocked(browserModule.isBrowserLaunchSuppressed).mockReturnValueOnce( + true, + ); const serverModule = await import("../lib/auth/server.js"); - const startLocalOAuthServerMock = vi.mocked(serverModule.startLocalOAuthServer); + const startLocalOAuthServerMock = vi.mocked( + serverModule.startLocalOAuthServer, + ); const { runCodexMultiAuthCli } = await import("../lib/codex-manager.js"); const exitCode = await runCodexMultiAuthCli(["auth", "login"]); @@ -3904,7 +4059,9 @@ describe("codex manager cli commands", () => { activeIndexByFamily: { codex: 0 }, accounts: [] as Array>, }; - loadAccountsMock.mockImplementation(async () => structuredClone(storageState)); + loadAccountsMock.mockImplementation(async () => + structuredClone(storageState), + ); saveAccountsMock.mockImplementation(async (nextStorage) => { storageState = structuredClone(nextStorage); }); @@ -3938,7 +4095,9 @@ describe("codex manager cli commands", () => { expect(exitCode).toBe(0); expect(promptQuestionMock).toHaveBeenCalledWith(""); expect(openBrowserUrlMock).not.toHaveBeenCalled(); - expect(vi.mocked(serverModule.startLocalOAuthServer)).not.toHaveBeenCalled(); + expect( + vi.mocked(serverModule.startLocalOAuthServer), + ).not.toHaveBeenCalled(); expect(storageState.accounts).toHaveLength(1); }); @@ -3950,7 +4109,9 @@ describe("codex manager cli commands", () => { activeIndexByFamily: { codex: 0 }, accounts: [] as Array>, }; - loadAccountsMock.mockImplementation(async () => structuredClone(storageState)); + loadAccountsMock.mockImplementation(async () => + structuredClone(storageState), + ); saveAccountsMock.mockImplementation(async (nextStorage) => { storageState = structuredClone(nextStorage); }); @@ -3965,7 +4126,9 @@ describe("codex manager cli commands", () => { state: "oauth-state", url: "https://auth.openai.com/mock", }); - const exchangeAuthorizationCodeMock = vi.mocked(authModule.exchangeAuthorizationCode); + const exchangeAuthorizationCodeMock = vi.mocked( + authModule.exchangeAuthorizationCode, + ); const browserModule = await import("../lib/auth/browser.js"); const openBrowserUrlMock = vi.mocked(browserModule.openBrowserUrl); @@ -3977,7 +4140,9 @@ describe("codex manager cli commands", () => { expect(exitCode).toBe(0); expect(promptQuestionMock).toHaveBeenCalledWith(""); expect(openBrowserUrlMock).not.toHaveBeenCalled(); - expect(vi.mocked(serverModule.startLocalOAuthServer)).not.toHaveBeenCalled(); + expect( + vi.mocked(serverModule.startLocalOAuthServer), + ).not.toHaveBeenCalled(); expect(exchangeAuthorizationCodeMock).not.toHaveBeenCalled(); expect(storageState.accounts).toHaveLength(0); }); @@ -3991,7 +4156,9 @@ describe("codex manager cli commands", () => { activeIndexByFamily: { codex: 0 }, accounts: [] as Array>, }; - loadAccountsMock.mockImplementation(async () => structuredClone(storageState)); + loadAccountsMock.mockImplementation(async () => + structuredClone(storageState), + ); saveAccountsMock.mockImplementation(async (nextStorage) => { storageState = structuredClone(nextStorage); }); @@ -4025,7 +4192,9 @@ describe("codex manager cli commands", () => { expect(exitCode).toBe(0); expect(promptQuestionMock).toHaveBeenCalledWith(""); expect(openBrowserUrlMock).not.toHaveBeenCalled(); - expect(vi.mocked(serverModule.startLocalOAuthServer)).not.toHaveBeenCalled(); + expect( + vi.mocked(serverModule.startLocalOAuthServer), + ).not.toHaveBeenCalled(); expect(storageState.accounts).toHaveLength(1); }); @@ -4037,7 +4206,9 @@ describe("codex manager cli commands", () => { activeIndexByFamily: { codex: 0 }, accounts: [] as Array>, }; - loadAccountsMock.mockImplementation(async () => structuredClone(storageState)); + loadAccountsMock.mockImplementation(async () => + structuredClone(storageState), + ); saveAccountsMock.mockImplementation(async (nextStorage) => { storageState = structuredClone(nextStorage); }); @@ -4050,7 +4221,9 @@ describe("codex manager cli commands", () => { state: "oauth-state", url: "https://auth.openai.com/mock", }); - const exchangeAuthorizationCodeMock = vi.mocked(authModule.exchangeAuthorizationCode); + const exchangeAuthorizationCodeMock = vi.mocked( + authModule.exchangeAuthorizationCode, + ); const browserModule = await import("../lib/auth/browser.js"); const openBrowserUrlMock = vi.mocked(browserModule.openBrowserUrl); @@ -4062,7 +4235,9 @@ describe("codex manager cli commands", () => { expect(exitCode).toBe(0); expect(promptQuestionMock).toHaveBeenCalledWith(""); expect(openBrowserUrlMock).not.toHaveBeenCalled(); - expect(vi.mocked(serverModule.startLocalOAuthServer)).not.toHaveBeenCalled(); + expect( + vi.mocked(serverModule.startLocalOAuthServer), + ).not.toHaveBeenCalled(); expect(exchangeAuthorizationCodeMock).not.toHaveBeenCalled(); expect(storageState.accounts).toHaveLength(0); }); @@ -4108,7 +4283,9 @@ describe("codex manager cli commands", () => { mtimeMs: now - 60_000, }, ]); - restoreAccountsFromBackupMock.mockResolvedValue(structuredClone(restoredStorage)); + restoreAccountsFromBackupMock.mockResolvedValue( + structuredClone(restoredStorage), + ); setCodexCliActiveSelectionMock.mockResolvedValueOnce(true); selectMock .mockResolvedValueOnce("restore-backup") @@ -4130,7 +4307,9 @@ describe("codex manager cli commands", () => { "/mock/backups/manual-choice.json", { persist: false }, ); - expect(confirmMock).toHaveBeenCalledWith("Load manual-choice.json (1 account)?"); + expect(confirmMock).toHaveBeenCalledWith( + "Load manual-choice.json (1 account)?", + ); expect(saveAccountsMock).toHaveBeenCalledTimes(1); expect(setCodexCliActiveSelectionMock).toHaveBeenCalledWith( expect.objectContaining({ @@ -4186,7 +4365,9 @@ describe("codex manager cli commands", () => { mtimeMs: now, }, ]); - restoreAccountsFromBackupMock.mockResolvedValue(structuredClone(restoredStorage)); + restoreAccountsFromBackupMock.mockResolvedValue( + structuredClone(restoredStorage), + ); setCodexCliActiveSelectionMock.mockResolvedValueOnce(true); selectMock .mockResolvedValueOnce("restore-backup") @@ -4242,9 +4423,7 @@ describe("codex manager cli commands", () => { mtimeMs: now, }, ]); - selectMock - .mockResolvedValueOnce("browser") - .mockResolvedValueOnce("cancel"); + selectMock.mockResolvedValueOnce("browser").mockResolvedValueOnce("cancel"); promptAddAnotherAccountMock.mockResolvedValueOnce(true); promptLoginModeMock.mockResolvedValueOnce({ mode: "cancel" }); @@ -4292,12 +4471,12 @@ describe("codex manager cli commands", () => { label: string; value?: string; }>; - expect(firstSignInItems.some((item) => item.value === "restore-backup")).toBe( - true, - ); - expect(secondSignInItems.some((item) => item.value === "restore-backup")).toBe( - false, - ); + expect( + firstSignInItems.some((item) => item.value === "restore-backup"), + ).toBe(true); + expect( + secondSignInItems.some((item) => item.value === "restore-backup"), + ).toBe(false); expect(promptLoginModeMock).toHaveBeenCalledTimes(1); }); it("preserves distinct same-email workspaces when oauth login reuses a refresh token", async () => { @@ -4908,13 +5087,11 @@ describe("codex manager cli commands", () => { }); const accountsModule = await import("../lib/accounts.js"); const extractAccountIdMock = vi.mocked(accountsModule.extractAccountId); - extractAccountIdMock.mockImplementation( - (accessToken?: string) => { - if (accessToken === "access-alpha") return "workspace-alpha"; - if (accessToken === "access-beta") return "workspace-beta"; - return "acc_test"; - }, - ); + extractAccountIdMock.mockImplementation((accessToken?: string) => { + if (accessToken === "access-alpha") return "workspace-alpha"; + if (accessToken === "access-beta") return "workspace-beta"; + return "acc_test"; + }); promptLoginModeMock.mockResolvedValueOnce({ mode: "cancel" }); const { runCodexMultiAuthCli } = await import("../lib/codex-manager.js"); @@ -4993,13 +5170,11 @@ describe("codex manager cli commands", () => { }); const accountsModule = await import("../lib/accounts.js"); const extractAccountIdMock = vi.mocked(accountsModule.extractAccountId); - extractAccountIdMock.mockImplementation( - (accessToken?: string) => { - if (accessToken === "access-alpha") return "workspace-alpha"; - if (accessToken === "access-beta") return "workspace-beta"; - return "acc_test"; - }, - ); + extractAccountIdMock.mockImplementation((accessToken?: string) => { + if (accessToken === "access-alpha") return "workspace-alpha"; + if (accessToken === "access-beta") return "workspace-beta"; + return "acc_test"; + }); fetchCodexQuotaSnapshotMock .mockResolvedValueOnce({ status: 200, @@ -5745,9 +5920,7 @@ describe("codex manager cli commands", () => { const exitCode = await runCodexMultiAuthCli(["auth", "login"]); expect(exitCode).toBe(0); - expect(readSettingsHubPanelContract()).toEqual( - SETTINGS_HUB_MENU_ORDER, - ); + expect(readSettingsHubPanelContract()).toEqual(SETTINGS_HUB_MENU_ORDER); expect(selectSequence.remaining()).toBe(0); expect(saveDashboardDisplaySettingsMock).toHaveBeenCalled(); expect(savePluginConfigMock).toHaveBeenCalledTimes(1); @@ -5774,7 +5947,17 @@ describe("codex manager cli commands", () => { it("runs experimental oc sync with mandatory preview before apply", async () => { const now = Date.now(); setupInteractiveSettingsLogin(createSettingsStorage(now)); - detectOcChatgptMultiAuthTargetMock.mockReturnValue({ kind: "target", descriptor: { scope: "global", root: "C:/target", accountPath: "C:/target/openai-codex-accounts.json", backupRoot: "C:/target/backups", source: "default-global", resolution: "accounts" } }); + detectOcChatgptMultiAuthTargetMock.mockReturnValue({ + kind: "target", + descriptor: { + scope: "global", + root: "C:/target", + accountPath: "C:/target/openai-codex-accounts.json", + backupRoot: "C:/target/backups", + source: "default-global", + resolution: "accounts", + }, + }); planOcChatgptSyncMock.mockResolvedValue({ kind: "ready", target: { @@ -5830,7 +6013,11 @@ describe("codex manager cli commands", () => { expect(applyOcChatgptSyncMock).toHaveBeenCalledOnce(); expect(selectMock).toHaveBeenCalledWith( expect.arrayContaining([ - expect.objectContaining({ label: expect.stringContaining("Active selection: preserve-destination") }), + expect.objectContaining({ + label: expect.stringContaining( + "Active selection: preserve-destination", + ), + }), ]), expect.any(Object), ); @@ -5908,10 +6095,24 @@ describe("codex manager cli commands", () => { it("shows guidance when experimental oc sync target is ambiguous or unreadable", async () => { const now = Date.now(); setupInteractiveSettingsLogin(createSettingsStorage(now)); - detectOcChatgptMultiAuthTargetMock.mockReturnValue({ kind: "target", descriptor: { scope: "global", root: "C:/target", accountPath: "C:/target/openai-codex-accounts.json", backupRoot: "C:/target/backups", source: "default-global", resolution: "accounts" } }); + detectOcChatgptMultiAuthTargetMock.mockReturnValue({ + kind: "target", + descriptor: { + scope: "global", + root: "C:/target", + accountPath: "C:/target/openai-codex-accounts.json", + backupRoot: "C:/target/backups", + source: "default-global", + resolution: "accounts", + }, + }); planOcChatgptSyncMock.mockResolvedValue({ kind: "blocked-ambiguous", - detection: { kind: "ambiguous", reason: "multiple targets", candidates: [] }, + detection: { + kind: "ambiguous", + reason: "multiple targets", + candidates: [], + }, }); const selectSequence = queueSettingsSelectSequence([ { type: "experimental" }, @@ -5930,12 +6131,14 @@ describe("codex manager cli commands", () => { expect(applyOcChatgptSyncMock).not.toHaveBeenCalled(); }); - it("exports named pool backup from experimental settings", async () => { const now = Date.now(); setupInteractiveSettingsLogin(createSettingsStorage(now)); promptQuestionMock.mockResolvedValueOnce("backup-2026-03-10"); - runNamedBackupExportMock.mockResolvedValueOnce({ kind: "exported", path: "/mock/backups/backup-2026-03-10.json" }); + runNamedBackupExportMock.mockResolvedValueOnce({ + kind: "exported", + path: "/mock/backups/backup-2026-03-10.json", + }); const selectSequence = queueSettingsSelectSequence([ { type: "experimental" }, { type: "backup" }, @@ -5950,7 +6153,9 @@ describe("codex manager cli commands", () => { expect(exitCode).toBe(0); expect(selectSequence.remaining()).toBe(0); expect(promptQuestionMock).toHaveBeenCalledOnce(); - expect(runNamedBackupExportMock).toHaveBeenCalledWith({ name: "backup-2026-03-10" }); + expect(runNamedBackupExportMock).toHaveBeenCalledWith({ + name: "backup-2026-03-10", + }); }); it("supports backup hotkeys from experimental menu through result status", async () => { @@ -5984,7 +6189,10 @@ describe("codex manager cli commands", () => { const now = Date.now(); setupInteractiveSettingsLogin(createSettingsStorage(now)); promptQuestionMock.mockResolvedValueOnce("../bad-name"); - runNamedBackupExportMock.mockResolvedValueOnce({ kind: "collision", path: "/mock/backups/bad-name.json" }); + runNamedBackupExportMock.mockResolvedValueOnce({ + kind: "collision", + path: "/mock/backups/bad-name.json", + }); const selectSequence = queueSettingsSelectSequence([ { type: "experimental" }, { type: "backup" }, @@ -5999,18 +6207,49 @@ describe("codex manager cli commands", () => { expect(exitCode).toBe(0); expect(selectSequence.remaining()).toBe(0); expect(promptQuestionMock).toHaveBeenCalledOnce(); - expect(runNamedBackupExportMock).toHaveBeenCalledWith({ name: "../bad-name" }); + expect(runNamedBackupExportMock).toHaveBeenCalledWith({ + name: "../bad-name", + }); }); it("backs out of experimental sync preview without applying", async () => { const now = Date.now(); setupInteractiveSettingsLogin(createSettingsStorage(now)); - detectOcChatgptMultiAuthTargetMock.mockReturnValue({ kind: "target", descriptor: { scope: "global", root: "C:/target", accountPath: "C:/target/openai-codex-accounts.json", backupRoot: "C:/target/backups", source: "default-global", resolution: "accounts" } }); - normalizeAccountStorageMock.mockReturnValue({ version: 3, accounts: [], activeIndex: 0 }); + detectOcChatgptMultiAuthTargetMock.mockReturnValue({ + kind: "target", + descriptor: { + scope: "global", + root: "C:/target", + accountPath: "C:/target/openai-codex-accounts.json", + backupRoot: "C:/target/backups", + source: "default-global", + resolution: "accounts", + }, + }); + normalizeAccountStorageMock.mockReturnValue({ + version: 3, + accounts: [], + activeIndex: 0, + }); planOcChatgptSyncMock.mockResolvedValue({ kind: "ready", - target: { scope: "global", root: "C:/target", accountPath: "C:/target/openai-codex-accounts.json", backupRoot: "C:/target/backups", source: "default-global", resolution: "accounts" }, - preview: { payload: { version: 3, accounts: [], activeIndex: 0 }, merged: { version: 3, accounts: [], activeIndex: 0 }, toAdd: [], toUpdate: [], toSkip: [], unchangedDestinationOnly: [], activeSelectionBehavior: "preserve-destination" }, + target: { + scope: "global", + root: "C:/target", + accountPath: "C:/target/openai-codex-accounts.json", + backupRoot: "C:/target/backups", + source: "default-global", + resolution: "accounts", + }, + preview: { + payload: { version: 3, accounts: [], activeIndex: 0 }, + merged: { version: 3, accounts: [], activeIndex: 0 }, + toAdd: [], + toUpdate: [], + toSkip: [], + unchangedDestinationOnly: [], + activeSelectionBehavior: "preserve-destination", + }, payload: { version: 3, accounts: [], activeIndex: 0 }, destination: { version: 3, accounts: [], activeIndex: 0 }, }); @@ -6078,7 +6317,11 @@ describe("codex manager cli commands", () => { }); planOcChatgptSyncMock.mockResolvedValue({ kind: "blocked-ambiguous", - detection: { kind: "ambiguous", reason: "multiple targets", candidates: [] }, + detection: { + kind: "ambiguous", + reason: "multiple targets", + candidates: [], + }, }); const selectSequence = queueSettingsSelectSequence([ { type: "experimental" }, @@ -6237,9 +6480,7 @@ describe("codex manager cli commands", () => { const exitCode = await runCodexMultiAuthCli(["auth", "login"]); expect(exitCode).toBe(0); - expect(readSettingsHubPanelContract()).toEqual( - SETTINGS_HUB_MENU_ORDER, - ); + expect(readSettingsHubPanelContract()).toEqual(SETTINGS_HUB_MENU_ORDER); expect(selectSequence.remaining()).toBe(0); expect(saveDashboardDisplaySettingsMock).toHaveBeenCalledTimes(4); expect(saveDashboardDisplaySettingsMock.mock.calls[0]?.[0]).toEqual( @@ -6276,7 +6517,6 @@ describe("codex manager cli commands", () => { ); }); - it("moves guardian controls into experimental settings", async () => { const now = Date.now(); setupInteractiveSettingsLogin(createSettingsStorage(now)); @@ -6299,7 +6539,8 @@ describe("codex manager cli commands", () => { expect(savePluginConfigMock).toHaveBeenCalledWith( expect.objectContaining({ proactiveRefreshGuardian: !(defaults.proactiveRefreshGuardian ?? false), - proactiveRefreshIntervalMs: (defaults.proactiveRefreshIntervalMs ?? 60000) + 60000, + proactiveRefreshIntervalMs: + (defaults.proactiveRefreshIntervalMs ?? 60000) + 60000, }), ); }); @@ -6359,8 +6600,7 @@ describe("codex manager cli commands", () => { preemptiveQuotaRemainingPercent5h: (defaults.preemptiveQuotaRemainingPercent5h ?? 0) + 1, storageBackupEnabled: !(defaults.storageBackupEnabled ?? false), - tokenRefreshSkewMs: - (defaults.tokenRefreshSkewMs ?? 60_000) + 10_000, + tokenRefreshSkewMs: (defaults.tokenRefreshSkewMs ?? 60_000) + 10_000, parallelProbing: !(defaults.parallelProbing ?? false), fetchTimeoutMs: (defaults.fetchTimeoutMs ?? 60_000) + 5_000, }), @@ -7121,7 +7361,8 @@ describe("codex manager cli commands", () => { ); vi.mocked(accountsModule.extractAccountEmail).mockImplementation( (accessToken?: string) => { - if (accessToken === "access-alpha-refreshed") return "owner@example.com"; + if (accessToken === "access-alpha-refreshed") + return "owner@example.com"; return undefined; }, ); @@ -7243,21 +7484,19 @@ describe("codex manager cli commands", () => { }); const accountsModule = await import("../lib/accounts.js"); const extractAccountIdMock = vi.mocked(accountsModule.extractAccountId); - const extractAccountEmailMock = vi.mocked(accountsModule.extractAccountEmail); - extractAccountIdMock.mockImplementation( - (accessToken?: string) => { - if (accessToken === "access-alpha-stale") return "shared-workspace"; - if (accessToken === "access-alpha-refreshed") return "shared-workspace"; - if (accessToken === "access-beta") return "shared-workspace"; - return "acc_test"; - }, - ); - extractAccountEmailMock.mockImplementation( - (accessToken?: string) => { - if (accessToken === "access-alpha-refreshed") return "owner@example.com"; - return undefined; - }, + const extractAccountEmailMock = vi.mocked( + accountsModule.extractAccountEmail, ); + extractAccountIdMock.mockImplementation((accessToken?: string) => { + if (accessToken === "access-alpha-stale") return "shared-workspace"; + if (accessToken === "access-alpha-refreshed") return "shared-workspace"; + if (accessToken === "access-beta") return "shared-workspace"; + return "acc_test"; + }); + extractAccountEmailMock.mockImplementation((accessToken?: string) => { + if (accessToken === "access-alpha-refreshed") return "owner@example.com"; + return undefined; + }); fetchCodexQuotaSnapshotMock .mockResolvedValueOnce({ status: 200, @@ -7419,7 +7658,8 @@ describe("codex manager cli commands", () => { ); vi.mocked(accountsModule.extractAccountEmail).mockImplementation( (accessToken?: string) => { - if (accessToken === "access-alpha-refreshed") return "owner@example.com"; + if (accessToken === "access-alpha-refreshed") + return "owner@example.com"; return undefined; }, ); From 3aa75203069fad99587c867f92983170923d7330 Mon Sep 17 00:00:00 2001 From: ndycode Date: Sat, 21 Mar 2026 05:02:35 +0800 Subject: [PATCH 2/4] test: cover disabled rate-limited status output --- test/codex-manager-cli.test.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/test/codex-manager-cli.test.ts b/test/codex-manager-cli.test.ts index 6a6a824d..24200c28 100644 --- a/test/codex-manager-cli.test.ts +++ b/test/codex-manager-cli.test.ts @@ -633,9 +633,7 @@ describe("codex manager cli commands", () => { addedAt: now - 2_000, lastUsed: now - 500, enabled: false, - rateLimits: { - codex_rpm: { remaining: 0, resetAt: now + 60_000 }, - }, + rateLimitResetTimes: { codex: now + 60_000 }, }, ], }); @@ -650,7 +648,7 @@ describe("codex manager cli commands", () => { expect.stringContaining("1. 1. active@example.com [current]"), ); expect(logSpy).toHaveBeenCalledWith( - expect.stringContaining("2. 2. disabled@example.com [disabled]"), + expect.stringContaining("2. 2. disabled@example.com [disabled, rate-limited]"), ); }); From 3cc6b4d99278e0a1fb714448bf5af93b8cb2ae0e Mon Sep 17 00:00:00 2001 From: ndycode Date: Sat, 21 Mar 2026 12:08:48 +0800 Subject: [PATCH 3/4] add missing auth status assertions --- test/codex-manager-cli.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/codex-manager-cli.test.ts b/test/codex-manager-cli.test.ts index 24200c28..99f9bff6 100644 --- a/test/codex-manager-cli.test.ts +++ b/test/codex-manager-cli.test.ts @@ -643,7 +643,11 @@ describe("codex manager cli commands", () => { const exitCode = await runCodexMultiAuthCli(["auth", "status"]); expect(exitCode).toBe(0); + expect(setStoragePathMock).toHaveBeenCalledWith(null); expect(logSpy).toHaveBeenCalledWith("Accounts (2)"); + expect(logSpy).toHaveBeenCalledWith( + "Storage: /mock/openai-codex-accounts.json", + ); expect(logSpy).toHaveBeenCalledWith( expect.stringContaining("1. 1. active@example.com [current]"), ); From d6eb35d020e9647a92c650bce7ef35ac1a1bf76e Mon Sep 17 00:00:00 2001 From: ndycode Date: Sat, 21 Mar 2026 12:19:16 +0800 Subject: [PATCH 4/4] align auth status test labels with cli output --- test/codex-manager-cli.test.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/test/codex-manager-cli.test.ts b/test/codex-manager-cli.test.ts index 99f9bff6..f0f6b13b 100644 --- a/test/codex-manager-cli.test.ts +++ b/test/codex-manager-cli.test.ts @@ -82,7 +82,9 @@ vi.mock("../lib/accounts.js", () => ({ extractAccountEmail: vi.fn(() => undefined), extractAccountId: vi.fn(() => "acc_test"), formatAccountLabel: vi.fn((account: { email?: string }, index: number) => - account.email ? `${index + 1}. ${account.email}` : `Account ${index + 1}`, + account.email + ? `Account ${index + 1} (${account.email})` + : `Account ${index + 1}`, ), formatCooldown: vi.fn(() => null), formatWaitTime: vi.fn( @@ -649,10 +651,12 @@ describe("codex manager cli commands", () => { "Storage: /mock/openai-codex-accounts.json", ); expect(logSpy).toHaveBeenCalledWith( - expect.stringContaining("1. 1. active@example.com [current]"), + expect.stringContaining("1. Account 1 (active@example.com) [current]"), ); expect(logSpy).toHaveBeenCalledWith( - expect.stringContaining("2. 2. disabled@example.com [disabled, rate-limited]"), + expect.stringContaining( + "2. Account 2 (disabled@example.com) [disabled, rate-limited]", + ), ); });