"""Tests for AI controller.""" import pytest from fastapi.testclient import TestClient from unittest.mock import patch, AsyncMock, MagicMock from src.main import app @pytest.fixture def client(): """Create a test client.""" return TestClient(app) @pytest.fixture def mock_ai_client(): """Create a mock AI client.""" mock = AsyncMock() return mock class TestAIHealth: """Test /ai/health endpoint.""" @patch("src.controllers.ai_controller.get_ai_client") def test_health_returns_200(self, mock_get_client, client): """AI health should return 200.""" mock_client = AsyncMock() mock_client.health_check.return_value = True mock_get_client.return_value = mock_client response = client.get("/ai/health") assert response.status_code == 200 @patch("src.controllers.ai_controller.get_ai_client") def test_health_returns_healthy_status(self, mock_get_client, client): """AI health should return healthy status when service is up.""" mock_client = AsyncMock() mock_client.health_check.return_value = True mock_get_client.return_value = mock_client response = client.get("/ai/health") data = response.json() assert data["service"] == "core-ai" assert data["status"] == "healthy" assert data["accessible"] is True @patch("src.controllers.ai_controller.get_ai_client") def test_health_returns_unhealthy_status(self, mock_get_client, client): """AI health should return unhealthy status when service is down.""" mock_client = AsyncMock() mock_client.health_check.return_value = False mock_get_client.return_value = mock_client response = client.get("/ai/health") data = response.json() assert data["status"] == "unhealthy" assert data["accessible"] is False @patch("src.controllers.ai_controller.get_ai_client") def test_health_handles_exception(self, mock_get_client, client): """AI health should handle exceptions gracefully.""" mock_client = AsyncMock() mock_client.health_check.side_effect = Exception("Connection refused") mock_get_client.return_value = mock_client response = client.get("/ai/health") data = response.json() assert data["status"] == "error" assert data["accessible"] is False assert "error" in data class TestAIMetrics: """Test /ai/metrics endpoint.""" @patch("src.controllers.ai_controller.get_ai_client") def test_metrics_returns_200(self, mock_get_client, client): """AI metrics should return 200.""" mock_client = AsyncMock() mock_client.get_metrics.return_value = { "uptime_seconds": 3600, "agent": {"total_requests": 100} } mock_get_client.return_value = mock_client response = client.get("/ai/metrics") assert response.status_code == 200 @patch("src.controllers.ai_controller.get_ai_client") def test_metrics_returns_data(self, mock_get_client, client): """AI metrics should return metrics data.""" metrics_data = { "uptime_seconds": 3600, "agent": {"total_requests": 100}, "tools": {"total_calls": 250} } mock_client = AsyncMock() mock_client.get_metrics.return_value = metrics_data mock_get_client.return_value = mock_client response = client.get("/ai/metrics") data = response.json() assert data["uptime_seconds"] == 3600 assert data["agent"]["total_requests"] == 100 @patch("src.controllers.ai_controller.get_ai_client") def test_metrics_returns_503_on_error(self, mock_get_client, client): """AI metrics should return 503 when service unavailable.""" mock_client = AsyncMock() mock_client.get_metrics.side_effect = Exception("Service unavailable") mock_get_client.return_value = mock_client response = client.get("/ai/metrics") assert response.status_code == 503 class TestAIErrors: """Test /ai/metrics/errors endpoint.""" @patch("src.controllers.ai_controller.get_ai_client") def test_errors_returns_200(self, mock_get_client, client): """AI errors should return 200.""" mock_client = AsyncMock() mock_client.get_recent_errors.return_value = [] mock_get_client.return_value = mock_client response = client.get("/ai/metrics/errors") assert response.status_code == 200 @patch("src.controllers.ai_controller.get_ai_client") def test_errors_returns_error_list(self, mock_get_client, client): """AI errors should return list of errors.""" errors = [ {"timestamp": "2025-12-03T19:45:12Z", "error": "Timeout"}, {"timestamp": "2025-12-03T19:46:00Z", "error": "Connection refused"} ] mock_client = AsyncMock() mock_client.get_recent_errors.return_value = errors mock_get_client.return_value = mock_client response = client.get("/ai/metrics/errors") data = response.json() assert "errors" in data assert "total" in data assert data["total"] == 2 @patch("src.controllers.ai_controller.get_ai_client") def test_errors_accepts_limit_parameter(self, mock_get_client, client): """AI errors should accept limit parameter.""" mock_client = AsyncMock() mock_client.get_recent_errors.return_value = [] mock_get_client.return_value = mock_client response = client.get("/ai/metrics/errors?limit=5") assert response.status_code == 200 mock_client.get_recent_errors.assert_called_with(limit=5) @patch("src.controllers.ai_controller.get_ai_client") def test_errors_returns_503_on_error(self, mock_get_client, client): """AI errors should return 503 when service unavailable.""" mock_client = AsyncMock() mock_client.get_recent_errors.side_effect = Exception("Service unavailable") mock_get_client.return_value = mock_client response = client.get("/ai/metrics/errors") assert response.status_code == 503 class TestAIToolFailures: """Test /ai/metrics/tool-failures endpoint.""" @patch("src.controllers.ai_controller.get_ai_client") def test_tool_failures_returns_200(self, mock_get_client, client): """Tool failures should return 200.""" mock_client = AsyncMock() mock_client.get_tool_failures.return_value = [] mock_get_client.return_value = mock_client response = client.get("/ai/metrics/tool-failures") assert response.status_code == 200 @patch("src.controllers.ai_controller.get_ai_client") def test_tool_failures_returns_failure_list(self, mock_get_client, client): """Tool failures should return list of failures.""" failures = [ {"tool_name": "list_containers", "error": "Connection refused"} ] mock_client = AsyncMock() mock_client.get_tool_failures.return_value = failures mock_get_client.return_value = mock_client response = client.get("/ai/metrics/tool-failures") data = response.json() assert "failures" in data assert "total" in data assert data["total"] == 1 @patch("src.controllers.ai_controller.get_ai_client") def test_tool_failures_accepts_limit_parameter(self, mock_get_client, client): """Tool failures should accept limit parameter.""" mock_client = AsyncMock() mock_client.get_tool_failures.return_value = [] mock_get_client.return_value = mock_client response = client.get("/ai/metrics/tool-failures?limit=10") assert response.status_code == 200 mock_client.get_tool_failures.assert_called_with(limit=10) @patch("src.controllers.ai_controller.get_ai_client") def test_tool_failures_returns_503_on_error(self, mock_get_client, client): """Tool failures should return 503 when service unavailable.""" mock_client = AsyncMock() mock_client.get_tool_failures.side_effect = Exception("Service unavailable") mock_get_client.return_value = mock_client response = client.get("/ai/metrics/tool-failures") assert response.status_code == 503 class TestAIMetricsReset: """Test /ai/metrics/reset endpoint.""" @patch("src.controllers.ai_controller.get_ai_client") def test_reset_returns_200(self, mock_get_client, client): """Reset metrics should return 200.""" mock_client = AsyncMock() mock_client.reset_metrics.return_value = True mock_get_client.return_value = mock_client response = client.post("/ai/metrics/reset") assert response.status_code == 200 @patch("src.controllers.ai_controller.get_ai_client") def test_reset_returns_success_message(self, mock_get_client, client): """Reset metrics should return success message.""" mock_client = AsyncMock() mock_client.reset_metrics.return_value = True mock_get_client.return_value = mock_client response = client.post("/ai/metrics/reset") data = response.json() assert data["success"] is True assert "message" in data @patch("src.controllers.ai_controller.get_ai_client") def test_reset_returns_503_on_error(self, mock_get_client, client): """Reset metrics should return 503 when service unavailable.""" mock_client = AsyncMock() mock_client.reset_metrics.side_effect = Exception("Service unavailable") mock_get_client.return_value = mock_client response = client.post("/ai/metrics/reset") assert response.status_code == 503