import { describe, expect, it } from "vitest"; import { PermissionKey, SystemRole, type PermissionKey as PermissionKeyValue } from "@capakraken/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 access", () => { it("hides advanced tools unless the dedicated assistant permission is granted", () => { const withoutAdvanced = getToolNames([ PermissionKey.VIEW_PLANNING, PermissionKey.VIEW_COSTS, ]); const withAdvanced = getToolNames([ PermissionKey.VIEW_PLANNING, PermissionKey.VIEW_COSTS, PermissionKey.USE_ASSISTANT_ADVANCED_TOOLS, ]); expect(withoutAdvanced).not.toContain("find_best_project_resource"); expect(withAdvanced).toContain("find_best_project_resource"); expect(withAdvanced).toContain("get_chargeability_report"); expect(withAdvanced).toContain("get_resource_computation_graph"); expect(withAdvanced).toContain("get_project_computation_graph"); }); it("keeps user self-service tools available to plain authenticated users", () => { const userNames = getToolNames([], SystemRole.USER); expect(userNames).toContain("get_current_user"); expect(userNames).toContain("get_dashboard_layout"); expect(userNames).toContain("save_dashboard_layout"); expect(userNames).toContain("get_favorite_project_ids"); expect(userNames).toContain("toggle_favorite_project"); expect(userNames).toContain("get_column_preferences"); expect(userNames).toContain("set_column_preferences"); expect(userNames).toContain("get_mfa_status"); expect(userNames).toContain("list_notifications"); expect(userNames).toContain("get_unread_notification_count"); expect(userNames).toContain("list_tasks"); expect(userNames).toContain("get_task_counts"); expect(userNames).toContain("create_reminder"); expect(userNames).toContain("list_reminders"); expect(userNames).toContain("update_reminder"); expect(userNames).toContain("delete_reminder"); }); it("keeps admin-only user tools hidden from non-admin roles", () => { const adminNames = getToolNames([], SystemRole.ADMIN); const managerNames = getToolNames([], SystemRole.MANAGER); const userNames = getToolNames([], SystemRole.USER); expect(adminNames).toContain("list_users"); expect(adminNames).toContain("get_active_user_count"); expect(adminNames).toContain("create_user"); expect(adminNames).toContain("set_user_password"); expect(adminNames).toContain("update_user_role"); expect(adminNames).toContain("update_user_name"); expect(adminNames).toContain("link_user_resource"); expect(adminNames).toContain("auto_link_users_by_email"); expect(adminNames).toContain("set_user_permissions"); expect(adminNames).toContain("reset_user_permissions"); expect(adminNames).toContain("get_effective_user_permissions"); expect(adminNames).toContain("disable_user_totp"); expect(managerNames).not.toContain("list_users"); expect(managerNames).not.toContain("create_user"); expect(managerNames).not.toContain("set_user_permissions"); expect(managerNames).not.toContain("disable_user_totp"); expect(userNames).not.toContain("list_users"); expect(userNames).not.toContain("get_active_user_count"); expect(userNames).not.toContain("create_user"); expect(userNames).not.toContain("set_user_password"); expect(userNames).not.toContain("update_user_role"); expect(userNames).not.toContain("update_user_name"); expect(userNames).not.toContain("link_user_resource"); expect(userNames).not.toContain("auto_link_users_by_email"); expect(userNames).not.toContain("set_user_permissions"); expect(userNames).not.toContain("reset_user_permissions"); expect(userNames).not.toContain("get_effective_user_permissions"); expect(userNames).not.toContain("disable_user_totp"); }); it("keeps assignable users and manager notification lifecycle tools behind manager/admin role", () => { const managerNames = getToolNames([], SystemRole.MANAGER); const adminNames = getToolNames([], SystemRole.ADMIN); const userNames = getToolNames([], SystemRole.USER); expect(managerNames).toContain("list_assignable_users"); expect(managerNames).toContain("create_notification"); expect(managerNames).toContain("create_task_for_user"); expect(managerNames).toContain("assign_task"); expect(managerNames).toContain("send_broadcast"); expect(managerNames).toContain("list_broadcasts"); expect(managerNames).toContain("get_broadcast_detail"); expect(adminNames).toContain("list_assignable_users"); expect(adminNames).toContain("create_task_for_user"); expect(adminNames).toContain("send_broadcast"); expect(userNames).not.toContain("list_assignable_users"); expect(userNames).not.toContain("create_notification"); expect(userNames).not.toContain("create_task_for_user"); expect(userNames).not.toContain("assign_task"); expect(userNames).not.toContain("send_broadcast"); expect(userNames).not.toContain("list_broadcasts"); expect(userNames).not.toContain("get_broadcast_detail"); }); it("continues to hide cost-aware advanced tools when viewCosts is missing", () => { const names = getToolNames([PermissionKey.USE_ASSISTANT_ADVANCED_TOOLS]); expect(names).not.toContain("find_best_project_resource"); expect(names).not.toContain("get_chargeability_report"); expect(names).not.toContain("get_resource_computation_graph"); expect(names).not.toContain("get_project_computation_graph"); }); it("keeps controller-grade readmodels hidden from plain users while allowing controller roles", () => { const controllerNames = getToolNames([ PermissionKey.VIEW_COSTS, PermissionKey.USE_ASSISTANT_ADVANCED_TOOLS, ], SystemRole.CONTROLLER); const userNames = getToolNames([ PermissionKey.VIEW_COSTS, PermissionKey.USE_ASSISTANT_ADVANCED_TOOLS, ], SystemRole.USER); expect(controllerNames).toContain("query_change_history"); expect(controllerNames).toContain("get_entity_timeline"); expect(controllerNames).toContain("search_by_skill"); expect(controllerNames).toContain("export_resources_csv"); expect(controllerNames).toContain("export_projects_csv"); expect(controllerNames).toContain("list_audit_log_entries"); expect(controllerNames).toContain("get_audit_log_entry"); expect(controllerNames).toContain("get_audit_log_timeline"); expect(controllerNames).toContain("get_audit_activity_summary"); expect(controllerNames).toContain("get_chargeability_report"); expect(controllerNames).toContain("get_resource_computation_graph"); expect(controllerNames).toContain("get_project_computation_graph"); expect(userNames).not.toContain("query_change_history"); expect(userNames).not.toContain("get_entity_timeline"); expect(userNames).not.toContain("search_by_skill"); expect(userNames).not.toContain("export_resources_csv"); expect(userNames).not.toContain("export_projects_csv"); expect(userNames).not.toContain("list_audit_log_entries"); expect(userNames).not.toContain("get_audit_log_entry"); expect(userNames).not.toContain("get_audit_log_timeline"); expect(userNames).not.toContain("get_audit_activity_summary"); expect(userNames).not.toContain("get_chargeability_report"); expect(userNames).not.toContain("get_resource_computation_graph"); expect(userNames).not.toContain("get_project_computation_graph"); }); it("keeps entity-scoped comment tools available to plain authenticated users", () => { const userNames = getToolNames([], SystemRole.USER); expect(userNames).toContain("list_comments"); expect(userNames).toContain("create_comment"); expect(userNames).toContain("resolve_comment"); }); it("keeps planning read tools behind the explicit planning permission", () => { const userWithoutPlanning = getToolNames([], SystemRole.USER); const userWithPlanning = getToolNames([PermissionKey.VIEW_PLANNING], SystemRole.USER); expect(userWithoutPlanning).not.toContain("list_allocations"); expect(userWithoutPlanning).not.toContain("list_demands"); expect(userWithoutPlanning).not.toContain("list_blueprints"); expect(userWithoutPlanning).not.toContain("get_blueprint"); expect(userWithoutPlanning).not.toContain("list_clients"); expect(userWithoutPlanning).not.toContain("list_roles"); expect(userWithoutPlanning).not.toContain("list_management_levels"); expect(userWithoutPlanning).not.toContain("list_utilization_categories"); expect(userWithoutPlanning).not.toContain("check_resource_availability"); expect(userWithoutPlanning).not.toContain("find_capacity"); expect(userWithoutPlanning).not.toContain("get_staffing_suggestions"); 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("get_blueprint"); expect(userWithPlanning).toContain("list_clients"); expect(userWithPlanning).toContain("list_roles"); expect(userWithPlanning).toContain("list_management_levels"); expect(userWithPlanning).toContain("list_utilization_categories"); expect(userWithPlanning).toContain("check_resource_availability"); expect(userWithPlanning).toContain("find_capacity"); expect(userWithPlanning).not.toContain("get_staffing_suggestions"); expect(userWithPlanning).not.toContain("find_best_project_resource"); }); it("keeps cost-aware staffing assistant tools behind cost and advanced gates", () => { const planningOnly = getToolNames([PermissionKey.VIEW_PLANNING], SystemRole.USER); const planningAndCosts = getToolNames([ PermissionKey.VIEW_PLANNING, PermissionKey.VIEW_COSTS, ], SystemRole.USER); const planningCostsAndAdvanced = getToolNames([ PermissionKey.VIEW_PLANNING, PermissionKey.VIEW_COSTS, PermissionKey.USE_ASSISTANT_ADVANCED_TOOLS, ], SystemRole.USER); expect(planningOnly).not.toContain("get_staffing_suggestions"); expect(planningOnly).not.toContain("find_best_project_resource"); expect(planningAndCosts).toContain("get_staffing_suggestions"); expect(planningAndCosts).not.toContain("find_best_project_resource"); expect(planningCostsAndAdvanced).toContain("get_staffing_suggestions"); expect(planningCostsAndAdvanced).toContain("find_best_project_resource"); }); it("keeps controller-only project and dashboard reads hidden from plain users", () => { const controllerNames = getToolNames([ PermissionKey.VIEW_COSTS, PermissionKey.USE_ASSISTANT_ADVANCED_TOOLS, PermissionKey.VIEW_PLANNING, ], SystemRole.CONTROLLER); const userNames = getToolNames([ PermissionKey.VIEW_COSTS, PermissionKey.USE_ASSISTANT_ADVANCED_TOOLS, PermissionKey.VIEW_PLANNING, ], SystemRole.USER); expect(controllerNames).toContain("search_projects"); expect(controllerNames).toContain("get_project"); expect(controllerNames).toContain("get_statistics"); expect(controllerNames).toContain("get_dashboard_detail"); expect(controllerNames).toContain("get_skill_gaps"); expect(controllerNames).toContain("get_project_health"); expect(controllerNames).toContain("get_budget_forecast"); expect(controllerNames).toContain("get_budget_status"); expect(controllerNames).toContain("get_shoring_ratio"); expect(userNames).not.toContain("search_projects"); expect(userNames).not.toContain("get_project"); expect(userNames).not.toContain("get_statistics"); expect(userNames).not.toContain("get_dashboard_detail"); expect(userNames).not.toContain("get_skill_gaps"); expect(userNames).not.toContain("get_project_health"); expect(userNames).not.toContain("get_budget_forecast"); expect(userNames).not.toContain("get_budget_status"); expect(userNames).not.toContain("get_shoring_ratio"); }); it("keeps legacy controller-only analysis and report tools hidden from plain users", () => { const controllerNames = getToolNames([ PermissionKey.VIEW_COSTS, PermissionKey.VIEW_PLANNING, ], SystemRole.CONTROLLER); const userNames = getToolNames([ PermissionKey.VIEW_COSTS, PermissionKey.VIEW_PLANNING, ], SystemRole.USER); expect(controllerNames).toContain("detect_anomalies"); expect(controllerNames).toContain("get_insights_summary"); expect(controllerNames).toContain("run_report"); expect(controllerNames).toContain("lookup_rate"); expect(controllerNames).toContain("simulate_scenario"); expect(controllerNames).toContain("generate_project_narrative"); expect(controllerNames).toContain("list_rate_cards"); expect(controllerNames).toContain("resolve_rate"); expect(userNames).not.toContain("detect_anomalies"); expect(userNames).not.toContain("get_insights_summary"); expect(userNames).not.toContain("run_report"); expect(userNames).not.toContain("lookup_rate"); expect(userNames).not.toContain("simulate_scenario"); expect(userNames).not.toContain("generate_project_narrative"); expect(userNames).not.toContain("list_rate_cards"); expect(userNames).not.toContain("resolve_rate"); }); it("keeps cost-sensitive legacy rate tools hidden without viewCosts", () => { const controllerWithoutCosts = getToolNames([], SystemRole.CONTROLLER); const controllerWithCosts = getToolNames([PermissionKey.VIEW_COSTS], SystemRole.CONTROLLER); expect(controllerWithoutCosts).not.toContain("list_rate_cards"); expect(controllerWithoutCosts).not.toContain("resolve_rate"); expect(controllerWithCosts).toContain("list_rate_cards"); expect(controllerWithCosts).toContain("resolve_rate"); }); });