import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { render, renderHook, screen, waitFor } from "@testing-library/react"; import type React from "react"; import { describe, expect, it, vi } from "vitest"; import { fetchMe, fetchTenant, type TenantSummary, type UserProfileResponse, } from "../../../lib/adminApi"; import { TenantPermissionGuard } from "../components/TenantPermissionGuard"; import { useTenantPermission } from "./useTenantPermission"; vi.mock("../../../lib/adminApi", () => ({ fetchMe: vi.fn(), fetchTenant: vi.fn(), })); function createWrapper() { const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false }, }, }); return ({ children }: { children: React.ReactNode }) => ( {children} ); } function mockProfile( overrides: Partial, ): UserProfileResponse { return { id: "user-id", email: "user@example.com", name: "Test User", phone: "", role: "user", department: "", affiliationType: "general", ...overrides, }; } function mockTenant(overrides: Partial): TenantSummary { return { id: "tenant-id", type: "COMPANY", name: "Test Tenant", slug: "test-tenant", description: "", status: "active", memberCount: 0, createdAt: "2026-01-01T00:00:00Z", updatedAt: "2026-01-01T00:00:00Z", ...overrides, }; } describe("useTenantPermission", () => { it("returns true for all permissions if user is super_admin", async () => { vi.mocked(fetchMe).mockResolvedValue( mockProfile({ id: "user-super", role: "super_admin", }), ); vi.mocked(fetchTenant).mockResolvedValue( mockTenant({ id: "tenant-1", name: "Super Tenant", userPermissions: { view: false, manage: false, manage_admins: false }, }), ); const { result } = renderHook(() => useTenantPermission("tenant-1"), { wrapper: createWrapper(), }); await waitFor(() => { expect(result.current.isLoading).toBe(false); }); expect(result.current.hasPermission("view")).toBe(true); expect(result.current.hasPermission("manage")).toBe(true); expect(result.current.hasPermission("manage_admins")).toBe(true); }); it("returns permissions mapped from userPermissions for normal admins/users", async () => { vi.mocked(fetchMe).mockResolvedValue( mockProfile({ id: "user-admin", role: "tenant_admin", }), ); vi.mocked(fetchTenant).mockResolvedValue( mockTenant({ id: "tenant-2", name: "Tenant Admin Corp", userPermissions: { view: true, manage: true, manage_admins: false }, }), ); const { result } = renderHook(() => useTenantPermission("tenant-2"), { wrapper: createWrapper(), }); await waitFor(() => { expect(result.current.isLoading).toBe(false); }); expect(result.current.hasPermission("view")).toBe(true); expect(result.current.hasPermission("manage")).toBe(true); expect(result.current.hasPermission("manage_admins")).toBe(false); }); }); describe("TenantPermissionGuard", () => { it("renders children when user has permission", async () => { vi.mocked(fetchMe).mockResolvedValue( mockProfile({ id: "user-admin", role: "tenant_admin", }), ); vi.mocked(fetchTenant).mockResolvedValue( mockTenant({ id: "tenant-3", userPermissions: { view: true, manage: true, manage_admins: false }, }), ); render( Access Denied} >
Access Granted
, { wrapper: createWrapper() }, ); await waitFor(() => { expect(screen.getByText("Access Granted")).toBeInTheDocument(); }); expect(screen.queryByText("Access Denied")).not.toBeInTheDocument(); }); it("renders fallback when user lacks permission", async () => { vi.mocked(fetchMe).mockResolvedValue( mockProfile({ id: "user-admin", role: "tenant_admin", }), ); vi.mocked(fetchTenant).mockResolvedValue( mockTenant({ id: "tenant-4", userPermissions: { view: true, manage: false, manage_admins: false }, }), ); render( Access Denied} >
Access Granted
, { wrapper: createWrapper() }, ); await waitFor(() => { expect(screen.getByText("Access Denied")).toBeInTheDocument(); }); expect(screen.queryByText("Access Granted")).not.toBeInTheDocument(); }); });