Skip to content

Commit 9c396ee

Browse files
byteciiWendong-Fan
andauthored
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>
1 parent f3648d6 commit 9c396ee

31 files changed

Lines changed: 287 additions & 253 deletions

.github/workflows/pre-commit.yml

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -30,34 +30,6 @@ jobs:
3030
run: uv sync --group dev
3131

3232
- name: Run pre-commit
33-
run: |
34-
uv run pre-commit run --files \
35-
$(find \
36-
app/agent \
37-
app/controller \
38-
app/exception \
39-
app/middleware \
40-
app/model \
41-
app/service \
42-
benchmark \
43-
tests/app \
44-
-type f ! -path '*__pycache__*') \
45-
app/__init__.py \
46-
app/router.py \
47-
app/component/__init__.py \
48-
app/component/pydantic/__init__.py \
49-
app/utils/listen/__init__.py \
50-
app/utils/server/__init__.py \
51-
app/utils/toolkit/__init__.py \
52-
app/utils/toolkit/google_calendar_toolkit.py \
53-
app/utils/toolkit/google_gmail_mcp_toolkit.py \
54-
app/utils/toolkit/linkedin_toolkit.py \
55-
app/utils/toolkit/reddit_toolkit.py \
56-
app/utils/toolkit/slack_toolkit.py \
57-
app/utils/toolkit/twitter_toolkit.py \
58-
app/utils/toolkit/whatsapp_toolkit.py \
59-
app/utils/workforce.py \
60-
app/utils/single_agent_worker.py \
61-
tests/conftest.py
33+
run: uv run pre-commit run --files $(git ls-files .)
6234
env:
6335
SKIP: no-commit-to-branch

backend/app/utils/telemetry/workforce_metrics.py

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -168,20 +168,18 @@ def initialize_tracer_provider() -> None:
168168
_GLOBAL_TRACER_PROVIDER = provider
169169

170170

171-
def get_tracer_provider() -> TracerProvider:
171+
def get_tracer_provider() -> TracerProvider | None:
172172
"""Get the global TracerProvider instance.
173173
174174
Returns:
175-
TracerProvider: The global tracer provider
176-
177-
Raises:
178-
RuntimeError: If called before initialization
175+
TracerProvider if initialized, None otherwise
179176
"""
180177
if _GLOBAL_TRACER_PROVIDER is None:
181-
raise RuntimeError(
178+
logger.warning(
182179
"TracerProvider not initialized. "
183180
"Call initialize_tracer_provider() during app startup."
184181
)
182+
return None
185183
return _GLOBAL_TRACER_PROVIDER
186184

187185

@@ -258,22 +256,28 @@ def __init__(self, project_id: str, task_id: str):
258256
# Get the global shared tracer provider
259257
# This ensures only one BatchSpanProcessor is running
260258
provider = get_tracer_provider()
261-
262-
# Get tracer from the shared provider
263-
# Use CAMEL version for instrumentation versioning
264-
self.tracer = provider.get_tracer(
265-
TRACER_NAME_WORKFORCE, camel.__version__
266-
)
267-
self.root_span = self.tracer.start_span(
268-
f"{SPAN_WORKFORCE_EXECUTION}:{task_id}"
269-
)
270-
# Langfuse-specific attributes
271-
self.root_span.set_attribute(ATTR_LANGFUSE_SESSION_ID, project_id)
272-
tags = json.dumps(DEFAULT_LANGFUSE_TAGS.copy())
273-
self.root_span.set_attribute(ATTR_LANGFUSE_TAGS, tags)
274-
# Custom attributes
275-
self.root_span.set_attribute(ATTR_PROJECT_ID, project_id)
276-
self.root_span.set_attribute(ATTR_TASK_ID, task_id)
259+
if provider is None:
260+
# TracerProvider not initialized (e.g., app startup not
261+
# completed or running in test environment)
262+
self.enabled = False
263+
else:
264+
# Get tracer from the shared provider
265+
# Use CAMEL version for instrumentation versioning
266+
self.tracer = provider.get_tracer(
267+
TRACER_NAME_WORKFORCE, camel.__version__
268+
)
269+
self.root_span = self.tracer.start_span(
270+
f"{SPAN_WORKFORCE_EXECUTION}:{task_id}"
271+
)
272+
# Langfuse-specific attributes
273+
self.root_span.set_attribute(
274+
ATTR_LANGFUSE_SESSION_ID, project_id
275+
)
276+
tags = json.dumps(DEFAULT_LANGFUSE_TAGS.copy())
277+
self.root_span.set_attribute(ATTR_LANGFUSE_TAGS, tags)
278+
# Custom attributes
279+
self.root_span.set_attribute(ATTR_PROJECT_ID, project_id)
280+
self.root_span.set_attribute(ATTR_TASK_ID, task_id)
277281

278282
# Track active spans for task execution
279283
self.task_spans = {}

backend/tests/app/agent/factory/test_browser.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ def test_browser_agent_creation(sample_chat_data):
3535
_mod = "app.agent.factory.browser"
3636
with (
3737
patch(f"{_mod}.agent_model") as mock_agent_model,
38+
patch(
39+
f"{_mod}.get_working_directory", return_value="/tmp/test_workdir"
40+
),
3841
patch("asyncio.create_task"),
3942
patch(f"{_mod}.HumanToolkit") as mock_human_toolkit,
4043
patch(f"{_mod}.HybridBrowserToolkit") as mock_browser_toolkit,

backend/tests/app/agent/factory/test_developer.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ async def test_developer_agent_creation(sample_chat_data):
3636
_mod = "app.agent.factory.developer"
3737
with (
3838
patch(f"{_mod}.agent_model") as mock_agent_model,
39+
patch(
40+
f"{_mod}.get_working_directory", return_value="/tmp/test_workdir"
41+
),
3942
patch("asyncio.create_task"),
4043
patch(f"{_mod}.HumanToolkit") as mock_human_toolkit,
4144
patch(f"{_mod}.NoteTakingToolkit") as mock_note_toolkit,
@@ -82,6 +85,9 @@ async def test_developer_agent_with_multiple_toolkits(sample_chat_data):
8285
_mod = "app.agent.factory.developer"
8386
with (
8487
patch(f"{_mod}.agent_model") as mock_agent_model,
88+
patch(
89+
f"{_mod}.get_working_directory", return_value="/tmp/test_workdir"
90+
),
8591
patch("asyncio.create_task"),
8692
patch(f"{_mod}.HumanToolkit") as mock_human_toolkit,
8793
patch(f"{_mod}.NoteTakingToolkit") as mock_note_toolkit,

backend/tests/app/agent/factory/test_document.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ async def test_document_agent_creation(sample_chat_data):
3636
_mod = "app.agent.factory.document"
3737
with (
3838
patch(f"{_mod}.agent_model") as mock_agent_model,
39+
patch(
40+
f"{_mod}.get_working_directory", return_value="/tmp/test_workdir"
41+
),
3942
patch("asyncio.create_task"),
4043
patch(f"{_mod}.HumanToolkit") as mock_human_toolkit,
4144
patch(f"{_mod}.FileToolkit") as mock_file_toolkit,

backend/tests/app/agent/factory/test_multi_modal.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ def test_multi_modal_agent_creation(sample_chat_data):
3535
_mod = "app.agent.factory.multi_modal"
3636
with (
3737
patch(f"{_mod}.agent_model") as mock_agent_model,
38+
patch(
39+
f"{_mod}.get_working_directory", return_value="/tmp/test_workdir"
40+
),
3841
patch("asyncio.create_task"),
3942
patch(f"{_mod}.HumanToolkit") as mock_human_toolkit,
4043
patch(f"{_mod}.VideoDownloaderToolkit") as mock_video_toolkit,

backend/tests/app/agent/factory/test_social_media.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ async def test_social_media_agent_creation(sample_chat_data):
3636
mod = "app.agent.factory.social_media"
3737
with (
3838
patch(f"{mod}.agent_model") as mock_agent_model,
39+
patch(
40+
f"{mod}.get_working_directory", return_value="/tmp/test_workdir"
41+
),
3942
patch("asyncio.create_task"),
4043
patch(f"{mod}.WhatsAppToolkit") as mock_whatsapp_toolkit,
4144
patch(f"{mod}.TwitterToolkit") as mock_twitter_toolkit,
File renamed without changes.

backend/tests/unit/controller/test_chat_controller.py renamed to backend/tests/app/controller/test_chat_controller.py

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
1414

1515
import os
16-
from unittest.mock import MagicMock, patch
16+
from unittest.mock import AsyncMock, MagicMock, patch
1717

1818
import pytest
1919
from fastapi import Response
@@ -50,13 +50,14 @@ async def test_post_chat_endpoint_success(
5050

5151
with (
5252
patch(
53-
"app.controller.chat_controller.create_task_lock",
53+
"app.controller.chat_controller.get_or_create_task_lock",
5454
return_value=mock_task_lock,
5555
),
5656
patch(
5757
"app.controller.chat_controller.step_solve"
5858
) as mock_step_solve,
5959
patch("app.controller.chat_controller.load_dotenv"),
60+
patch("app.controller.chat_controller.set_current_task_id"),
6061
patch("pathlib.Path.mkdir"),
6162
patch("pathlib.Path.home", return_value=MagicMock()),
6263
):
@@ -71,9 +72,6 @@ async def mock_generator():
7172

7273
assert isinstance(response, StreamingResponse)
7374
assert response.media_type == "text/event-stream"
74-
mock_step_solve.assert_called_once_with(
75-
chat_data, mock_request, mock_task_lock
76-
)
7775

7876
@pytest.mark.asyncio
7977
async def test_post_chat_sets_environment_variables(
@@ -84,13 +82,14 @@ async def test_post_chat_sets_environment_variables(
8482

8583
with (
8684
patch(
87-
"app.controller.chat_controller.create_task_lock",
85+
"app.controller.chat_controller.get_or_create_task_lock",
8886
return_value=mock_task_lock,
8987
),
9088
patch(
9189
"app.controller.chat_controller.step_solve"
9290
) as mock_step_solve,
9391
patch("app.controller.chat_controller.load_dotenv"),
92+
patch("app.controller.chat_controller.set_current_task_id"),
9493
patch("pathlib.Path.mkdir"),
9594
patch("pathlib.Path.home", return_value=MagicMock()),
9695
patch.dict(os.environ, {}, clear=True),
@@ -133,18 +132,24 @@ def test_improve_chat_success(self, mock_task_lock):
133132
# put_queue is invoked when creating the coroutine passed to asyncio.run
134133
mock_task_lock.put_queue.assert_called_once()
135134

136-
def test_improve_chat_task_done_error(self, mock_task_lock):
137-
"""Test improvement fails when task is done."""
135+
def test_improve_chat_task_done_resets_to_confirming(self, mock_task_lock):
136+
"""Test improvement when task is done resets status to confirming."""
138137
task_id = "test_task_123"
139138
supplement_data = SupplementChat(question="Improve this code")
140139
mock_task_lock.status = Status.done
141140

142-
with patch(
143-
"app.controller.chat_controller.get_task_lock",
144-
return_value=mock_task_lock,
141+
with (
142+
patch(
143+
"app.controller.chat_controller.get_task_lock",
144+
return_value=mock_task_lock,
145+
),
146+
patch("asyncio.run") as mock_run,
145147
):
146-
with pytest.raises(UserException):
147-
improve(task_id, supplement_data)
148+
response = improve(task_id, supplement_data)
149+
150+
assert mock_task_lock.status == Status.confirming
151+
assert isinstance(response, Response)
152+
assert response.status_code == 201
148153

149154
def test_supplement_chat_success(self, mock_task_lock):
150155
"""Test successful chat supplementation."""
@@ -244,16 +249,18 @@ def test_chat_endpoint_integration(
244249
"""Test chat endpoint through FastAPI test client."""
245250
with (
246251
patch(
247-
"app.controller.chat_controller.create_task_lock"
252+
"app.controller.chat_controller.get_or_create_task_lock"
248253
) as mock_create_lock,
249254
patch(
250255
"app.controller.chat_controller.step_solve"
251256
) as mock_step_solve,
252257
patch("app.controller.chat_controller.load_dotenv"),
258+
patch("app.controller.chat_controller.set_current_task_id"),
253259
patch("pathlib.Path.mkdir"),
254260
patch("pathlib.Path.home", return_value=MagicMock()),
255261
):
256262
mock_task_lock = MagicMock()
263+
mock_task_lock.put_queue = AsyncMock()
257264
mock_create_lock.return_value = mock_task_lock
258265

259266
async def mock_generator():
@@ -455,8 +462,12 @@ async def test_post_environment_setup_failure(
455462

456463
with (
457464
patch(
458-
"app.controller.chat_controller.create_task_lock"
465+
"app.controller.chat_controller.get_or_create_task_lock"
459466
) as mock_create_lock,
467+
patch(
468+
"app.controller.chat_controller.sanitize_env_path",
469+
return_value="/tmp/fake.env",
470+
),
460471
patch(
461472
"app.controller.chat_controller.load_dotenv",
462473
side_effect=Exception("Env load failed"),

0 commit comments

Comments
 (0)