forked from baron/baron-sso
120 lines
3.6 KiB
TypeScript
120 lines
3.6 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
|
import {
|
|
type AuditLog,
|
|
type Consent,
|
|
installDevApiMock,
|
|
makeClient,
|
|
seedAuth,
|
|
} from "./helpers/devfront-fixtures";
|
|
|
|
test.describe("DevFront audit logs", () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
page.on("dialog", async (dialog) => {
|
|
await dialog.accept();
|
|
});
|
|
await seedAuth(page);
|
|
});
|
|
|
|
test("filtering and cursor pagination", async ({ page }) => {
|
|
const state = {
|
|
clients: [makeClient("client-audit", { name: "Audit app" })],
|
|
consents: [] as Consent[],
|
|
auditLogsByCursor: {
|
|
"": {
|
|
items: [
|
|
{
|
|
event_id: "evt-1",
|
|
timestamp: "2026-03-03T09:00:00.000Z",
|
|
user_id: "actor-a",
|
|
event_type: "CLIENT_UPDATE",
|
|
status: "success",
|
|
ip_address: "127.0.0.1",
|
|
user_agent: "playwright",
|
|
details: JSON.stringify({
|
|
action: "UPDATE_CLIENT",
|
|
target_id: "client-audit",
|
|
tenant_id: "tenant-a",
|
|
}),
|
|
},
|
|
],
|
|
next_cursor: "cursor-2",
|
|
},
|
|
"cursor-2": {
|
|
items: [
|
|
{
|
|
event_id: "evt-2",
|
|
timestamp: "2026-03-03T09:01:00.000Z",
|
|
user_id: "actor-b",
|
|
event_type: "CLIENT_ROTATE_SECRET",
|
|
status: "success",
|
|
ip_address: "127.0.0.1",
|
|
user_agent: "playwright",
|
|
details: JSON.stringify({
|
|
action: "ROTATE_SECRET",
|
|
target_id: "client-audit",
|
|
tenant_id: "tenant-a",
|
|
}),
|
|
},
|
|
],
|
|
},
|
|
},
|
|
};
|
|
await installDevApiMock(page, state);
|
|
|
|
await page.goto("/audit-logs");
|
|
await expect(page.getByText("UPDATE_CLIENT")).toBeVisible();
|
|
|
|
await page
|
|
.getByPlaceholder(/Client ID로 필터|Filter by Client ID/i)
|
|
.fill("client-audit");
|
|
await page
|
|
.getByPlaceholder(/액션으로 필터|Filter by Action/i)
|
|
.fill("ROTATE_SECRET");
|
|
|
|
await page.getByRole("button", { name: /더 보기|Load more/i }).click();
|
|
await expect(page.getByText("ROTATE_SECRET")).toBeVisible();
|
|
});
|
|
|
|
test("realtime create/update actions should be recorded", async ({
|
|
page,
|
|
}) => {
|
|
const state = {
|
|
clients: [makeClient("client-realtime", { name: "Realtime app" })],
|
|
consents: [] as Consent[],
|
|
auditLogs: [] as AuditLog[],
|
|
auditLogsByCursor: undefined,
|
|
};
|
|
await installDevApiMock(page, state);
|
|
|
|
await page.goto("/clients/new");
|
|
await page
|
|
.getByPlaceholder("My Awesome Application")
|
|
.fill("Realtime New App");
|
|
await page
|
|
.getByPlaceholder(/https:\/\/app\.example\.com\/callback/i)
|
|
.fill("https://realtime.example.com/callback");
|
|
await page.getByRole("button", { name: /앱 생성|Create/i }).click();
|
|
await expect.poll(() => state.auditLogs.length).toBeGreaterThanOrEqual(1);
|
|
|
|
await page.goto("/clients/client-realtime/settings");
|
|
await page
|
|
.getByPlaceholder("My Awesome Application")
|
|
.fill("Realtime Updated");
|
|
await page.getByRole("button", { name: /^저장$|^Save$/i }).click();
|
|
await expect.poll(() => state.auditLogs.length).toBeGreaterThanOrEqual(2);
|
|
|
|
const actions = state.auditLogs
|
|
.map((item) => {
|
|
try {
|
|
return JSON.parse(item.details)?.action as string | undefined;
|
|
} catch {
|
|
return undefined;
|
|
}
|
|
})
|
|
.filter((value): value is string => Boolean(value));
|
|
|
|
expect(actions).toContain("CREATE_CLIENT");
|
|
expect(actions).toContain("UPDATE_CLIENT");
|
|
});
|
|
});
|