fix(housekeeping): complete people read fidelity

This commit is contained in:
Simo committed 2026-08-29 10:34:44 +02:00
1 parent d1382c839e
commit 7920d4f46c
8 files changed
+564 -37

No files matched your search

@@ -283,6 +283,52 @@ describe("People users query", () => {
});
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 }),
@@ -292,7 +338,7 @@ describe("People community and staff queries", () => {
id: 0,
name: "Corrupt",
description: "",
ownerId: -1,
userId: -1,
ownerUsername: null,
memberCount: 0,
createdAt: null,
@@ -320,8 +366,9 @@ describe("People community and staff queries", () => {
id: 2,
name: "Builders",
description: "Build",
ownerId: 4,
userId: 4,
ownerUsername: "Owner",
roomId: 7,
memberCount: 3,
createdAt: "2026-08-01T00:00:00.000Z",
href: "/ase/people/community/guilds/2" as const,
@@ -335,7 +382,7 @@ describe("People community and staff queries", () => {
id: 2,
name: "Builders",
description: "Build",
ownerId: 4,
userId: 4,
ownerUsername: "Owner",
memberCount: 3,
createdAt: "2026-08-01T00:00:00.000Z",
@@ -421,6 +468,103 @@ describe("People community and staff queries", () => {
});
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 = [
@@ -446,6 +590,7 @@ describe("People support query", () => {
const query = createPeopleSupportQuery({
loadQueue: async () => queue,
loadTickets: async () => ({ rows: [], total: 0 }),
loadTicketDesk: async () => ({ rows: [], total: 0 }),
loadTicket: async () => ({
id: 11,
subject: "CMS ticket",
@@ -454,6 +599,7 @@ describe("People support query", () => {
creatorId: 7,
creatorUsername: "Seven",
updatedAt: "2026-08-20T00:00:00.000Z",
replyCount: 0,
href: "/ase/people/support/tickets/11",
category: "general",
assigneeId: null,
@@ -513,6 +659,7 @@ describe("People support query", () => {
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 }),
@@ -556,6 +703,15 @@ describe("People support query", () => {
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 }),
@@ -589,6 +745,7 @@ describe("People support query", () => {
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 }),
@@ -627,6 +784,7 @@ describe("People support query", () => {
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 }),
@@ -665,6 +823,71 @@ describe("People support query", () => {
});
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 () => ({