Files
Epicnabbo-Catalogus-Updated…/src/features/housekeeping/domains/people/queries/people-queries.test.ts
T

1032 lines
27 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
import { PERMS } from "@/lib/permission-slugs";
import type { HousekeepingCapabilityContext } from "../../../foundation/contracts";
import { PEOPLE_ROUTES } from "../routes";
import { createPeopleCommunityQuery } from "./community";
import { createPeopleModerationQuery } from "./moderation";
import { createPeopleStaffQuery } from "./staff";
import { createPeopleSupportQuery } from "./support";
import { createPeopleUsersQuery } from "./users";
function context(granted: readonly string[]): HousekeepingCapabilityContext {
const permissions = new Set(granted);
return {
actor: { id: 42, username: "operator", rank: 99 },
isSuperAdmin: false,
has: (slug) => permissions.has(slug),
hasAny: (...slugs) => slugs.some((slug) => permissions.has(slug)),
hasAll: (...slugs) => slugs.every((slug) => permissions.has(slug)),
};
}
const rawUser = (id: number, username: string) => ({
id,
username,
rank: 5,
online: "1",
mail: `${username.toLowerCase()}@example.test`,
ipCurrent: `203.0.113.${id}`,
bannedUntil: null,
});
describe("People users query", () => {
it("includes watched state and permission-rank data required by detail and edit", async () => {
const query = createPeopleUsersQuery({
loadUsers: async () => ({ rows: [], total: 0 }),
loadUser: async () => ({
...rawUser(9, "Nine"),
motto: "",
look: "hd-180-1",
accountCreated: 1_700_000_000,
lastLogin: 1_700_000_100,
}),
loadMultiAccounts: async () => ({ rows: [], total: 0 }),
loadSanctions: async () => [],
loadWatchedUserIds: async () => new Set([9]),
loadPermissionRanks: async () => [
{ id: 1, name: "User" },
{ id: 5, name: "Moderator" },
],
} as never);
const result = await query.run(context([PERMS.USERS_VIEW]), {
routeId: "people.users.detail",
id: 9,
});
expect(result).toMatchObject({
ok: true,
data: {
user: {
watched: true,
permission: {
rankName: "Moderator",
ranks: [
{ id: 1, name: "User" },
{ id: 5, name: "Moderator" },
],
},
},
},
});
});
it("preserves a DB-paged username window without locale re-sort or offset slicing", async () => {
const query = createPeopleUsersQuery({
loadUsers: async () => ({
rows: [rawUser(10, "user10"), rawUser(2, "user2")],
total: 20,
}),
loadUser: async () => null,
loadMultiAccounts: async () => ({ rows: [], total: 0 }),
loadSanctions: async () => [],
loadWatchedUserIds: async () => new Set(),
loadPermissionRanks: async () => [{ id: 5, name: "Moderator" }],
});
const result = await query.run(context([PERMS.USERS_VIEW]), {
routeId: "people.users.list",
list: { sort: "username", order: "asc", offset: 2, pageSize: 2 },
});
expect(result).toMatchObject({
ok: true,
data: {
kind: "users",
page: { items: [{ id: 10 }, { id: 2 }], offset: 2, pageSize: 2 },
},
});
});
it("normalizes list input and projects PII for admin users view", async () => {
const loadUsers = vi.fn(async () => ({
rows: [rawUser(1, "Alice"), rawUser(2, "alice"), rawUser(3, "alice")],
total: 3,
}));
const query = createPeopleUsersQuery({
loadUsers,
loadUser: async () => null,
loadMultiAccounts: async () => ({ rows: [], total: 0 }),
loadSanctions: async () => [],
loadWatchedUserIds: async () => new Set(),
loadPermissionRanks: async () => [{ id: 5, name: "Moderator" }],
});
const result = await query.run(context([PERMS.USERS_VIEW]), {
routeId: "people.users.list",
list: {
pageSize: 5_000,
offset: -9,
search: ` ali\u0000${"x".repeat(200)} `,
sort: "username",
order: "asc",
},
});
expect(loadUsers).toHaveBeenCalledWith(
expect.objectContaining({
pageSize: 100,
offset: 0,
search: `alix${"x".repeat(124)}`,
sort: "username",
}),
{ includeMail: true, includeIp: true },
);
expect(result).toMatchObject({
ok: true,
data: {
kind: "users",
page: {
items: [
{
id: 1,
mail: "[email protected]",
ipCurrent: "203.0.113.1",
href: "/ase/people/users/1",
},
{ id: 2 },
{ id: 3 },
],
total: 3,
pageSize: 100,
offset: 0,
},
},
});
});
it("returns a base-only projection to mod users and fails closed for neither permission", async () => {
const loadUsers = vi.fn(async () => ({
rows: [rawUser(1, "Alice")],
total: 1,
}));
const query = createPeopleUsersQuery({
loadUsers,
loadUser: async () => null,
loadMultiAccounts: async () => ({ rows: [], total: 0 }),
loadSanctions: async () => [],
loadWatchedUserIds: async () => new Set(),
loadPermissionRanks: async () => [{ id: 5, name: "Moderator" }],
});
const moderator = await query.run(context([PERMS.MOD_USERS_VIEW]), {
routeId: "people.users.list",
list: {},
});
expect(loadUsers).toHaveBeenLastCalledWith(expect.anything(), {
includeMail: false,
includeIp: false,
});
expect(moderator).toMatchObject({
ok: true,
data: {
page: { items: [{ mail: null, ipCurrent: null }] },
},
});
loadUsers.mockClear();
const forbidden = await query.run(context([]), {
routeId: "people.users.list",
list: {},
});
expect(forbidden).toMatchObject({
ok: false,
error: { code: "FORBIDDEN" },
});
expect(loadUsers).not.toHaveBeenCalled();
});
it("maps missing users, invalid ids, and adapter failures to stable errors", async () => {
const loadUser = vi.fn(async () => null);
const query = createPeopleUsersQuery({
loadUsers: async () => {
throw new Error("count unavailable");
},
loadUser,
loadMultiAccounts: async () => ({ rows: [], total: 0 }),
loadSanctions: async () => [],
loadWatchedUserIds: async () => new Set(),
loadPermissionRanks: async () => [{ id: 5, name: "Moderator" }],
});
const invalid = await query.run(context([PERMS.USERS_VIEW]), {
routeId: "people.users.detail",
id: -1,
});
expect(invalid).toMatchObject({
ok: false,
error: { code: "VALIDATION", fieldErrors: { id: ["invalid"] } },
});
expect(loadUser).not.toHaveBeenCalled();
const missing = await query.run(context([PERMS.USERS_VIEW]), {
routeId: "people.users.detail",
id: 404,
});
expect(missing).toMatchObject({
ok: false,
error: { code: "NOT_FOUND" },
});
const unavailable = await query.run(context([PERMS.USERS_VIEW]), {
routeId: "people.users.list",
list: {},
});
expect(unavailable).toMatchObject({
ok: false,
error: { code: "DEPENDENCY_UNAVAILABLE" },
});
});
it("returns JSON-safe user details with sanctions and canonical links", async () => {
const query = createPeopleUsersQuery({
loadUsers: async () => ({ rows: [], total: 0 }),
loadUser: async () => ({
...rawUser(9, "Nine"),
motto: "Hello",
look: "hd-180-1",
accountCreated: 1_700_000_000,
lastLogin: new Date("2026-08-29T09:00:00.000Z"),
}),
loadMultiAccounts: async () => ({ rows: [], total: 0 }),
loadSanctions: async () => [
{
id: 8,
kind: "account",
reason: "test",
createdAt: "2026-08-20T00:00:00.000Z",
expiresAt: null,
active: true,
},
],
loadWatchedUserIds: async () => new Set(),
loadPermissionRanks: async () => [{ id: 5, name: "Moderator" }],
});
const result = await query.run(context([PERMS.USERS_VIEW]), {
routeId: "people.users.detail",
id: 9,
});
expect(result).toMatchObject({
ok: true,
data: {
kind: "user",
user: {
id: 9,
href: "/ase/people/users/9",
accountCreated: "2023-11-14T22:13:20.000Z",
lastLogin: "2026-08-29T09:00:00.000Z",
sanctions: [{ id: 8 }],
},
},
});
expect(() => JSON.stringify(result)).not.toThrow();
});
});
describe("People community and staff queries", () => {
it("accepts only the guild user and room zero sentinels", async () => {
const guild = {
id: 12,
name: "Unassigned guild",
description: "",
userId: 0,
ownerUsername: null,
roomId: 0,
memberCount: 0,
threadCount: 0,
createdAt: null,
href: "/ase/people/community/guilds/12" as const,
members: [],
};
const query = createPeopleCommunityQuery({
loadOnline: async () => ({ rows: [], total: 0 }),
loadGuilds: async () => ({ rows: [], total: 0 }),
loadGuild: async () => guild as never,
});
expect(
await query.run(context([PERMS.USERS_VIEW]), {
routeId: "people.community.guild-detail",
id: 12,
}),
).toMatchObject({
ok: true,
data: { guild: { id: 12, userId: 0, roomId: 0 } },
});
const invalidQuery = createPeopleCommunityQuery({
loadOnline: async () => ({ rows: [], total: 0 }),
loadGuilds: async () => ({ rows: [], total: 0 }),
loadGuild: async () => ({ ...guild, userId: -1 }) as never,
});
expect(
await invalidQuery.run(context([PERMS.USERS_VIEW]), {
routeId: "people.community.guild-detail",
id: 12,
}),
).toMatchObject({
ok: false,
error: { code: "DEPENDENCY_UNAVAILABLE" },
});
});
it("fails closed when a community adapter returns a corrupt entity id", async () => {
const query = createPeopleCommunityQuery({
loadOnline: async () => ({ rows: [], total: 0 }),
loadGuilds: async () => ({ rows: [], total: 0 }),
loadGuild: async () =>
({
id: 0,
name: "Corrupt",
description: "",
userId: -1,
ownerUsername: null,
memberCount: 0,
createdAt: null,
href: "/ase/people/community/guilds/0",
roomId: 0,
threadCount: 0,
members: [],
}) as never,
});
const result = await query.run(context([PERMS.USERS_VIEW]), {
routeId: "people.community.guild-detail",
id: 1,
});
expect(result).toMatchObject({
ok: false,
error: { code: "DEPENDENCY_UNAVAILABLE" },
});
});
it("loads online, guild list, and guild detail workflows through narrow adapters", async () => {
const loadOnline = vi.fn(async () => ({ rows: [], total: 0 }));
const loadGuilds = vi.fn(async () => ({
rows: [
{
id: 2,
name: "Builders",
description: "Build",
userId: 4,
ownerUsername: "Owner",
roomId: 7,
memberCount: 3,
createdAt: "2026-08-01T00:00:00.000Z",
href: "/ase/people/community/guilds/2" as const,
},
],
total: 1,
}));
const loadGuild = vi.fn(async (id: number) =>
id === 2
? {
id: 2,
name: "Builders",
description: "Build",
userId: 4,
ownerUsername: "Owner",
memberCount: 3,
createdAt: "2026-08-01T00:00:00.000Z",
href: "/ase/people/community/guilds/2" as const,
roomId: 7,
threadCount: 1,
members: [],
}
: null,
);
const query = createPeopleCommunityQuery({
loadOnline,
loadGuilds,
loadGuild,
});
expect(
await query.run(context([PERMS.USERS_VIEW]), {
routeId: "people.community.online",
list: {},
}),
).toMatchObject({ ok: true, data: { kind: "online" } });
expect(
await query.run(context([PERMS.USERS_VIEW]), {
routeId: "people.community.guilds",
list: {},
}),
).toMatchObject({
ok: true,
data: { kind: "guilds", page: { items: [{ id: 2 }] } },
});
expect(
await query.run(context([PERMS.USERS_VIEW]), {
routeId: "people.community.guild-detail",
id: 404,
}),
).toMatchObject({ ok: false, error: { code: "NOT_FOUND" } });
});
it("loads applications, teams, and moderation-team data with serializable DTOs", async () => {
const query = createPeopleStaffQuery({
loadApplications: async () => ({
rows: [
{
id: 11,
userId: 5,
username: "Applicant",
rankId: 3,
content: "Why me",
createdAt: "2026-08-29T00:00:00.000Z",
},
],
total: 1,
}),
loadTeams: async () => ({ rows: [], total: 0 }),
loadModerationTeam: async () => ({ rows: [], total: 0 }),
});
const applications = await query.run(context([PERMS.USERS_VIEW]), {
routeId: "people.staff.applications",
list: {},
});
const teams = await query.run(context([PERMS.USERS_VIEW]), {
routeId: "people.staff.teams",
list: {},
});
const moderationTeam = await query.run(context([PERMS.MOD_TEAM_VIEW]), {
routeId: "people.staff.moderation-team",
list: {},
});
expect(applications).toMatchObject({
ok: true,
data: { kind: "applications", page: { items: [{ id: 11 }] } },
});
expect(teams).toMatchObject({ ok: true, data: { kind: "teams" } });
expect(moderationTeam).toMatchObject({
ok: true,
data: { kind: "moderation-team" },
});
expect(() => JSON.stringify(applications)).not.toThrow();
});
});
describe("People support query", () => {
it("routes the ticket desk to its CMS-only context loader", async () => {
const loadTickets = vi.fn(async () => ({
rows: [
{
source: "help" as const,
id: 12,
subject: "Help row",
status: "open",
priority: "normal",
creatorId: 7,
creatorUsername: "Seven",
updatedAt: null,
href: "/ase/people/support/help-tickets/12" as const,
},
],
total: 1,
}));
const loadTicketDesk = vi.fn(async () => ({
rows: [
{
source: "cms" as const,
id: 91,
subject: "Urgent CMS ticket",
status: "open",
priority: "urgent",
creatorId: 7,
creatorUsername: "Seven",
updatedAt: null,
href: "/ase/people/support/tickets/91" as const,
category: "safety",
assigneeId: 8,
messageCount: 4,
},
{
source: "cms" as const,
id: 92,
subject: "High-priority CMS ticket",
status: "open",
priority: "high",
creatorId: 9,
creatorUsername: "Nine",
updatedAt: null,
href: "/ase/people/support/tickets/92" as const,
category: "account",
assigneeId: null,
messageCount: 2,
},
],
total: 2,
}));
const query = createPeopleSupportQuery({
loadQueue: async () => ({
tickets: 1,
helpTickets: 1,
cfh: 0,
activeBans: 0,
}),
loadTickets,
loadTicketDesk,
loadTicket: async () => null,
loadTemplates: async () => ({ rows: [], total: 0 }),
loadHelpTickets: async () => ({ rows: [], total: 0 }),
loadHelpTicket: async () => null,
loadSupportStaff: async () => [],
loadActiveBan: async () => null,
} as never);
const result = await query.run(context([PERMS.TICKETS_VIEW]), {
routeId: "people.support.ticket-desk",
list: {},
});
expect(result).toMatchObject({
ok: true,
data: {
page: {
items: [
{
id: 91,
priority: "urgent",
category: "safety",
assigneeId: 8,
messageCount: 4,
},
{
id: 92,
priority: "high",
category: "account",
assigneeId: null,
messageCount: 2,
},
],
},
},
});
expect(loadTicketDesk).toHaveBeenCalledOnce();
expect(loadTickets).not.toHaveBeenCalled();
});
it("hydrates desk/detail support context and the help-ticket active ban", async () => {
const queue = { tickets: 2, helpTickets: 1, cfh: 3, activeBans: 4 };
const staff = [
{
id: 8,
username: "Helper",
rank: 5,
href: "/ase/people/users/8" as const,
},
];
const activeBan = {
id: 31,
userId: 7,
username: "Seven",
staffId: 8,
staffUsername: "Helper",
type: "account",
reason: "permanent",
createdAt: "2026-08-01T00:00:00.000Z",
expiresAt: 0 as const,
active: true,
};
const query = createPeopleSupportQuery({
loadQueue: async () => queue,
loadTickets: async () => ({ rows: [], total: 0 }),
loadTicketDesk: async () => ({ rows: [], total: 0 }),
loadTicket: async () => ({
id: 11,
subject: "CMS ticket",
status: "open",
priority: "normal",
creatorId: 7,
creatorUsername: "Seven",
updatedAt: "2026-08-20T00:00:00.000Z",
replyCount: 0,
href: "/ase/people/support/tickets/11",
category: "general",
assigneeId: null,
messages: [],
}),
loadTemplates: async () => ({ rows: [], total: 0 }),
loadHelpTickets: async () => ({ rows: [], total: 0 }),
loadHelpTicket: async () => ({
id: 12,
title: "Help ticket",
open: true,
userId: 7,
username: "Seven",
updatedAt: "2026-08-20T00:00:00.000Z",
href: "/ase/people/support/help-tickets/12",
categoryId: 2,
categoryName: "Help",
content: "Body",
replies: [],
}),
loadSupportStaff: async () => staff,
loadActiveBan: async () => activeBan,
} as never);
const desk = await query.run(context([PERMS.TICKETS_VIEW]), {
routeId: "people.support.ticket-desk",
list: {},
});
const detail = await query.run(context([PERMS.TICKETS_VIEW]), {
routeId: "people.support.ticket-detail",
id: 11,
});
const help = await query.run(context([PERMS.TICKETS_VIEW]), {
routeId: "people.support.help-ticket-detail",
id: 12,
});
expect(desk).toMatchObject({
ok: true,
data: { kind: "ticket-desk", queue, staff },
});
expect(detail).toMatchObject({
ok: true,
data: { ticket: { queue, staff } },
});
expect(help).toMatchObject({
ok: true,
data: { ticket: { queue, staff, activeBan } },
});
});
it("fails closed when support returns a corrupt detail shape", async () => {
const query = createPeopleSupportQuery({
loadQueue: async () => ({
tickets: 0,
helpTickets: 0,
cfh: 0,
activeBans: 0,
}),
loadTickets: async () => ({ rows: [], total: 0 }),
loadTicketDesk: async () => ({ rows: [], total: 0 }),
loadTicket: async () =>
({ id: 0, href: "/ase/people/support/tickets/0" }) as never,
loadTemplates: async () => ({ rows: [], total: 0 }),
loadHelpTickets: async () => ({ rows: [], total: 0 }),
loadHelpTicket: async () => null,
loadSupportStaff: async () => [],
loadActiveBan: async () => null,
});
const result = await query.run(context([PERMS.TICKETS_VIEW]), {
routeId: "people.support.ticket-detail",
id: 1,
});
expect(result).toMatchObject({
ok: false,
error: { code: "DEPENDENCY_UNAVAILABLE" },
});
});
it.each([
["status", [7, 3]],
["updatedAt", [8, 4]],
] as const)(
"preserves the DB-paged ticket window for %s ordering",
async (sort, ids) => {
const rows = ids.map((id, index) => ({
source: "cms" as const,
id,
subject: `Ticket ${id}`,
status: index === 0 ? "open" : "pending",
priority: "normal",
creatorId: 1,
creatorUsername: "Reporter",
updatedAt: `2026-08-${20 - index}T00:00:00.000Z`,
href: `/ase/people/support/tickets/${id}` as const,
}));
const query = createPeopleSupportQuery({
loadQueue: async () => ({
tickets: 0,
helpTickets: 0,
cfh: 0,
activeBans: 0,
}),
loadTickets: async () => ({ rows, total: 40 }),
loadTicketDesk: async () => ({
rows: rows.map((row) => ({
...row,
category: "general",
assigneeId: null,
messageCount: 0,
})),
total: 40,
}),
loadTicket: async () => null,
loadTemplates: async () => ({ rows: [], total: 0 }),
loadHelpTickets: async () => ({ rows: [], total: 0 }),
loadHelpTicket: async () => null,
loadSupportStaff: async () => [],
loadActiveBan: async () => null,
});
const result = await query.run(context([PERMS.TICKETS_VIEW]), {
routeId: "people.support.ticket-desk",
list: { sort, order: "desc", offset: 10, pageSize: 2 },
});
expect(result).toMatchObject({
ok: true,
data: { page: { items: ids.map((id) => ({ id })), offset: 10 } },
});
},
);
it("returns the four-source queue snapshot and fails closed when a count fails", async () => {
const loadQueue = vi
.fn()
.mockResolvedValueOnce({
tickets: 2,
helpTickets: 3,
cfh: 4,
activeBans: 5,
})
.mockRejectedValueOnce(new Error("count failed"));
const query = createPeopleSupportQuery({
loadQueue,
loadTickets: async () => ({ rows: [], total: 0 }),
loadTicketDesk: async () => ({ rows: [], total: 0 }),
loadTicket: async () => null,
loadTemplates: async () => ({ rows: [], total: 0 }),
loadHelpTickets: async () => ({ rows: [], total: 0 }),
loadHelpTicket: async () => null,
loadSupportStaff: async () => [],
loadActiveBan: async () => null,
});
expect(
await query.run(context([PERMS.TICKETS_VIEW]), {
routeId: "people.support.queue",
}),
).toMatchObject({
ok: true,
data: {
kind: "queue",
queue: { tickets: 2, helpTickets: 3, cfh: 4, activeBans: 5 },
},
});
expect(
await query.run(context([PERMS.TICKETS_VIEW]), {
routeId: "people.support.queue",
}),
).toMatchObject({
ok: false,
error: { code: "DEPENDENCY_UNAVAILABLE" },
});
});
it("covers ticket desk/templates/detail and help-ticket list/detail reads", async () => {
const query = createPeopleSupportQuery({
loadQueue: async () => ({
tickets: 0,
helpTickets: 0,
cfh: 0,
activeBans: 0,
}),
loadTickets: async () => ({ rows: [], total: 0 }),
loadTicketDesk: async () => ({ rows: [], total: 0 }),
loadTicket: async () => null,
loadTemplates: async () => ({ rows: [], total: 0 }),
loadHelpTickets: async () => ({ rows: [], total: 0 }),
loadHelpTicket: async () => null,
loadSupportStaff: async () => [],
loadActiveBan: async () => null,
});
for (const routeId of [
"people.support.tickets",
"people.support.ticket-desk",
"people.support.help-tickets",
] as const) {
expect(
await query.run(context([PERMS.TICKETS_VIEW, PERMS.MOD_TICKETS_VIEW]), {
routeId,
list: {},
}),
).toMatchObject({ ok: true });
}
expect(
await query.run(context([PERMS.TICKETS_EDIT]), {
routeId: "people.support.ticket-templates",
list: {},
}),
).toMatchObject({ ok: true, data: { kind: "ticket-templates" } });
for (const input of [
{ routeId: "people.support.ticket-detail" as const, id: 91 },
{ routeId: "people.support.help-ticket-detail" as const, id: 92 },
]) {
expect(
await query.run(context([PERMS.TICKETS_VIEW]), input),
).toMatchObject({ ok: false, error: { code: "NOT_FOUND" } });
}
});
});
describe("People moderation query", () => {
it("accepts only the CFH participant and room zero sentinels", async () => {
const ticket = {
id: 41,
state: 0,
senderId: 0,
senderUsername: null,
reportedId: 0,
reportedUsername: null,
moderatorId: 0,
issue: "unassigned",
createdAt: null,
href: "/ase/people/moderation/cfh/41" as const,
roomId: 0,
activeBan: null,
};
const adapters = {
loadOverview: async () => ({
tickets: 0,
helpTickets: 0,
cfh: 0,
activeBans: 0,
staffOnline: 0,
recentActions: 0,
}),
loadCfh: async () => ({ rows: [], total: 0 }),
loadCfhDetail: async () => ticket,
loadBans: async () => ({ rows: [], total: 0 }),
loadIpRules: async () => ({ blacklist: [], whitelist: [] }),
loadVpnSettings: async () => [],
loadWordFilter: async () => ({ rows: [], total: 0 }),
};
const query = createPeopleModerationQuery(adapters);
expect(
await query.run(context([PERMS.MOD_CFH_VIEW]), {
routeId: "people.moderation.cfh-detail",
id: 41,
}),
).toMatchObject({
ok: true,
data: {
ticket: {
id: 41,
senderId: 0,
reportedId: 0,
moderatorId: 0,
roomId: 0,
},
},
});
const invalidQuery = createPeopleModerationQuery({
...adapters,
loadCfhDetail: async () => ({ ...ticket, reportedId: -1 }),
});
expect(
await invalidQuery.run(context([PERMS.MOD_CFH_VIEW]), {
routeId: "people.moderation.cfh-detail",
id: 41,
}),
).toMatchObject({
ok: false,
error: { code: "DEPENDENCY_UNAVAILABLE" },
});
});
it("declares exactly the same eleven-permission union as the moderation overview route", () => {
const query = createPeopleModerationQuery({
loadOverview: async () => ({
tickets: 0,
helpTickets: 0,
cfh: 0,
activeBans: 0,
staffOnline: 0,
recentActions: 0,
}),
loadCfh: async () => ({ rows: [], total: 0 }),
loadCfhDetail: async () => null,
loadBans: async () => ({ rows: [], total: 0 }),
loadIpRules: async () => ({ blacklist: [], whitelist: [] }),
loadVpnSettings: async () => [],
loadWordFilter: async () => ({ rows: [], total: 0 }),
});
const route = PEOPLE_ROUTES.find(
(candidate) => candidate.id === "people.moderation.overview",
);
expect(route).toBeDefined();
expect(query.capability).toEqual(route?.capability);
expect(query.capability.slugs).toHaveLength(11);
});
it("fails closed when moderation returns a corrupt detail shape", async () => {
const query = createPeopleModerationQuery({
loadOverview: async () => ({
tickets: 0,
helpTickets: 0,
cfh: 0,
activeBans: 0,
staffOnline: 0,
recentActions: 0,
}),
loadCfh: async () => ({ rows: [], total: 0 }),
loadCfhDetail: async () =>
({ id: -1, href: "/ase/people/moderation/cfh/-1" }) as never,
loadBans: async () => ({ rows: [], total: 0 }),
loadIpRules: async () => ({ blacklist: [], whitelist: [] }),
loadVpnSettings: async () => [],
loadWordFilter: async () => ({ rows: [], total: 0 }),
});
const result = await query.run(context([PERMS.MOD_CFH_VIEW]), {
routeId: "people.moderation.cfh-detail",
id: 1,
});
expect(result).toMatchObject({
ok: false,
error: { code: "DEPENDENCY_UNAVAILABLE" },
});
});
it("covers overview, CFH, bans, IP, VPN, and word-filter read workflows", async () => {
const query = createPeopleModerationQuery({
loadOverview: async () => ({
tickets: 1,
helpTickets: 2,
cfh: 3,
activeBans: 4,
staffOnline: 5,
recentActions: 6,
}),
loadCfh: async () => ({ rows: [], total: 0 }),
loadCfhDetail: async () => null,
loadBans: async () => ({ rows: [], total: 0 }),
loadIpRules: async () => ({ blacklist: [], whitelist: [] }),
loadVpnSettings: async () => [],
loadWordFilter: async () => ({ rows: [], total: 0 }),
});
expect(
await query.run(context([PERMS.MODERATION_VIEW]), {
routeId: "people.moderation.overview",
}),
).toMatchObject({
ok: true,
data: { kind: "overview", snapshot: { cfh: 3, activeBans: 4 } },
});
expect(
await query.run(context([PERMS.MOD_CFH_VIEW]), {
routeId: "people.moderation.cfh",
list: {},
}),
).toMatchObject({ ok: true, data: { kind: "cfh" } });
expect(
await query.run(context([PERMS.MOD_CFH_VIEW]), {
routeId: "people.moderation.cfh-detail",
id: 77,
}),
).toMatchObject({ ok: false, error: { code: "NOT_FOUND" } });
expect(
await query.run(context([PERMS.MOD_BANS_VIEW]), {
routeId: "people.moderation.bans",
list: {},
}),
).toMatchObject({ ok: true, data: { kind: "bans" } });
for (const input of [
{ routeId: "people.moderation.ip" as const },
{ routeId: "people.moderation.vpn" as const },
]) {
expect(
await query.run(context([PERMS.SETTINGS_VIEW]), input),
).toMatchObject({ ok: true });
}
expect(
await query.run(context([PERMS.WORDFILTER_VIEW]), {
routeId: "people.moderation.word-filter",
list: {},
}),
).toMatchObject({ ok: true, data: { kind: "word-filter" } });
});
it("maps adapter exceptions to dependency unavailable without partial data", async () => {
const down = async () => {
throw new Error("database unavailable");
};
const query = createPeopleModerationQuery({
loadOverview: down,
loadCfh: down,
loadCfhDetail: down,
loadBans: down,
loadIpRules: down,
loadVpnSettings: down,
loadWordFilter: down,
});
expect(
await query.run(context([PERMS.MOD_BANS_VIEW]), {
routeId: "people.moderation.bans",
list: {},
}),
).toMatchObject({
ok: false,
error: {
code: "DEPENDENCY_UNAVAILABLE",
messageKey: "errors.housekeeping.dependencyUnavailable",
},
});
});
});