codex-proxy / src /routes /__tests__ /accounts-delete-warnings.test.ts
icebear
feat: account management page with batch operations (#146)
7516302 unverified
raw
history blame
4.08 kB
/**
* Tests that deleting an account also clears its quota warnings.
* Regression test for issue #100.
*/
import { describe, it, expect, vi, beforeEach } from "vitest";
// Mock fs before importing anything
vi.mock("fs", () => ({
readFileSync: vi.fn(() => { throw new Error("ENOENT"); }),
writeFileSync: vi.fn(),
renameSync: vi.fn(),
existsSync: vi.fn(() => false),
mkdirSync: vi.fn(),
}));
vi.mock("../../paths.js", () => ({
getDataDir: vi.fn(() => "/tmp/test-data"),
getConfigDir: vi.fn(() => "/tmp/test-config"),
}));
vi.mock("../../config.js", () => ({
getConfig: vi.fn(() => ({
auth: {
jwt_token: null,
rotation_strategy: "least_used",
rate_limit_backoff_seconds: 60,
},
server: { proxy_api_key: null },
})),
}));
const mockIsTokenExpired = vi.hoisted(() => vi.fn(() => false));
vi.mock("../../auth/jwt-utils.js", () => ({
decodeJwtPayload: vi.fn(() => ({ exp: Math.floor(Date.now() / 1000) + 3600 })),
extractChatGptAccountId: vi.fn((token: string) => `acct-${token.slice(0, 8)}`),
extractUserProfile: vi.fn((token: string) => ({
email: `${token.slice(0, 4)}@test.com`,
chatgpt_plan_type: "free",
})),
isTokenExpired: mockIsTokenExpired,
}));
vi.mock("../../utils/jitter.js", () => ({
jitter: vi.fn((val: number) => val),
}));
vi.mock("../../models/model-store.js", () => ({
getModelPlanTypes: vi.fn(() => []),
isPlanFetched: vi.fn(() => true),
}));
import { Hono } from "hono";
import { AccountPool } from "../../auth/account-pool.js";
import { createAccountRoutes } from "../../routes/accounts.js";
import { updateWarnings, getActiveWarnings, clearWarnings } from "../../auth/quota-warnings.js";
const mockScheduler = {
scheduleOne: vi.fn(),
clearOne: vi.fn(),
start: vi.fn(),
stop: vi.fn(),
};
describe("DELETE /auth/accounts/:id clears quota warnings", () => {
let pool: AccountPool;
let app: Hono;
beforeEach(() => {
mockIsTokenExpired.mockReturnValue(false);
pool = new AccountPool();
const routes = createAccountRoutes(pool, mockScheduler as never);
app = new Hono();
app.route("/", routes);
// Clean up warnings state between tests
for (const w of getActiveWarnings()) {
clearWarnings(w.accountId);
}
});
it("should clear quota warnings when an account is deleted", async () => {
// Add an account
const token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.test-delete-warnings";
const addResp = await app.request("/auth/accounts", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ token }),
});
expect(addResp.status).toBe(200);
const { account } = await addResp.json();
const accountId = account.id;
// Simulate quota warnings for this account
updateWarnings(accountId, [
{
accountId,
email: "test@test.com",
window: "primary",
level: "critical",
usedPercent: 95,
resetAt: null,
},
]);
expect(getActiveWarnings().some((w) => w.accountId === accountId)).toBe(true);
// Delete the account
const delResp = await app.request(`/auth/accounts/${encodeURIComponent(accountId)}`, {
method: "DELETE",
});
expect(delResp.status).toBe(200);
// Warnings should be cleared
expect(getActiveWarnings().some((w) => w.accountId === accountId)).toBe(false);
});
it("should not fail when deleting account with no warnings", async () => {
const token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.test-no-warnings";
const addResp = await app.request("/auth/accounts", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ token }),
});
const { account } = await addResp.json();
// No warnings set — delete should still succeed
const delResp = await app.request(`/auth/accounts/${encodeURIComponent(account.id)}`, {
method: "DELETE",
});
expect(delResp.status).toBe(200);
const body = await delResp.json();
expect(body.success).toBe(true);
});
});