mirror of
https://github.com/eigent-ai/eigent.git
synced 2026-04-28 03:30:06 +00:00
tests: fix and refactor tests (#1262)
Co-authored-by: bytecii <bytecii@users.noreply.github.com> Co-authored-by: Wendong-Fan <133094783+Wendong-Fan@users.noreply.github.com>
This commit is contained in:
parent
f3648d6057
commit
9c396ee015
31 changed files with 287 additions and 253 deletions
|
|
@ -168,20 +168,18 @@ def initialize_tracer_provider() -> None:
|
|||
_GLOBAL_TRACER_PROVIDER = provider
|
||||
|
||||
|
||||
def get_tracer_provider() -> TracerProvider:
|
||||
def get_tracer_provider() -> TracerProvider | None:
|
||||
"""Get the global TracerProvider instance.
|
||||
|
||||
Returns:
|
||||
TracerProvider: The global tracer provider
|
||||
|
||||
Raises:
|
||||
RuntimeError: If called before initialization
|
||||
TracerProvider if initialized, None otherwise
|
||||
"""
|
||||
if _GLOBAL_TRACER_PROVIDER is None:
|
||||
raise RuntimeError(
|
||||
logger.warning(
|
||||
"TracerProvider not initialized. "
|
||||
"Call initialize_tracer_provider() during app startup."
|
||||
)
|
||||
return None
|
||||
return _GLOBAL_TRACER_PROVIDER
|
||||
|
||||
|
||||
|
|
@ -258,22 +256,28 @@ class WorkforceMetricsCallback(WorkforceMetrics):
|
|||
# Get the global shared tracer provider
|
||||
# This ensures only one BatchSpanProcessor is running
|
||||
provider = get_tracer_provider()
|
||||
|
||||
# Get tracer from the shared provider
|
||||
# Use CAMEL version for instrumentation versioning
|
||||
self.tracer = provider.get_tracer(
|
||||
TRACER_NAME_WORKFORCE, camel.__version__
|
||||
)
|
||||
self.root_span = self.tracer.start_span(
|
||||
f"{SPAN_WORKFORCE_EXECUTION}:{task_id}"
|
||||
)
|
||||
# Langfuse-specific attributes
|
||||
self.root_span.set_attribute(ATTR_LANGFUSE_SESSION_ID, project_id)
|
||||
tags = json.dumps(DEFAULT_LANGFUSE_TAGS.copy())
|
||||
self.root_span.set_attribute(ATTR_LANGFUSE_TAGS, tags)
|
||||
# Custom attributes
|
||||
self.root_span.set_attribute(ATTR_PROJECT_ID, project_id)
|
||||
self.root_span.set_attribute(ATTR_TASK_ID, task_id)
|
||||
if provider is None:
|
||||
# TracerProvider not initialized (e.g., app startup not
|
||||
# completed or running in test environment)
|
||||
self.enabled = False
|
||||
else:
|
||||
# Get tracer from the shared provider
|
||||
# Use CAMEL version for instrumentation versioning
|
||||
self.tracer = provider.get_tracer(
|
||||
TRACER_NAME_WORKFORCE, camel.__version__
|
||||
)
|
||||
self.root_span = self.tracer.start_span(
|
||||
f"{SPAN_WORKFORCE_EXECUTION}:{task_id}"
|
||||
)
|
||||
# Langfuse-specific attributes
|
||||
self.root_span.set_attribute(
|
||||
ATTR_LANGFUSE_SESSION_ID, project_id
|
||||
)
|
||||
tags = json.dumps(DEFAULT_LANGFUSE_TAGS.copy())
|
||||
self.root_span.set_attribute(ATTR_LANGFUSE_TAGS, tags)
|
||||
# Custom attributes
|
||||
self.root_span.set_attribute(ATTR_PROJECT_ID, project_id)
|
||||
self.root_span.set_attribute(ATTR_TASK_ID, task_id)
|
||||
|
||||
# Track active spans for task execution
|
||||
self.task_spans = {}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,9 @@ def test_browser_agent_creation(sample_chat_data):
|
|||
_mod = "app.agent.factory.browser"
|
||||
with (
|
||||
patch(f"{_mod}.agent_model") as mock_agent_model,
|
||||
patch(
|
||||
f"{_mod}.get_working_directory", return_value="/tmp/test_workdir"
|
||||
),
|
||||
patch("asyncio.create_task"),
|
||||
patch(f"{_mod}.HumanToolkit") as mock_human_toolkit,
|
||||
patch(f"{_mod}.HybridBrowserToolkit") as mock_browser_toolkit,
|
||||
|
|
|
|||
|
|
@ -36,6 +36,9 @@ async def test_developer_agent_creation(sample_chat_data):
|
|||
_mod = "app.agent.factory.developer"
|
||||
with (
|
||||
patch(f"{_mod}.agent_model") as mock_agent_model,
|
||||
patch(
|
||||
f"{_mod}.get_working_directory", return_value="/tmp/test_workdir"
|
||||
),
|
||||
patch("asyncio.create_task"),
|
||||
patch(f"{_mod}.HumanToolkit") as mock_human_toolkit,
|
||||
patch(f"{_mod}.NoteTakingToolkit") as mock_note_toolkit,
|
||||
|
|
@ -82,6 +85,9 @@ async def test_developer_agent_with_multiple_toolkits(sample_chat_data):
|
|||
_mod = "app.agent.factory.developer"
|
||||
with (
|
||||
patch(f"{_mod}.agent_model") as mock_agent_model,
|
||||
patch(
|
||||
f"{_mod}.get_working_directory", return_value="/tmp/test_workdir"
|
||||
),
|
||||
patch("asyncio.create_task"),
|
||||
patch(f"{_mod}.HumanToolkit") as mock_human_toolkit,
|
||||
patch(f"{_mod}.NoteTakingToolkit") as mock_note_toolkit,
|
||||
|
|
|
|||
|
|
@ -36,6 +36,9 @@ async def test_document_agent_creation(sample_chat_data):
|
|||
_mod = "app.agent.factory.document"
|
||||
with (
|
||||
patch(f"{_mod}.agent_model") as mock_agent_model,
|
||||
patch(
|
||||
f"{_mod}.get_working_directory", return_value="/tmp/test_workdir"
|
||||
),
|
||||
patch("asyncio.create_task"),
|
||||
patch(f"{_mod}.HumanToolkit") as mock_human_toolkit,
|
||||
patch(f"{_mod}.FileToolkit") as mock_file_toolkit,
|
||||
|
|
|
|||
|
|
@ -35,6 +35,9 @@ def test_multi_modal_agent_creation(sample_chat_data):
|
|||
_mod = "app.agent.factory.multi_modal"
|
||||
with (
|
||||
patch(f"{_mod}.agent_model") as mock_agent_model,
|
||||
patch(
|
||||
f"{_mod}.get_working_directory", return_value="/tmp/test_workdir"
|
||||
),
|
||||
patch("asyncio.create_task"),
|
||||
patch(f"{_mod}.HumanToolkit") as mock_human_toolkit,
|
||||
patch(f"{_mod}.VideoDownloaderToolkit") as mock_video_toolkit,
|
||||
|
|
|
|||
|
|
@ -36,6 +36,9 @@ async def test_social_media_agent_creation(sample_chat_data):
|
|||
mod = "app.agent.factory.social_media"
|
||||
with (
|
||||
patch(f"{mod}.agent_model") as mock_agent_model,
|
||||
patch(
|
||||
f"{mod}.get_working_directory", return_value="/tmp/test_workdir"
|
||||
),
|
||||
patch("asyncio.create_task"),
|
||||
patch(f"{mod}.WhatsAppToolkit") as mock_whatsapp_toolkit,
|
||||
patch(f"{mod}.TwitterToolkit") as mock_twitter_toolkit,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
# ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
|
||||
|
||||
import os
|
||||
from unittest.mock import MagicMock, patch
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from fastapi import Response
|
||||
|
|
@ -50,13 +50,14 @@ class TestChatController:
|
|||
|
||||
with (
|
||||
patch(
|
||||
"app.controller.chat_controller.create_task_lock",
|
||||
"app.controller.chat_controller.get_or_create_task_lock",
|
||||
return_value=mock_task_lock,
|
||||
),
|
||||
patch(
|
||||
"app.controller.chat_controller.step_solve"
|
||||
) as mock_step_solve,
|
||||
patch("app.controller.chat_controller.load_dotenv"),
|
||||
patch("app.controller.chat_controller.set_current_task_id"),
|
||||
patch("pathlib.Path.mkdir"),
|
||||
patch("pathlib.Path.home", return_value=MagicMock()),
|
||||
):
|
||||
|
|
@ -71,9 +72,6 @@ class TestChatController:
|
|||
|
||||
assert isinstance(response, StreamingResponse)
|
||||
assert response.media_type == "text/event-stream"
|
||||
mock_step_solve.assert_called_once_with(
|
||||
chat_data, mock_request, mock_task_lock
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_post_chat_sets_environment_variables(
|
||||
|
|
@ -84,13 +82,14 @@ class TestChatController:
|
|||
|
||||
with (
|
||||
patch(
|
||||
"app.controller.chat_controller.create_task_lock",
|
||||
"app.controller.chat_controller.get_or_create_task_lock",
|
||||
return_value=mock_task_lock,
|
||||
),
|
||||
patch(
|
||||
"app.controller.chat_controller.step_solve"
|
||||
) as mock_step_solve,
|
||||
patch("app.controller.chat_controller.load_dotenv"),
|
||||
patch("app.controller.chat_controller.set_current_task_id"),
|
||||
patch("pathlib.Path.mkdir"),
|
||||
patch("pathlib.Path.home", return_value=MagicMock()),
|
||||
patch.dict(os.environ, {}, clear=True),
|
||||
|
|
@ -133,18 +132,24 @@ class TestChatController:
|
|||
# put_queue is invoked when creating the coroutine passed to asyncio.run
|
||||
mock_task_lock.put_queue.assert_called_once()
|
||||
|
||||
def test_improve_chat_task_done_error(self, mock_task_lock):
|
||||
"""Test improvement fails when task is done."""
|
||||
def test_improve_chat_task_done_resets_to_confirming(self, mock_task_lock):
|
||||
"""Test improvement when task is done resets status to confirming."""
|
||||
task_id = "test_task_123"
|
||||
supplement_data = SupplementChat(question="Improve this code")
|
||||
mock_task_lock.status = Status.done
|
||||
|
||||
with patch(
|
||||
"app.controller.chat_controller.get_task_lock",
|
||||
return_value=mock_task_lock,
|
||||
with (
|
||||
patch(
|
||||
"app.controller.chat_controller.get_task_lock",
|
||||
return_value=mock_task_lock,
|
||||
),
|
||||
patch("asyncio.run") as mock_run,
|
||||
):
|
||||
with pytest.raises(UserException):
|
||||
improve(task_id, supplement_data)
|
||||
response = improve(task_id, supplement_data)
|
||||
|
||||
assert mock_task_lock.status == Status.confirming
|
||||
assert isinstance(response, Response)
|
||||
assert response.status_code == 201
|
||||
|
||||
def test_supplement_chat_success(self, mock_task_lock):
|
||||
"""Test successful chat supplementation."""
|
||||
|
|
@ -244,16 +249,18 @@ class TestChatControllerIntegration:
|
|||
"""Test chat endpoint through FastAPI test client."""
|
||||
with (
|
||||
patch(
|
||||
"app.controller.chat_controller.create_task_lock"
|
||||
"app.controller.chat_controller.get_or_create_task_lock"
|
||||
) as mock_create_lock,
|
||||
patch(
|
||||
"app.controller.chat_controller.step_solve"
|
||||
) as mock_step_solve,
|
||||
patch("app.controller.chat_controller.load_dotenv"),
|
||||
patch("app.controller.chat_controller.set_current_task_id"),
|
||||
patch("pathlib.Path.mkdir"),
|
||||
patch("pathlib.Path.home", return_value=MagicMock()),
|
||||
):
|
||||
mock_task_lock = MagicMock()
|
||||
mock_task_lock.put_queue = AsyncMock()
|
||||
mock_create_lock.return_value = mock_task_lock
|
||||
|
||||
async def mock_generator():
|
||||
|
|
@ -455,8 +462,12 @@ class TestChatControllerErrorCases:
|
|||
|
||||
with (
|
||||
patch(
|
||||
"app.controller.chat_controller.create_task_lock"
|
||||
"app.controller.chat_controller.get_or_create_task_lock"
|
||||
) as mock_create_lock,
|
||||
patch(
|
||||
"app.controller.chat_controller.sanitize_env_path",
|
||||
return_value="/tmp/fake.env",
|
||||
),
|
||||
patch(
|
||||
"app.controller.chat_controller.load_dotenv",
|
||||
side_effect=Exception("Env load failed"),
|
||||
|
|
@ -15,6 +15,7 @@
|
|||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from fastapi import HTTPException
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app.controller.model_controller import (
|
||||
|
|
@ -73,11 +74,9 @@ class TestModelController:
|
|||
"app.controller.model_controller.create_agent",
|
||||
side_effect=Exception("Invalid model configuration"),
|
||||
):
|
||||
response = await validate_model(request_data)
|
||||
assert isinstance(response, ValidateModelResponse)
|
||||
assert response.is_valid is False
|
||||
assert response.is_tool_calls is False
|
||||
assert "Invalid model name" in response.message
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
await validate_model(request_data)
|
||||
assert exc_info.value.status_code == 400
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_validate_model_step_failure(self):
|
||||
|
|
@ -93,12 +92,9 @@ class TestModelController:
|
|||
"app.controller.model_controller.create_agent",
|
||||
return_value=mock_agent,
|
||||
):
|
||||
response = await validate_model(request_data)
|
||||
|
||||
assert isinstance(response, ValidateModelResponse)
|
||||
assert response.is_valid is False
|
||||
assert response.is_tool_calls is False
|
||||
assert "API call failed" in response.message
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
await validate_model(request_data)
|
||||
assert exc_info.value.status_code == 400
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_validate_model_tool_calls_false(self):
|
||||
|
|
@ -130,8 +126,10 @@ class TestModelController:
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_validate_model_with_minimal_parameters(self):
|
||||
"""Test model validation with minimal parameters."""
|
||||
request_data = ValidateModelRequest() # Uses default values
|
||||
"""Test model validation with minimal parameters (no API key)."""
|
||||
request_data = (
|
||||
ValidateModelRequest()
|
||||
) # Uses default values, api_key is None
|
||||
|
||||
mock_agent = MagicMock()
|
||||
mock_response = MagicMock()
|
||||
|
|
@ -144,12 +142,12 @@ class TestModelController:
|
|||
"app.controller.model_controller.create_agent",
|
||||
return_value=mock_agent,
|
||||
):
|
||||
# api_key is None by default, which passes the empty string check
|
||||
# The agent step succeeds, so validation should pass
|
||||
response = await validate_model(request_data)
|
||||
assert isinstance(response, ValidateModelResponse)
|
||||
assert response.is_valid is False
|
||||
assert response.is_tool_calls is False
|
||||
assert response.error_code is not None
|
||||
assert response.error is not None
|
||||
assert response.is_valid is True
|
||||
assert response.is_tool_calls is True
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_validate_model_no_response(self):
|
||||
|
|
@ -222,13 +220,7 @@ class TestModelControllerIntegration:
|
|||
):
|
||||
response = client.post("/model/validate", json=request_data)
|
||||
|
||||
assert (
|
||||
response.status_code == 200
|
||||
) # Returns 200 with error in response body
|
||||
response_data = response.json()
|
||||
assert response_data["is_valid"] is False
|
||||
assert response_data["is_tool_calls"] is False
|
||||
assert "Invalid model name" in response_data["message"]
|
||||
assert response.status_code == 400
|
||||
|
||||
|
||||
@pytest.mark.model_backend
|
||||
|
|
@ -267,10 +259,9 @@ class TestModelControllerErrorCases:
|
|||
"app.controller.model_controller.create_agent",
|
||||
side_effect=ValueError("Invalid configuration"),
|
||||
):
|
||||
response = await validate_model(request_data)
|
||||
|
||||
assert response.is_valid is False
|
||||
assert "Invalid configuration" in response.message
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
await validate_model(request_data)
|
||||
assert exc_info.value.status_code == 400
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_validate_model_with_network_error(self):
|
||||
|
|
@ -288,10 +279,9 @@ class TestModelControllerErrorCases:
|
|||
"app.controller.model_controller.create_agent",
|
||||
return_value=mock_agent,
|
||||
):
|
||||
response = await validate_model(request_data)
|
||||
|
||||
assert response.is_valid is False
|
||||
assert "Network unreachable" in response.message
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
await validate_model(request_data)
|
||||
assert exc_info.value.status_code == 400
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_validate_model_with_malformed_tool_calls_response(self):
|
||||
|
|
@ -346,36 +336,21 @@ class TestModelControllerErrorCases:
|
|||
api_key="", # Empty API key
|
||||
)
|
||||
|
||||
response = await validate_model(request_data)
|
||||
|
||||
assert response.is_valid is False
|
||||
assert response.is_tool_calls is False
|
||||
assert response.message == "Invalid key. Validation failed."
|
||||
assert response.error_code == "invalid_api_key"
|
||||
assert response.error is not None
|
||||
assert response.error["message"] == "Invalid key. Validation failed."
|
||||
assert response.error["type"] == "invalid_request_error"
|
||||
assert response.error["code"] == "invalid_api_key"
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
await validate_model(request_data)
|
||||
assert exc_info.value.status_code == 400
|
||||
detail = exc_info.value.detail
|
||||
assert detail["error_code"] == "invalid_api_key"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_validate_model_invalid_model_type(self):
|
||||
"""Test model validation with invalid model type."""
|
||||
"""Test model validation with invalid model type raises HTTPException."""
|
||||
request_data = ValidateModelRequest(
|
||||
model_platform="openai",
|
||||
model_type="INVALID_MODEL_TYPE",
|
||||
api_key="test_key",
|
||||
)
|
||||
|
||||
response = await validate_model(request_data)
|
||||
assert response.is_valid is False
|
||||
assert response.is_tool_calls is False
|
||||
assert response.message == "Invalid model name. Validation failed."
|
||||
assert response.error_code is not None
|
||||
assert "model_not_found" in response.error_code
|
||||
assert response.error is not None
|
||||
assert (
|
||||
response.error["message"]
|
||||
== "Invalid model name. Validation failed."
|
||||
)
|
||||
assert response.error["type"] == "invalid_request_error"
|
||||
assert response.error["code"] == "model_not_found"
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
await validate_model(request_data)
|
||||
assert exc_info.value.status_code == 400
|
||||
|
|
@ -15,6 +15,7 @@
|
|||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from fastapi import HTTPException
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app.controller.tool_controller import install_tool
|
||||
|
|
@ -37,14 +38,18 @@ class TestToolController:
|
|||
return_value=mock_toolkit,
|
||||
):
|
||||
result = await install_tool(tool_name)
|
||||
assert result == ["create_page", "update_page"]
|
||||
assert result["success"] is True
|
||||
assert result["tools"] == ["create_page", "update_page"]
|
||||
assert result["count"] == 2
|
||||
assert result["toolkit_name"] == "NotionMCPToolkit"
|
||||
mock_toolkit.connect.assert_called_once()
|
||||
mock_toolkit.disconnect.assert_called_once()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_install_unknown_tool(self):
|
||||
result = await install_tool("unknown_tool")
|
||||
assert result == {"error": "Tool not found"}
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
await install_tool("unknown_tool")
|
||||
assert exc_info.value.status_code == 404
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_install_notion_tool_connection_failure(self):
|
||||
|
|
@ -54,8 +59,11 @@ class TestToolController:
|
|||
"app.controller.tool_controller.NotionMCPToolkit",
|
||||
return_value=mock_toolkit,
|
||||
):
|
||||
with pytest.raises(Exception, match="Connection failed"):
|
||||
await install_tool("notion")
|
||||
result = await install_tool("notion")
|
||||
assert result["success"] is True
|
||||
assert result["tools"] == []
|
||||
assert result["count"] == 0
|
||||
assert "warning" in result
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_install_notion_tool_get_tools_failure(self):
|
||||
|
|
@ -67,8 +75,11 @@ class TestToolController:
|
|||
"app.controller.tool_controller.NotionMCPToolkit",
|
||||
return_value=mock_toolkit,
|
||||
):
|
||||
with pytest.raises(Exception, match="Failed to get tools"):
|
||||
await install_tool("notion")
|
||||
result = await install_tool("notion")
|
||||
assert result["success"] is True
|
||||
assert result["tools"] == []
|
||||
assert result["count"] == 0
|
||||
assert "warning" in result
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_install_notion_tool_disconnect_failure(self):
|
||||
|
|
@ -81,8 +92,11 @@ class TestToolController:
|
|||
"app.controller.tool_controller.NotionMCPToolkit",
|
||||
return_value=mock_toolkit,
|
||||
):
|
||||
with pytest.raises(Exception, match="Disconnect failed"):
|
||||
await install_tool("notion")
|
||||
result = await install_tool("notion")
|
||||
assert result["success"] is True
|
||||
assert result["tools"] == []
|
||||
assert result["count"] == 0
|
||||
assert "warning" in result
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_install_notion_tool_empty_tools(self):
|
||||
|
|
@ -93,7 +107,9 @@ class TestToolController:
|
|||
return_value=mock_toolkit,
|
||||
):
|
||||
result = await install_tool("notion")
|
||||
assert result == []
|
||||
assert result["success"] is True
|
||||
assert result["tools"] == []
|
||||
assert result["count"] == 0
|
||||
mock_toolkit.connect.assert_called_once()
|
||||
mock_toolkit.disconnect.assert_called_once()
|
||||
|
||||
|
|
@ -117,7 +133,9 @@ class TestToolController:
|
|||
return_value=mock_toolkit,
|
||||
):
|
||||
result = await install_tool("notion")
|
||||
assert result == names
|
||||
assert result["success"] is True
|
||||
assert result["tools"] == names
|
||||
assert result["count"] == 4
|
||||
mock_toolkit.connect.assert_called_once()
|
||||
mock_toolkit.disconnect.assert_called_once()
|
||||
|
||||
|
|
@ -145,7 +163,10 @@ class TestToolControllerIntegration:
|
|||
response = client.post(f"/install/tool/{tool_name}")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json() == ["create_page", "update_page"]
|
||||
data = response.json()
|
||||
assert data["success"] is True
|
||||
assert data["tools"] == ["create_page", "update_page"]
|
||||
assert data["count"] == 2
|
||||
|
||||
def test_install_unknown_tool_endpoint_integration(
|
||||
self, client: TestClient
|
||||
|
|
@ -155,8 +176,7 @@ class TestToolControllerIntegration:
|
|||
|
||||
response = client.post(f"/install/tool/{tool_name}")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"error": "Tool not found"}
|
||||
assert response.status_code == 404
|
||||
|
||||
def test_install_notion_tool_endpoint_with_connection_error(
|
||||
self, client: TestClient
|
||||
|
|
@ -171,9 +191,12 @@ class TestToolControllerIntegration:
|
|||
"app.controller.tool_controller.NotionMCPToolkit",
|
||||
return_value=mock_toolkit,
|
||||
):
|
||||
# The exception should be raised by the endpoint since there's no error handling
|
||||
with pytest.raises(Exception, match="Connection failed"):
|
||||
client.post(f"/install/tool/{tool_name}")
|
||||
response = client.post(f"/install/tool/{tool_name}")
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["success"] is True
|
||||
assert data["tools"] == []
|
||||
assert "warning" in data
|
||||
|
||||
|
||||
@pytest.mark.model_backend
|
||||
|
|
@ -211,8 +234,11 @@ class TestToolControllerErrorCases:
|
|||
"app.controller.tool_controller.NotionMCPToolkit",
|
||||
return_value=mock_toolkit,
|
||||
):
|
||||
with pytest.raises(AttributeError):
|
||||
await install_tool("notion")
|
||||
# Inner except catches the AttributeError and returns success with empty tools
|
||||
result = await install_tool("notion")
|
||||
assert result["success"] is True
|
||||
assert result["tools"] == []
|
||||
assert "warning" in result
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_install_tool_with_none_toolkit(self):
|
||||
|
|
@ -220,23 +246,29 @@ class TestToolControllerErrorCases:
|
|||
"app.controller.tool_controller.NotionMCPToolkit",
|
||||
return_value=None,
|
||||
):
|
||||
with pytest.raises(AttributeError):
|
||||
await install_tool("notion")
|
||||
# Inner except catches AttributeError on None.connect()
|
||||
result = await install_tool("notion")
|
||||
assert result["success"] is True
|
||||
assert result["tools"] == []
|
||||
assert "warning" in result
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_install_tool_with_special_characters_in_name(self):
|
||||
result = await install_tool("notion@#$%")
|
||||
assert result == {"error": "Tool not found"}
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
await install_tool("notion@#$%")
|
||||
assert exc_info.value.status_code == 404
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_install_tool_with_empty_string_name(self):
|
||||
result = await install_tool("")
|
||||
assert result == {"error": "Tool not found"}
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
await install_tool("")
|
||||
assert exc_info.value.status_code == 404
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_install_tool_with_none_name(self):
|
||||
result = await install_tool(None)
|
||||
assert result == {"error": "Tool not found"}
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
await install_tool(None)
|
||||
assert exc_info.value.status_code == 404
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_install_notion_tool_partial_failure(self):
|
||||
|
|
@ -252,5 +284,8 @@ class TestToolControllerErrorCases:
|
|||
"app.controller.tool_controller.NotionMCPToolkit",
|
||||
return_value=mock_toolkit,
|
||||
):
|
||||
with pytest.raises(AttributeError):
|
||||
await install_tool("notion")
|
||||
# Inner except catches the AttributeError from tools[2].func
|
||||
result = await install_tool("notion")
|
||||
assert result["success"] is True
|
||||
assert result["tools"] == []
|
||||
assert "warning" in result
|
||||
|
|
@ -71,7 +71,6 @@ class TestCollectPreviousTaskContext:
|
|||
assert "Previous Task Result:" in result
|
||||
assert "Successfully created script.py" in result
|
||||
assert "=== END OF PREVIOUS TASK CONTEXT ===" in result
|
||||
assert "=== NEW TASK ===" in result
|
||||
|
||||
def test_collect_previous_task_context_with_generated_files(
|
||||
self, temp_dir
|
||||
|
|
@ -208,7 +207,6 @@ class TestCollectPreviousTaskContext:
|
|||
# Should still have the structural elements
|
||||
assert "=== CONTEXT FROM PREVIOUS TASK ===" in result
|
||||
assert "=== END OF PREVIOUS TASK CONTEXT ===" in result
|
||||
assert "=== NEW TASK ===" in result
|
||||
|
||||
# Should not have content sections for empty inputs
|
||||
assert "Previous Task:" not in result
|
||||
|
|
@ -289,7 +287,6 @@ class TestBuildContextForWorkforce:
|
|||
# Create mock TaskLock
|
||||
task_lock = MagicMock(spec=TaskLock)
|
||||
task_lock.conversation_history = [
|
||||
{"role": "user", "content": "Create a Python script"},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "I will create a Python script for you",
|
||||
|
|
@ -304,14 +301,10 @@ class TestBuildContextForWorkforce:
|
|||
|
||||
result = build_context_for_workforce(task_lock, options)
|
||||
|
||||
# Should include conversation history
|
||||
# Should include conversation history header
|
||||
assert "=== CONVERSATION HISTORY ===" in result
|
||||
assert "user: Create a Python script" in result
|
||||
assert "assistant: I will create a Python script for you" in result
|
||||
|
||||
# Should include previous task context
|
||||
assert "=== CONTEXT FROM PREVIOUS TASK ===" in result
|
||||
assert "Script created successfully" in result
|
||||
# build_conversation_context only processes assistant and task_result roles
|
||||
assert "I will create a Python script for you" in result
|
||||
|
||||
def test_build_context_for_workforce_empty_history(self, temp_dir):
|
||||
"""Test build_context_for_workforce with empty conversation history."""
|
||||
|
|
@ -329,15 +322,17 @@ class TestBuildContextForWorkforce:
|
|||
assert result == ""
|
||||
|
||||
def test_build_context_for_workforce_task_result_role(self, temp_dir):
|
||||
"""Test build_context_for_workforce handles 'task_result' role specially."""
|
||||
"""Test build_context_for_workforce handles 'task_result' role."""
|
||||
task_lock = MagicMock(spec=TaskLock)
|
||||
task_lock.conversation_history = [
|
||||
{"role": "user", "content": "First question"},
|
||||
{
|
||||
"role": "task_result",
|
||||
"content": "Full task context from previous task",
|
||||
},
|
||||
{"role": "user", "content": "Second question"},
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "Task completed successfully",
|
||||
},
|
||||
]
|
||||
task_lock.last_task_result = "Final result"
|
||||
task_lock.last_task_summary = "Task summary"
|
||||
|
|
@ -347,22 +342,18 @@ class TestBuildContextForWorkforce:
|
|||
|
||||
result = build_context_for_workforce(task_lock, options)
|
||||
|
||||
# Should simplify task_result display
|
||||
assert "[Previous Task Completed]" in result
|
||||
assert (
|
||||
"Full task context from previous task" not in result
|
||||
) # Should not show full content
|
||||
assert "user: First question" in result
|
||||
assert "user: Second question" in result
|
||||
# build_conversation_context appends string task_result content directly
|
||||
assert "Full task context from previous task" in result
|
||||
assert "Task completed successfully" in result
|
||||
|
||||
def test_build_context_for_workforce_with_last_task_result(self, temp_dir):
|
||||
"""Test build_context_for_workforce includes last task result context."""
|
||||
# Create some files in temp directory
|
||||
(temp_dir / "output.txt").write_text("Task output")
|
||||
|
||||
"""Test build_context_for_workforce with assistant entries."""
|
||||
task_lock = MagicMock(spec=TaskLock)
|
||||
task_lock.conversation_history = [
|
||||
{"role": "user", "content": "Test question"}
|
||||
{
|
||||
"role": "assistant",
|
||||
"content": "Task completed with output.txt",
|
||||
},
|
||||
]
|
||||
task_lock.last_task_result = "Task completed with output.txt"
|
||||
task_lock.last_task_summary = "File creation task"
|
||||
|
|
@ -372,13 +363,9 @@ class TestBuildContextForWorkforce:
|
|||
|
||||
result = build_context_for_workforce(task_lock, options)
|
||||
|
||||
# Should include conversation history and task context
|
||||
# Should include conversation history
|
||||
assert "=== CONVERSATION HISTORY ===" in result
|
||||
assert "user: Test question" in result
|
||||
assert "=== CONTEXT FROM PREVIOUS TASK ===" in result
|
||||
assert "Task completed with output.txt" in result
|
||||
assert "File creation task" in result
|
||||
assert "output.txt" in result # Generated file should be listed
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
|
|
@ -586,17 +573,14 @@ class TestChatServiceAgentOperations:
|
|||
|
||||
@pytest.mark.asyncio
|
||||
async def test_question_confirm_simple_query(self, mock_camel_agent):
|
||||
"""Test question_confirm with simple query that gets direct response."""
|
||||
mock_camel_agent.step.return_value.msgs[
|
||||
0
|
||||
].content = "Hello! How can I help you today?"
|
||||
"""Test question_confirm with simple query returns False."""
|
||||
mock_camel_agent.step.return_value.msgs[0].content = "no"
|
||||
mock_camel_agent.chat_history = []
|
||||
|
||||
result = await question_confirm(mock_camel_agent, "hello")
|
||||
|
||||
# Should return SSE formatted response for simple queries
|
||||
assert "wait_confirm" in result
|
||||
assert "Hello! How can I help you today?" in result
|
||||
# Should return False for simple queries (no "yes" in response)
|
||||
assert result is False
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_question_confirm_complex_task(self, mock_camel_agent):
|
||||
|
|
@ -666,6 +650,10 @@ class TestChatServiceAgentOperations:
|
|||
|
||||
with (
|
||||
patch("app.service.chat_service.agent_model") as mock_agent_model,
|
||||
patch(
|
||||
"app.service.chat_service.get_working_directory",
|
||||
return_value="/tmp/test_workdir",
|
||||
),
|
||||
patch(
|
||||
"app.service.chat_service.Workforce",
|
||||
return_value=mock_workforce,
|
||||
|
|
@ -682,6 +670,10 @@ class TestChatServiceAgentOperations:
|
|||
"app.agent.toolkit.human_toolkit.get_task_lock",
|
||||
return_value=mock_task_lock,
|
||||
),
|
||||
patch(
|
||||
"app.service.chat_service.WorkforceMetricsCallback",
|
||||
return_value=MagicMock(),
|
||||
),
|
||||
):
|
||||
mock_agent_model.return_value = MagicMock()
|
||||
|
||||
|
|
@ -738,23 +730,15 @@ class TestChatServiceIntegration:
|
|||
"def hello(): print('Hello World')"
|
||||
)
|
||||
|
||||
# Mock file_save_path method to return our temp directory
|
||||
with patch.object(
|
||||
Chat, "file_save_path", return_value=str(working_dir)
|
||||
):
|
||||
# Test the context building directly
|
||||
context = build_context_for_workforce(task_lock, options)
|
||||
# Test the context building directly
|
||||
# build_context_for_workforce now only calls build_conversation_context
|
||||
# which only processes assistant and task_result roles
|
||||
context = build_context_for_workforce(task_lock, options)
|
||||
|
||||
# Verify context includes conversation history
|
||||
assert "=== CONVERSATION HISTORY ===" in context
|
||||
assert "user: Create a Python script" in context
|
||||
assert "assistant: Script created successfully" in context
|
||||
|
||||
# Verify context includes task context with files
|
||||
assert "=== CONTEXT FROM PREVIOUS TASK ===" in context
|
||||
assert "def hello(): print('Hello World')" in context
|
||||
assert "Python Hello World Script" in context
|
||||
assert "script.py" in context
|
||||
# Verify context includes conversation history header
|
||||
assert "=== CONVERSATION HISTORY ===" in context
|
||||
# assistant entries are included
|
||||
assert "Script created successfully" in context
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_step_solve_new_task_state_context_collection(
|
||||
|
|
@ -793,7 +777,6 @@ class TestChatServiceIntegration:
|
|||
assert "main.py" in result
|
||||
assert "config.json" in result
|
||||
assert "=== END OF PREVIOUS TASK CONTEXT ===" in result
|
||||
assert "=== NEW TASK ===" in result
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_step_solve_end_action_context_collection(
|
||||
|
|
@ -1008,26 +991,23 @@ class TestChatServiceErrorCases:
|
|||
# Should log warning
|
||||
mock_logger.warning.assert_called_once()
|
||||
|
||||
def test_collect_previous_task_context_relpath_exception(self, temp_dir):
|
||||
"""Test collect_previous_task_context handles os.path.relpath exceptions."""
|
||||
def test_collect_previous_task_context_abspath_used(self, temp_dir):
|
||||
"""Test collect_previous_task_context uses absolute paths for files."""
|
||||
working_directory = str(temp_dir)
|
||||
|
||||
# Create a test file
|
||||
(temp_dir / "test.txt").write_text("test content")
|
||||
|
||||
with patch("os.path.relpath", side_effect=ValueError("Invalid path")):
|
||||
with patch("app.service.chat_service.logger") as mock_logger:
|
||||
result = collect_previous_task_context(
|
||||
working_directory=working_directory,
|
||||
previous_task_content="Test task",
|
||||
previous_task_result="Test result",
|
||||
previous_summary="Test summary",
|
||||
)
|
||||
result = collect_previous_task_context(
|
||||
working_directory=working_directory,
|
||||
previous_task_content="Test task",
|
||||
previous_task_result="Test result",
|
||||
previous_summary="Test summary",
|
||||
)
|
||||
|
||||
# Should handle the exception gracefully
|
||||
assert "=== CONTEXT FROM PREVIOUS TASK ===" in result
|
||||
# Should log warning about file collection failure
|
||||
mock_logger.warning.assert_called_once()
|
||||
# Should include absolute path for the file
|
||||
assert "=== CONTEXT FROM PREVIOUS TASK ===" in result
|
||||
assert "test.txt" in result
|
||||
|
||||
def test_build_context_for_workforce_missing_attributes(self, temp_dir):
|
||||
"""Test build_context_for_workforce handles missing attributes gracefully."""
|
||||
|
|
@ -1045,20 +1025,18 @@ class TestChatServiceErrorCases:
|
|||
# Should handle missing attributes gracefully
|
||||
assert result == ""
|
||||
|
||||
def test_build_context_for_workforce_file_save_path_exception(self):
|
||||
"""Test build_context_for_workforce handles file_save_path exceptions."""
|
||||
def test_build_context_for_workforce_empty_conversation(self):
|
||||
"""Test build_context_for_workforce returns empty for empty conversation."""
|
||||
task_lock = MagicMock(spec=TaskLock)
|
||||
task_lock.conversation_history = []
|
||||
task_lock.last_task_result = "Test result"
|
||||
task_lock.last_task_summary = "Test summary"
|
||||
|
||||
options = MagicMock()
|
||||
options.file_save_path.side_effect = Exception("Path error")
|
||||
|
||||
with patch("app.service.chat_service.logger") as mock_logger:
|
||||
# Should handle exception when getting file path
|
||||
with pytest.raises(Exception, match="Path error"):
|
||||
build_context_for_workforce(task_lock, options)
|
||||
# Should return empty string for empty conversation history
|
||||
result = build_context_for_workforce(task_lock, options)
|
||||
assert result == ""
|
||||
|
||||
def test_collect_previous_task_context_unicode_handling(self, temp_dir):
|
||||
"""Test collect_previous_task_context handles unicode content correctly."""
|
||||
|
|
@ -1216,6 +1194,22 @@ class TestChatServiceErrorCases:
|
|||
"app.service.chat_service.agent_model",
|
||||
side_effect=Exception("Agent creation failed"),
|
||||
),
|
||||
patch(
|
||||
"app.agent.factory.developer.agent_model",
|
||||
side_effect=Exception("Agent creation failed"),
|
||||
),
|
||||
patch(
|
||||
"app.agent.factory.browser.agent_model",
|
||||
side_effect=Exception("Agent creation failed"),
|
||||
),
|
||||
patch(
|
||||
"app.agent.factory.document.agent_model",
|
||||
side_effect=Exception("Agent creation failed"),
|
||||
),
|
||||
patch(
|
||||
"app.agent.factory.multi_modal.agent_model",
|
||||
side_effect=Exception("Agent creation failed"),
|
||||
),
|
||||
):
|
||||
with pytest.raises(Exception, match="Agent creation failed"):
|
||||
await construct_workforce(options)
|
||||
|
|
@ -519,32 +519,29 @@ class TestPeriodicCleanup:
|
|||
@pytest.mark.asyncio
|
||||
async def test_periodic_cleanup_handles_exceptions(self):
|
||||
"""Test that periodic cleanup handles exceptions gracefully."""
|
||||
import app.service.task as task_module
|
||||
|
||||
# Create a stale task lock
|
||||
task_lock = create_task_lock("test_task")
|
||||
task_lock.last_accessed = datetime.now() - timedelta(hours=3)
|
||||
|
||||
# Mock delete_task_lock to raise exception
|
||||
# Mock delete_task_lock to raise exception and call through module
|
||||
with (
|
||||
patch(
|
||||
"app.service.task.delete_task_lock",
|
||||
patch.object(
|
||||
task_module,
|
||||
"delete_task_lock",
|
||||
side_effect=Exception("Test error"),
|
||||
),
|
||||
patch(
|
||||
"app.service.task.logger.error",
|
||||
) as mock_logger,
|
||||
patch.object(task_module, "logger") as mock_logger,
|
||||
):
|
||||
# Directly call the cleanup logic
|
||||
# that should trigger the exception
|
||||
# Simulate what _periodic_cleanup does when encountering an error
|
||||
try:
|
||||
await delete_task_lock("test_task")
|
||||
await task_module.delete_task_lock("test_task")
|
||||
except Exception as e:
|
||||
import logging
|
||||
|
||||
task_logger = logging.getLogger("task_service")
|
||||
task_logger.error(f"Error during task cleanup: {e}")
|
||||
task_module.logger.error(f"Error in periodic cleanup: {e}")
|
||||
|
||||
# Should have logged the error
|
||||
mock_logger.assert_called()
|
||||
mock_logger.error.assert_called()
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
|
|
@ -33,6 +33,7 @@ class TestSingleAgentWorker:
|
|||
mock_worker = MagicMock(spec=ListenChatAgent)
|
||||
mock_worker.role_name = "test_worker"
|
||||
mock_worker.agent_id = "worker_123"
|
||||
mock_worker.agent_name = "test_worker"
|
||||
|
||||
worker = SingleAgentWorker(
|
||||
description="Test worker description",
|
||||
|
|
@ -57,6 +58,7 @@ class TestSingleAgentWorker:
|
|||
mock_worker = MagicMock(spec=ListenChatAgent)
|
||||
mock_worker.role_name = "test_worker"
|
||||
mock_worker.agent_id = "worker_123"
|
||||
mock_worker.agent_name = "test_worker"
|
||||
|
||||
worker = SingleAgentWorker(
|
||||
description="Test worker",
|
||||
|
|
@ -123,6 +125,7 @@ class TestSingleAgentWorker:
|
|||
mock_worker = MagicMock(spec=ListenChatAgent)
|
||||
mock_worker.role_name = "test_worker"
|
||||
mock_worker.agent_id = "worker_123"
|
||||
mock_worker.agent_name = "test_worker"
|
||||
|
||||
worker = SingleAgentWorker(
|
||||
description="Test worker",
|
||||
|
|
@ -178,6 +181,7 @@ class TestSingleAgentWorker:
|
|||
mock_worker = MagicMock(spec=ListenChatAgent)
|
||||
mock_worker.role_name = "test_worker"
|
||||
mock_worker.agent_id = "test_agent_123"
|
||||
mock_worker.agent_name = "test_worker"
|
||||
|
||||
worker = SingleAgentWorker(
|
||||
description="Test worker",
|
||||
|
|
@ -247,6 +251,7 @@ class TestSingleAgentWorker:
|
|||
mock_worker = MagicMock(spec=ListenChatAgent)
|
||||
mock_worker.role_name = "test_worker"
|
||||
mock_worker.agent_id = "test_agent_123"
|
||||
mock_worker.agent_name = "test_worker"
|
||||
|
||||
worker = SingleAgentWorker(
|
||||
description="Test worker", worker=mock_worker
|
||||
|
|
@ -280,6 +285,7 @@ class TestSingleAgentWorker:
|
|||
mock_worker = MagicMock(spec=ListenChatAgent)
|
||||
mock_worker.role_name = "test_worker"
|
||||
mock_worker.agent_id = "test_agent_123"
|
||||
mock_worker.agent_name = "test_worker"
|
||||
|
||||
worker = SingleAgentWorker(
|
||||
description="Test worker",
|
||||
|
|
@ -333,6 +339,7 @@ class TestSingleAgentWorker:
|
|||
mock_worker = MagicMock(spec=ListenChatAgent)
|
||||
mock_worker.role_name = "test_worker"
|
||||
mock_worker.agent_id = "test_agent_123"
|
||||
mock_worker.agent_name = "test_worker"
|
||||
|
||||
worker = SingleAgentWorker(
|
||||
description="Test worker",
|
||||
|
|
@ -382,6 +389,7 @@ class TestSingleAgentWorker:
|
|||
mock_worker = MagicMock(spec=ListenChatAgent)
|
||||
mock_worker.role_name = "test_worker"
|
||||
mock_worker.agent_id = "test_agent_123"
|
||||
mock_worker.agent_name = "test_worker"
|
||||
|
||||
worker = SingleAgentWorker(
|
||||
description="Test worker",
|
||||
|
|
@ -431,6 +439,7 @@ class TestSingleAgentWorker:
|
|||
mock_worker = MagicMock(spec=ListenChatAgent)
|
||||
mock_worker.role_name = "test_worker"
|
||||
mock_worker.agent_id = "test_agent_123"
|
||||
mock_worker.agent_name = "test_worker"
|
||||
|
||||
worker = SingleAgentWorker(
|
||||
description="Test worker",
|
||||
|
|
@ -476,6 +485,7 @@ class TestSingleAgentWorker:
|
|||
|
||||
mock_worker = MagicMock(spec=ListenChatAgent)
|
||||
mock_worker.agent_id = "test_agent_123"
|
||||
mock_worker.agent_name = "test_worker"
|
||||
worker = SingleAgentWorker(description="Test", worker=mock_worker)
|
||||
|
||||
assert isinstance(worker, BaseSingleAgentWorker)
|
||||
|
|
@ -491,6 +501,7 @@ class TestSingleAgentWorkerIntegration:
|
|||
mock_worker = MagicMock(spec=ListenChatAgent)
|
||||
mock_worker.role_name = "integration_worker"
|
||||
mock_worker.agent_id = "test_agent_123"
|
||||
mock_worker.agent_name = "integration_worker"
|
||||
|
||||
worker = SingleAgentWorker(
|
||||
description="Integration test worker",
|
||||
|
|
@ -568,6 +579,7 @@ class TestSingleAgentWorkerErrorCases:
|
|||
"""Test _process_task when agent returns None response."""
|
||||
mock_worker = MagicMock(spec=ListenChatAgent)
|
||||
mock_worker.agent_id = "test_agent_123"
|
||||
mock_worker.agent_name = "test_worker"
|
||||
worker = SingleAgentWorker(
|
||||
description="Test",
|
||||
worker=mock_worker,
|
||||
|
|
@ -600,6 +612,7 @@ class TestSingleAgentWorkerErrorCases:
|
|||
"""Test _process_task with malformed response structure."""
|
||||
mock_worker = MagicMock(spec=ListenChatAgent)
|
||||
mock_worker.agent_id = "test_agent_123"
|
||||
mock_worker.agent_name = "test_worker"
|
||||
worker = SingleAgentWorker(
|
||||
description="Test",
|
||||
worker=mock_worker,
|
||||
|
|
@ -637,6 +650,7 @@ class TestSingleAgentWorkerErrorCases:
|
|||
mock_worker = MagicMock(spec=ListenChatAgent)
|
||||
mock_worker.agent_id = "test_agent_123"
|
||||
mock_worker.role_name = "test_worker"
|
||||
mock_worker.agent_name = "test_worker"
|
||||
worker = SingleAgentWorker(
|
||||
description="Test",
|
||||
worker=mock_worker,
|
||||
|
|
@ -344,6 +344,20 @@ async def async_mock_agent() -> AsyncGenerator[AsyncMock, None]:
|
|||
yield agent
|
||||
|
||||
|
||||
# Safety net: clean up any MagicMock-named directories that tests may
|
||||
# accidentally create when mock objects are used as file paths.
|
||||
@pytest.fixture(autouse=True, scope="session")
|
||||
def _cleanup_magicmock_dirs():
|
||||
"""Remove MagicMock-named directories from backend/ after test session."""
|
||||
yield
|
||||
import shutil
|
||||
|
||||
backend_dir = Path(__file__).parent.parent
|
||||
for entry in backend_dir.iterdir():
|
||||
if "MagicMock" in entry.name:
|
||||
shutil.rmtree(entry, ignore_errors=True)
|
||||
|
||||
|
||||
# Markers for test categorization
|
||||
pytest_plugins = ["pytest_asyncio"]
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue