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

72 lines
2.6 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
const mocks = vi.hoisted(() => ({
tx: {
hrEmployeeProfile: { findFirst: vi.fn(), findMany: vi.fn() },
hrLocation: { findFirst: vi.fn(), findMany: vi.fn() },
},
}));
vi.mock("~/utils/db/.server/tenantRls.server", () => ({
withTenantRLS: (_tenantId: string, callback: (tx: typeof mocks.tx) => unknown) => callback(mocks.tx),
}));
import {
isHrAudienceVisible,
listHrAudienceOptions,
requireHrAudienceViewer,
requireValidHrAudience,
} from "./hrAudience.server";
describe("HR audience object access", () => {
beforeEach(() => {
vi.clearAllMocks();
mocks.tx.hrEmployeeProfile.findFirst.mockResolvedValue(null);
mocks.tx.hrEmployeeProfile.findMany.mockResolvedValue([]);
mocks.tx.hrLocation.findFirst.mockResolvedValue(null);
mocks.tx.hrLocation.findMany.mockResolvedValue([]);
});
it("matches only the viewer's exact team or location", () => {
const viewer = { team: "Montage Nord", locationId: "location-1" };
expect(isHrAudienceVisible("all", null, viewer)).toBe(true);
expect(isHrAudienceVisible("team", "Montage Nord", viewer)).toBe(true);
expect(isHrAudienceVisible("team", "Montage Süd", viewer)).toBe(false);
expect(isHrAudienceVisible("location", "location-1", viewer)).toBe(true);
expect(isHrAudienceVisible("location", "location-2", viewer)).toBe(false);
});
it("returns 404 semantics for a direct audience URL outside the viewer scope", async () => {
mocks.tx.hrEmployeeProfile.findFirst.mockResolvedValue({ team: "Montage Nord", locationId: "location-1" });
await expect(
requireHrAudienceViewer({
tenantId: "tenant-1",
userId: "employee-1",
kind: "team",
ref: "Montage Süd",
canManage: false,
}),
).rejects.toThrow();
});
it("rejects an audience reference that does not belong to the tenant", async () => {
await expect(
requireValidHrAudience({
tenantId: "tenant-1",
kind: "location",
ref: "foreign-location",
}),
).rejects.toThrow();
});
it("builds named team and location options without exposing raw labels as UI copy", async () => {
mocks.tx.hrEmployeeProfile.findMany.mockResolvedValue([{ team: "Montage Nord" }, { team: "Service" }]);
mocks.tx.hrLocation.findMany.mockResolvedValue([{ id: "location-1", name: "Zentrale Köln" }]);
await expect(listHrAudienceOptions("tenant-1")).resolves.toEqual([
{ kind: "team", ref: "Montage Nord", label: "Montage Nord" },
{ kind: "team", ref: "Service", label: "Service" },
{ kind: "location", ref: "location-1", label: "Zentrale Köln" },
]);
});
});