"""Tests for Tools & Autotest API endpoints""" import pytest from unittest.mock import patch, MagicMock class TestToolsAPI: """Test cases for Tools endpoints""" def test_list_available_tools(self, client): """Test listing all available tools""" response = client.get("/api/tools/list") assert response.status_code == 200 data = response.json() assert "tools" in data # Check for expected tools tools = data["tools"] assert "search" in tools assert "calculator" in tools assert "weather" in tools def test_get_tool_detail(self, client): """Test getting a specific tool's details""" response = client.get("/api/tools/list/search") assert response.status_code == 200 data = response.json() assert data["name"] == "网络搜索" assert "parameters" in data def test_get_tool_detail_not_found(self, client): """Test getting a non-existent tool""" response = client.get("/api/tools/list/non-existent-tool") assert response.status_code == 404 def test_health_check(self, client): """Test health check endpoint""" response = client.get("/api/tools/health") assert response.status_code == 200 data = response.json() assert data["status"] == "healthy" assert "timestamp" in data assert "tools" in data class TestAutotestAPI: """Test cases for Autotest endpoints""" def test_autotest_no_models(self, client): """Test autotest without specifying model IDs""" response = client.post("/api/tools/autotest") assert response.status_code == 200 data = response.json() assert "id" in data assert "tests" in data assert "summary" in data # Should have test failures since no models provided assert data["summary"]["total"] > 0 def test_autotest_with_llm_model(self, client, sample_llm_model_data): """Test autotest with an LLM model""" # Create an LLM model first create_response = client.post("/api/llm", json=sample_llm_model_data) model_id = create_response.json()["id"] # Run autotest response = client.post(f"/api/tools/autotest?llm_model_id={model_id}&test_asr=false") assert response.status_code == 200 data = response.json() assert "tests" in data assert "summary" in data def test_autotest_with_asr_model(self, client, sample_asr_model_data): """Test autotest with an ASR model""" # Create an ASR model first create_response = client.post("/api/asr", json=sample_asr_model_data) model_id = create_response.json()["id"] # Run autotest response = client.post(f"/api/tools/autotest?asr_model_id={model_id}&test_llm=false") assert response.status_code == 200 data = response.json() assert "tests" in data assert "summary" in data def test_autotest_with_both_models(self, client, sample_llm_model_data, sample_asr_model_data): """Test autotest with both LLM and ASR models""" # Create models llm_response = client.post("/api/llm", json=sample_llm_model_data) llm_id = llm_response.json()["id"] asr_response = client.post("/api/asr", json=sample_asr_model_data) asr_id = asr_response.json()["id"] # Run autotest response = client.post( f"/api/tools/autotest?llm_model_id={llm_id}&asr_model_id={asr_id}" ) assert response.status_code == 200 data = response.json() assert "tests" in data assert "summary" in data @patch('httpx.Client') def test_autotest_llm_model_success(self, mock_client_class, client, sample_llm_model_data): """Test autotest for a specific LLM model with successful connection""" # Create an LLM model first create_response = client.post("/api/llm", json=sample_llm_model_data) model_id = create_response.json()["id"] # Mock the HTTP response for successful connection mock_client = MagicMock() mock_response = MagicMock() mock_response.status_code = 200 mock_response.json.return_value = { "choices": [{"message": {"content": "OK"}}] } mock_response.raise_for_status = MagicMock() mock_response.iter_bytes = MagicMock(return_value=[b'chunk1', b'chunk2']) mock_client.post.return_value = mock_response mock_client.__enter__ = MagicMock(return_value=mock_client) mock_client.__exit__ = MagicMock(return_value=False) with patch('app.routers.tools.httpx.Client', return_value=mock_client): response = client.post(f"/api/tools/autotest/llm/{model_id}") assert response.status_code == 200 data = response.json() assert "tests" in data assert "summary" in data @patch('httpx.Client') def test_autotest_asr_model_success(self, mock_client_class, client, sample_asr_model_data): """Test autotest for a specific ASR model with successful connection""" # Create an ASR model first create_response = client.post("/api/asr", json=sample_asr_model_data) model_id = create_response.json()["id"] # Mock the HTTP response for successful connection mock_client = MagicMock() mock_response = MagicMock() mock_response.status_code = 200 mock_response.raise_for_status = MagicMock() mock_client.get.return_value = mock_response mock_client.__enter__ = MagicMock(return_value=mock_client) mock_client.__exit__ = MagicMock(return_value=False) with patch('app.routers.tools.httpx.Client', return_value=mock_client): response = client.post(f"/api/tools/autotest/asr/{model_id}") assert response.status_code == 200 data = response.json() assert "tests" in data assert "summary" in data def test_autotest_llm_model_not_found(self, client): """Test autotest for a non-existent LLM model""" response = client.post("/api/tools/autotest/llm/non-existent-id") assert response.status_code == 200 data = response.json() # Should have a failure test assert any(not t["passed"] for t in data["tests"]) def test_autotest_asr_model_not_found(self, client): """Test autotest for a non-existent ASR model""" response = client.post("/api/tools/autotest/asr/non-existent-id") assert response.status_code == 200 data = response.json() # Should have a failure test assert any(not t["passed"] for t in data["tests"]) @patch('httpx.Client') def test_test_message_success(self, mock_client_class, client, sample_llm_model_data): """Test sending a test message to an LLM model""" # Create an LLM model first create_response = client.post("/api/llm", json=sample_llm_model_data) model_id = create_response.json()["id"] # Mock the HTTP response mock_client = MagicMock() mock_response = MagicMock() mock_response.status_code = 200 mock_response.json.return_value = { "choices": [{"message": {"content": "Hello! This is a test reply."}}], "usage": {"total_tokens": 10} } mock_response.raise_for_status = MagicMock() mock_client.post.return_value = mock_response mock_client.__enter__ = MagicMock(return_value=mock_client) mock_client.__exit__ = MagicMock(return_value=False) with patch('app.routers.tools.httpx.Client', return_value=mock_client): response = client.post( f"/api/tools/test-message?llm_model_id={model_id}", json={"message": "Hello!"} ) assert response.status_code == 200 data = response.json() assert data["success"] == True assert "reply" in data def test_test_message_model_not_found(self, client): """Test sending a test message to a non-existent model""" response = client.post( "/api/tools/test-message?llm_model_id=non-existent", json={"message": "Hello!"} ) assert response.status_code == 404 def test_autotest_result_structure(self, client): """Test that autotest results have the correct structure""" response = client.post("/api/tools/autotest") assert response.status_code == 200 data = response.json() # Check required fields assert "id" in data assert "started_at" in data assert "duration_ms" in data assert "tests" in data assert "summary" in data # Check summary structure assert "passed" in data["summary"] assert "failed" in data["summary"] assert "total" in data["summary"] # Check test structure if data["tests"]: test = data["tests"][0] assert "name" in test assert "passed" in test assert "message" in test assert "duration_ms" in test def test_tools_have_required_fields(self, client): """Test that all tools have required fields""" response = client.get("/api/tools/list") assert response.status_code == 200 data = response.json() for tool_id, tool in data["tools"].items(): assert "name" in tool assert "description" in tool assert "parameters" in tool # Check parameters structure params = tool["parameters"] assert "type" in params assert "properties" in params def test_calculator_tool_parameters(self, client): """Test calculator tool has correct parameters""" response = client.get("/api/tools/list/calculator") assert response.status_code == 200 data = response.json() assert data["name"] == "计算器" assert "expression" in data["parameters"]["properties"] assert "required" in data["parameters"] assert "expression" in data["parameters"]["required"] def test_translate_tool_parameters(self, client): """Test translate tool has correct parameters""" response = client.get("/api/tools/list/translate") assert response.status_code == 200 data = response.json() assert data["name"] == "翻译" assert "text" in data["parameters"]["properties"] assert "target_lang" in data["parameters"]["properties"] class TestToolResourceCRUD: """Test cases for persistent tool resource CRUD endpoints.""" def test_list_tool_resources_contains_system_tools(self, client): response = client.get("/api/tools/resources") assert response.status_code == 200 payload = response.json() assert payload["total"] >= 1 ids = [item["id"] for item in payload["list"]] assert "search" in ids def test_create_update_delete_tool_resource(self, client): create_resp = client.post("/api/tools/resources", json={ "name": "自定义网页抓取", "description": "抓取页面并提取正文", "category": "query", "icon": "Globe", "enabled": True, }) assert create_resp.status_code == 200 created = create_resp.json() tool_id = created["id"] assert created["name"] == "自定义网页抓取" assert created["is_system"] is False update_resp = client.put(f"/api/tools/resources/{tool_id}", json={ "name": "自定义网页检索", "category": "system", }) assert update_resp.status_code == 200 updated = update_resp.json() assert updated["name"] == "自定义网页检索" assert updated["category"] == "system" get_resp = client.get(f"/api/tools/resources/{tool_id}") assert get_resp.status_code == 200 assert get_resp.json()["id"] == tool_id delete_resp = client.delete(f"/api/tools/resources/{tool_id}") assert delete_resp.status_code == 200 missing_resp = client.get(f"/api/tools/resources/{tool_id}") assert missing_resp.status_code == 404 def test_system_tool_can_be_updated_and_deleted(self, client): list_resp = client.get("/api/tools/resources") assert list_resp.status_code == 200 assert any(item["id"] == "search" for item in list_resp.json()["list"]) update_resp = client.put("/api/tools/resources/search", json={"name": "更新后的搜索工具", "category": "query"}) assert update_resp.status_code == 200 assert update_resp.json()["name"] == "更新后的搜索工具" delete_resp = client.delete("/api/tools/resources/search") assert delete_resp.status_code == 200 get_resp = client.get("/api/tools/resources/search") assert get_resp.status_code == 404