from __future__ import annotations from unittest.mock import AsyncMock, MagicMock import pytest from yuxi.channels.services.doctor import ConfigDoctor, DiagnosisIssue class TestDiagnosisIssue: def test_create_issue(self): issue = DiagnosisIssue( severity="error", channel_id="wx", category="credential", message="Missing app_id", ) assert issue.severity == "error" assert issue.channel_id == "wx" assert issue.category == "credential" assert issue.message == "Missing app_id" assert issue.auto_fixable is False def test_auto_fixable_flag(self): issue = DiagnosisIssue( severity="warning", channel_id="tg", category="connectivity", message="Timeout", auto_fixable=True, ) assert issue.auto_fixable is True def _make_channel_manager_mock(): manager = MagicMock() manager._registry = MagicMock() manager._registry.list_channels.return_value = ["wx", "tg"] manager._registry.get.return_value = MagicMock() manager._channels_config = { "wx": { "enabled": True, "required_fields": ["app_id", "app_secret"], "app_id": "test_id", }, "tg": { "enabled": True, "token": "test_token", }, } manager._adapters = {} return manager class TestConfigDoctor: @pytest.mark.asyncio async def test_diagnose_all_channels(self): manager = _make_channel_manager_mock() doctor = ConfigDoctor(manager) issues = await doctor.diagnose() assert len(issues) >= 1 @pytest.mark.asyncio async def test_diagnose_single_channel(self): manager = _make_channel_manager_mock() doctor = ConfigDoctor(manager) issues = await doctor.diagnose(channel_id="wx") missing_secret = [i for i in issues if "app_secret" in i.message] assert len(missing_secret) == 1 assert missing_secret[0].severity == "error" assert missing_secret[0].category == "credential" @pytest.mark.asyncio async def test_diagnose_missing_required_fields(self): manager = _make_channel_manager_mock() manager._channels_config["wx"]["required_fields"] = ["app_id", "app_secret", "token"] doctor = ConfigDoctor(manager) issues = await doctor.diagnose(channel_id="wx") credential_issues = [i for i in issues if i.category == "credential"] assert any("app_secret" in i.message for i in credential_issues) assert any("token" in i.message for i in credential_issues) assert not any("app_id" in i.message for i in credential_issues) @pytest.mark.asyncio async def test_diagnose_no_adapter_registered(self): manager = MagicMock() manager._registry.list_channels.return_value = ["unknown"] manager._registry.get.return_value = None manager._channels_config = {"unknown": {"enabled": True}} manager._adapters = {} doctor = ConfigDoctor(manager) issues = await doctor.diagnose(channel_id="unknown") registry_issues = [i for i in issues if i.category == "registry"] assert len(registry_issues) == 1 assert registry_issues[0].severity == "error" @pytest.mark.asyncio async def test_auto_fix_non_fixable_returns_false(self): manager = _make_channel_manager_mock() doctor = ConfigDoctor(manager) issue = DiagnosisIssue( severity="error", channel_id="wx", category="credential", message="missing", auto_fixable=False ) result = await doctor.auto_fix(issue) assert result is False @pytest.mark.asyncio async def test_auto_fix_connectivity_restarts_channel(self): manager = _make_channel_manager_mock() manager.restart_channel = AsyncMock() doctor = ConfigDoctor(manager) issue = DiagnosisIssue( severity="error", channel_id="wx", category="connectivity", message="timeout", auto_fixable=True ) result = await doctor.auto_fix(issue) assert result is True manager.restart_channel.assert_called_once_with("wx") @pytest.mark.asyncio async def test_suggest_migration_returns_empty_list(self): manager = _make_channel_manager_mock() doctor = ConfigDoctor(manager) suggestions = await doctor.suggest_migration() assert suggestions == []