feat(master-data): scope detail reads to resource overview
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
import { PermissionKey, SystemRole } from "@capakraken/shared";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { countryRouter } from "../router/country.js";
|
||||
import { orgUnitRouter } from "../router/org-unit.js";
|
||||
import { createCallerFactory } from "../trpc.js";
|
||||
|
||||
function createProtectedContext(
|
||||
db: Record<string, unknown>,
|
||||
overrides: { granted?: PermissionKey[]; denied?: PermissionKey[] } | null = null,
|
||||
) {
|
||||
return {
|
||||
session: {
|
||||
user: { email: "user@example.com", name: "User", image: null },
|
||||
expires: "2099-01-01T00:00:00.000Z",
|
||||
},
|
||||
db: db as never,
|
||||
dbUser: {
|
||||
id: "user_1",
|
||||
systemRole: SystemRole.USER,
|
||||
permissionOverrides: overrides,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
describe("master-data router authorization", () => {
|
||||
it("keeps minimal country lookups available to authenticated users", async () => {
|
||||
const findFirst = vi.fn().mockResolvedValue({
|
||||
id: "country_de",
|
||||
code: "DE",
|
||||
name: "Germany",
|
||||
isActive: true,
|
||||
dailyWorkingHours: 8,
|
||||
});
|
||||
const caller = createCallerFactory(countryRouter)(createProtectedContext({
|
||||
country: {
|
||||
findUnique: vi.fn().mockResolvedValue(null),
|
||||
findFirst,
|
||||
},
|
||||
}));
|
||||
|
||||
const result = await caller.resolveByIdentifier({ identifier: "de" });
|
||||
|
||||
expect(result).toEqual({
|
||||
id: "country_de",
|
||||
code: "DE",
|
||||
name: "Germany",
|
||||
isActive: true,
|
||||
dailyWorkingHours: 8,
|
||||
});
|
||||
expect(findFirst).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("requires resource overview access for detailed country reads with resource counts", async () => {
|
||||
const caller = createCallerFactory(countryRouter)(createProtectedContext({
|
||||
country: {
|
||||
findFirst: vi.fn(),
|
||||
findUnique: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
await expect(caller.getByIdentifier({ identifier: "DE" })).rejects.toMatchObject({
|
||||
code: "FORBIDDEN",
|
||||
message: "Resource overview access required",
|
||||
});
|
||||
await expect(caller.getById({ id: "country_de" })).rejects.toMatchObject({
|
||||
code: "FORBIDDEN",
|
||||
message: "Resource overview access required",
|
||||
});
|
||||
});
|
||||
|
||||
it("allows detailed country reads for users with resource overview access", async () => {
|
||||
const findFirst = vi.fn().mockResolvedValue({
|
||||
id: "country_de",
|
||||
code: "DE",
|
||||
name: "Germany",
|
||||
isActive: true,
|
||||
dailyWorkingHours: 8,
|
||||
scheduleRules: null,
|
||||
metroCities: [{ id: "city_muc", name: "Munich", countryId: "country_de" }],
|
||||
_count: { resources: 12 },
|
||||
});
|
||||
const caller = createCallerFactory(countryRouter)(createProtectedContext({
|
||||
country: {
|
||||
findUnique: vi.fn().mockResolvedValue(null),
|
||||
findFirst,
|
||||
},
|
||||
}, {
|
||||
granted: [PermissionKey.VIEW_ALL_RESOURCES],
|
||||
}));
|
||||
|
||||
const result = await caller.getByIdentifier({ identifier: "DE" });
|
||||
|
||||
expect(result._count.resources).toBe(12);
|
||||
expect(findFirst).toHaveBeenCalledWith(expect.objectContaining({
|
||||
include: expect.objectContaining({
|
||||
metroCities: expect.any(Object),
|
||||
_count: expect.any(Object),
|
||||
}),
|
||||
}));
|
||||
});
|
||||
|
||||
it("keeps minimal org-unit lookups available to authenticated users", async () => {
|
||||
const findFirst = vi.fn()
|
||||
.mockResolvedValueOnce(null)
|
||||
.mockResolvedValueOnce({
|
||||
id: "ou_1",
|
||||
name: "Delivery",
|
||||
shortName: "DEL",
|
||||
level: 5,
|
||||
isActive: true,
|
||||
});
|
||||
const caller = createCallerFactory(orgUnitRouter)(createProtectedContext({
|
||||
orgUnit: {
|
||||
findUnique: vi.fn().mockResolvedValue(null),
|
||||
findFirst,
|
||||
},
|
||||
}));
|
||||
|
||||
const result = await caller.resolveByIdentifier({ identifier: "DEL" });
|
||||
|
||||
expect(result).toEqual({
|
||||
id: "ou_1",
|
||||
name: "Delivery",
|
||||
shortName: "DEL",
|
||||
level: 5,
|
||||
isActive: true,
|
||||
});
|
||||
});
|
||||
|
||||
it("requires resource overview access for detailed org-unit reads with staffing counts", async () => {
|
||||
const caller = createCallerFactory(orgUnitRouter)(createProtectedContext({
|
||||
orgUnit: {
|
||||
findFirst: vi.fn(),
|
||||
findUnique: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
await expect(caller.getByIdentifier({ identifier: "Delivery" })).rejects.toMatchObject({
|
||||
code: "FORBIDDEN",
|
||||
message: "Resource overview access required",
|
||||
});
|
||||
await expect(caller.getById({ id: "ou_1" })).rejects.toMatchObject({
|
||||
code: "FORBIDDEN",
|
||||
message: "Resource overview access required",
|
||||
});
|
||||
});
|
||||
|
||||
it("allows detailed org-unit reads for users with resource overview access", async () => {
|
||||
const findFirst = vi.fn().mockResolvedValue({
|
||||
id: "ou_1",
|
||||
name: "Delivery",
|
||||
shortName: "DEL",
|
||||
level: 5,
|
||||
parentId: null,
|
||||
sortOrder: 10,
|
||||
isActive: true,
|
||||
_count: { resources: 7 },
|
||||
});
|
||||
const caller = createCallerFactory(orgUnitRouter)(createProtectedContext({
|
||||
orgUnit: {
|
||||
findUnique: vi.fn().mockResolvedValue(null),
|
||||
findFirst,
|
||||
},
|
||||
}, {
|
||||
granted: [PermissionKey.VIEW_ALL_RESOURCES],
|
||||
}));
|
||||
|
||||
const result = await caller.getByIdentifier({ identifier: "Delivery" });
|
||||
|
||||
expect(result._count.resources).toBe(7);
|
||||
expect(findFirst).toHaveBeenCalledWith(expect.objectContaining({
|
||||
include: { _count: { select: { resources: true } } },
|
||||
}));
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user