feat(blueprint): scope summary reads to planning audience
This commit is contained in:
@@ -387,6 +387,7 @@ describe("assistant router tool gating", () => {
|
||||
|
||||
expect(userWithoutPlanning).not.toContain("list_allocations");
|
||||
expect(userWithoutPlanning).not.toContain("list_demands");
|
||||
expect(userWithoutPlanning).not.toContain("list_blueprints");
|
||||
expect(userWithoutPlanning).not.toContain("list_clients");
|
||||
expect(userWithoutPlanning).not.toContain("list_roles");
|
||||
expect(userWithoutPlanning).not.toContain("list_management_levels");
|
||||
@@ -397,6 +398,7 @@ describe("assistant router tool gating", () => {
|
||||
expect(userWithoutPlanning).not.toContain("find_best_project_resource");
|
||||
expect(userWithPlanning).toContain("list_allocations");
|
||||
expect(userWithPlanning).toContain("list_demands");
|
||||
expect(userWithPlanning).toContain("list_blueprints");
|
||||
expect(userWithPlanning).toContain("list_clients");
|
||||
expect(userWithPlanning).toContain("list_roles");
|
||||
expect(userWithPlanning).toContain("list_management_levels");
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { PermissionKey, SystemRole } from "@capakraken/shared";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { blueprintRouter } from "../router/blueprint.js";
|
||||
import { clientRouter } from "../router/client.js";
|
||||
import { countryRouter } from "../router/country.js";
|
||||
import { managementLevelRouter } from "../router/management-level.js";
|
||||
@@ -26,6 +27,52 @@ function createProtectedContext(
|
||||
}
|
||||
|
||||
describe("master-data router authorization", () => {
|
||||
it("requires planning read access for blueprint summaries with project counts", async () => {
|
||||
const findMany = vi.fn();
|
||||
const caller = createCallerFactory(blueprintRouter)(createProtectedContext({
|
||||
blueprint: {
|
||||
findMany,
|
||||
},
|
||||
}));
|
||||
|
||||
await expect(caller.listSummaries()).rejects.toMatchObject({
|
||||
code: "FORBIDDEN",
|
||||
message: "Planning read access required",
|
||||
});
|
||||
|
||||
expect(findMany).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("allows blueprint summaries for users with planning access", async () => {
|
||||
const findMany = vi.fn().mockResolvedValue([
|
||||
{
|
||||
id: "bp_1",
|
||||
name: "Consulting Blueprint",
|
||||
_count: { projects: 4 },
|
||||
},
|
||||
]);
|
||||
const caller = createCallerFactory(blueprintRouter)(createProtectedContext({
|
||||
blueprint: {
|
||||
findMany,
|
||||
},
|
||||
}, {
|
||||
granted: [PermissionKey.VIEW_PLANNING],
|
||||
}));
|
||||
|
||||
const result = await caller.listSummaries();
|
||||
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0]?._count.projects).toBe(4);
|
||||
expect(findMany).toHaveBeenCalledWith({
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
_count: { select: { projects: true } },
|
||||
},
|
||||
orderBy: { name: "asc" },
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps country lists available to authenticated users as safe lookup data", async () => {
|
||||
const findMany = vi.fn().mockResolvedValue([
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user