# #region Test.Assistant.Resolvers [C:2] [TYPE Module] [SEMANTICS test,assistant,resolver,environment,dashboard] # @BRIEF Tests for _resolvers.py — all environment, dashboard, provider resolution functions. # @RELATION BINDS_TO -> [AssistantResolvers] # @TEST_CONTRACT: _extract_id -> returns first regex match group or None # @TEST_CONTRACT: _resolve_env_id -> matches by id or name, returns canonical id # @TEST_CONTRACT: _is_production_env -> detects production via env metadata or token text # @TEST_CONTRACT: _resolve_provider_id -> uses token match, bound provider, then active/default # @TEST_CONTRACT: _get_default_environment_id -> uses settings.default, is_default flag, or first # @TEST_CONTRACT: _resolve_dashboard_id_by_ref -> matches by slug, title, or partial title # @TEST_CONTRACT: _resolve_dashboard_id_entity -> resolves via numeric id, string token, or ref # @TEST_CONTRACT: _get_environment_name_by_id -> returns name or fallback # @TEST_CONTRACT: _extract_result_deep_links -> builds actions from task result # @TEST_CONTRACT: _build_task_observability_summary -> builds text summary per plugin type # @TEST_EDGE: _resolve_provider_id_no_providers -> returns None # @TEST_EDGE: _resolve_provider_id_bound_provider_not_in_list -> falls through to active # @TEST_EDGE: _resolve_provider_id_bound_provider_config_error -> falls through gracefully # @TEST_EDGE: _resolve_dashboard_id_by_ref_no_env -> returns None # @TEST_EDGE: _resolve_dashboard_id_by_ref_superset_error -> returns None # @TEST_EDGE: _resolve_dashboard_id_entity_int_dashboard_id -> returns as-is # @TEST_EDGE: _resolve_dashboard_id_entity_string_digit -> converts to int # @TEST_EDGE: _build_task_observability_summary_unknown_plugin -> returns empty for non-terminal # @TEST_EDGE: _build_task_observability_summary_migration_with_empty_lists -> handles gracefully from datetime import UTC, datetime from unittest.mock import AsyncMock, MagicMock, patch import pytest from src.schemas.auth import User def _make_env(id_: str, name_: str, stage="DEV", is_production=False, is_default=False): env = MagicMock() env.id = id_ env.name = name_ env.stage = stage env.is_production = is_production env.is_default = is_default return env class TestExtractId: """_extract_id — regex extraction.""" def test_match_first_pattern(self): from src.api.routes.assistant._resolvers import _extract_id result = _extract_id("ref is abc123", [r"ref is (\w+)"]) assert result == "abc123" def test_match_second_pattern(self): from src.api.routes.assistant._resolvers import _extract_id result = _extract_id("id: 42", [r"nope", r"id: (\d+)"]) assert result == "42" def test_no_match(self): from src.api.routes.assistant._resolvers import _extract_id result = _extract_id("nothing here", [r"ref is (\w+)"]) assert result is None def test_case_insensitive(self): from src.api.routes.assistant._resolvers import _extract_id result = _extract_id("REF IS ABC123", [r"ref is (\w+)"]) assert result == "ABC123" class TestResolveEnvId: """_resolve_env_id — environment resolution.""" def test_match_by_id(self): from src.api.routes.assistant._resolvers import _resolve_env_id cm = MagicMock() cm.get_environments.return_value = [_make_env("env-dev", "Development")] assert _resolve_env_id("env-dev", cm) == "env-dev" def test_match_by_name(self): from src.api.routes.assistant._resolvers import _resolve_env_id cm = MagicMock() cm.get_environments.return_value = [_make_env("env-dev", "Development")] assert _resolve_env_id("Development", cm) == "env-dev" def test_case_insensitive(self): from src.api.routes.assistant._resolvers import _resolve_env_id cm = MagicMock() cm.get_environments.return_value = [_make_env("ENV-PROD", "PRODUCTION")] assert _resolve_env_id("env-prod", cm) == "ENV-PROD" assert _resolve_env_id("production", cm) == "ENV-PROD" def test_none_token(self): from src.api.routes.assistant._resolvers import _resolve_env_id assert _resolve_env_id(None, MagicMock()) is None def test_unknown_token(self): from src.api.routes.assistant._resolvers import _resolve_env_id cm = MagicMock() cm.get_environments.return_value = [_make_env("env-dev", "Development")] assert _resolve_env_id("unknown", cm) is None class TestIsProductionEnv: """_is_production_env — production detection.""" def test_token_text_prod(self): from src.api.routes.assistant._resolvers import _is_production_env assert _is_production_env("prod", MagicMock()) is True assert _is_production_env("PRODUCTION", MagicMock()) is True def test_token_text_non_prod(self): from src.api.routes.assistant._resolvers import _is_production_env assert _is_production_env("dev", MagicMock()) is False assert _is_production_env("staging", MagicMock()) is False def test_none_token(self): from src.api.routes.assistant._resolvers import _is_production_env assert _is_production_env(None, MagicMock()) is False def test_via_env_metadata(self): from src.api.routes.assistant._resolvers import _is_production_env cm = MagicMock() prod_env = _make_env("env-prod", "Production", is_production=True) cm.get_environments.return_value = [prod_env] assert _is_production_env("env-prod", cm) is True def test_via_env_name_contains_prod(self): from src.api.routes.assistant._resolvers import _is_production_env cm = MagicMock() env = _make_env("env-p", "PRODUCTION SERVER") cm.get_environments.return_value = [env] assert _is_production_env("env-p", cm) is True def test_cyrillic_prod(self): from src.api.routes.assistant._resolvers import _is_production_env assert _is_production_env("\u043f\u0440\u043e\u0434", MagicMock()) is True class TestResolveProviderId: """_resolve_provider_id — LLM provider resolution.""" def test_match_by_token(self): from src.api.routes.assistant._resolvers import _resolve_provider_id svc = MagicMock() p1 = MagicMock(id="p1", name="OpenAI", is_active=True) svc.get_all_providers.return_value = [p1] with patch("src.api.routes.assistant._resolvers.LLMProviderService", return_value=svc): assert _resolve_provider_id("p1", MagicMock()) == "p1" assert _resolve_provider_id("OpenAI", MagicMock()) == "p1" def test_no_providers_returns_none(self): from src.api.routes.assistant._resolvers import _resolve_provider_id svc = MagicMock() svc.get_all_providers.return_value = [] with patch("src.api.routes.assistant._resolvers.LLMProviderService", return_value=svc): assert _resolve_provider_id(None, MagicMock()) is None def test_no_token_uses_active(self): from src.api.routes.assistant._resolvers import _resolve_provider_id svc = MagicMock() p1 = MagicMock(id="p1", name="Provider1", is_active=False) p2 = MagicMock(id="p2", name="Provider2", is_active=True) svc.get_all_providers.return_value = [p1, p2] with patch("src.api.routes.assistant._resolvers.LLMProviderService", return_value=svc): assert _resolve_provider_id(None, MagicMock()) == "p2" def test_no_token_no_active_uses_first(self): from src.api.routes.assistant._resolvers import _resolve_provider_id svc = MagicMock() p1 = MagicMock(id="p1", name="Provider1", is_active=False) p2 = MagicMock(id="p2", name="Provider2", is_active=False) svc.get_all_providers.return_value = [p1, p2] with patch("src.api.routes.assistant._resolvers.LLMProviderService", return_value=svc): assert _resolve_provider_id(None, MagicMock()) == "p1" def test_token_no_match_uses_bound_provider(self): from src.api.routes.assistant._resolvers import _resolve_provider_id db = MagicMock() cm = MagicMock() cm.get_config.return_value.settings.llm = MagicMock() svc = MagicMock() p1 = MagicMock(id="p1", name="OpenAI", is_active=False) svc.get_all_providers.return_value = [p1] with patch("src.api.routes.assistant._resolvers.LLMProviderService", return_value=svc), \ patch("src.api.routes.assistant._resolvers.resolve_bound_provider_id", return_value="p1") as mock_bound: result = _resolve_provider_id("nonexistent_provider", db, config_manager=cm, task_key="validation") assert result == "p1" mock_bound.assert_called_once() def test_bound_provider_not_in_list_falls_through(self): from src.api.routes.assistant._resolvers import _resolve_provider_id db = MagicMock() cm = MagicMock() cm.get_config.return_value.settings.llm = MagicMock() svc = MagicMock() p1 = MagicMock(id="p1", name="OpenAI", is_active=True) svc.get_all_providers.return_value = [p1] with patch("src.api.routes.assistant._resolvers.LLMProviderService", return_value=svc), \ patch("src.api.routes.assistant._resolvers.resolve_bound_provider_id", return_value="p_unknown"): result = _resolve_provider_id("nonexistent", db, config_manager=cm, task_key="x") assert result == "p1" # falls through to active def test_config_error_does_not_crash(self): from src.api.routes.assistant._resolvers import _resolve_provider_id db = MagicMock() cm = MagicMock() cm.get_config.side_effect = RuntimeError("Config error") svc = MagicMock() p1 = MagicMock(id="p1", name="Provider", is_active=True) svc.get_all_providers.return_value = [p1] with patch("src.api.routes.assistant._resolvers.LLMProviderService", return_value=svc): result = _resolve_provider_id(None, db, config_manager=cm, task_key="x") assert result == "p1" # falls through to active class TestGetDefaultEnvironmentId: """_get_default_environment_id — default env resolution.""" def test_no_environments(self): from src.api.routes.assistant._resolvers import _get_default_environment_id cm = MagicMock() cm.get_environments.return_value = [] assert _get_default_environment_id(cm) is None def test_uses_config_default(self): from src.api.routes.assistant._resolvers import _get_default_environment_id cm = MagicMock() envs = [_make_env("env-dev", "Development"), _make_env("env-prod", "Production")] cm.get_environments.return_value = envs cm.get_config.return_value.settings.default_environment_id = "env-dev" assert _get_default_environment_id(cm) == "env-dev" def test_config_default_not_in_list(self): from src.api.routes.assistant._resolvers import _get_default_environment_id cm = MagicMock() envs = [_make_env("env-dev", "Development")] cm.get_environments.return_value = envs cm.get_config.return_value.settings.default_environment_id = "missing" # Falls through to is_default flag or first assert _get_default_environment_id(cm) == "env-dev" def test_uses_is_default_flag(self): from src.api.routes.assistant._resolvers import _get_default_environment_id cm = MagicMock() envs = [ _make_env("env-a", "First"), _make_env("env-b", "Default", is_default=True), ] cm.get_environments.return_value = envs cm.get_config.return_value.settings.default_environment_id = None assert _get_default_environment_id(cm) == "env-b" def test_falls_back_to_first(self): from src.api.routes.assistant._resolvers import _get_default_environment_id cm = MagicMock() envs = [_make_env("env-first", "First"), _make_env("env-second", "Second")] cm.get_environments.return_value = envs cm.get_config.return_value = None assert _get_default_environment_id(cm) == "env-first" def test_config_manager_no_get_config_attr(self): from src.api.routes.assistant._resolvers import _get_default_environment_id cm = MagicMock() cm.get_environments.return_value = [_make_env("env-dev", "Dev")] # Remove get_config to simulate missing method if hasattr(cm, 'get_config'): del cm.get_config assert _get_default_environment_id(cm) == "env-dev" class TestGetEnvironmentNameById: """_get_environment_name_by_id — human-readable name.""" def test_found(self): from src.api.routes.assistant._resolvers import _get_environment_name_by_id cm = MagicMock() cm.get_environments.return_value = [_make_env("env-dev", "Development")] assert _get_environment_name_by_id("env-dev", cm) == "Development" def test_not_found_returns_id(self): from src.api.routes.assistant._resolvers import _get_environment_name_by_id cm = MagicMock() cm.get_environments.return_value = [_make_env("env-dev", "Development")] assert _get_environment_name_by_id("missing", cm) == "missing" def test_none_returns_unknown(self): from src.api.routes.assistant._resolvers import _get_environment_name_by_id assert _get_environment_name_by_id(None, MagicMock()) == "unknown" class TestResolveDashboardIdByRef: """_resolve_dashboard_id_by_ref — dashboard resolution by slug/title.""" @pytest.mark.asyncio async def test_none_ref(self): from src.api.routes.assistant._resolvers import _resolve_dashboard_id_by_ref assert await _resolve_dashboard_id_by_ref(None, "env-id", MagicMock()) is None @pytest.mark.asyncio async def test_no_env_id(self): from src.api.routes.assistant._resolvers import _resolve_dashboard_id_by_ref assert await _resolve_dashboard_id_by_ref("my-dash", None, MagicMock()) is None @pytest.mark.asyncio async def test_env_not_found(self): from src.api.routes.assistant._resolvers import _resolve_dashboard_id_by_ref cm = MagicMock() cm.get_environments.return_value = [_make_env("other-env", "Other")] assert await _resolve_dashboard_id_by_ref("my-dash", "env-dev", cm) is None @pytest.mark.asyncio async def test_matches_by_slug(self): from src.api.routes.assistant._resolvers import _resolve_dashboard_id_by_ref cm = MagicMock() cm.get_environments.return_value = [_make_env("env-dev", "Dev")] with patch("src.api.routes.assistant._resolvers.SupersetClient") as mock_cls: client = MagicMock() mock_cls.return_value = client client.get_dashboards = AsyncMock(return_value=(None, [ {"id": 42, "slug": "my-dash", "dashboard_title": "My Dashboard"}, {"id": 99, "slug": "other", "dashboard_title": "Other"}, ])) result = await _resolve_dashboard_id_by_ref("my-dash", "env-dev", cm) assert result == 42 @pytest.mark.asyncio async def test_matches_by_title_exact(self): from src.api.routes.assistant._resolvers import _resolve_dashboard_id_by_ref cm = MagicMock() cm.get_environments.return_value = [_make_env("env-dev", "Dev")] with patch("src.api.routes.assistant._resolvers.SupersetClient") as mock_cls: client = MagicMock() mock_cls.return_value = client client.get_dashboards = AsyncMock(return_value=(None, [ {"id": 7, "slug": None, "dashboard_title": "Sales Dashboard"}, ])) result = await _resolve_dashboard_id_by_ref("Sales Dashboard", "env-dev", cm) assert result == 7 @pytest.mark.asyncio async def test_partial_title_match_unique(self): from src.api.routes.assistant._resolvers import _resolve_dashboard_id_by_ref cm = MagicMock() cm.get_environments.return_value = [_make_env("env-dev", "Dev")] with patch("src.api.routes.assistant._resolvers.SupersetClient") as mock_cls: client = MagicMock() mock_cls.return_value = client client.get_dashboards = AsyncMock(return_value=(None, [ {"id": 15, "slug": None, "dashboard_title": "Customer Analytics"}, {"id": 20, "slug": None, "dashboard_title": "Finance Report"}, ])) result = await _resolve_dashboard_id_by_ref("Analytics", "env-dev", cm) assert result == 15 @pytest.mark.asyncio async def test_partial_title_match_ambiguous_returns_none(self): from src.api.routes.assistant._resolvers import _resolve_dashboard_id_by_ref cm = MagicMock() cm.get_environments.return_value = [_make_env("env-dev", "Dev")] with patch("src.api.routes.assistant._resolvers.SupersetClient") as mock_cls: client = MagicMock() mock_cls.return_value = client client.get_dashboards = AsyncMock(return_value=(None, [ {"id": 1, "slug": None, "dashboard_title": "Dashboard A"}, {"id": 2, "slug": None, "dashboard_title": "Dashboard B"}, ])) result = await _resolve_dashboard_id_by_ref("Dashboard", "env-dev", cm) assert result is None @pytest.mark.asyncio async def test_superset_error_returns_none(self): from src.api.routes.assistant._resolvers import _resolve_dashboard_id_by_ref cm = MagicMock() cm.get_environments.return_value = [_make_env("env-dev", "Dev")] with patch("src.api.routes.assistant._resolvers.SupersetClient") as mock_cls: client = MagicMock() mock_cls.return_value = client client.get_dashboards = AsyncMock(side_effect=ConnectionError("API down")) result = await _resolve_dashboard_id_by_ref("my-dash", "env-dev", cm) assert result is None class TestResolveDashboardIdEntity: """_resolve_dashboard_id_entity — entity resolution.""" @pytest.mark.asyncio async def test_int_dashboard_id(self): from src.api.routes.assistant._resolvers import _resolve_dashboard_id_entity result = await _resolve_dashboard_id_entity({"dashboard_id": 42}, MagicMock()) assert result == 42 @pytest.mark.asyncio async def test_string_digit_dashboard_id(self): from src.api.routes.assistant._resolvers import _resolve_dashboard_id_entity result = await _resolve_dashboard_id_entity({"dashboard_id": "99"}, MagicMock()) assert result == 99 @pytest.mark.asyncio async def test_string_non_digit_becomes_ref(self): from src.api.routes.assistant._resolvers import _resolve_dashboard_id_entity cm = MagicMock() cm.get_environments.return_value = [_make_env("env-dev", "Dev")] with patch("src.api.routes.assistant._resolvers._resolve_dashboard_id_by_ref", new=AsyncMock(return_value=42)): result = await _resolve_dashboard_id_entity( {"dashboard_id": "my-slug"}, cm, env_hint="dev" ) assert result == 42 @pytest.mark.asyncio async def test_explicit_ref_alone(self): from src.api.routes.assistant._resolvers import _resolve_dashboard_id_entity cm = MagicMock() cm.get_environments.return_value = [_make_env("env-dev", "Dev")] with patch("src.api.routes.assistant._resolvers._resolve_dashboard_id_by_ref", new=AsyncMock(return_value=77)): result = await _resolve_dashboard_id_entity( {"dashboard_ref": "my-dash"}, cm ) assert result == 77 @pytest.mark.asyncio async def test_no_identifiers_returns_none(self): from src.api.routes.assistant._resolvers import _resolve_dashboard_id_entity result = await _resolve_dashboard_id_entity({}, MagicMock()) assert result is None @pytest.mark.asyncio async def test_env_hint_overrides_entity_env(self): from src.api.routes.assistant._resolvers import _resolve_dashboard_id_entity cm = MagicMock() cm.get_environments.return_value = [_make_env("env-prod", "Prod")] with patch("src.api.routes.assistant._resolvers._resolve_dashboard_id_by_ref", new=AsyncMock(return_value=42)): result = await _resolve_dashboard_id_entity( {"dashboard_ref": "sales", "environment": "env-dev"}, cm, env_hint="env-prod", ) assert result == 42 class TestExtractResultDeepLinks: """_extract_result_deep_links — deep-link actions from task.""" def test_migration_plugin(self): from src.api.routes.assistant._resolvers import _extract_result_deep_links cm = MagicMock() cm.get_environments.return_value = [_make_env("tgt-env", "Target")] task = MagicMock() task.plugin_id = "superset-migration" task.params = {"target_env_id": "tgt-env", "selected_ids": [42]} task.result = {"migrated_dashboards": [{"id": 42}]} actions = _extract_result_deep_links(task, cm) assert len(actions) >= 2 # open_route + open_diff assert any(a.type == "open_route" for a in actions) assert any(a.type == "open_diff" for a in actions) def test_migration_plugin_result_has_no_migrated_dashboards(self): from src.api.routes.assistant._resolvers import _extract_result_deep_links cm = MagicMock() task = MagicMock() task.plugin_id = "superset-migration" task.params = {"target_env_id": "env-prod", "selected_ids": [99]} task.result = {} # no migrated_dashboards key actions = _extract_result_deep_links(task, cm) # Falls back to params.selected_ids assert len(actions) >= 1 # open_diff with id=99 def test_backup_plugin(self): from src.api.routes.assistant._resolvers import _extract_result_deep_links cm = MagicMock() cm.get_environments.return_value = [_make_env("env-dev", "Development")] task = MagicMock() task.plugin_id = "superset-backup" task.params = {"environment_id": "env-dev", "dashboard_ids": [7]} task.result = {"dashboards": [{"id": 7}], "environment": "env-dev"} actions = _extract_result_deep_links(task, cm) assert len(actions) >= 2 def test_validation_plugin_without_env(self): from src.api.routes.assistant._resolvers import _extract_result_deep_links cm = MagicMock() task = MagicMock() task.plugin_id = "llm_dashboard_validation" task.params = {"dashboard_id": 42} # no environment_id task.result = {} actions = _extract_result_deep_links(task, cm) assert len(actions) == 1 # open_diff only (no env -> no open_route) assert actions[0].type == "open_diff" def test_validation_plugin_with_env(self): from src.api.routes.assistant._resolvers import _extract_result_deep_links cm = MagicMock() cm.get_environments.return_value = [_make_env("env-dev", "Development")] task = MagicMock() task.plugin_id = "llm_dashboard_validation" task.params = {"dashboard_id": 42, "environment_id": "env-dev"} task.result = {} actions = _extract_result_deep_links(task, cm) assert len(actions) == 2 # open_route + open_diff def test_unknown_plugin_no_actions(self): from src.api.routes.assistant._resolvers import _extract_result_deep_links cm = MagicMock() task = MagicMock() task.plugin_id = "some_unknown" task.params = {} task.result = {} actions = _extract_result_deep_links(task, cm) assert actions == [] class TestBuildTaskObservabilitySummary: """_build_task_observability_summary — task summary for different plugins.""" def test_migration_success(self): from src.api.routes.assistant._resolvers import _build_task_observability_summary cm = MagicMock() cm.get_environments.return_value = [_make_env("tgt", "Target")] task = MagicMock() task.plugin_id = "superset-migration" task.status = "SUCCESS" task.params = {"target_env_id": "tgt"} task.result = { "migrated_dashboards": [{"id": 1}, {"id": 2}], "failed_dashboards": [], "selected_dashboards": 2, "mapping_count": 5, } summary = _build_task_observability_summary(task, cm) assert "Сводка миграции" in summary assert "перенесено 2" in summary assert "маппингов 5" in summary def test_migration_with_failures(self): from src.api.routes.assistant._resolvers import _build_task_observability_summary cm = MagicMock() cm.get_environments.return_value = [_make_env("tgt", "Target")] task = MagicMock() task.plugin_id = "superset-migration" task.status = "PARTIAL" task.params = {"target_env_id": "tgt"} task.result = { "migrated_dashboards": [{"id": 1}], "failed_dashboards": [{"id": 2, "title": "Broken", "error": "timeout"}], "selected_dashboards": 2, "mapping_count": 3, } summary = _build_task_observability_summary(task, cm) assert "Сводка миграции" in summary assert "с ошибками 1" in summary assert "Broken" in summary def test_migration_empty_lists(self): from src.api.routes.assistant._resolvers import _build_task_observability_summary cm = MagicMock() cm.get_environments.return_value = [_make_env("tgt", "Target")] task = MagicMock() task.plugin_id = "superset-migration" task.status = "SUCCESS" task.params = {"target_env_id": "tgt"} task.result = { "migrated_dashboards": [], "failed_dashboards": [], "mapping_count": 0, } summary = _build_task_observability_summary(task, cm) assert "Сводка миграции" in summary assert "перенесено 0" in summary def test_backup_success(self): from src.api.routes.assistant._resolvers import _build_task_observability_summary cm = MagicMock() cm.get_environments.return_value = [_make_env("env-dev", "Development")] task = MagicMock() task.plugin_id = "superset-backup" task.status = "SUCCESS" task.params = {"environment_id": "env-dev"} task.result = { "total_dashboards": 10, "backed_up_dashboards": 10, "failed_dashboards": 0, "failures": [], } summary = _build_task_observability_summary(task, cm) assert "Сводка бэкапа" in summary assert "успешно 10" in summary assert "среда" in summary.lower() def test_backup_with_failures(self): from src.api.routes.assistant._resolvers import _build_task_observability_summary cm = MagicMock() cm.get_environments.return_value = [_make_env("env-dev", "Development")] task = MagicMock() task.plugin_id = "superset-backup" task.status = "PARTIAL" task.params = {"environment_id": "env-dev"} task.result = { "total_dashboards": 5, "backed_up_dashboards": 3, "failed_dashboards": 2, "failures": [{"id": 4, "title": "Dashboard X", "error": "API error"}], } summary = _build_task_observability_summary(task, cm) assert "с ошибками 2" in summary assert "Dashboard X" in summary def test_validation_summary(self): from src.api.routes.assistant._resolvers import _build_task_observability_summary cm = MagicMock() task = MagicMock() task.plugin_id = "llm_dashboard_validation" task.status = "SUCCESS" task.params = {} task.result = { "status": "passed", "summary": "All checks passed", "issues": [], } summary = _build_task_observability_summary(task, cm) assert "Сводка валидации" in summary assert "статус passed" in summary def test_validation_result_without_summary(self): from src.api.routes.assistant._resolvers import _build_task_observability_summary cm = MagicMock() task = MagicMock() task.plugin_id = "llm_dashboard_validation" task.status = "FAILED" task.params = {} task.result = {} # no status or summary summary = _build_task_observability_summary(task, cm) assert "Сводка валидации" in summary assert "Итог недоступен" in summary def test_unknown_plugin_terminal_status(self): from src.api.routes.assistant._resolvers import _build_task_observability_summary cm = MagicMock() task = MagicMock() task.plugin_id = "custom-plugin" task.status = "SUCCESS" task.params = {} task.result = {} summary = _build_task_observability_summary(task, cm) assert "Задача завершена" in summary def test_unknown_plugin_non_terminal_status(self): from src.api.routes.assistant._resolvers import _build_task_observability_summary cm = MagicMock() task = MagicMock() task.plugin_id = "custom-plugin" task.status = "RUNNING" task.params = {} task.result = {} summary = _build_task_observability_summary(task, cm) assert summary == "" # #endregion Test.Assistant.Resolvers