forked from baron/baron-sso
265 lines
8.2 KiB
TypeScript
265 lines
8.2 KiB
TypeScript
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
|
import type React from "react";
|
|
import { MemoryRouter } from "react-router-dom";
|
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import {
|
|
fetchAdminRPUsageDaily,
|
|
fetchDataIntegrityReport,
|
|
} from "../../lib/adminApi";
|
|
import AuthPage from "../auth/AuthPage";
|
|
import GlobalOverviewPage from "./GlobalOverviewPage";
|
|
|
|
let currentRole = "super_admin";
|
|
|
|
vi.mock("../../lib/adminApi", () => ({
|
|
fetchMe: vi.fn(async () => ({ role: currentRole })),
|
|
fetchAdminOverviewStats: vi.fn(async () => ({
|
|
totalTenants: 10,
|
|
totalUsers: 152,
|
|
oidcClients: 3,
|
|
auditEvents24h: 18,
|
|
})),
|
|
fetchAllTenants: vi.fn(async () => ({
|
|
items: [
|
|
{
|
|
id: "group-1",
|
|
type: "COMPANY_GROUP",
|
|
name: "한맥그룹",
|
|
slug: "hanmac-group",
|
|
description: "",
|
|
status: "active",
|
|
memberCount: 0,
|
|
createdAt: "2026-05-06T00:00:00Z",
|
|
updatedAt: "2026-05-06T00:00:00Z",
|
|
},
|
|
{
|
|
id: "company-1",
|
|
type: "COMPANY",
|
|
name: "한맥",
|
|
slug: "hanmac",
|
|
description: "",
|
|
status: "active",
|
|
memberCount: 0,
|
|
createdAt: "2026-05-06T00:00:00Z",
|
|
updatedAt: "2026-05-06T00:00:00Z",
|
|
},
|
|
{
|
|
id: "org-1",
|
|
type: "ORGANIZATION",
|
|
name: "개발팀",
|
|
slug: "dev-team",
|
|
description: "",
|
|
status: "active",
|
|
memberCount: 0,
|
|
createdAt: "2026-05-06T00:00:00Z",
|
|
updatedAt: "2026-05-06T00:00:00Z",
|
|
},
|
|
{
|
|
id: "personal-1",
|
|
type: "PERSONAL",
|
|
name: "개인",
|
|
slug: "personal",
|
|
description: "",
|
|
status: "active",
|
|
memberCount: 0,
|
|
createdAt: "2026-05-06T00:00:00Z",
|
|
updatedAt: "2026-05-06T00:00:00Z",
|
|
},
|
|
],
|
|
limit: 1000,
|
|
offset: 0,
|
|
total: 4,
|
|
})),
|
|
fetchAdminRPUsageDaily: vi.fn(async () => ({
|
|
days: 14,
|
|
period: "day",
|
|
items: [
|
|
{
|
|
date: "2026-05-05",
|
|
tenantId: "company-1",
|
|
tenantType: "COMPANY",
|
|
tenantName: "한맥",
|
|
clientId: "orgfront",
|
|
clientName: "OrgFront",
|
|
loginRequests: 12,
|
|
otherRequests: 4,
|
|
uniqueSubjects: 8,
|
|
},
|
|
{
|
|
date: "2026-05-06",
|
|
tenantId: "company-1",
|
|
tenantType: "COMPANY",
|
|
tenantName: "한맥",
|
|
clientId: "adminfront",
|
|
clientName: "AdminFront",
|
|
loginRequests: 7,
|
|
otherRequests: 3,
|
|
uniqueSubjects: 5,
|
|
},
|
|
{
|
|
date: "2026-09-28",
|
|
tenantId: "company-1",
|
|
tenantType: "COMPANY",
|
|
tenantName: "한맥",
|
|
clientId: "devfront",
|
|
clientName: "DevFront",
|
|
loginRequests: 2,
|
|
otherRequests: 1,
|
|
uniqueSubjects: 2,
|
|
},
|
|
],
|
|
})),
|
|
fetchDataIntegrityReport: vi.fn(async () => ({
|
|
status: "fail",
|
|
checkedAt: "2026-05-14T00:00:00Z",
|
|
summary: {
|
|
totalChecks: 5,
|
|
passed: 4,
|
|
warnings: 0,
|
|
failures: 1,
|
|
},
|
|
sections: [
|
|
{
|
|
key: "tenant_integrity",
|
|
label: "테넌트 정합성",
|
|
status: "pass",
|
|
checks: [],
|
|
},
|
|
{
|
|
key: "user_integrity",
|
|
label: "사용자 정합성",
|
|
status: "fail",
|
|
checks: [],
|
|
},
|
|
],
|
|
})),
|
|
}));
|
|
|
|
function renderWithProviders(ui: React.ReactElement) {
|
|
const queryClient = new QueryClient({
|
|
defaultOptions: {
|
|
queries: { retry: false },
|
|
mutations: { retry: false },
|
|
},
|
|
});
|
|
|
|
return render(
|
|
<QueryClientProvider client={queryClient}>
|
|
<MemoryRouter>{ui}</MemoryRouter>
|
|
</QueryClientProvider>,
|
|
);
|
|
}
|
|
|
|
describe("admin overview and auth guard pages", () => {
|
|
beforeEach(() => {
|
|
currentRole = "super_admin";
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
it("renders usage trend chart without quick navigation or permission checker", async () => {
|
|
renderWithProviders(<GlobalOverviewPage />);
|
|
|
|
expect(
|
|
await screen.findByText("회사별 앱별 로그인 요청 현황"),
|
|
).toBeInTheDocument();
|
|
expect(
|
|
await screen.findByLabelText("일 단위 RP 요청 현황"),
|
|
).toBeInTheDocument();
|
|
expect(await screen.findByText("05.05")).toBeInTheDocument();
|
|
expect(await screen.findByText("05.06")).toBeInTheDocument();
|
|
expect(screen.queryByText("빠른 작업")).not.toBeInTheDocument();
|
|
expect(screen.queryByText("빠른 이동")).not.toBeInTheDocument();
|
|
expect(screen.queryByText("테넌트 추가")).not.toBeInTheDocument();
|
|
expect(screen.queryByText("ReBAC 권한 검증 도구")).not.toBeInTheDocument();
|
|
});
|
|
|
|
it("renders overview tenant count from the fully fetched tenant list", async () => {
|
|
renderWithProviders(<GlobalOverviewPage />);
|
|
|
|
expect(
|
|
(await screen.findByText("전체 테넌트 수")).parentElement,
|
|
).toHaveTextContent("4");
|
|
expect(screen.getByText("OIDC 클라이언트").parentElement).toHaveTextContent(
|
|
"3",
|
|
);
|
|
expect(screen.getByText("전체 사용자 수").parentElement).toHaveTextContent(
|
|
"152",
|
|
);
|
|
expect(screen.getByText("24시간 이벤트").parentElement).toHaveTextContent(
|
|
"18",
|
|
);
|
|
});
|
|
|
|
it("limits the overview graph choices to company tenants", async () => {
|
|
renderWithProviders(<GlobalOverviewPage />);
|
|
|
|
await screen.findByText("회사별 앱별 로그인 요청 현황");
|
|
|
|
expect(
|
|
await screen.findByRole("checkbox", { name: "한맥 (hanmac)" }),
|
|
).toBeInTheDocument();
|
|
expect(
|
|
screen.queryByText("한맥그룹 (hanmac-group)"),
|
|
).not.toBeInTheDocument();
|
|
expect(screen.queryByText("개발팀 (dev-team)")).not.toBeInTheDocument();
|
|
expect(screen.queryByText("개인 (personal)")).not.toBeInTheDocument();
|
|
});
|
|
|
|
it("changes the RP usage perspective and targets a permitted company", async () => {
|
|
renderWithProviders(<GlobalOverviewPage />);
|
|
|
|
await screen.findByText("회사별 앱별 로그인 요청 현황");
|
|
fireEvent.click(screen.getByRole("button", { name: "주" }));
|
|
expect(await screen.findAllByText("19(05월1주)")).not.toHaveLength(0);
|
|
expect(await screen.findAllByText("40(10월1주)")).not.toHaveLength(0);
|
|
fireEvent.click(screen.getByRole("button", { name: "월" }));
|
|
fireEvent.click(screen.getByRole("checkbox", { name: "한맥 (hanmac)" }));
|
|
|
|
await waitFor(() => {
|
|
expect(fetchAdminRPUsageDaily).toHaveBeenLastCalledWith({
|
|
days: 90,
|
|
period: "month",
|
|
});
|
|
});
|
|
expect(
|
|
screen.queryByText("한맥그룹 (hanmac-group)"),
|
|
).not.toBeInTheDocument();
|
|
expect(screen.queryByText("개발팀 (dev-team)")).not.toBeInTheDocument();
|
|
expect(screen.queryByText("개인 (personal)")).not.toBeInTheDocument();
|
|
expect(await screen.findAllByText("05월")).not.toHaveLength(0);
|
|
});
|
|
|
|
it("shows the latest integrity summary at the bottom for super admins only", async () => {
|
|
renderWithProviders(<GlobalOverviewPage />);
|
|
|
|
expect(await screen.findByText("정합성 최종 검증")).toBeInTheDocument();
|
|
expect(screen.getByText("실패 1건")).toBeInTheDocument();
|
|
expect(screen.getByText("테넌트 정합성")).toBeInTheDocument();
|
|
expect(screen.getByText("사용자 정합성")).toBeInTheDocument();
|
|
expect(fetchDataIntegrityReport).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("does not fetch or show the integrity summary for non-super admins", async () => {
|
|
currentRole = "tenant_admin";
|
|
|
|
renderWithProviders(<GlobalOverviewPage />);
|
|
|
|
await screen.findByText("회사별 앱별 로그인 요청 현황");
|
|
expect(screen.queryByText("정합성 최종 검증")).not.toBeInTheDocument();
|
|
expect(fetchDataIntegrityReport).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("moves the permission checker to the auth guard page and removes mock guardrails", () => {
|
|
renderWithProviders(<AuthPage />);
|
|
|
|
expect(screen.getByText("인증가드")).toBeInTheDocument();
|
|
expect(screen.getByText("ReBAC 권한 검증 도구")).toBeInTheDocument();
|
|
expect(screen.queryByText("Admin auth guardrails")).not.toBeInTheDocument();
|
|
expect(
|
|
screen.queryByText("IDP session placeholder"),
|
|
).not.toBeInTheDocument();
|
|
expect(screen.queryByText("Admin login")).not.toBeInTheDocument();
|
|
});
|
|
});
|