Files
HiveGrid-Wireframes/03 Quellen und Referenzen/hivegrid-cloud/apps/web/app/utils/integrations/taskService.notification.test.ts
T

85 lines
2.8 KiB
TypeScript

// @vitest-environment node
import { beforeEach, describe, expect, it, vi } from "vitest";
vi.mock("@hivegrid/foundation", async (importOriginal) => {
const actual = await importOriginal<typeof import("@hivegrid/foundation")>();
return { ...actual, notify: vi.fn().mockResolvedValue(undefined) };
});
vi.mock("~/services/search/searchIndexProjection.server", () => ({
publishSearchProjectionDelete: vi.fn().mockResolvedValue(undefined),
publishTaskSearchUpsert: vi.fn().mockResolvedValue(undefined),
}));
vi.mock("~/utils/db/.server/tenantRls.server", () => ({
withTenantRLS: (_tenantId: string, fn: (tx: unknown) => unknown) => fn(globalThis.__taskNotificationTestTx),
}));
vi.mock("~/utils/db.server", () => ({ db: {} }));
import { notify } from "@hivegrid/foundation";
import { createTask } from "./taskService.server";
declare global {
// eslint-disable-next-line no-var
var __taskNotificationTestTx: Record<string, unknown>;
}
function createTx() {
return {
tenantUser: { findFirst: vi.fn().mockResolvedValue({ id: "member_1" }) },
task: {
create: vi.fn().mockImplementation(({ data }: { data: Record<string, unknown> }) =>
Promise.resolve({ id: "task_1", ...data })
),
},
};
}
const ctx = { userId: "user_actor", permissions: ["tasks.write"], groupIds: [] } as never;
describe("task assignment notification", () => {
beforeEach(() => {
vi.mocked(notify).mockClear();
globalThis.__taskNotificationTestTx = createTx();
});
it("notifies a foreign assignee with an idempotent source key and task deeplink", async () => {
await createTask({
tenantId: "tenant_1",
tenantSlug: "acme",
ctx,
title: "Angebot nachfassen",
assignedToUserId: "user_assignee",
});
expect(notify).toHaveBeenCalledTimes(1);
expect(notify).toHaveBeenCalledWith(
expect.objectContaining({
tenantId: "tenant_1",
userId: "user_assignee",
channel: "IN_APP",
category: "tasks",
messageKey: "tasks.notification.assigned",
sourceKey: "task-assigned:task_1:user_assignee",
payloadMetadata: expect.objectContaining({ deeplink: "/acme/tasks/task_1" }),
}),
expect.anything()
);
});
it("stays silent on self-assignment and on group-only assignment", async () => {
await createTask({ tenantId: "tenant_1", tenantSlug: "acme", ctx, title: "Selbst", assignedToUserId: "user_actor" });
expect(notify).not.toHaveBeenCalled();
});
it("never fails the task write when the notification throws", async () => {
vi.mocked(notify).mockRejectedValueOnce(new Error("notification backend down"));
const task = await createTask({
tenantId: "tenant_1",
tenantSlug: "acme",
ctx,
title: "Robust",
assignedToUserId: "user_assignee",
});
expect(task.id).toBe("task_1");
});
});