import { renderToStaticMarkup } from "react-dom/server"; import { describe, expect, it } from "vitest"; import { PERMS } from "@/lib/permission-slugs"; import { fail, type HousekeepingCapabilityContext, type HousekeepingResult, ok, } from "../../../foundation/contracts"; import { HOUSEKEEPING_MANIFESTS } from "../../../manifests"; import { HOUSEKEEPING_ROUTE_HANDLERS } from "../../../route-handlers"; import { peopleManifest } from "../manifest"; import type { PeopleCommunityQueryData } from "../queries/community"; import type { PeopleStaffQueryData } from "../queries/staff"; import type { PeopleUsersQueryData } from "../queries/users"; import { PEOPLE_PRIMARY_ROUTE_HANDLERS, PEOPLE_PRIMARY_ROUTE_IDS, } from "../route-handlers"; import { PeopleCommunityPage } from "./community"; import { PeopleMultiAccountsPage } from "./multi-accounts"; import { PeopleStaffPage } from "./staff"; import { PeopleUserDetailPage } from "./user-detail"; import { PeopleUserEditPage } from "./user-edit"; import { PeopleUsersPage } from "./users"; const correlationId = "people-pages-test"; function capabilityContext( granted: readonly string[], ): HousekeepingCapabilityContext { const permissions = new Set(granted); return { actor: { id: 42, username: "operator", rank: 6 }, isSuperAdmin: false, has: (slug) => permissions.has(slug), hasAny: (...slugs) => slugs.some((slug) => permissions.has(slug)), hasAll: (...slugs) => slugs.every((slug) => permissions.has(slug)), }; } const readContext = capabilityContext([PERMS.USERS_VIEW]); const modReadContext = capabilityContext([PERMS.MOD_USERS_VIEW]); const user = { id: 7, username: "Alice", rank: 2, online: true, mail: "alice@example.test", ipCurrent: "198.51.100.7", bannedUntil: null, href: "/ase/people/users/7" as const, }; const detail = { ...user, motto: "Ready", look: "hd-180-1", accountCreated: "2026-08-01T00:00:00.000Z", lastLogin: "2026-08-28T00:00:00.000Z", sanctions: [], watched: false, permission: { rankName: "Member", ranks: [{ id: 2, name: "Member" }] }, }; type PageCase = { readonly name: string; readonly render: ( result?: HousekeepingResult, partialDependencies?: readonly string[], ) => string; readonly empty: unknown; readonly ready: unknown; }; const cases: readonly PageCase[] = [ { name: "users", render: (result, partialDependencies) => renderToStaticMarkup( } partialDependencies={partialDependencies} />, ), empty: { kind: "users", page: { items: [], total: 0, pageSize: 20, offset: 0 }, }, ready: { kind: "users", page: { items: [user], total: 1, pageSize: 20, offset: 0 }, }, }, { name: "user-detail", render: (result, partialDependencies) => renderToStaticMarkup( } partialDependencies={partialDependencies} />, ), empty: { kind: "user", user: { ...detail, sanctions: [] } }, ready: { kind: "user", user: { ...detail, sanctions: [ { id: 3, kind: "account", reason: "spam", createdAt: null, expiresAt: 0, active: true, }, ], }, }, }, { name: "user-edit", render: (result, partialDependencies) => renderToStaticMarkup( } partialDependencies={partialDependencies} />, ), empty: fail("NOT_FOUND", "errors.housekeeping.notFound", correlationId), ready: { kind: "user", user: { ...detail, watched: true } }, }, { name: "multi-accounts", render: (result, partialDependencies) => renderToStaticMarkup( } partialDependencies={partialDependencies} />, ), empty: { kind: "multi-accounts", page: { items: [], total: 0, pageSize: 20, offset: 0 }, }, ready: { kind: "multi-accounts", page: { items: [{ key: "198.51.100.7", accountCount: 2, accounts: [user] }], total: 1, pageSize: 20, offset: 0, }, }, }, { name: "community", render: (result, partialDependencies) => renderToStaticMarkup( } partialDependencies={partialDependencies} />, ), empty: { kind: "guilds", page: { items: [], total: 0, pageSize: 20, offset: 0 }, }, ready: { kind: "guilds", page: { items: [ { id: 9, name: "Builders", description: "Rooms", userId: 7, ownerUsername: "Alice", roomId: 4, memberCount: 2, createdAt: null, href: "/ase/people/community/guilds/9", }, ], total: 1, pageSize: 20, offset: 0, }, }, }, { name: "staff", render: (result, partialDependencies) => renderToStaticMarkup( } partialDependencies={partialDependencies} />, ), empty: { kind: "applications", page: { items: [], total: 0, pageSize: 20, offset: 0 }, }, ready: { kind: "applications", page: { items: [ { id: 5, userId: 7, username: "Alice", rankId: 4, content: "Experienced", createdAt: null, }, ], total: 1, pageSize: 20, offset: 0, }, }, }, ]; describe.each(cases)("People $name page", ({ render, empty, ready }) => { it("renders loading, forbidden, dependency error, empty, partial, and ready states", () => { expect(render()).toContain('data-housekeeping-state="loading"'); expect( render(fail("FORBIDDEN", "errors.housekeeping.forbidden", correlationId)), ).toContain('data-housekeeping-state="forbidden"'); expect( render( fail( "DEPENDENCY_UNAVAILABLE", "errors.housekeeping.dependencyUnavailable", correlationId, ), ), ).toContain('data-housekeeping-state="error"'); const emptyResult = typeof empty === "object" && empty !== null && "ok" in empty ? (empty as HousekeepingResult) : ok(empty, correlationId); expect(render(emptyResult)).toContain('data-housekeeping-state="empty"'); expect(render(ok(ready, correlationId), ["secondary"])).toContain( 'data-housekeeping-state="partial"', ); expect(render(ok(ready, correlationId))).toContain( 'data-housekeeping-state="ready"', ); }); }); describe("People primary route registration", () => { it("registers exactly the nine real primary routes and handlers", () => { const expected = [ "people.users.list", "people.users.edit", "people.users.multi-accounts", "people.users.detail", "people.community.online", "people.community.guilds", "people.community.guild-detail", "people.staff.applications", "people.staff.teams", ]; expect(PEOPLE_PRIMARY_ROUTE_IDS).toEqual(expected); expect(peopleManifest.routes.map((route) => route.id)).toEqual(expected); expect( PEOPLE_PRIMARY_ROUTE_HANDLERS.map((handler) => handler.routeId), ).toEqual(expected); expect( HOUSEKEEPING_ROUTE_HANDLERS.map((handler) => handler.routeId), ).toEqual(expect.arrayContaining(expected)); expect( HOUSEKEEPING_MANIFESTS.flatMap((manifest) => manifest.routes).map( (route) => route.id, ), ).toEqual(HOUSEKEEPING_ROUTE_HANDLERS.map((handler) => handler.routeId)); }); it("uses canonical People links and exposes PII and actions only with exact capabilities", () => { const redacted = { ...detail, mail: null, ipCurrent: null, }; const modHtml = renderToStaticMarkup( , ); expect(modHtml).not.toContain("alice@example.test"); expect(modHtml).not.toContain("198.51.100.7"); expect(modHtml).not.toContain("people.user.update"); const editorHtml = renderToStaticMarkup( , ); expect(editorHtml).toContain('href="/ase/people/users/7/edit"'); expect(editorHtml).toContain("people.user.update"); expect(editorHtml).not.toContain("/admin"); }); });