fix(housekeeping): complete people read fidelity
This commit is contained in:
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 () => ({
|
||||
|
||||
Reference in new issue
Block a user