Update features and dependencies
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
|
||||
const mockFindFirst = vi.fn();
|
||||
const mockFindMany = vi.fn();
|
||||
const mockInsert = vi.fn();
|
||||
const mockDelete = vi.fn();
|
||||
|
||||
vi.mock("@epicure/db", () => ({
|
||||
db: {
|
||||
query: {
|
||||
siteSettings: {
|
||||
findFirst: mockFindFirst,
|
||||
findMany: mockFindMany,
|
||||
},
|
||||
},
|
||||
insert: mockInsert,
|
||||
delete: mockDelete,
|
||||
},
|
||||
siteSettings: { key: "key", value: "value", isSecret: "is_secret", updatedAt: "updated_at", updatedById: "updated_by_id" },
|
||||
eq: vi.fn((a, b) => ({ a, b })),
|
||||
}));
|
||||
|
||||
const insertChain = {
|
||||
values: vi.fn().mockReturnThis(),
|
||||
onConflictDoUpdate: vi.fn().mockResolvedValue(undefined),
|
||||
};
|
||||
const deleteChain = {
|
||||
where: vi.fn().mockResolvedValue(undefined),
|
||||
};
|
||||
|
||||
const { getSiteSetting, setSiteSetting, isSecretKey } = await import("../site-settings");
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mockInsert.mockReturnValue(insertChain);
|
||||
mockDelete.mockReturnValue(deleteChain);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
delete process.env["OPENAI_API_KEY"];
|
||||
delete process.env["ANTHROPIC_API_KEY"];
|
||||
});
|
||||
|
||||
describe("isSecretKey", () => {
|
||||
it("marks API keys as secret", () => {
|
||||
expect(isSecretKey("OPENAI_API_KEY")).toBe(true);
|
||||
expect(isSecretKey("ANTHROPIC_API_KEY")).toBe(true);
|
||||
expect(isSecretKey("VAPID_PRIVATE_KEY")).toBe(true);
|
||||
});
|
||||
|
||||
it("marks non-secret settings as public", () => {
|
||||
expect(isSecretKey("OPENROUTER_DEFAULT_MODEL")).toBe(false);
|
||||
expect(isSecretKey("OLLAMA_BASE_URL")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getSiteSetting", () => {
|
||||
it("returns DB value when present (plain)", async () => {
|
||||
mockFindFirst.mockResolvedValue({ value: "gpt-4o", isSecret: false });
|
||||
const val = await getSiteSetting("OPENROUTER_DEFAULT_MODEL");
|
||||
expect(val).toBe("gpt-4o");
|
||||
});
|
||||
|
||||
it("decrypts DB value when secret", async () => {
|
||||
// Pre-encrypt something using the real encrypt function
|
||||
const { encrypt } = await import("../encrypt");
|
||||
const ciphertext = encrypt("sk-real-key");
|
||||
mockFindFirst.mockResolvedValue({ value: ciphertext, isSecret: true });
|
||||
|
||||
const val = await getSiteSetting("OPENAI_API_KEY");
|
||||
expect(val).toBe("sk-real-key");
|
||||
});
|
||||
|
||||
it("falls back to env var when no DB row", async () => {
|
||||
mockFindFirst.mockResolvedValue(null);
|
||||
process.env["OPENAI_API_KEY"] = "sk-from-env";
|
||||
const val = await getSiteSetting("OPENAI_API_KEY");
|
||||
expect(val).toBe("sk-from-env");
|
||||
});
|
||||
|
||||
it("returns null when neither DB nor env is set", async () => {
|
||||
mockFindFirst.mockResolvedValue(null);
|
||||
const val = await getSiteSetting("OPENAI_API_KEY");
|
||||
expect(val).toBeNull();
|
||||
});
|
||||
|
||||
it("falls back to env when DB throws", async () => {
|
||||
mockFindFirst.mockRejectedValue(new Error("DB error"));
|
||||
process.env["ANTHROPIC_API_KEY"] = "sk-anthropic-env";
|
||||
const val = await getSiteSetting("ANTHROPIC_API_KEY");
|
||||
expect(val).toBe("sk-anthropic-env");
|
||||
});
|
||||
});
|
||||
|
||||
describe("setSiteSetting", () => {
|
||||
it("deletes the row when value is null", async () => {
|
||||
await setSiteSetting("OPENAI_API_KEY", null, "admin-user");
|
||||
expect(mockDelete).toHaveBeenCalled();
|
||||
expect(mockInsert).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("deletes the row when value is empty string", async () => {
|
||||
await setSiteSetting("OPENAI_API_KEY", "", "admin-user");
|
||||
expect(mockDelete).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("inserts encrypted value for secret key", async () => {
|
||||
await setSiteSetting("OPENAI_API_KEY", "sk-test", "admin-user");
|
||||
expect(mockInsert).toHaveBeenCalled();
|
||||
const inserted = insertChain.values.mock.calls[0]![0] as Record<string, unknown>;
|
||||
expect(inserted.isSecret).toBe(true);
|
||||
// stored value should not be plain text
|
||||
expect(inserted.value).not.toBe("sk-test");
|
||||
expect(typeof inserted.value).toBe("string");
|
||||
});
|
||||
|
||||
it("inserts plain value for non-secret key", async () => {
|
||||
await setSiteSetting("OPENROUTER_DEFAULT_MODEL", "gpt-4o", "admin-user");
|
||||
const inserted = insertChain.values.mock.calls[0]![0] as Record<string, unknown>;
|
||||
expect(inserted.isSecret).toBe(false);
|
||||
expect(inserted.value).toBe("gpt-4o");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user