== User stories == US1: Контекст с дашборда/датасета → /agent с URL params US2: Guardrails card — env badge, 7 risk tones, countdown, permission_denied US3: Tools optimization — retry, timeout, summarise, RBAC + context affinity == Backend == - _context.py (NEW): UIContext validation (7 checks) - _tool_filter.py (NEW): RBAC + context affinity pipeline - _confirmation.py: build_confirmation_contract_v2, permission_denied_payload - tools.py: superset_list_databases, retry/summarise/timeout wrappers - app.py: _inject_uicontext, _inject_env_id_into_tools, database prefetch в runtime context - _persistence.py: prefetch_databases() - agent_superset_explore.py: GET /databases endpoint - _llm_async_http.py, _persistence.py: fix double /v1 в LLM URL (LM Studio Unexpected endpoint) == Frontend == - AgentChatModel.svelte.ts: 5 atoms, 3 actions, countdown, context - AgentChat.svelte: production banner, process steps, debug panel - ConfirmationCard.svelte: 7 risk tones, permission_denied, countdown - ToolCallCard.svelte: retrying/timeout/cancelled states - StreamProcessor.svelte.ts: tool_retry, timeout, permission_denied - TopNavbar: sparkles icon + Ассистент - sidebarNavigation: AI section - DashboardHeader, datasets/+page: contextual AI buttons - Icon: sparkles, brain, cpu icons - tailwind: assistant category colors - i18n: en/ru nav keys == Tests == - 159 backend agent tests (+16 US3: retry, timeout, summarise, contracts) - 2544 frontend tests (+11 model + component tests) - 15 JSON fixtures (10 API + 5 model) == Specs == - specs/035-agent-chat-context/: spec, UX, plan, tasks, research, data-model, contracts, quickstart, traceability, fixtures, checklists Closes #035
83 lines
3.2 KiB
Python
83 lines
3.2 KiB
Python
# #region Test.Agent.Feature035 [C:3] [TYPE Module] [SEMANTICS test,agent-chat,context,tools,rbac]
|
|
# @BRIEF Contract tests for feature 035 context filtering, invocation RBAC, and tool response summarisation.
|
|
# @RELATION BINDS_TO -> [AgentChat.ToolFilter]
|
|
# @RELATION BINDS_TO -> [AgentChat.Tools]
|
|
# @TEST_EDGE: dashboard_context_admin -> Dashboard affinity keeps dashboard tools plus mandatory capabilities.
|
|
# @TEST_EDGE: dashboard_context_viewer -> RBAC removes admin-only dashboard tools.
|
|
# @TEST_EDGE: invocation_guard_denied -> Mutating tool rejects before HTTP side effect.
|
|
|
|
import pytest
|
|
from types import SimpleNamespace
|
|
|
|
from src.agent._tool_filter import build_tool_pipeline
|
|
from src.agent.context import set_user_role
|
|
from src.agent.tools import _guard_tool_permission, _summarise_response
|
|
|
|
|
|
def _tools(names: list[str]) -> list[SimpleNamespace]:
|
|
return [SimpleNamespace(name=name) for name in names]
|
|
|
|
|
|
# #region test_dashboard_context_filters_tools [C:2] [TYPE Function]
|
|
# @BRIEF Dashboard context keeps only dashboard-affinity tools and mandatory capabilities.
|
|
def test_dashboard_context_filters_tools():
|
|
tools = _tools([
|
|
"search_dashboards",
|
|
"get_health_summary",
|
|
"deploy_dashboard",
|
|
"superset_execute_sql",
|
|
"run_backup",
|
|
"show_capabilities",
|
|
])
|
|
|
|
result = [tool.name for tool in build_tool_pipeline(tools, "admin", "dashboard")]
|
|
|
|
assert result == [
|
|
"search_dashboards",
|
|
"get_health_summary",
|
|
"deploy_dashboard",
|
|
"show_capabilities",
|
|
]
|
|
# #endregion test_dashboard_context_filters_tools
|
|
|
|
|
|
# #region test_dashboard_context_viewer_removes_admin_tools [C:2] [TYPE Function]
|
|
# @BRIEF Viewer role removes admin-only tools even when they are dashboard-affinity tools.
|
|
def test_dashboard_context_viewer_removes_admin_tools():
|
|
tools = _tools([
|
|
"search_dashboards",
|
|
"deploy_dashboard",
|
|
"execute_migration",
|
|
"show_capabilities",
|
|
])
|
|
|
|
result = [tool.name for tool in build_tool_pipeline(tools, "viewer", "dashboard")]
|
|
|
|
assert result == ["search_dashboards", "show_capabilities"]
|
|
# #endregion test_dashboard_context_viewer_removes_admin_tools
|
|
|
|
|
|
# #region test_invocation_guard_blocks_mutating_tool [C:2] [TYPE Function]
|
|
# @BRIEF Invocation guard rejects admin-only tools for non-admin role before side effects.
|
|
def test_invocation_guard_blocks_mutating_tool():
|
|
set_user_role("viewer")
|
|
|
|
with pytest.raises(PermissionError, match="PERMISSION_DENIED:deploy_dashboard:admin:viewer"):
|
|
_guard_tool_permission("deploy_dashboard")
|
|
# #endregion test_invocation_guard_blocks_mutating_tool
|
|
|
|
|
|
# #region test_summarise_response_preserves_json_array_shape [C:2] [TYPE Function]
|
|
# @BRIEF Large JSON arrays are summarised as top-N plus total count, not cut mid-structure.
|
|
def test_summarise_response_preserves_json_array_shape():
|
|
text = "[" + ",".join(f'{{"id":{idx},"name":"dashboard-{idx}"}}' for idx in range(20)) + "]"
|
|
|
|
summary = _summarise_response(text, limit=100)
|
|
|
|
assert summary.startswith("Found 20 items:")
|
|
assert "dashboard-0" in summary
|
|
assert "15 more items" in summary
|
|
# #endregion test_summarise_response_preserves_json_array_shape
|
|
|
|
# #endregion Test.Agent.Feature035
|