refactor: extract findKeyInCacheOrDb to DRY up DB fallback pattern (TDD)
All checks were successful
Build & Deploy to Staging / Build & Deploy to Staging (push) Successful in 20m17s
All checks were successful
Build & Deploy to Staging / Build & Deploy to Staging (push) Successful in 20m17s
- New shared helper findKeyInCacheOrDb(column, value) for DB lookups - Refactored downgradeByCustomer, updateKeyEmail, updateEmailByCustomer, and findKeyByCustomerId to use the shared helper - Eliminated ~60 lines of duplicated SELECT/row-mapping code - 3 TDD tests added (keys-db-fallback-helper.test.ts) - 636 tests passing, 0 tsc errors
This commit is contained in:
parent
4e00feb860
commit
25cb5e2e94
2 changed files with 103 additions and 64 deletions
68
src/__tests__/keys-db-fallback-helper.test.ts
Normal file
68
src/__tests__/keys-db-fallback-helper.test.ts
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
|
||||
vi.unmock("../services/keys.js");
|
||||
|
||||
// The DB mock is set up in setup.ts — we need to control queryWithRetry
|
||||
const mockQueryWithRetry = vi.fn();
|
||||
vi.mock("../services/db.js", () => ({
|
||||
default: { query: vi.fn(), end: vi.fn() },
|
||||
pool: { query: vi.fn(), end: vi.fn() },
|
||||
queryWithRetry: (...args: unknown[]) => mockQueryWithRetry(...args),
|
||||
connectWithRetry: vi.fn(),
|
||||
initDatabase: vi.fn(),
|
||||
cleanupStaleData: vi.fn(),
|
||||
}));
|
||||
|
||||
import { findKeyInCacheOrDb } from "../services/keys.js";
|
||||
|
||||
describe("findKeyInCacheOrDb", () => {
|
||||
beforeEach(() => {
|
||||
mockQueryWithRetry.mockReset();
|
||||
});
|
||||
|
||||
it("returns null when DB finds no row", async () => {
|
||||
mockQueryWithRetry.mockResolvedValue({ rows: [] });
|
||||
const result = await findKeyInCacheOrDb("stripe_customer_id", "cus_nonexistent");
|
||||
expect(result).toBeNull();
|
||||
expect(mockQueryWithRetry).toHaveBeenCalledWith(
|
||||
expect.stringContaining("WHERE stripe_customer_id = $1"),
|
||||
["cus_nonexistent"]
|
||||
);
|
||||
});
|
||||
|
||||
it("returns ApiKey when DB finds a row", async () => {
|
||||
mockQueryWithRetry.mockResolvedValue({
|
||||
rows: [{
|
||||
key: "df_pro_abc",
|
||||
tier: "pro",
|
||||
email: "test@example.com",
|
||||
created_at: "2026-01-01T00:00:00.000Z",
|
||||
stripe_customer_id: "cus_123",
|
||||
}],
|
||||
});
|
||||
const result = await findKeyInCacheOrDb("stripe_customer_id", "cus_123");
|
||||
expect(result).toEqual({
|
||||
key: "df_pro_abc",
|
||||
tier: "pro",
|
||||
email: "test@example.com",
|
||||
createdAt: "2026-01-01T00:00:00.000Z",
|
||||
stripeCustomerId: "cus_123",
|
||||
});
|
||||
});
|
||||
|
||||
it("handles Date objects in created_at", async () => {
|
||||
mockQueryWithRetry.mockResolvedValue({
|
||||
rows: [{
|
||||
key: "df_pro_abc",
|
||||
tier: "pro",
|
||||
email: "test@example.com",
|
||||
created_at: new Date("2026-01-01T00:00:00.000Z"),
|
||||
stripe_customer_id: null,
|
||||
}],
|
||||
});
|
||||
const result = await findKeyInCacheOrDb("key", "df_pro_abc");
|
||||
expect(result).not.toBeNull();
|
||||
expect(result!.createdAt).toBe("2026-01-01T00:00:00.000Z");
|
||||
expect(result!.stripeCustomerId).toBeUndefined();
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue