forked from baron/baron-sso
126 lines
3.5 KiB
TypeScript
126 lines
3.5 KiB
TypeScript
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
import { render, screen, waitFor } from "@testing-library/react";
|
|
import userEvent from "@testing-library/user-event";
|
|
import { MemoryRouter } from "react-router-dom";
|
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import {
|
|
fetchApiKeys,
|
|
rotateApiKeySecret,
|
|
updateApiKeyScopes,
|
|
} from "../../lib/adminApi";
|
|
import ApiKeyListPage from "./ApiKeyListPage";
|
|
|
|
vi.mock("../../lib/i18n", () => ({
|
|
t: (_key: string, fallback?: string) => fallback ?? "",
|
|
}));
|
|
|
|
vi.mock("../../lib/adminApi", () => ({
|
|
fetchApiKeys: vi.fn(async () => ({
|
|
items: [
|
|
{
|
|
id: "api-key-id",
|
|
name: "org-context-client",
|
|
client_id: "client-id-stable",
|
|
scopes: ["audit:read"],
|
|
status: "active",
|
|
createdAt: "2026-05-13T00:00:00Z",
|
|
},
|
|
],
|
|
total: 1,
|
|
})),
|
|
deleteApiKey: vi.fn(async () => undefined),
|
|
updateApiKeyScopes: vi.fn(async () => ({
|
|
id: "api-key-id",
|
|
name: "org-context-client",
|
|
client_id: "client-id-stable",
|
|
scopes: ["audit:read", "org-context:read"],
|
|
status: "active",
|
|
createdAt: "2026-05-13T00:00:00Z",
|
|
})),
|
|
rotateApiKeySecret: vi.fn(async () => ({
|
|
apiKey: {
|
|
id: "api-key-id",
|
|
name: "org-context-client",
|
|
client_id: "client-id-stable",
|
|
scopes: ["audit:read"],
|
|
status: "active",
|
|
createdAt: "2026-05-13T00:00:00Z",
|
|
},
|
|
clientSecret: "rotated-secret",
|
|
})),
|
|
}));
|
|
|
|
function renderPage() {
|
|
const queryClient = new QueryClient({
|
|
defaultOptions: {
|
|
queries: { retry: false },
|
|
mutations: { retry: false },
|
|
},
|
|
});
|
|
|
|
return render(
|
|
<QueryClientProvider client={queryClient}>
|
|
<MemoryRouter>
|
|
<ApiKeyListPage />
|
|
</MemoryRouter>
|
|
</QueryClientProvider>,
|
|
);
|
|
}
|
|
|
|
describe("ApiKeyListPage", () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
vi.spyOn(window, "confirm").mockReturnValue(true);
|
|
});
|
|
|
|
it("updates scopes without changing client_id", async () => {
|
|
const user = userEvent.setup({ delay: null });
|
|
renderPage();
|
|
|
|
expect(await screen.findByText("client-id-stable")).toBeInTheDocument();
|
|
|
|
await user.click(screen.getByRole("button", { name: /권한 수정/ }));
|
|
await user.click(screen.getByRole("button", { name: /조직 Context 조회/ }));
|
|
await user.click(screen.getByRole("button", { name: /권한 저장/ }));
|
|
|
|
await waitFor(() => {
|
|
expect(updateApiKeyScopes).toHaveBeenCalledWith("api-key-id", {
|
|
scopes: expect.arrayContaining(["audit:read", "org-context:read"]),
|
|
});
|
|
});
|
|
}, 15_000);
|
|
|
|
it("rotates only the secret and shows the one-time secret", async () => {
|
|
const user = userEvent.setup();
|
|
renderPage();
|
|
|
|
expect(await screen.findByText("client-id-stable")).toBeInTheDocument();
|
|
|
|
await user.click(screen.getByRole("button", { name: /Secret 재발급/ }));
|
|
|
|
await waitFor(() => {
|
|
expect(rotateApiKeySecret).toHaveBeenCalledWith("api-key-id");
|
|
});
|
|
expect(
|
|
await screen.findByDisplayValue("rotated-secret"),
|
|
).toBeInTheDocument();
|
|
expect(fetchApiKeys).toHaveBeenCalled();
|
|
});
|
|
|
|
it("refresh button refetches the list without navigation", async () => {
|
|
const user = userEvent.setup();
|
|
renderPage();
|
|
|
|
await screen.findByText("client-id-stable");
|
|
|
|
const refreshButton = screen.getByRole("button", { name: /새로고침/ });
|
|
expect(refreshButton).toHaveAttribute("type", "button");
|
|
|
|
await user.click(refreshButton);
|
|
|
|
await waitFor(() => {
|
|
expect(fetchApiKeys).toHaveBeenCalledTimes(2);
|
|
});
|
|
});
|
|
});
|