server/audience.test.ts
Rama main · réplica de lectura del repositorio original.
import { describe, expect, it } from "vitest";
import {
audienceIncludes, audienceMember, audienceRoom, baseBotId, isLayerId, layerAudience, layerId, mayRead, memberAudience, parseAudience,
roomAudience, threadAudienceFrom, writesInto, type Audience,
} from "./audience.ts";
const ADA = "p_adaadaadaadaadaadaada1", BEN = "p_benbenbenbenbenbenben1";
const viewer = (person: string | null, rooms: string[] = [], admin = false) => ({ admin, person, seesRoom: (id: string) => rooms.includes(id) });
describe("the read rule", () => {
const cases: Array<[Audience, Audience, boolean]> = [
["team", "team", true], ["team", roomAudience("r1"), true], ["team", memberAudience(ADA), true],
[roomAudience("r1"), roomAudience("r1"), true], [roomAudience("r1"), roomAudience("r2"), false], [roomAudience("r1"), memberAudience(ADA), false], [roomAudience("r1"), "team", false],
[memberAudience(ADA), memberAudience(ADA), true], [memberAudience(ADA), memberAudience(BEN), false], [memberAudience(ADA), roomAudience("r1"), false], [memberAudience(ADA), "team", false],
];
it.each(cases)("a turn for %s may read %s: %s", (input, turn, allowed) => {
expect(mayRead(input, turn)).toBe(allowed);
});
it("writes only into its own audience", () => {
for (const turn of ["team", roomAudience("r1"), memberAudience(ADA)] as Audience[]) expect(writesInto(turn)).toBe(turn);
});
});
describe("who an audience includes", () => {
it("the team's: everyone; a room's: whoever sees the room, and the admins; a member's: that member only", () => {
expect(audienceIncludes("team", viewer(null))).toBe(true);
expect(audienceIncludes(roomAudience("r1"), viewer(ADA, ["r1"]))).toBe(true);
expect(audienceIncludes(roomAudience("r1"), viewer(ADA, []))).toBe(false);
expect(audienceIncludes(memberAudience(ADA), viewer(ADA))).toBe(true);
expect(audienceIncludes(memberAudience(ADA), viewer(BEN))).toBe(false);
expect(audienceIncludes(memberAudience(ADA), viewer(null))).toBe(false);
expect(audienceIncludes(roomAudience("r1"), viewer(null, [], true))).toBe(true);
});
it("an Admin, the Owner included, never sees another person's private chat, only their own", () => {
expect(audienceIncludes(memberAudience(ADA), viewer(BEN, [], true))).toBe(false);
expect(audienceIncludes(memberAudience(ADA), viewer(null, [], true))).toBe(false);
expect(audienceIncludes(memberAudience(BEN), viewer(BEN, [], true))).toBe(true);
expect(audienceIncludes("team", viewer(BEN, [], true))).toBe(true);
});
});
describe("a conversation's audience", () => {
const markers = new Set(["p_nobodynobodynobodynob1", "p_outsideoutsideoutside1"]);
it("is the room's in a room, the person's for a conversation they opened, and the team's for the main chat, nobody's or a marker's", () => {
expect(threadAudienceFrom({ room: "r1", starter: ADA, markers })).toBe("room:r1");
expect(threadAudienceFrom({ room: null, starter: ADA, markers })).toBe(`member:${ADA}`);
expect(threadAudienceFrom({ room: undefined, starter: undefined, markers })).toBe("team");
expect(threadAudienceFrom({ room: null, starter: "p_nobodynobodynobodynob1", markers })).toBe("team");
expect(threadAudienceFrom({ room: null, starter: "p_outsideoutsideoutside1", markers })).toBe("team");
expect(threadAudienceFrom({ room: null, starter: "not a key!", markers })).toBe("team");
});
});
describe("memory layers", () => {
it("the team's layer is the bot's own folder; a room's and a member's are sibling folders that name the bot", () => {
expect(layerId("scout", "team")).toBe("scout");
expect(layerId("scout", roomAudience("r1"))).toBe("scout--room-r1");
expect(layerId("scout", memberAudience(ADA))).toBe(`scout--member-${ADA}`);
for (const id of ["scout", "scout--room-r1", `scout--member-${ADA}`]) expect(baseBotId(id)).toBe("scout");
expect(isLayerId("scout")).toBe(false);
expect(isLayerId("scout--room-r1")).toBe(true);
expect(layerAudience("scout")).toBe("team");
expect(layerAudience("scout--room-r1")).toBe("room:r1");
expect(layerAudience(`scout--member-${ADA}`)).toBe(`member:${ADA}`);
// A layer id is a valid bot-shaped id, so every memory reader and writer takes it unchanged.
expect(`scout--member-${ADA}`).toMatch(/^[\w-]+$/);
});
it("parses and takes apart what it made", () => {
expect(parseAudience("team")).toBe("team");
expect(parseAudience("room:r1")).toBe("room:r1");
expect(parseAudience(`member:${ADA}`)).toBe(`member:${ADA}`);
expect(parseAudience("room:")).toBeNull();
expect(parseAudience("everyone")).toBeNull();
expect(parseAudience(7)).toBeNull();
expect(audienceRoom("room:r1")).toBe("r1");
expect(audienceRoom("team")).toBeNull();
expect(audienceMember(`member:${ADA}`)).toBe(ADA);
expect(audienceMember("room:r1")).toBeNull();
});
});