Skip to content

Commit 8e280ce

Browse files
committed
test: migrate the deferred small suites to the shared mock factories
Follow-up to the §4.4.2 fixture extraction: migrate the five small deferred suites onto test/helpers/cli-test-fixtures.ts where the mock shapes match, with zero test-semantics change (274/274 across the five migrated suites plus the five canary suites already migrated, identical counts and failure lists before and after via stash comparison). - index-retry: the storage vi.mock now delegates to storageModuleMock with the suite's bespoke stub overrides kept inline (no test asserts on them). Left inline: accounts.js/config.js (divergent shapes, deliberately not force-unified), fetch-helpers, request-transformer, wait-utils, recovery, update-notice and the plugin tool shim (no helper counterparts). - accounts-edge: storage mocks unified via pickMocks(createStorageMocks) + storageModuleMock; codex-cli writer via createCodexCliWriterMocks + codexCliWriterModuleMock. Left inline: codex-cli state (this suite needs the actual module spread so isCodexCliSyncEnabled and friends stay real, diverging from the helper's full-replacement shape), codex-cli sync and rotation (no helper counterparts), and the local storage fixture builders (no activeIndexByFamily plus past timestamps, deliberately different from accountStorageV3Fixture). - accounts-load-from-disk: storage module shape delegates to storageModuleMock; codex-cli state/writer shapes delegate to the shared factories created inside the vi.mock factories. The vi.fn instances stay in vi.hoisted because the suite imports the accounts module statically, so the hoisted factories run before module-level consts would initialize. Left inline: codex-cli sync (no helper). - issue-474-pin-end-to-end: storage module shape delegates to storageModuleMock; instances stay in vi.hoisted for the same static-import reason. The fully typed createStorage builder stays local (the minimal as-never fixture builder would weaken its typing). - dashboard-display-panel: select mock unified via createUiPromptMocks + uiSelectModuleMock; the panel module is now imported lazily inside the tests (the canonical fixture pattern) so the module-level mock group stays hoisting-safe. Left inline: ui/runtime getUiRuntimeOptions (no helper counterpart). https://claude.ai/code/session_01XNtnkLbBiXZxfQQYLMpucB
1 parent 1715116 commit 8e280ce

5 files changed

Lines changed: 101 additions & 62 deletions

test/accounts-edge.test.ts

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,40 @@
11
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
22
import type { OAuthAuthDetails } from "../lib/types.js";
3-
4-
const mockLoadAccounts = vi.fn();
5-
const mockSaveAccounts = vi.fn();
6-
const mockWithAccountStorageTransaction = vi.fn();
3+
import {
4+
createCodexCliWriterMocks,
5+
createStorageMocks,
6+
pickMocks,
7+
} from "./helpers/cli-test-fixtures.js";
8+
9+
// Shared mock groups (test/helpers/cli-test-fixtures.ts); the vi.mock
10+
// factories below resolve the helper lazily so hoisting stays safe (the
11+
// accounts module is only ever imported dynamically via
12+
// importAccountsModule). Storage is narrowed to the exact set this suite
13+
// used to override. The codex-cli state mock stays inline: this suite needs
14+
// the actual module spread (isCodexCliSyncEnabled and friends must stay
15+
// real), which diverges from the helper's full-replacement shape.
16+
const storageMocks = pickMocks(createStorageMocks(), [
17+
"loadAccounts",
18+
"saveAccounts",
19+
"withAccountStorageTransaction",
20+
]);
21+
const codexCliWriterMocks = createCodexCliWriterMocks();
22+
const {
23+
loadAccounts: mockLoadAccounts,
24+
saveAccounts: mockSaveAccounts,
25+
withAccountStorageTransaction: mockWithAccountStorageTransaction,
26+
} = storageMocks;
27+
const { setCodexCliActiveSelection: mockSetCodexCliActiveSelection } =
28+
codexCliWriterMocks;
729
const mockLoadCodexCliState = vi.fn();
830
const mockSyncAccountStorageFromCodexCli = vi.fn();
9-
const mockSetCodexCliActiveSelection = vi.fn();
1031
const mockSelectHybridAccount = vi.fn();
1132

12-
vi.mock("../lib/storage.js", async (importOriginal) => {
13-
const actual = await importOriginal<typeof import("../lib/storage.js")>();
14-
return {
15-
...actual,
16-
loadAccounts: mockLoadAccounts,
17-
saveAccounts: mockSaveAccounts,
18-
withAccountStorageTransaction: mockWithAccountStorageTransaction,
19-
};
20-
});
33+
vi.mock("../lib/storage.js", async () =>
34+
(await import("./helpers/cli-test-fixtures.js")).storageModuleMock(
35+
storageMocks,
36+
),
37+
);
2138

2239
vi.mock("../lib/codex-cli/state.js", async (importOriginal) => {
2340
const actual =
@@ -32,9 +49,11 @@ vi.mock("../lib/codex-cli/sync.js", () => ({
3249
syncAccountStorageFromCodexCli: mockSyncAccountStorageFromCodexCli,
3350
}));
3451

35-
vi.mock("../lib/codex-cli/writer.js", () => ({
36-
setCodexCliActiveSelection: mockSetCodexCliActiveSelection,
37-
}));
52+
vi.mock("../lib/codex-cli/writer.js", async () =>
53+
(await import("./helpers/cli-test-fixtures.js")).codexCliWriterModuleMock(
54+
codexCliWriterMocks,
55+
),
56+
);
3857

3958
vi.mock("../lib/rotation.js", async (importOriginal) => {
4059
const actual = await importOriginal<typeof import("../lib/rotation.js")>();

test/accounts-load-from-disk.test.ts

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import { beforeEach, describe, expect, it, vi } from "vitest";
22
import { AccountManager } from "../lib/accounts.js";
33

4+
// This suite imports the accounts module (and the mocked modules) statically,
5+
// so the hoisted vi.mock factories run before the test module body evaluates.
6+
// The mock instances therefore stay in vi.hoisted; the module shapes delegate
7+
// to the shared factories (test/helpers/cli-test-fixtures.ts), which the
8+
// factories resolve lazily via dynamic import.
49
const {
510
mockLoadAccounts,
611
mockSaveAccounts,
@@ -11,27 +16,29 @@ const {
1116
mockWithAccountStorageTransaction: vi.fn(),
1217
}));
1318

14-
vi.mock("../lib/storage.js", async (importOriginal) => {
15-
const actual = await importOriginal<typeof import("../lib/storage.js")>();
16-
return {
17-
...actual,
19+
vi.mock("../lib/storage.js", async () =>
20+
(await import("./helpers/cli-test-fixtures.js")).storageModuleMock({
1821
loadAccounts: mockLoadAccounts,
1922
saveAccounts: mockSaveAccounts,
2023
withAccountStorageTransaction: mockWithAccountStorageTransaction,
21-
};
22-
});
24+
}),
25+
);
2326

2427
vi.mock("../lib/codex-cli/sync.js", () => ({
2528
syncAccountStorageFromCodexCli: vi.fn(),
2629
}));
2730

28-
vi.mock("../lib/codex-cli/state.js", () => ({
29-
loadCodexCliState: vi.fn(),
30-
}));
31+
vi.mock("../lib/codex-cli/state.js", async () => {
32+
const fixtures = await import("./helpers/cli-test-fixtures.js");
33+
return fixtures.codexCliStateModuleMock(fixtures.createCodexCliStateMocks());
34+
});
3135

32-
vi.mock("../lib/codex-cli/writer.js", () => ({
33-
setCodexCliActiveSelection: vi.fn().mockResolvedValue(undefined),
34-
}));
36+
vi.mock("../lib/codex-cli/writer.js", async () => {
37+
const fixtures = await import("./helpers/cli-test-fixtures.js");
38+
return fixtures.codexCliWriterModuleMock(
39+
fixtures.createCodexCliWriterMocks(),
40+
);
41+
});
3542

3643
import { syncAccountStorageFromCodexCli } from "../lib/codex-cli/sync.js";
3744
import { loadCodexCliState } from "../lib/codex-cli/state.js";

test/dashboard-display-panel.test.ts

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,35 @@ import {
44
DEFAULT_DASHBOARD_DISPLAY_SETTINGS,
55
} from "../lib/dashboard-settings.js";
66
import { UI_COPY } from "../lib/ui/copy.js";
7-
import {
8-
type DashboardDisplayPanelDeps,
9-
promptDashboardDisplayPanel,
10-
} from "../lib/codex-manager/dashboard-display-panel.js";
11-
12-
const { selectMock, getUiRuntimeOptionsMock } = vi.hoisted(() => ({
13-
selectMock: vi.fn(),
14-
getUiRuntimeOptionsMock: vi.fn(() => ({ theme: "test-theme" })),
15-
}));
16-
17-
vi.mock("../lib/ui/select.js", () => ({
18-
select: selectMock,
19-
}));
7+
import type { DashboardDisplayPanelDeps } from "../lib/codex-manager/dashboard-display-panel.js";
8+
import { createUiPromptMocks } from "./helpers/cli-test-fixtures.js";
9+
10+
// Shared ui-prompt mock group (test/helpers/cli-test-fixtures.ts). The panel
11+
// module is only imported lazily inside promptPanel below, so the hoisted
12+
// vi.mock factories never run before these module-level consts initialize.
13+
// The ui/runtime mock stays inline: the helper has no factory for it.
14+
const uiMocks = createUiPromptMocks();
15+
const { select: selectMock } = uiMocks;
16+
const getUiRuntimeOptionsMock = vi.fn(() => ({ theme: "test-theme" }));
17+
18+
vi.mock("../lib/ui/select.js", async () =>
19+
(await import("./helpers/cli-test-fixtures.js")).uiSelectModuleMock(uiMocks),
20+
);
2021

2122
vi.mock("../lib/ui/runtime.js", () => ({
2223
getUiRuntimeOptions: getUiRuntimeOptionsMock,
2324
}));
2425

26+
/** Lazily import the panel under test so the mocks above are wired first. */
27+
async function promptPanel(
28+
settings: DashboardDisplaySettings,
29+
): Promise<DashboardDisplaySettings | null> {
30+
const { promptDashboardDisplayPanel } = await import(
31+
"../lib/codex-manager/dashboard-display-panel.js"
32+
);
33+
return promptDashboardDisplayPanel(settings, buildDeps());
34+
}
35+
2536
const stdinIsTTYDescriptor = Object.getOwnPropertyDescriptor(
2637
process.stdin,
2738
"isTTY",
@@ -146,7 +157,7 @@ describe("promptDashboardDisplayPanel", () => {
146157
it("returns null without TTY access", async () => {
147158
setInteractiveTTY(false);
148159

149-
const result = await promptDashboardDisplayPanel(createSettings(), buildDeps());
160+
const result = await promptPanel(createSettings());
150161

151162
expect(result).toBeNull();
152163
expect(selectMock).not.toHaveBeenCalled();
@@ -160,7 +171,7 @@ describe("promptDashboardDisplayPanel", () => {
160171
})
161172
.mockResolvedValueOnce({ type: "save" });
162173

163-
const result = await promptDashboardDisplayPanel(createSettings(), buildDeps());
174+
const result = await promptPanel(createSettings());
164175

165176
expect(result?.menuShowStatusBadge).toBe(false);
166177
expect(selectMock).toHaveBeenCalledTimes(2);
@@ -175,7 +186,7 @@ describe("promptDashboardDisplayPanel", () => {
175186
.mockResolvedValueOnce({ type: "reset" })
176187
.mockResolvedValueOnce({ type: "save" });
177188

178-
const result = await promptDashboardDisplayPanel(createSettings(), buildDeps());
189+
const result = await promptPanel(createSettings());
179190

180191
expect(result?.menuShowStatusBadge).toBe(
181192
DEFAULT_DASHBOARD_DISPLAY_SETTINGS.menuShowStatusBadge,
@@ -187,12 +198,11 @@ describe("promptDashboardDisplayPanel", () => {
187198
.mockResolvedValueOnce({ type: "cycle-sort-mode" })
188199
.mockResolvedValueOnce({ type: "save" });
189200

190-
const result = await promptDashboardDisplayPanel(
201+
const result = await promptPanel(
191202
createSettings({
192203
menuSortMode: "manual",
193204
menuSortEnabled: false,
194205
}),
195-
buildDeps(),
196206
);
197207

198208
expect(result?.menuSortMode).toBe("ready-first");
@@ -204,12 +214,11 @@ describe("promptDashboardDisplayPanel", () => {
204214
.mockResolvedValueOnce({ type: "cycle-layout-mode" })
205215
.mockResolvedValueOnce({ type: "save" });
206216

207-
const result = await promptDashboardDisplayPanel(
217+
const result = await promptPanel(
208218
createSettings({
209219
menuLayoutMode: "compact-details",
210220
menuShowDetailsForUnselectedRows: false,
211221
}),
212-
buildDeps(),
213222
);
214223

215224
expect(result?.menuLayoutMode).toBe("expanded-rows");
@@ -219,7 +228,7 @@ describe("promptDashboardDisplayPanel", () => {
219228
it("returns null when the panel is cancelled", async () => {
220229
selectMock.mockResolvedValueOnce({ type: "cancel" });
221230

222-
const result = await promptDashboardDisplayPanel(createSettings(), buildDeps());
231+
const result = await promptPanel(createSettings());
223232

224233
expect(result).toBeNull();
225234
});

test/index-retry.test.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -378,10 +378,11 @@ vi.mock("../lib/accounts.js", async () => {
378378
};
379379
});
380380

381-
vi.mock("../lib/storage.js", async () => {
382-
const actual = await vi.importActual("../lib/storage.js");
383-
return {
384-
...actual,
381+
// Shared module shape (test/helpers/cli-test-fixtures.ts): actual storage
382+
// module spread plus this suite's bespoke stub overrides. The overrides stay
383+
// inline (plain stubs, not vi.fn instances) because no test asserts on them.
384+
vi.mock("../lib/storage.js", async () =>
385+
(await import("./helpers/cli-test-fixtures.js")).storageModuleMock({
385386
getStoragePath: () => "",
386387
loadAccounts: async () => null,
387388
saveAccounts: async () => {},
@@ -395,8 +396,8 @@ vi.mock("../lib/storage.js", async () => {
395396
setStorageBackupEnabled: () => {},
396397
exportAccounts: async () => {},
397398
importAccounts: async () => ({ imported: 0, total: 0 }),
398-
};
399-
});
399+
}),
400+
);
400401

401402
vi.mock("../lib/recovery.js", () => ({
402403
createSessionRecoveryHook: () => null,

test/issue-474-pin-end-to-end.test.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ import {
2626
* pin contract and affinity invalidation hold across real network requests.
2727
*/
2828

29+
// This suite imports the storage module (and its consumers) statically, so
30+
// the hoisted vi.mock factory runs before the test module body evaluates.
31+
// The mock instances therefore stay in vi.hoisted; the module shape delegates
32+
// to the shared storage factory (test/helpers/cli-test-fixtures.ts), which
33+
// the factory resolves lazily via dynamic import.
2934
const {
3035
saveAccountsMock,
3136
withAccountStorageTransactionMock,
@@ -34,14 +39,12 @@ const {
3439
withAccountStorageTransactionMock: vi.fn(),
3540
}));
3641

37-
vi.mock("../lib/storage.js", async (importOriginal) => {
38-
const actual = await importOriginal<typeof import("../lib/storage.js")>();
39-
return {
40-
...actual,
42+
vi.mock("../lib/storage.js", async () =>
43+
(await import("./helpers/cli-test-fixtures.js")).storageModuleMock({
4144
saveAccounts: saveAccountsMock,
4245
withAccountStorageTransaction: withAccountStorageTransactionMock,
43-
};
44-
});
46+
}),
47+
);
4548

4649
const CLIENT_API_KEY = "runtime-secret";
4750
const ACCOUNT_COUNT = 2;

0 commit comments

Comments
 (0)