forked from baron/baron-sso
185 lines
4.8 KiB
TypeScript
185 lines
4.8 KiB
TypeScript
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 }) => (
|
|
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
|
);
|
|
}
|
|
|
|
function mockProfile(
|
|
overrides: Partial<UserProfileResponse>,
|
|
): UserProfileResponse {
|
|
return {
|
|
id: "user-id",
|
|
email: "user@example.com",
|
|
name: "Test User",
|
|
phone: "",
|
|
role: "user",
|
|
department: "",
|
|
affiliationType: "general",
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function mockTenant(overrides: Partial<TenantSummary>): 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(
|
|
<TenantPermissionGuard
|
|
tenantId="tenant-3"
|
|
relation="manage"
|
|
fallback={<div>Access Denied</div>}
|
|
>
|
|
<div>Access Granted</div>
|
|
</TenantPermissionGuard>,
|
|
{ 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(
|
|
<TenantPermissionGuard
|
|
tenantId="tenant-4"
|
|
relation="manage"
|
|
fallback={<div>Access Denied</div>}
|
|
>
|
|
<div>Access Granted</div>
|
|
</TenantPermissionGuard>,
|
|
{ wrapper: createWrapper() },
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getByText("Access Denied")).toBeInTheDocument();
|
|
});
|
|
expect(screen.queryByText("Access Granted")).not.toBeInTheDocument();
|
|
});
|
|
});
|