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

184 lines
4.3 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
assertPeopleSerializable,
createPeoplePage,
normalizePeopleListInput,
normalizePeopleUser,
type PeopleUserDetail,
peopleCfhHref,
peopleGuildHref,
peopleHelpTicketHref,
peopleTicketHref,
peopleUserHref,
toPeopleIsoDate,
} from "./models";
describe("People list normalization", () => {
it("clamps adversarial page size and offset values and bounds search text", () => {
expect(
normalizePeopleListInput(
{
pageSize: 99_999,
offset: -50,
search: ` alice\u0000${"x".repeat(300)} `,
sort: "not-a-column",
order: "sideways",
},
["id", "username"],
"username",
),
).toEqual({
pageSize: 100,
offset: 0,
search: `alicex${"x".repeat(122)}`,
sort: "username",
order: "asc",
});
expect(
normalizePeopleListInput(
{ pageSize: Number.NaN, offset: Number.POSITIVE_INFINITY },
["id"],
"id",
),
).toMatchObject({ pageSize: 20, offset: 0 });
expect(
normalizePeopleListInput({ offset: 999_999 }, ["id"], "id").offset,
).toBe(10_000);
});
it("wraps rows already sorted and paged by the adapter", () => {
const input = normalizePeopleListInput(
{ pageSize: 2, offset: 1, sort: "username", order: "asc" },
["id", "username"],
"id",
);
const page = createPeoplePage(
[
{ id: 3, username: "alice" },
{ id: 1, username: "Alice" },
],
4,
input,
);
expect(page).toEqual({
items: [
{ id: 3, username: "alice" },
{ id: 1, username: "Alice" },
],
total: 4,
pageSize: 2,
offset: 1,
});
});
});
describe("People canonical models", () => {
const rawUser = {
id: 7n,
username: "Alice",
rank: "5",
online: "1",
mail: "[email protected]",
ipCurrent: "203.0.113.9",
bannedUntil: "1700000000",
};
it("projects mail and current IP independently and never substitutes a redaction", () => {
expect(
normalizePeopleUser(rawUser, { includeMail: false, includeIp: false }),
).toEqual({
id: 7,
username: "Alice",
rank: 5,
online: true,
mail: null,
ipCurrent: null,
bannedUntil: 1_700_000_000,
href: "/ase/people/users/7",
});
expect(
normalizePeopleUser(rawUser, { includeMail: true, includeIp: false }),
).toMatchObject({ mail: "[email protected]", ipCurrent: null });
expect(
normalizePeopleUser(rawUser, { includeMail: false, includeIp: true }),
).toMatchObject({ mail: null, ipCurrent: "203.0.113.9" });
});
it("normalizes BigInt and Date values to JSON-safe DTO primitives", () => {
const detail: PeopleUserDetail = {
...normalizePeopleUser(rawUser, {
includeMail: true,
includeIp: true,
}),
motto: "Hello",
look: "hd-180-1",
accountCreated: toPeopleIsoDate(new Date("2026-08-29T10:20:30.000Z")),
lastLogin: toPeopleIsoDate(1_700_000_000),
sanctions: [],
watched: false,
permission: {
rankName: "Moderator",
ranks: [{ id: 5, name: "Moderator" }],
},
};
expect(JSON.parse(JSON.stringify(detail))).toEqual({
id: 7,
username: "Alice",
rank: 5,
online: true,
mail: "[email protected]",
ipCurrent: "203.0.113.9",
bannedUntil: 1_700_000_000,
href: "/ase/people/users/7",
motto: "Hello",
look: "hd-180-1",
accountCreated: "2026-08-29T10:20:30.000Z",
lastLogin: "2023-11-14T22:13:20.000Z",
sanctions: [],
watched: false,
permission: {
rankName: "Moderator",
ranks: [{ id: 5, name: "Moderator" }],
},
});
});
it("builds canonical People entity links only", () => {
expect([
peopleUserHref(4),
peopleGuildHref(5),
peopleTicketHref(6),
peopleHelpTicketHref(7),
peopleCfhHref(8),
]).toEqual([
"/ase/people/users/4",
"/ase/people/community/guilds/5",
"/ase/people/support/tickets/6",
"/ase/people/support/help-tickets/7",
"/ase/people/moderation/cfh/8",
]);
});
it("rejects corrupt identifiers and non-serializable date values", () => {
expect(() =>
normalizePeopleUser(
{ ...rawUser, id: 0 },
{ includeMail: false, includeIp: false },
),
).toThrow();
expect(() => peopleUserHref(Number.MAX_SAFE_INTEGER + 1)).toThrow();
expect(() => peopleGuildHref(-4)).toThrow();
expect(() => toPeopleIsoDate("not-a-date")).toThrow();
expect(() =>
assertPeopleSerializable({
id: 7,
userId: 0,
href: "/ase/people/users/7",
}),
).toThrow();
});
});