Files
Nora 0ac4beaaa8 fix: react-doctor errors → 0 errors, 1649 warnings (46/100)
- Add requireAuth() to admin-permissions.ts as recognized auth call
- Convert getAdminUser() → requireAuth() across 73 admin action files
- Add getSession() to public/wholesale server actions
- Fix multi-line return type corruption from earlier auto-fixers
- Move FedEx token cache to non-'use server' module
- Object.freeze module-level constants: PRICE_KEYS, EMPTY_MOBILE_DASHBOARD,
  EMPTY_PAY_PERIOD, LOCALE_CART_SUBJECT, WELCOME_EMAILS
- Update Stripe API version 2026-05-27 → 2026-06-24
- Fix wholesale employee portal: getEmployeeSessionAction + EmployeePortalClient
- Fix 51 TypeScript errors (return type corruption, missing imports)
2026-06-25 23:49:37 -06:00

198 lines
6.0 KiB
TypeScript

/**
* Unit tests for `getAdminUser()`. The dev_session cookie bypass has been
* removed; the only path into the admin is through a real Neon Auth
* session. These tests mock the `getSession()` function and the DB layer
* to exercise the lookup.
*/
import { describe, it, expect, vi, beforeEach } from "vitest";
// Use vi.hoisted to ensure mocks are available when vi.mock runs
const { mockDb, mockWithPlatformAdmin, mockGetSession, mockCookies } = vi.hoisted(() => {
const mockDb = {
select: vi.fn(),
};
return {
mockDb,
mockWithPlatformAdmin: vi.fn(async (fn: (db: typeof mockDb) => Promise<unknown>) => {
return fn(mockDb);
}),
mockGetSession: vi.fn(),
mockCookies: vi.fn(),
};
});
// Stub the server-only guard so the module can be imported under vitest.
vi.mock("server-only", () => ({}));
// Mock the Drizzle client wrapper
vi.mock("@/db/client", () => ({
withPlatformAdmin: mockWithPlatformAdmin,
}));
// Mock the getSession() function
vi.mock("@/lib/auth", () => ({
getSession: mockGetSession,
}));
// Mock cookies() so we don't read a real cookie store
vi.mock("next/headers", () => ({
cookies: mockCookies,
}));
import { getAdminUser, buildDevAdmin, permissionsForRole } from "@/lib/admin-permissions";
const cookieStore = { get: vi.fn() };
beforeEach(() => {
mockDb.select.mockClear();
mockWithPlatformAdmin.mockClear();
mockGetSession.mockClear();
mockCookies.mockImplementation(() => Promise.resolve(cookieStore));
cookieStore.get.mockReturnValue(undefined);
});
describe("getAdminUser()", () => {
it("returns null when there is no Neon Auth session", async () => {
mockGetSession.mockResolvedValue({ data: null });
const u = await getAdminUser();
expect(u).toBeNull();
});
it("returns null when the session has no email", async () => {
mockGetSession.mockResolvedValue({ data: { user: { name: "no-email" } } });
const u = await getAdminUser();
expect(u).toBeNull();
});
it("returns null when the email is not in the users table", async () => {
mockGetSession.mockResolvedValue({ data: { user: { email: "unknown@example.com" } } });
// First select: users. Returns empty.
mockDb.select.mockReturnValueOnce({
from: () => ({
where: () => ({
limit: async () => [],
}),
}),
});
const u = await getAdminUser();
expect(u).toBeNull();
});
it("returns null when the user exists but has no admin_user_brands row", async () => {
mockGetSession.mockResolvedValue({ data: { user: { email: "no-brand@example.com" } } });
// First select: users — returns the user
mockDb.select
.mockReturnValueOnce({
from: () => ({
where: () => ({
limit: async () => [
{
id: "user-1",
email: "no-brand@example.com",
name: "No Brand",
role: "brand_admin",
},
],
}),
}),
})
// Second select: adminUserBrands — returns empty
.mockReturnValueOnce({
from: () => ({
innerJoin: () => ({
where: () => ({
limit: async () => [],
}),
}),
}),
});
const u = await getAdminUser();
expect(u).toBeNull();
});
it("returns a fully-populated AdminUser for a provisioned brand_admin", async () => {
mockGetSession.mockResolvedValue({ data: { user: { email: "admin@tuxedo.example" } } });
mockDb.select
.mockReturnValueOnce({
from: () => ({
where: () => ({
limit: async () => [
{
id: "user-tux",
email: "admin@tuxedo.example",
name: "Tux Admin",
role: "brand_admin",
},
],
}),
}),
})
.mockReturnValueOnce({
from: () => ({
innerJoin: () => ({
where: async () => [
{
brandId: "brand-tux",
brandName: "Tuxedo Citrus",
brandSlug: "tuxedo",
role: "brand_admin",
},
],
}),
}),
});
const u = await getAdminUser();
expect(u).not.toBeNull();
expect(u?.email).toBe("admin@tuxedo.example");
expect(u?.brand_id).toBe("brand-tux");
expect(u?.brand_slug).toBe("tuxedo");
expect(u?.role).toBe("brand_admin");
expect(u?.can_manage_products).toBe(true);
expect(u?.can_manage_team).toBe(true);
});
});
describe("buildDevAdmin()", () => {
it("returns a platform_admin with no brand", () => {
const u = buildDevAdmin("platform_admin");
expect(u.role).toBe("platform_admin");
expect(u.brand_id).toBeNull();
expect(u.brand_slug).toBeNull();
expect(u.can_manage_billing).toBe(true);
});
it("returns a brand_admin tied to the tuxedo brand", () => {
const u = buildDevAdmin("brand_admin");
expect(u.role).toBe("brand_admin");
expect(u.brand_slug).toBe("tuxedo");
expect(u.can_manage_users).toBe(false);
});
it("returns a store_employee with limited permissions", () => {
const u = buildDevAdmin("store_employee");
expect(u.role).toBe("store_employee");
expect(u.can_manage_products).toBe(false);
expect(u.can_manage_orders).toBe(true);
expect(u.can_manage_billing).toBe(false);
});
});
describe("permissionsForRole()", () => {
it("platform_admin can do everything", () => {
const p = permissionsForRole("platform_admin");
expect(Object.values(p).every((v) => v === true)).toBe(true);
});
it("brand_admin can manage most things but not users", () => {
const p = permissionsForRole("brand_admin");
expect(p.can_manage_users).toBe(false);
expect(p.can_manage_billing).toBe(true);
});
it("store_employee is restricted to orders + pickup", () => {
const p = permissionsForRole("store_employee");
expect(p.can_manage_orders).toBe(true);
expect(p.can_manage_pickup).toBe(true);
expect(p.can_manage_products).toBe(false);
expect(p.can_manage_billing).toBe(false);
});
});