test: improve db.ts and keys.ts coverage
Some checks failed
Build & Deploy to Staging / Build & Deploy to Staging (push) Has been cancelled

This commit is contained in:
OpenClaw Subagent 2026-03-12 20:08:23 +01:00
parent db35a0e521
commit ae8b32e1c4
2 changed files with 228 additions and 0 deletions

View file

@ -0,0 +1,129 @@
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
// Unmock keys service — we want real implementation
vi.unmock("../services/keys.js");
// DB is still mocked by setup.ts
import { queryWithRetry } from "../services/db.js";
describe("keys service — uncovered branches", () => {
let keys: typeof import("../services/keys.js");
beforeEach(async () => {
vi.clearAllMocks();
vi.resetModules();
keys = await import("../services/keys.js");
});
describe("loadKeys error path", () => {
it("sets keysCache to empty array when query fails (ignoring env seeds)", async () => {
const origKeys = process.env.API_KEYS;
delete process.env.API_KEYS;
vi.mocked(queryWithRetry).mockRejectedValueOnce(new Error("DB down"));
await keys.loadKeys();
// Cache should be empty — no keys valid
expect(keys.isValidKey("anything")).toBe(false);
expect(keys.getAllKeys()).toEqual([]);
process.env.API_KEYS = origKeys;
});
});
describe("loadKeys with API_KEYS env var", () => {
const origEnv = process.env.API_KEYS;
afterEach(() => {
if (origEnv !== undefined) {
process.env.API_KEYS = origEnv;
} else {
delete process.env.API_KEYS;
}
});
it("seeds keys from API_KEYS env var into cache and upserts to DB", async () => {
process.env.API_KEYS = "seed_key_1,seed_key_2";
// loadKeys query returns empty (no existing keys)
vi.mocked(queryWithRetry).mockResolvedValueOnce({ rows: [], rowCount: 0 } as any);
// Two upsert calls for seed keys
vi.mocked(queryWithRetry).mockResolvedValue({ rows: [], rowCount: 0 } as any);
await keys.loadKeys();
expect(keys.isValidKey("seed_key_1")).toBe(true);
expect(keys.isValidKey("seed_key_2")).toBe(true);
expect(keys.getKeyInfo("seed_key_1")?.tier).toBe("pro");
expect(keys.getKeyInfo("seed_key_1")?.email).toBe("seed@docfast.dev");
// 1 SELECT + 2 upserts
expect(vi.mocked(queryWithRetry)).toHaveBeenCalledTimes(3);
});
it("does not duplicate seed keys already in cache", async () => {
process.env.API_KEYS = "existing_key";
// loadKeys returns the key already in DB
vi.mocked(queryWithRetry).mockResolvedValueOnce({
rows: [{ key: "existing_key", tier: "pro", email: "x@y.com", created_at: "2025-01-01T00:00:00Z", stripe_customer_id: null }],
rowCount: 1,
} as any);
await keys.loadKeys();
// Should not upsert — key already exists
expect(vi.mocked(queryWithRetry)).toHaveBeenCalledTimes(1);
expect(keys.getAllKeys()).toHaveLength(1);
});
});
describe("createFreeKey — email already exists in cache", () => {
it("returns existing free key for same email without DB insert", async () => {
// Load empty cache
vi.mocked(queryWithRetry).mockResolvedValueOnce({ rows: [], rowCount: 0 } as any);
await keys.loadKeys();
// First create
vi.mocked(queryWithRetry).mockResolvedValueOnce({ rows: [], rowCount: 1 } as any);
const first = await keys.createFreeKey("dup@test.com");
// Second call should return cached key without hitting DB
vi.mocked(queryWithRetry).mockClear();
const second = await keys.createFreeKey("dup@test.com");
expect(second.key).toBe(first.key);
expect(vi.mocked(queryWithRetry)).not.toHaveBeenCalled();
});
});
describe("createProKey — customer already in cache", () => {
it("updates tier to pro and returns existing entry", async () => {
// Load cache with a free key that has a stripe customer
vi.mocked(queryWithRetry).mockResolvedValueOnce({
rows: [{
key: "df_free_old",
tier: "free",
email: "user@test.com",
created_at: "2025-01-01T00:00:00Z",
stripe_customer_id: "cus_existing",
}],
rowCount: 1,
} as any);
await keys.loadKeys();
// Clear mock call history from loadKeys
vi.mocked(queryWithRetry).mockClear();
// The UPDATE query
vi.mocked(queryWithRetry).mockResolvedValueOnce({ rows: [], rowCount: 1 } as any);
const result = await keys.createProKey("user@test.com", "cus_existing");
expect(result.key).toBe("df_free_old");
expect(result.tier).toBe("pro");
// Should have called UPDATE, not INSERT
const updateCall = vi.mocked(queryWithRetry).mock.calls[0];
expect(updateCall[0]).toContain("UPDATE");
});
});
});