234 lines
12 KiB
TypeScript
234 lines
12 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { PermissionKey, SystemRole, type PermissionKey as PermissionKeyValue } from "@capakraken/shared";
|
|
import { getAvailableAssistantTools } from "../router/assistant-tool-policy.js";
|
|
import { TOOL_DEFINITIONS } from "../router/assistant-tools.js";
|
|
|
|
function getToolNames(
|
|
permissions: PermissionKeyValue[],
|
|
userRole: SystemRole = SystemRole.ADMIN,
|
|
) {
|
|
return getAvailableAssistantTools(new Set(permissions), userRole).map((tool) => tool.function.name);
|
|
}
|
|
|
|
describe("assistant tool policy admin parity", () => {
|
|
it("keeps import/dispo parity tools aligned to router roles and permissions", () => {
|
|
const managerNames = getToolNames([PermissionKey.IMPORT_DATA], SystemRole.MANAGER);
|
|
const controllerNames = getToolNames([], SystemRole.CONTROLLER);
|
|
const adminNames = getToolNames([], SystemRole.ADMIN);
|
|
const userNames = getToolNames([PermissionKey.IMPORT_DATA], SystemRole.USER);
|
|
|
|
expect(managerNames).toContain("import_csv_data");
|
|
expect(controllerNames).toContain("export_resources_csv");
|
|
expect(controllerNames).toContain("export_projects_csv");
|
|
expect(adminNames).toContain("list_dispo_import_batches");
|
|
expect(adminNames).toContain("get_dispo_import_batch");
|
|
expect(adminNames).toContain("stage_dispo_import_batch");
|
|
expect(adminNames).toContain("validate_dispo_import_batch");
|
|
expect(adminNames).toContain("cancel_dispo_import_batch");
|
|
expect(adminNames).toContain("list_dispo_staged_resources");
|
|
expect(adminNames).toContain("list_dispo_staged_projects");
|
|
expect(adminNames).toContain("list_dispo_staged_assignments");
|
|
expect(adminNames).toContain("list_dispo_staged_vacations");
|
|
expect(adminNames).toContain("list_dispo_staged_unresolved_records");
|
|
expect(adminNames).toContain("resolve_dispo_staged_record");
|
|
expect(adminNames).toContain("commit_dispo_import_batch");
|
|
expect(userNames).not.toContain("import_csv_data");
|
|
expect(userNames).not.toContain("export_resources_csv");
|
|
expect(userNames).not.toContain("export_projects_csv");
|
|
expect(userNames).not.toContain("list_dispo_import_batches");
|
|
expect(userNames).not.toContain("get_dispo_import_batch");
|
|
expect(userNames).not.toContain("stage_dispo_import_batch");
|
|
expect(userNames).not.toContain("validate_dispo_import_batch");
|
|
expect(userNames).not.toContain("list_dispo_staged_resources");
|
|
expect(userNames).not.toContain("commit_dispo_import_batch");
|
|
});
|
|
|
|
it("keeps settings and webhook admin tools hidden while preserving protected parity tools", () => {
|
|
const adminNames = getToolNames([], SystemRole.ADMIN);
|
|
const managerNames = getToolNames([], SystemRole.MANAGER);
|
|
const userNames = getToolNames([], SystemRole.USER);
|
|
|
|
expect(adminNames).toContain("get_system_settings");
|
|
expect(adminNames).toContain("update_system_settings");
|
|
expect(adminNames).toContain("clear_stored_runtime_secrets");
|
|
expect(adminNames).toContain("test_ai_connection");
|
|
expect(adminNames).toContain("test_smtp_connection");
|
|
expect(adminNames).toContain("test_gemini_connection");
|
|
expect(adminNames).toContain("list_system_role_configs");
|
|
expect(adminNames).toContain("update_system_role_config");
|
|
expect(adminNames).toContain("list_webhooks");
|
|
expect(adminNames).toContain("get_webhook");
|
|
expect(adminNames).toContain("create_webhook");
|
|
expect(adminNames).toContain("update_webhook");
|
|
expect(adminNames).toContain("delete_webhook");
|
|
expect(adminNames).toContain("test_webhook");
|
|
expect(adminNames).toContain("get_ai_configured");
|
|
|
|
expect(managerNames).not.toContain("get_system_settings");
|
|
expect(managerNames).not.toContain("update_system_settings");
|
|
expect(managerNames).not.toContain("clear_stored_runtime_secrets");
|
|
expect(managerNames).not.toContain("test_ai_connection");
|
|
expect(managerNames).not.toContain("get_ai_configured");
|
|
expect(managerNames).not.toContain("list_system_role_configs");
|
|
expect(managerNames).not.toContain("update_system_role_config");
|
|
expect(managerNames).not.toContain("list_webhooks");
|
|
expect(managerNames).not.toContain("create_webhook");
|
|
|
|
expect(userNames).not.toContain("get_system_settings");
|
|
expect(userNames).not.toContain("update_system_settings");
|
|
expect(userNames).not.toContain("clear_stored_runtime_secrets");
|
|
expect(userNames).not.toContain("test_ai_connection");
|
|
expect(userNames).not.toContain("get_ai_configured");
|
|
expect(userNames).not.toContain("list_system_role_configs");
|
|
expect(userNames).not.toContain("update_system_role_config");
|
|
expect(userNames).not.toContain("list_webhooks");
|
|
expect(userNames).not.toContain("create_webhook");
|
|
});
|
|
|
|
it("keeps client deletion admin-only while still allowing manager client maintenance", () => {
|
|
const adminNames = getToolNames([], SystemRole.ADMIN);
|
|
const managerNames = getToolNames([], SystemRole.MANAGER);
|
|
const userNames = getToolNames([], SystemRole.USER);
|
|
|
|
expect(adminNames).toContain("create_client");
|
|
expect(adminNames).toContain("update_client");
|
|
expect(adminNames).toContain("delete_client");
|
|
expect(managerNames).toContain("create_client");
|
|
expect(managerNames).toContain("update_client");
|
|
expect(managerNames).not.toContain("delete_client");
|
|
expect(userNames).not.toContain("create_client");
|
|
expect(userNames).not.toContain("update_client");
|
|
expect(userNames).not.toContain("delete_client");
|
|
});
|
|
|
|
it("keeps holiday calendar catalog tools admin-only while leaving preview available", () => {
|
|
const adminNames = getToolNames([], SystemRole.ADMIN);
|
|
const managerNames = getToolNames([], SystemRole.MANAGER);
|
|
const userNames = getToolNames([], SystemRole.USER);
|
|
|
|
expect(adminNames).toContain("list_holiday_calendars");
|
|
expect(adminNames).toContain("get_holiday_calendar");
|
|
expect(adminNames).toContain("preview_resolved_holiday_calendar");
|
|
expect(adminNames).toContain("create_holiday_calendar");
|
|
expect(managerNames).not.toContain("list_holiday_calendars");
|
|
expect(managerNames).not.toContain("get_holiday_calendar");
|
|
expect(managerNames).toContain("preview_resolved_holiday_calendar");
|
|
expect(userNames).not.toContain("list_holiday_calendars");
|
|
expect(userNames).not.toContain("get_holiday_calendar");
|
|
expect(userNames).toContain("preview_resolved_holiday_calendar");
|
|
expect(managerNames).not.toContain("create_holiday_calendar");
|
|
expect(managerNames).not.toContain("update_holiday_calendar");
|
|
expect(managerNames).not.toContain("delete_holiday_calendar");
|
|
expect(managerNames).not.toContain("create_holiday_calendar_entry");
|
|
expect(managerNames).not.toContain("update_holiday_calendar_entry");
|
|
expect(managerNames).not.toContain("delete_holiday_calendar_entry");
|
|
});
|
|
|
|
it("keeps country and metro-city mutation tools admin-only while leaving read tools available", () => {
|
|
const adminNames = getToolNames([], SystemRole.ADMIN);
|
|
const managerNames = getToolNames([], SystemRole.MANAGER);
|
|
const userNames = getToolNames([], SystemRole.USER);
|
|
const userWithResourceOverview = getToolNames([PermissionKey.VIEW_ALL_RESOURCES], SystemRole.USER);
|
|
const userWithManagedResources = getToolNames([PermissionKey.MANAGE_RESOURCES], SystemRole.USER);
|
|
|
|
expect(adminNames).toContain("list_countries");
|
|
expect(adminNames).toContain("create_country");
|
|
expect(adminNames).toContain("update_country");
|
|
expect(adminNames).toContain("create_metro_city");
|
|
expect(adminNames).toContain("update_metro_city");
|
|
expect(adminNames).toContain("delete_metro_city");
|
|
expect(managerNames).toContain("list_countries");
|
|
expect(managerNames).not.toContain("create_country");
|
|
expect(managerNames).not.toContain("update_country");
|
|
expect(managerNames).not.toContain("create_metro_city");
|
|
expect(managerNames).not.toContain("update_metro_city");
|
|
expect(managerNames).not.toContain("delete_metro_city");
|
|
expect(userNames).not.toContain("search_resources");
|
|
expect(userNames).not.toContain("get_country");
|
|
expect(userNames).not.toContain("list_org_units");
|
|
expect(userWithResourceOverview).toContain("search_resources");
|
|
expect(userWithResourceOverview).toContain("get_country");
|
|
expect(userWithResourceOverview).toContain("list_org_units");
|
|
expect(userWithManagedResources).toContain("search_resources");
|
|
expect(userWithManagedResources).toContain("get_country");
|
|
expect(userWithManagedResources).toContain("list_org_units");
|
|
});
|
|
|
|
it("attaches explicit access metadata to legacy monolithic tools with restricted visibility", () => {
|
|
const toolAccess = new Map(
|
|
TOOL_DEFINITIONS.map((tool) => [tool.function.name, tool.access]),
|
|
);
|
|
|
|
expect(toolAccess.get("run_report")).toEqual({
|
|
allowedSystemRoles: [SystemRole.ADMIN, SystemRole.MANAGER, SystemRole.CONTROLLER],
|
|
});
|
|
expect(toolAccess.get("simulate_scenario")).toEqual({
|
|
allowedSystemRoles: [SystemRole.ADMIN, SystemRole.MANAGER, SystemRole.CONTROLLER],
|
|
});
|
|
expect(toolAccess.get("detect_anomalies")).toEqual({
|
|
allowedSystemRoles: [SystemRole.ADMIN, SystemRole.MANAGER, SystemRole.CONTROLLER],
|
|
});
|
|
expect(toolAccess.get("get_insights_summary")).toEqual({
|
|
allowedSystemRoles: [SystemRole.ADMIN, SystemRole.MANAGER, SystemRole.CONTROLLER],
|
|
});
|
|
expect(toolAccess.get("lookup_rate")).toEqual({
|
|
allowedSystemRoles: [SystemRole.ADMIN, SystemRole.MANAGER, SystemRole.CONTROLLER],
|
|
requiredPermissions: [PermissionKey.VIEW_COSTS],
|
|
});
|
|
expect(toolAccess.get("list_rate_cards")).toEqual({
|
|
allowedSystemRoles: [SystemRole.ADMIN, SystemRole.MANAGER, SystemRole.CONTROLLER],
|
|
requiresCostView: true,
|
|
});
|
|
expect(toolAccess.get("resolve_rate")).toEqual({
|
|
allowedSystemRoles: [SystemRole.ADMIN, SystemRole.MANAGER, SystemRole.CONTROLLER],
|
|
requiresCostView: true,
|
|
});
|
|
expect(toolAccess.get("import_csv_data")).toEqual({
|
|
requiredPermissions: [PermissionKey.IMPORT_DATA],
|
|
allowedSystemRoles: [SystemRole.ADMIN, SystemRole.MANAGER],
|
|
});
|
|
});
|
|
|
|
it("aligns assistant tool visibility with router role and permission rules", () => {
|
|
const managerWithRolePermission = getToolNames(
|
|
[PermissionKey.MANAGE_ROLES],
|
|
SystemRole.MANAGER,
|
|
);
|
|
const managerWithoutRolePermission = getToolNames([], SystemRole.MANAGER);
|
|
|
|
expect(managerWithRolePermission).toContain("create_role");
|
|
expect(managerWithRolePermission).toContain("update_role");
|
|
expect(managerWithRolePermission).toContain("delete_role");
|
|
expect(managerWithRolePermission).toContain("create_client");
|
|
expect(managerWithRolePermission).toContain("update_client");
|
|
expect(managerWithRolePermission).not.toContain("create_org_unit");
|
|
expect(managerWithRolePermission).not.toContain("update_org_unit");
|
|
|
|
expect(managerWithoutRolePermission).not.toContain("create_role");
|
|
expect(managerWithoutRolePermission).not.toContain("update_role");
|
|
expect(managerWithoutRolePermission).not.toContain("delete_role");
|
|
expect(managerWithoutRolePermission).toContain("create_client");
|
|
expect(managerWithoutRolePermission).toContain("update_client");
|
|
|
|
const adminWithRolePermission = getToolNames(
|
|
[PermissionKey.MANAGE_ROLES],
|
|
SystemRole.ADMIN,
|
|
);
|
|
expect(adminWithRolePermission).toContain("create_org_unit");
|
|
expect(adminWithRolePermission).toContain("update_org_unit");
|
|
|
|
const standardUserTools = getToolNames([], SystemRole.USER);
|
|
expect(standardUserTools).toContain("get_vacation_balance");
|
|
expect(standardUserTools).toContain("create_vacation");
|
|
expect(standardUserTools).toContain("cancel_vacation");
|
|
expect(standardUserTools).not.toContain("approve_vacation");
|
|
expect(standardUserTools).not.toContain("reject_vacation");
|
|
expect(standardUserTools).not.toContain("set_entitlement");
|
|
|
|
const managerVacationTools = getToolNames([], SystemRole.MANAGER);
|
|
expect(managerVacationTools).toContain("approve_vacation");
|
|
expect(managerVacationTools).toContain("reject_vacation");
|
|
expect(managerVacationTools).toContain("set_entitlement");
|
|
});
|
|
});
|