첫 커밋: 로컬 프로젝트 업로드

This commit is contained in:
2026-06-10 15:51:34 +09:00
commit 6a8dbeb2e9
1211 changed files with 312864 additions and 0 deletions

View File

@@ -0,0 +1,253 @@
import { beforeEach, describe, expect, it } from "vitest";
import type { ClientSummary, DevAuditLog } from "../../lib/devApi";
import {
buildRecentClientChangeDetails,
buildRecentClientChanges,
getRecentClientActionLabel,
} from "./recentClientChanges";
function makeClient(id: string, name = id): ClientSummary {
return {
id,
name,
type: "private",
status: "active",
createdAt: "2026-05-27T00:00:00.000Z",
redirectUris: [],
scopes: [],
};
}
function makeAuditLog(
eventId: string,
timestamp: string,
action: string,
targetId: string,
details: Record<string, unknown>,
): DevAuditLog {
return {
event_id: eventId,
timestamp,
user_id: "actor-1",
event_type: "AUDIT",
status: "success",
ip_address: "127.0.0.1",
user_agent: "vitest",
details: JSON.stringify({
action,
target_id: targetId,
...details,
}),
};
}
describe("recent client changes", () => {
beforeEach(() => {
window.localStorage.clear();
window.history.replaceState({}, "", "/");
});
function mockLocale(locale: "ko" | "en") {
window.localStorage.clear();
window.history.replaceState({}, "", `/${locale}`);
}
it("translates action labels and relation details by locale", () => {
mockLocale("en");
expect(getRecentClientActionLabel("CREATE_CLIENT")).toBe("App creation");
expect(getRecentClientActionLabel("UPDATE_CLIENT")).toBe(
"Settings changes",
);
expect(getRecentClientActionLabel("UPDATE_CLIENT_STATUS")).toBe(
"Status changes",
);
expect(getRecentClientActionLabel("ROTATE_SECRET")).toBe(
"Client secret rotation",
);
expect(getRecentClientActionLabel("ADD_RELATION")).toBe("Add Relationship");
expect(getRecentClientActionLabel("REMOVE_RELATION")).toBe(
"Remove Relationship",
);
expect(getRecentClientActionLabel("DELETE_CLIENT")).toBe("App deletion");
expect(getRecentClientActionLabel("OTHER_ACTION")).toBe("OTHER_ACTION");
expect(
buildRecentClientChangeDetails("ROTATE_SECRET", {
after: {},
}),
).toEqual([{ label: "Client Secret", value: "Secret Rotated" }]);
expect(
buildRecentClientChangeDetails("ADD_RELATION", {
after: {
relation: "admins",
subject: "User:1",
},
}),
).toEqual([
{ label: "Relation", value: "admins" },
{ label: "Subject", value: "User:1" },
]);
});
it("builds recent client changes with sorting, filtering, and detail slicing", () => {
mockLocale("ko");
const clients = [
makeClient("client-a", "Alpha"),
makeClient("client-b", ""),
];
const auditLogs = [
makeAuditLog(
"evt-1",
"2026-05-27T07:00:00.000Z",
"CREATE_CLIENT",
"client-a",
{
after: { name: "Alpha", type: "private", status: "active" },
},
),
makeAuditLog(
"evt-2",
"2026-05-27T08:00:00.000Z",
"UPDATE_CLIENT",
"client-a",
{
before: {
name: "Alpha old",
status: "inactive",
sameField: "same",
oldField: "old-value",
},
after: {
name: "Alpha new",
status: "active",
sameField: "same",
newField: "new-value",
},
},
),
makeAuditLog(
"evt-3",
"2026-05-27T09:00:00.000Z",
"UPDATE_CLIENT_STATUS",
"client-a",
{
before: { status: "inactive" },
after: { status: "active" },
},
),
makeAuditLog(
"evt-4",
"2026-05-27T10:00:00.000Z",
"ADD_RELATION",
"client-b",
{
after: {
relation: "audit_viewer",
subject: "User:89692983-f512-4d96-845d-ac6123d08b95",
},
},
),
makeAuditLog(
"evt-5",
"2026-05-27T11:00:00.000Z",
"REMOVE_RELATION",
"client-b",
{
before: {
relation: "admins",
subject: "User:89692983-f512-4d96-845d-ac6123d08b95",
},
},
),
makeAuditLog(
"evt-6",
"2026-05-27T12:00:00.000Z",
"ROTATE_SECRET",
"client-a",
{
after: {},
},
),
makeAuditLog(
"evt-7",
"2026-05-27T13:00:00.000Z",
"DELETE_CLIENT",
"client-a",
{
before: {
name: "Alpha",
status: "inactive",
},
},
),
makeAuditLog(
"evt-8",
"2026-05-27T14:00:00.000Z",
"UNSUPPORTED_ACTION",
"client-a",
{
after: { name: "Ignored" },
},
),
];
const changes = buildRecentClientChanges(auditLogs, clients);
expect(changes).toHaveLength(7);
expect(changes[0]).toMatchObject({
eventId: "evt-7",
clientName: "Alpha",
actionLabel: "앱 삭제",
});
expect(changes[1]).toMatchObject({
eventId: "evt-6",
clientName: "Alpha",
actionLabel: "클라이언트 시크릿 재발급",
detailLabels: [
{
label: "클라이언트 시크릿",
value: "Client Secret이 재발급되었습니다.",
},
],
});
expect(changes[2]).toMatchObject({
eventId: "evt-5",
clientName: "client-b",
actionLabel: "관계 삭제",
detailLabels: [
{ label: "관계", value: "admins" },
{
label: "주체",
value: "User:89692983-f512-4d96-845d-ac6123d08b95",
},
],
});
expect(changes[4]).toMatchObject({
eventId: "evt-3",
actionLabel: "상태 변경",
clientName: "Alpha",
detailLabels: [{ value: "inactive → active" }],
});
expect(changes[5]).toMatchObject({
eventId: "evt-2",
actionLabel: "설정 변경",
detailLabels: [
{ label: "애플리케이션", value: "Alpha old → Alpha new" },
{ label: "상태", value: "inactive → active" },
{ label: "oldField", value: "old-value" },
],
});
expect(changes[6]).toMatchObject({
eventId: "evt-1",
actionLabel: "앱 생성",
detailLabels: [
{ label: "애플리케이션", value: "Alpha" },
{ label: "유형", value: "private" },
{ label: "상태", value: "active" },
],
});
});
});