b41c1d2501
CI / Architecture Guardrails (push) Successful in 2m38s
CI / Assistant Split Regression (push) Successful in 3m33s
CI / Typecheck (push) Successful in 3m51s
CI / Lint (push) Successful in 5m2s
CI / E2E Tests (push) Has been cancelled
CI / Fresh-Linux Docker Deploy (push) Has been cancelled
CI / Release Images (push) Has been cancelled
CI / Build (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
rename(phase 1): CapaKraken → Nexus across code, UI, docs, CI (#61) Co-authored-by: Hartmut Nörenberg <hn@hartmut-noerenberg.com> Co-committed-by: Hartmut Nörenberg <hn@hartmut-noerenberg.com>
146 lines
8.2 KiB
TypeScript
146 lines
8.2 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { PermissionKey, SystemRole, type PermissionKey as PermissionKeyValue } from "@nexus/shared";
|
|
import { getAvailableAssistantTools } from "../router/assistant-tool-policy.js";
|
|
|
|
function getToolNames(permissions: PermissionKeyValue[], userRole: SystemRole = SystemRole.ADMIN) {
|
|
return getAvailableAssistantTools(new Set(permissions), userRole).map(
|
|
(tool) => tool.function.name,
|
|
);
|
|
}
|
|
|
|
describe("assistant tool policy planning flows", () => {
|
|
it("requires both controller role and advanced assistant access for timeline detail tools", () => {
|
|
const controllerWithAdvanced = getToolNames(
|
|
[PermissionKey.USE_ASSISTANT_ADVANCED_TOOLS],
|
|
SystemRole.CONTROLLER,
|
|
);
|
|
const controllerWithoutAdvanced = getToolNames([], SystemRole.CONTROLLER);
|
|
const userWithAdvanced = getToolNames(
|
|
[PermissionKey.USE_ASSISTANT_ADVANCED_TOOLS],
|
|
SystemRole.USER,
|
|
);
|
|
|
|
expect(controllerWithAdvanced).toContain("get_timeline_entries_view");
|
|
expect(controllerWithAdvanced).toContain("get_timeline_holiday_overlays");
|
|
expect(controllerWithAdvanced).toContain("get_project_timeline_context");
|
|
expect(controllerWithAdvanced).toContain("preview_project_shift");
|
|
expect(controllerWithoutAdvanced).not.toContain("get_timeline_entries_view");
|
|
expect(controllerWithoutAdvanced).not.toContain("get_timeline_holiday_overlays");
|
|
expect(controllerWithoutAdvanced).not.toContain("get_project_timeline_context");
|
|
expect(controllerWithoutAdvanced).not.toContain("preview_project_shift");
|
|
expect(userWithAdvanced).not.toContain("get_timeline_entries_view");
|
|
expect(userWithAdvanced).not.toContain("get_timeline_holiday_overlays");
|
|
expect(userWithAdvanced).not.toContain("get_project_timeline_context");
|
|
expect(userWithAdvanced).not.toContain("preview_project_shift");
|
|
});
|
|
|
|
it("exposes self-service timeline tools to authenticated users without advanced assistant access", () => {
|
|
const userNames = getToolNames([], SystemRole.USER);
|
|
const viewerNames = getToolNames([], SystemRole.VIEWER);
|
|
const controllerNames = getToolNames([], SystemRole.CONTROLLER);
|
|
|
|
expect(userNames).toContain("get_my_timeline_entries_view");
|
|
expect(userNames).toContain("get_my_timeline_holiday_overlays");
|
|
expect(viewerNames).toContain("get_my_timeline_entries_view");
|
|
expect(viewerNames).toContain("get_my_timeline_holiday_overlays");
|
|
expect(controllerNames).toContain("get_my_timeline_entries_view");
|
|
expect(controllerNames).toContain("get_my_timeline_holiday_overlays");
|
|
});
|
|
|
|
it("keeps timeline write parity tools behind manager/admin role, manageAllocations, and advanced assistant access", () => {
|
|
const managerNames = getToolNames(
|
|
[PermissionKey.MANAGE_ALLOCATIONS, PermissionKey.USE_ASSISTANT_ADVANCED_TOOLS],
|
|
SystemRole.MANAGER,
|
|
);
|
|
const userNames = getToolNames(
|
|
[PermissionKey.MANAGE_ALLOCATIONS, PermissionKey.USE_ASSISTANT_ADVANCED_TOOLS],
|
|
SystemRole.USER,
|
|
);
|
|
const missingAdvancedNames = getToolNames(
|
|
[PermissionKey.MANAGE_ALLOCATIONS],
|
|
SystemRole.MANAGER,
|
|
);
|
|
|
|
expect(managerNames).toContain("update_timeline_allocation_inline");
|
|
expect(managerNames).toContain("apply_timeline_project_shift");
|
|
expect(managerNames).toContain("quick_assign_timeline_resource");
|
|
expect(managerNames).toContain("batch_quick_assign_timeline_resources");
|
|
expect(managerNames).toContain("batch_shift_timeline_allocations");
|
|
expect(userNames).not.toContain("update_timeline_allocation_inline");
|
|
expect(userNames).not.toContain("apply_timeline_project_shift");
|
|
expect(userNames).not.toContain("quick_assign_timeline_resource");
|
|
expect(userNames).not.toContain("batch_quick_assign_timeline_resources");
|
|
expect(userNames).not.toContain("batch_shift_timeline_allocations");
|
|
expect(missingAdvancedNames).not.toContain("update_timeline_allocation_inline");
|
|
expect(missingAdvancedNames).not.toContain("quick_assign_timeline_resource");
|
|
});
|
|
|
|
it("keeps estimate lifecycle mutations behind manager/admin role and their router permissions", () => {
|
|
const managerProjectNames = getToolNames([PermissionKey.MANAGE_PROJECTS], SystemRole.MANAGER);
|
|
const managerAllocationNames = getToolNames(
|
|
[PermissionKey.MANAGE_ALLOCATIONS],
|
|
SystemRole.MANAGER,
|
|
);
|
|
const userProjectNames = getToolNames([PermissionKey.MANAGE_PROJECTS], SystemRole.USER);
|
|
|
|
expect(managerProjectNames).toContain("create_estimate");
|
|
expect(managerProjectNames).toContain("clone_estimate");
|
|
expect(managerProjectNames).toContain("update_estimate_draft");
|
|
expect(managerProjectNames).toContain("submit_estimate_version");
|
|
expect(managerProjectNames).toContain("approve_estimate_version");
|
|
expect(managerProjectNames).toContain("create_estimate_revision");
|
|
expect(managerProjectNames).toContain("create_estimate_export");
|
|
expect(managerProjectNames).toContain("generate_estimate_weekly_phasing");
|
|
expect(managerProjectNames).toContain("update_estimate_commercial_terms");
|
|
expect(managerProjectNames).not.toContain("create_estimate_planning_handoff");
|
|
expect(managerAllocationNames).toContain("create_estimate_planning_handoff");
|
|
expect(managerAllocationNames).not.toContain("create_estimate");
|
|
expect(userProjectNames).not.toContain("create_estimate");
|
|
expect(userProjectNames).not.toContain("clone_estimate");
|
|
expect(userProjectNames).not.toContain("update_estimate_draft");
|
|
expect(userProjectNames).not.toContain("submit_estimate_version");
|
|
expect(userProjectNames).not.toContain("approve_estimate_version");
|
|
expect(userProjectNames).not.toContain("create_estimate_revision");
|
|
expect(userProjectNames).not.toContain("create_estimate_export");
|
|
expect(userProjectNames).not.toContain("generate_estimate_weekly_phasing");
|
|
expect(userProjectNames).not.toContain("update_estimate_commercial_terms");
|
|
expect(userProjectNames).not.toContain("create_estimate_planning_handoff");
|
|
});
|
|
|
|
it("keeps estimate read tools aligned to controller/manager/admin visibility and cost requirements", () => {
|
|
const controllerNames = getToolNames([PermissionKey.VIEW_COSTS], SystemRole.CONTROLLER);
|
|
const controllerWithoutCosts = getToolNames([], SystemRole.CONTROLLER);
|
|
const managerNames = getToolNames([PermissionKey.VIEW_COSTS], SystemRole.MANAGER);
|
|
const managerWithoutCosts = getToolNames([], SystemRole.MANAGER);
|
|
const userNames = getToolNames([PermissionKey.VIEW_COSTS], SystemRole.USER);
|
|
|
|
expect(controllerNames).toContain("search_estimates");
|
|
expect(controllerNames).toContain("get_estimate_detail");
|
|
expect(controllerNames).toContain("list_estimate_versions");
|
|
expect(controllerNames).toContain("get_estimate_version_snapshot");
|
|
expect(controllerNames).toContain("get_estimate_weekly_phasing");
|
|
expect(controllerNames).toContain("get_estimate_commercial_terms");
|
|
expect(controllerWithoutCosts).toContain("search_estimates");
|
|
expect(controllerWithoutCosts).not.toContain("get_estimate_detail");
|
|
expect(controllerWithoutCosts).toContain("list_estimate_versions");
|
|
expect(controllerWithoutCosts).not.toContain("get_estimate_version_snapshot");
|
|
expect(controllerWithoutCosts).toContain("get_estimate_weekly_phasing");
|
|
expect(controllerWithoutCosts).toContain("get_estimate_commercial_terms");
|
|
expect(managerNames).toContain("search_estimates");
|
|
expect(managerNames).toContain("get_estimate_detail");
|
|
expect(managerNames).toContain("list_estimate_versions");
|
|
expect(managerNames).toContain("get_estimate_version_snapshot");
|
|
expect(managerNames).toContain("get_estimate_weekly_phasing");
|
|
expect(managerNames).toContain("get_estimate_commercial_terms");
|
|
expect(managerWithoutCosts).toContain("search_estimates");
|
|
expect(managerWithoutCosts).toContain("list_estimate_versions");
|
|
expect(managerWithoutCosts).not.toContain("get_estimate_version_snapshot");
|
|
expect(userNames).not.toContain("search_estimates");
|
|
expect(userNames).not.toContain("get_estimate_detail");
|
|
expect(userNames).not.toContain("list_estimate_versions");
|
|
expect(userNames).not.toContain("get_estimate_version_snapshot");
|
|
expect(userNames).not.toContain("get_estimate_weekly_phasing");
|
|
expect(userNames).not.toContain("get_estimate_commercial_terms");
|
|
});
|
|
});
|