All checks were successful
Build & Deploy to Staging / Build & Deploy to Staging (push) Successful in 12m13s
108 lines
3.6 KiB
TypeScript
108 lines
3.6 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
|
|
// Unmock keys service — we want to test the real implementation
|
|
vi.unmock("../services/keys.js");
|
|
|
|
// DB is still mocked by setup.ts
|
|
import { queryWithRetry } from "../services/db.js";
|
|
|
|
describe("keys service", () => {
|
|
let keys: typeof import("../services/keys.js");
|
|
|
|
beforeEach(async () => {
|
|
vi.clearAllMocks();
|
|
vi.resetModules();
|
|
// Re-import to get fresh cache
|
|
keys = await import("../services/keys.js");
|
|
});
|
|
|
|
describe("after loadKeys", () => {
|
|
const mockRows = [
|
|
{ key: "df_free_abc", tier: "free", email: "a@b.com", created_at: "2025-01-01T00:00:00Z", stripe_customer_id: null },
|
|
{ key: "df_pro_xyz", tier: "pro", email: "pro@b.com", created_at: "2025-01-01T00:00:00Z", stripe_customer_id: "cus_123" },
|
|
];
|
|
|
|
beforeEach(async () => {
|
|
vi.mocked(queryWithRetry).mockResolvedValueOnce({ rows: mockRows, rowCount: 2 } as any);
|
|
await keys.loadKeys();
|
|
});
|
|
|
|
it("isValidKey returns true for cached keys", () => {
|
|
expect(keys.isValidKey("df_free_abc")).toBe(true);
|
|
expect(keys.isValidKey("df_pro_xyz")).toBe(true);
|
|
});
|
|
|
|
it("isValidKey returns false for unknown keys", () => {
|
|
expect(keys.isValidKey("unknown")).toBe(false);
|
|
});
|
|
|
|
it("isProKey returns true for pro tier, false for free", () => {
|
|
expect(keys.isProKey("df_pro_xyz")).toBe(true);
|
|
expect(keys.isProKey("df_free_abc")).toBe(false);
|
|
});
|
|
|
|
it("getKeyInfo returns correct ApiKey object", () => {
|
|
const info = keys.getKeyInfo("df_pro_xyz");
|
|
expect(info).toEqual({
|
|
key: "df_pro_xyz",
|
|
tier: "pro",
|
|
email: "pro@b.com",
|
|
createdAt: "2025-01-01T00:00:00Z",
|
|
stripeCustomerId: "cus_123",
|
|
});
|
|
});
|
|
|
|
it("getKeyInfo returns undefined for unknown key", () => {
|
|
expect(keys.getKeyInfo("nope")).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe("createFreeKey", () => {
|
|
beforeEach(async () => {
|
|
vi.mocked(queryWithRetry).mockResolvedValueOnce({ rows: [], rowCount: 0 } as any);
|
|
await keys.loadKeys();
|
|
});
|
|
|
|
it("creates key with df_free prefix", async () => {
|
|
vi.mocked(queryWithRetry).mockResolvedValueOnce({ rows: [], rowCount: 1 } as any);
|
|
const result = await keys.createFreeKey("new@test.com");
|
|
expect(result.key).toMatch(/^df_free_/);
|
|
expect(result.tier).toBe("free");
|
|
expect(result.email).toBe("new@test.com");
|
|
});
|
|
|
|
it("returns existing key for same email", async () => {
|
|
vi.mocked(queryWithRetry).mockResolvedValueOnce({ rows: [], rowCount: 1 } as any);
|
|
const first = await keys.createFreeKey("dup@test.com");
|
|
const second = await keys.createFreeKey("dup@test.com");
|
|
expect(second.key).toBe(first.key);
|
|
});
|
|
});
|
|
|
|
describe("createProKey", () => {
|
|
beforeEach(async () => {
|
|
vi.mocked(queryWithRetry).mockResolvedValueOnce({ rows: [], rowCount: 0 } as any);
|
|
await keys.loadKeys();
|
|
});
|
|
|
|
it("uses UPSERT and returns key", async () => {
|
|
const returnedRow = {
|
|
key: "df_pro_newkey",
|
|
tier: "pro",
|
|
email: "pro@test.com",
|
|
created_at: "2025-06-01T00:00:00Z",
|
|
stripe_customer_id: "cus_new",
|
|
};
|
|
vi.mocked(queryWithRetry).mockResolvedValueOnce({ rows: [returnedRow], rowCount: 1 } as any);
|
|
|
|
const result = await keys.createProKey("pro@test.com", "cus_new");
|
|
expect(result.tier).toBe("pro");
|
|
expect(result.stripeCustomerId).toBe("cus_new");
|
|
|
|
const call = vi.mocked(queryWithRetry).mock.calls.find(
|
|
(c) => typeof c[0] === "string" && c[0].includes("ON CONFLICT")
|
|
);
|
|
expect(call).toBeTruthy();
|
|
});
|
|
});
|
|
});
|