Skip to content

Commit 4f27fe5

Browse files
committed
test: close remaining PR 387 review nits
1 parent c5ebffd commit 4f27fe5

6 files changed

Lines changed: 118 additions & 8 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ If browser launch is blocked, use the alternate login paths in [docs/getting-sta
199199
| Accounts | `~/.codex/multi-auth/openai-codex-accounts.json` |
200200
| Flagged accounts | `~/.codex/multi-auth/openai-codex-flagged-accounts.json` |
201201
| Quota cache | `~/.codex/multi-auth/quota-cache.json` |
202+
| Runtime observability | `~/.codex/multi-auth/runtime-observability.json` |
202203
| Logs | `~/.codex/multi-auth/logs/codex-plugin/` |
203204
| Per-project accounts | `~/.codex/multi-auth/projects/<project-key>/openai-codex-accounts.json` |
204205

lib/codex-manager/commands/report.ts

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,20 @@ interface ModelInspection {
6060
capabilities: ReturnType<typeof getModelCapabilities>;
6161
}
6262

63+
function parsePositiveIntegerOption(
64+
rawValue: string,
65+
): number | null {
66+
const normalized = rawValue.trim();
67+
if (!/^\d+$/.test(normalized)) {
68+
return null;
69+
}
70+
const parsed = Number.parseInt(normalized, 10);
71+
if (!Number.isSafeInteger(parsed) || parsed < 1) {
72+
return null;
73+
}
74+
return parsed;
75+
}
76+
6377
const RETRYABLE_WRITE_CODES = new Set(["EBUSY", "EPERM"]);
6478

6579
export interface ReportCommandDeps {
@@ -163,17 +177,17 @@ function parseReportArgs(args: string[]): ParsedArgsResult<ReportCliOptions> {
163177
if (arg === "--max-accounts") {
164178
const value = args[i + 1];
165179
if (!value) return { ok: false, message: "Missing value for --max-accounts" };
166-
const parsed = Number.parseInt(value, 10);
167-
if (!Number.isFinite(parsed) || parsed < 1) {
180+
const parsed = parsePositiveIntegerOption(value);
181+
if (parsed === null) {
168182
return { ok: false, message: "--max-accounts must be a positive integer" };
169183
}
170184
options.maxAccounts = parsed;
171185
i += 1;
172186
continue;
173187
}
174188
if (arg.startsWith("--max-accounts=")) {
175-
const parsed = Number.parseInt(arg.slice("--max-accounts=".length), 10);
176-
if (!Number.isFinite(parsed) || parsed < 1) {
189+
const parsed = parsePositiveIntegerOption(arg.slice("--max-accounts=".length));
190+
if (parsed === null) {
177191
return { ok: false, message: "--max-accounts must be a positive integer" };
178192
}
179193
options.maxAccounts = parsed;
@@ -182,17 +196,17 @@ function parseReportArgs(args: string[]): ParsedArgsResult<ReportCliOptions> {
182196
if (arg === "--max-probes") {
183197
const value = args[i + 1];
184198
if (!value) return { ok: false, message: "Missing value for --max-probes" };
185-
const parsed = Number.parseInt(value, 10);
186-
if (!Number.isFinite(parsed) || parsed < 1) {
199+
const parsed = parsePositiveIntegerOption(value);
200+
if (parsed === null) {
187201
return { ok: false, message: "--max-probes must be a positive integer" };
188202
}
189203
options.maxProbes = parsed;
190204
i += 1;
191205
continue;
192206
}
193207
if (arg.startsWith("--max-probes=")) {
194-
const parsed = Number.parseInt(arg.slice("--max-probes=".length), 10);
195-
if (!Number.isFinite(parsed) || parsed < 1) {
208+
const parsed = parsePositiveIntegerOption(arg.slice("--max-probes=".length));
209+
if (parsed === null) {
196210
return { ok: false, message: "--max-probes must be a positive integer" };
197211
}
198212
options.maxProbes = parsed;

test/codex-manager-report-command.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,22 @@ describe("runReportCommand", () => {
100100
);
101101
});
102102

103+
it.each([
104+
["--max-accounts", "1.9", "--max-accounts must be a positive integer"],
105+
["--max-accounts", "1e3", "--max-accounts must be a positive integer"],
106+
["--max-accounts", "2foo", "--max-accounts must be a positive integer"],
107+
["--max-probes", "1.9", "--max-probes must be a positive integer"],
108+
["--max-probes", "1e3", "--max-probes must be a positive integer"],
109+
["--max-probes", "2foo", "--max-probes must be a positive integer"],
110+
] as const)("rejects malformed numeric flags %s %s", async (flag, value, message) => {
111+
const deps = createDeps();
112+
113+
const result = await runReportCommand([flag, value], deps);
114+
115+
expect(result).toBe(1);
116+
expect(deps.logError).toHaveBeenCalledWith(message);
117+
});
118+
103119
it("writes json report output when requested", async () => {
104120
const deps = createDeps();
105121

test/config-explain.test.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,42 @@ describe("getPluginConfigExplainReport", () => {
146146
});
147147
});
148148

149+
it("covers the rate-limit explain rows and env sources", async () => {
150+
const { getPluginConfigExplainReport } = await import("../lib/config.js");
151+
const keys = [
152+
"rateLimitDedupWindowMs",
153+
"rateLimitStateResetMs",
154+
"rateLimitMaxBackoffMs",
155+
"rateLimitShortRetryThresholdMs",
156+
] as const;
157+
const report = getPluginConfigExplainReport();
158+
for (const key of keys) {
159+
expect(expectEntry(report, key)).toBeDefined();
160+
}
161+
162+
const serialized = JSON.parse(JSON.stringify(report)) as {
163+
entries: Array<{ key: string; value: unknown; defaultValue: unknown; source: string }>;
164+
};
165+
for (const key of keys) {
166+
const entry = serialized.entries.find((item) => item.key === key);
167+
expect(entry).toBeDefined();
168+
expect(entry?.value).toBeDefined();
169+
expect(entry?.defaultValue).toBeDefined();
170+
}
171+
172+
vi.resetModules();
173+
process.env.CODEX_AUTH_RATE_LIMIT_DEDUP_WINDOW_MS = "3210";
174+
process.env.CODEX_AUTH_RATE_LIMIT_STATE_RESET_MS = "654321";
175+
process.env.CODEX_AUTH_RATE_LIMIT_MAX_BACKOFF_MS = "12345";
176+
process.env.CODEX_AUTH_RATE_LIMIT_SHORT_RETRY_THRESHOLD_MS = "250";
177+
const envMod = await import("../lib/config.js");
178+
const envReport = envMod.getPluginConfigExplainReport();
179+
for (const key of keys) {
180+
const entry = expectEntry(envReport, key);
181+
expect(entry?.source).toBe("env");
182+
}
183+
});
184+
149185
it("reports default and env sources", async () => {
150186
const mod = await import("../lib/config.js");
151187
let report = mod.getPluginConfigExplainReport();

test/fetch-helpers.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,20 @@ describe('Fetch Helpers Module', () => {
525525
expect(rateLimit).toBeUndefined();
526526
});
527527

528+
it('prioritizes entitlement over rate-limit when signals conflict', async () => {
529+
const body = {
530+
error: {
531+
code: 'usage_not_included',
532+
type: 'usage_limit_reached',
533+
message: 'mixed signals',
534+
},
535+
};
536+
const resp = new Response(JSON.stringify(body), { status: 404 });
537+
const { response: mapped, rateLimit } = await handleErrorResponse(resp);
538+
expect(mapped.status).toBe(403);
539+
expect(rateLimit).toBeUndefined();
540+
});
541+
528542
it('leaves non-usage 404 errors unchanged', async () => {
529543
const body = { error: { code: 'not_found', message: 'nope' } };
530544
const resp = new Response(JSON.stringify(body), { status: 404 });
@@ -1053,6 +1067,15 @@ describe('createEntitlementErrorResponse', () => {
10531067
expect(rateLimit?.retryAfterMs).toBeGreaterThan(0);
10541068
});
10551069

1070+
it('does not remap 404s with malformed json bodies', async () => {
1071+
const response = new Response('{ invalid json', { status: 404 });
1072+
1073+
const { response: result, rateLimit } = await handleErrorResponse(response);
1074+
1075+
expect(result.status).toBe(404);
1076+
expect(rateLimit).toBeUndefined();
1077+
});
1078+
10561079
it('does not treat non-429 rate-limit text as a cooldown signal', async () => {
10571080
const response = new Response('rate_limit_exceeded - upstream overloaded', {
10581081
status: 500,

test/storage.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2980,6 +2980,26 @@ describe("storage", () => {
29802980

29812981
unlinkSpy.mockRestore();
29822982
});
2983+
2984+
it("throws and does not write reset marker when the wal file cannot be removed", async () => {
2985+
await fs.writeFile(testStoragePath, "{}", "utf-8");
2986+
await fs.writeFile(`${testStoragePath}.wal`, "{}", "utf-8");
2987+
const resetMarkerPath = getIntentionalResetMarkerPath(testStoragePath);
2988+
const unlinkSpy = vi.spyOn(fs, "unlink").mockImplementation(async (targetPath) => {
2989+
if (String(targetPath) === `${testStoragePath}.wal`) {
2990+
const error = Object.assign(new Error("locked"), { code: "EBUSY" });
2991+
throw error;
2992+
}
2993+
return Promise.resolve();
2994+
});
2995+
2996+
try {
2997+
await expect(clearAccounts()).rejects.toMatchObject({ code: "EBUSY" });
2998+
expect(existsSync(resetMarkerPath)).toBe(false);
2999+
} finally {
3000+
unlinkSpy.mockRestore();
3001+
}
3002+
});
29833003
});
29843004

29853005
describe("StorageError with cause", () => {

0 commit comments

Comments
 (0)