Skip to content

Commit 06434de

Browse files
committed
test(release): add broad merged-feature coverage
1 parent 9ded89a commit 06434de

1 file changed

Lines changed: 214 additions & 0 deletions

File tree

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
import { existsSync, promises as fs } from "node:fs";
2+
import { tmpdir } from "node:os";
3+
import { join } from "node:path";
4+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
5+
import { clearCodexCliStateCache } from "../lib/codex-cli/state.js";
6+
import type { AccountStorageV3 } from "../lib/storage.js";
7+
import { removeWithRetry } from "./helpers/remove-with-retry.js";
8+
9+
describe("release-main-prs regressions", () => {
10+
let tempDir: string;
11+
let accountsPath: string;
12+
let authPath: string;
13+
let configPath: string;
14+
let previousMultiAuthDir: string | undefined;
15+
let previousAccountsPath: string | undefined;
16+
let previousAuthPath: string | undefined;
17+
let previousConfigPath: string | undefined;
18+
let previousSync: string | undefined;
19+
let previousEnforceFileStore: string | undefined;
20+
21+
beforeEach(async () => {
22+
previousMultiAuthDir = process.env.CODEX_MULTI_AUTH_DIR;
23+
previousAccountsPath = process.env.CODEX_CLI_ACCOUNTS_PATH;
24+
previousAuthPath = process.env.CODEX_CLI_AUTH_PATH;
25+
previousConfigPath = process.env.CODEX_CLI_CONFIG_PATH;
26+
previousSync = process.env.CODEX_MULTI_AUTH_SYNC_CODEX_CLI;
27+
previousEnforceFileStore =
28+
process.env.CODEX_MULTI_AUTH_ENFORCE_CLI_FILE_AUTH_STORE;
29+
30+
tempDir = await fs.mkdtemp(join(tmpdir(), "codex-release-main-prs-"));
31+
accountsPath = join(tempDir, "accounts.json");
32+
authPath = join(tempDir, "auth.json");
33+
configPath = join(tempDir, "config.toml");
34+
35+
process.env.CODEX_MULTI_AUTH_DIR = tempDir;
36+
process.env.CODEX_CLI_ACCOUNTS_PATH = accountsPath;
37+
process.env.CODEX_CLI_AUTH_PATH = authPath;
38+
process.env.CODEX_CLI_CONFIG_PATH = configPath;
39+
process.env.CODEX_MULTI_AUTH_SYNC_CODEX_CLI = "1";
40+
process.env.CODEX_MULTI_AUTH_ENFORCE_CLI_FILE_AUTH_STORE = "1";
41+
42+
vi.resetModules();
43+
clearCodexCliStateCache();
44+
});
45+
46+
afterEach(async () => {
47+
vi.restoreAllMocks();
48+
clearCodexCliStateCache();
49+
50+
const { setStoragePathDirect } = await import("../lib/storage.js");
51+
setStoragePathDirect(null);
52+
53+
if (previousMultiAuthDir === undefined) delete process.env.CODEX_MULTI_AUTH_DIR;
54+
else process.env.CODEX_MULTI_AUTH_DIR = previousMultiAuthDir;
55+
if (previousAccountsPath === undefined) delete process.env.CODEX_CLI_ACCOUNTS_PATH;
56+
else process.env.CODEX_CLI_ACCOUNTS_PATH = previousAccountsPath;
57+
if (previousAuthPath === undefined) delete process.env.CODEX_CLI_AUTH_PATH;
58+
else process.env.CODEX_CLI_AUTH_PATH = previousAuthPath;
59+
if (previousConfigPath === undefined) delete process.env.CODEX_CLI_CONFIG_PATH;
60+
else process.env.CODEX_CLI_CONFIG_PATH = previousConfigPath;
61+
if (previousSync === undefined) delete process.env.CODEX_MULTI_AUTH_SYNC_CODEX_CLI;
62+
else process.env.CODEX_MULTI_AUTH_SYNC_CODEX_CLI = previousSync;
63+
if (previousEnforceFileStore === undefined) {
64+
delete process.env.CODEX_MULTI_AUTH_ENFORCE_CLI_FILE_AUTH_STORE;
65+
} else {
66+
process.env.CODEX_MULTI_AUTH_ENFORCE_CLI_FILE_AUTH_STORE = previousEnforceFileStore;
67+
}
68+
69+
await removeWithRetry(tempDir, { recursive: true, force: true });
70+
});
71+
72+
it("keeps canonical storage and unified settings isolated from Codex CLI mirror files", async () => {
73+
const {
74+
saveUnifiedPluginConfig,
75+
saveUnifiedDashboardSettings,
76+
loadUnifiedPluginConfigSync,
77+
loadUnifiedDashboardSettings,
78+
} = await import("../lib/unified-settings.js");
79+
const { syncAccountStorageFromCodexCli } = await import("../lib/codex-cli/sync.js");
80+
const { MODEL_FAMILIES } = await import("../lib/prompts/codex.js");
81+
82+
await saveUnifiedPluginConfig({ codexMode: true, fetchTimeoutMs: 90_000 });
83+
await saveUnifiedDashboardSettings({
84+
menuShowLastUsed: false,
85+
uiThemePreset: "green",
86+
});
87+
88+
await fs.writeFile(
89+
accountsPath,
90+
JSON.stringify(
91+
{
92+
activeAccountId: "acc_mirror",
93+
accounts: [
94+
{
95+
accountId: "acc_mirror",
96+
email: "mirror@example.com",
97+
auth: {
98+
tokens: {
99+
access_token: "mirror-access",
100+
refresh_token: "mirror-refresh",
101+
},
102+
},
103+
},
104+
],
105+
},
106+
null,
107+
2,
108+
),
109+
"utf-8",
110+
);
111+
112+
const current: AccountStorageV3 = {
113+
version: 3,
114+
accounts: [
115+
{
116+
accountId: "acc_canonical",
117+
email: "canonical@example.com",
118+
refreshToken: "canonical-refresh",
119+
addedAt: 1,
120+
lastUsed: 1,
121+
},
122+
],
123+
activeIndex: 0,
124+
activeIndexByFamily: Object.fromEntries(MODEL_FAMILIES.map((family) => [family, 0])),
125+
};
126+
127+
const result = await syncAccountStorageFromCodexCli(current);
128+
129+
expect(result.changed).toBe(false);
130+
expect(result.storage).toBe(current);
131+
expect(result.storage?.accounts).toEqual(current.accounts);
132+
expect(loadUnifiedPluginConfigSync()).toEqual({
133+
codexMode: true,
134+
fetchTimeoutMs: 90_000,
135+
});
136+
expect(await loadUnifiedDashboardSettings()).toEqual({
137+
menuShowLastUsed: false,
138+
uiThemePreset: "green",
139+
});
140+
});
141+
142+
it("keeps flagged reset suppression active even when Codex CLI mirrors exist", async () => {
143+
const {
144+
clearFlaggedAccounts,
145+
getFlaggedAccountsPath,
146+
loadFlaggedAccounts,
147+
saveFlaggedAccounts,
148+
setStoragePathDirect,
149+
} = await import("../lib/storage.js");
150+
const { syncAccountStorageFromCodexCli } = await import("../lib/codex-cli/sync.js");
151+
152+
const storagePath = join(tempDir, "canonical-accounts.json");
153+
setStoragePathDirect(storagePath);
154+
155+
await saveFlaggedAccounts({
156+
version: 1,
157+
accounts: [
158+
{
159+
refreshToken: "stale-primary",
160+
flaggedAt: 1,
161+
addedAt: 1,
162+
lastUsed: 1,
163+
},
164+
],
165+
});
166+
167+
await fs.writeFile(
168+
accountsPath,
169+
JSON.stringify(
170+
{
171+
activeAccountId: "acc_mirror",
172+
accounts: [
173+
{
174+
accountId: "acc_mirror",
175+
email: "mirror@example.com",
176+
auth: {
177+
tokens: {
178+
access_token: "mirror-access",
179+
refresh_token: "mirror-refresh",
180+
},
181+
},
182+
},
183+
],
184+
},
185+
null,
186+
2,
187+
),
188+
"utf-8",
189+
);
190+
191+
const flaggedPath = getFlaggedAccountsPath();
192+
const originalUnlink = fs.unlink.bind(fs);
193+
const unlinkSpy = vi.spyOn(fs, "unlink").mockImplementation(async (targetPath) => {
194+
if (targetPath === flaggedPath) {
195+
const error = new Error("EPERM primary delete") as NodeJS.ErrnoException;
196+
error.code = "EPERM";
197+
throw error;
198+
}
199+
return originalUnlink(targetPath);
200+
});
201+
202+
await expect(clearFlaggedAccounts()).rejects.toThrow("EPERM primary delete");
203+
204+
const flagged = await loadFlaggedAccounts();
205+
const syncResult = await syncAccountStorageFromCodexCli(null);
206+
207+
expect(existsSync(flaggedPath)).toBe(true);
208+
expect(flagged.accounts).toHaveLength(0);
209+
expect(syncResult.changed).toBe(false);
210+
expect(syncResult.storage).toBeNull();
211+
212+
unlinkSpy.mockRestore();
213+
});
214+
});

0 commit comments

Comments
 (0)