- Add server-side `/api/v1/dashboard` endpoint to reduce client-side payload and round trips - Implement invoice upload and order-item SKU update APIs with complete test suites - Refactor order and order-items list endpoints (including MCP tools) to support proper PocketBase filter and sort params - Fix pagination validation for order-items route to clamp page ≥1 and perPage 1-500 - Add redirects in next.config.ts to map underscore collection paths to hyphenated API routes - Improve buyer tasks page to avoid redundant tab count refetches when switching tabs - Refactor collection confirm components with editable SKUs and updated accessibility labels - Add cached admin PocketBase auth to minimize repeated authentication calls - Add performance testing scripts for dashboard endpoint latency - Update quotes list route to default to excluding archived counts - Fix photo strip component text and labels for non-invoice documents - Refactor sprint page to lazy load supplier/customer data for pricing modal - Clean up unused imports in test files - Add full test coverage for all new APIs, services, and components
72 lines
2.4 KiB
TypeScript
72 lines
2.4 KiB
TypeScript
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
|
|
// Mock the pocketbase module so getAdminPocketBase doesn't try to open a socket.
|
|
const authWithPassword = vi.fn().mockResolvedValue({
|
|
token: "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjQ3NDYwODk2MDB9.sig",
|
|
});
|
|
const authStoreSave = vi.fn();
|
|
vi.mock("pocketbase", () => {
|
|
// Must be a class (or function) because the production code does `new PocketBase(url)`.
|
|
const FakePocketBase = vi.fn().mockImplementation(function () {
|
|
return {
|
|
authStore: {
|
|
save: authStoreSave,
|
|
record: { id: "admin-1" },
|
|
},
|
|
collection: () => ({
|
|
authWithPassword,
|
|
}),
|
|
};
|
|
});
|
|
return { default: FakePocketBase };
|
|
});
|
|
|
|
import { clearAdminPocketBaseAuthCache, getAdminPocketBase } from "./pocketbase-admin";
|
|
|
|
describe("getAdminPocketBase", () => {
|
|
const ORIGINAL_EMAIL = process.env.PB_ADMIN_EMAIL;
|
|
const ORIGINAL_PASSWORD = process.env.PB_ADMIN_PASSWORD;
|
|
|
|
beforeEach(() => {
|
|
process.env.PB_ADMIN_EMAIL = "admin@example.com";
|
|
process.env.PB_ADMIN_PASSWORD = "secret";
|
|
clearAdminPocketBaseAuthCache();
|
|
authWithPassword.mockClear();
|
|
authStoreSave.mockClear();
|
|
});
|
|
|
|
afterEach(() => {
|
|
clearAdminPocketBaseAuthCache();
|
|
if (ORIGINAL_EMAIL === undefined) delete process.env.PB_ADMIN_EMAIL;
|
|
else process.env.PB_ADMIN_EMAIL = ORIGINAL_EMAIL;
|
|
if (ORIGINAL_PASSWORD === undefined) delete process.env.PB_ADMIN_PASSWORD;
|
|
else process.env.PB_ADMIN_PASSWORD = ORIGINAL_PASSWORD;
|
|
});
|
|
|
|
it("returns a PocketBase instance and authenticates with the admin credentials", async () => {
|
|
const pb = await getAdminPocketBase();
|
|
expect(pb).toBeDefined();
|
|
expect(authWithPassword).toHaveBeenCalledWith("admin@example.com", "secret");
|
|
});
|
|
|
|
it("reuses the cached admin token on the second call", async () => {
|
|
await getAdminPocketBase();
|
|
authWithPassword.mockClear();
|
|
|
|
const pb = await getAdminPocketBase();
|
|
|
|
expect(pb).toBeDefined();
|
|
expect(authWithPassword).not.toHaveBeenCalled();
|
|
expect(authStoreSave).toHaveBeenCalledWith(
|
|
"eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjQ3NDYwODk2MDB9.sig",
|
|
);
|
|
});
|
|
|
|
it("throws a clear error when admin env vars are missing", async () => {
|
|
delete process.env.PB_ADMIN_EMAIL;
|
|
delete process.env.PB_ADMIN_PASSWORD;
|
|
await expect(getAdminPocketBase()).rejects.toThrow(
|
|
/PB_ADMIN_EMAIL and PB_ADMIN_PASSWORD are required/
|
|
);
|
|
});
|
|
});
|