79 lines
3.5 KiB
TypeScript
79 lines
3.5 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
|
|
const findMany = vi.fn();
|
|
vi.mock("~/utils/db.server", () => ({
|
|
db: { emailIntegrationMailbox: { findMany: (...args: unknown[]) => findMany(...args) } },
|
|
}));
|
|
|
|
import { maskEmailActivitiesForViewer, shouldMaskEmailActivity } from "./emailActivityMasking.server";
|
|
|
|
// VA-P23 — DoD 6: email.read_own vs read_all must filter the Activity-Stream,
|
|
// not just the inbox list. Foreign email activities collapse to the Klartext
|
|
// placeholder without subject/snippet details.
|
|
|
|
const OWN = new Set(["mb-own"]);
|
|
|
|
describe("shouldMaskEmailActivity (pure)", () => {
|
|
it("never masks non-email activities", () => {
|
|
expect(shouldMaskEmailActivity({ activityType: "note_added", summaryText: "x" }, { canReadAll: false, canReadOwn: false, ownMailboxIds: OWN })).toBe(false);
|
|
});
|
|
|
|
it("read_all sees everything", () => {
|
|
expect(
|
|
shouldMaskEmailActivity({ activityType: "email_received", summaryText: "x", metadata: { mailboxId: "mb-foreign" } }, { canReadAll: true, canReadOwn: true, ownMailboxIds: OWN }),
|
|
).toBe(false);
|
|
});
|
|
|
|
it("read_own sees own-mailbox activities but not foreign ones", () => {
|
|
const viewer = { canReadAll: false, canReadOwn: true, ownMailboxIds: OWN };
|
|
expect(shouldMaskEmailActivity({ activityType: "email_received", summaryText: "x", metadata: { mailboxId: "mb-own" } }, viewer)).toBe(false);
|
|
expect(shouldMaskEmailActivity({ activityType: "email_sent", summaryText: "x", metadata: { mailboxId: "mb-foreign" } }, viewer)).toBe(true);
|
|
});
|
|
|
|
it("masks conservatively when the mailbox reference is missing", () => {
|
|
expect(shouldMaskEmailActivity({ activityType: "email_received", summaryText: "x" }, { canReadAll: false, canReadOwn: true, ownMailboxIds: OWN })).toBe(true);
|
|
});
|
|
|
|
it("no email permission at all masks every email activity", () => {
|
|
expect(
|
|
shouldMaskEmailActivity({ activityType: "email_received", summaryText: "x", metadata: { mailboxId: "mb-own" } }, { canReadAll: false, canReadOwn: false, ownMailboxIds: new Set() }),
|
|
).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("maskEmailActivitiesForViewer", () => {
|
|
it("replaces masked rows with the Klartext placeholder and drops metadata", async () => {
|
|
findMany.mockResolvedValue([{ id: "mb-own" }]);
|
|
const rows = [
|
|
{ activityType: "email_received", summaryText: "Eingehende E-Mail: geheim", metadata: { mailboxId: "mb-foreign", snippet: "geheim" } },
|
|
{ activityType: "email_sent", summaryText: "Eigene Mail", metadata: { mailboxId: "mb-own" } },
|
|
{ activityType: "note_added", summaryText: "Notiz" },
|
|
];
|
|
const masked = await maskEmailActivitiesForViewer({
|
|
tenantId: "t1",
|
|
userId: "u1",
|
|
permissions: ["email.read_own", "email.write"],
|
|
rows,
|
|
maskedText: "E-Mail-Aktivität (nicht einsehbar)",
|
|
});
|
|
expect(masked[0].summaryText).toBe("E-Mail-Aktivität (nicht einsehbar)");
|
|
expect(masked[0].metadata).toBeUndefined();
|
|
expect(masked[1].summaryText).toBe("Eigene Mail");
|
|
expect(masked[2].summaryText).toBe("Notiz");
|
|
});
|
|
|
|
it("short-circuits without a DB query for read_all viewers", async () => {
|
|
findMany.mockClear();
|
|
const rows = [{ activityType: "email_received", summaryText: "x", metadata: { mailboxId: "mb-foreign" } }];
|
|
const result = await maskEmailActivitiesForViewer({
|
|
tenantId: "t1",
|
|
userId: "u1",
|
|
permissions: ["email.read_all"],
|
|
rows,
|
|
maskedText: "masked",
|
|
});
|
|
expect(result[0].summaryText).toBe("x");
|
|
expect(findMany).not.toHaveBeenCalled();
|
|
});
|
|
});
|