Skip to content

Commit 723d75e

Browse files
committed
♻️ fix monitoring unitest
1 parent 45aff3c commit 723d75e

File tree

3 files changed

+203
-73
lines changed

3 files changed

+203
-73
lines changed

test/backend/app/test_agent_app.py

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from apps.agent_app import router
12
import os
23
import sys
34
import types
@@ -23,22 +24,65 @@ class ToolConfig: # minimal stub for type reference
2324

2425
agent_model_stub.ToolConfig = ToolConfig
2526

27+
# Mock monitoring modules
28+
monitoring_stub = types.ModuleType("monitor")
29+
monitoring_manager_mock = pytest.importorskip("unittest.mock").MagicMock()
30+
31+
# Define a decorator that simply returns the original function unchanged
32+
33+
34+
def pass_through_decorator(*args, **kwargs):
35+
def decorator(func):
36+
return func
37+
return decorator
38+
39+
40+
monitoring_manager_mock.monitor_endpoint = pass_through_decorator
41+
monitoring_manager_mock.monitor_llm_call = pass_through_decorator
42+
monitoring_manager_mock.setup_fastapi_app = pytest.importorskip(
43+
"unittest.mock").MagicMock(return_value=True)
44+
monitoring_manager_mock.configure = pytest.importorskip(
45+
"unittest.mock").MagicMock()
46+
monitoring_manager_mock.add_span_event = pytest.importorskip(
47+
"unittest.mock").MagicMock()
48+
monitoring_manager_mock.set_span_attributes = pytest.importorskip(
49+
"unittest.mock").MagicMock()
50+
51+
monitoring_stub.get_monitoring_manager = lambda: monitoring_manager_mock
52+
monitoring_stub.monitoring_manager = monitoring_manager_mock
53+
monitoring_stub.MonitoringManager = pytest.importorskip(
54+
"unittest.mock").MagicMock
55+
monitoring_stub.MonitoringConfig = pytest.importorskip(
56+
"unittest.mock").MagicMock
57+
2658
# Ensure module hierarchy exists in sys.modules
2759
sys.modules['nexent'] = types.ModuleType('nexent')
2860
sys.modules['nexent.core'] = types.ModuleType('nexent.core')
2961
sys.modules['nexent.core.agents'] = types.ModuleType('nexent.core.agents')
3062
sys.modules['nexent.core.agents.agent_model'] = agent_model_stub
31-
sys.modules['database.client'] = pytest.importorskip("unittest.mock").MagicMock()
32-
sys.modules['database.agent_db'] = pytest.importorskip("unittest.mock").MagicMock()
33-
sys.modules['agents.create_agent_info'] = pytest.importorskip("unittest.mock").MagicMock()
34-
sys.modules['nexent.core.agents.run_agent'] = pytest.importorskip("unittest.mock").MagicMock()
63+
sys.modules['nexent.monitor'] = monitoring_stub
64+
sys.modules['nexent.monitor.monitoring'] = monitoring_stub
65+
sys.modules['database.client'] = pytest.importorskip(
66+
"unittest.mock").MagicMock()
67+
sys.modules['database.agent_db'] = pytest.importorskip(
68+
"unittest.mock").MagicMock()
69+
sys.modules['agents.create_agent_info'] = pytest.importorskip(
70+
"unittest.mock").MagicMock()
71+
sys.modules['nexent.core.agents.run_agent'] = pytest.importorskip(
72+
"unittest.mock").MagicMock()
3573
sys.modules['supabase'] = pytest.importorskip("unittest.mock").MagicMock()
3674
sys.modules['utils.auth_utils'] = pytest.importorskip(
3775
"unittest.mock").MagicMock()
3876
sys.modules['utils.config_utils'] = pytest.importorskip(
3977
"unittest.mock").MagicMock()
4078
sys.modules['utils.thread_utils'] = pytest.importorskip(
4179
"unittest.mock").MagicMock()
80+
# Mock utils.monitoring to return our monitoring_manager_mock
81+
utils_monitoring_mock = pytest.importorskip("unittest.mock").MagicMock()
82+
utils_monitoring_mock.monitoring_manager = monitoring_manager_mock
83+
utils_monitoring_mock.setup_fastapi_app = pytest.importorskip(
84+
"unittest.mock").MagicMock(return_value=True)
85+
sys.modules['utils.monitoring'] = utils_monitoring_mock
4286
sys.modules['agents.agent_run_manager'] = pytest.importorskip(
4387
"unittest.mock").MagicMock()
4488
sys.modules['services.agent_service'] = pytest.importorskip(
@@ -48,7 +92,6 @@ class ToolConfig: # minimal stub for type reference
4892
sys.modules['services.memory_config_service'] = pytest.importorskip(
4993
"unittest.mock").MagicMock()
5094

51-
from apps.agent_app import router
5295

5396
# Create FastAPI app for testing
5497
app = FastAPI()
@@ -108,7 +151,7 @@ def test_agent_stop_api_success(mocker, mock_conversation_id):
108151
# Mock the authentication function to return user_id
109152
mock_get_user_id = mocker.patch("apps.agent_app.get_current_user_id")
110153
mock_get_user_id.return_value = ("test_user_id", "test_tenant_id")
111-
154+
112155
mock_stop_tasks = mocker.patch("apps.agent_app.stop_agent_tasks")
113156
mock_stop_tasks.return_value = {"status": "success"}
114157

@@ -119,7 +162,8 @@ def test_agent_stop_api_success(mocker, mock_conversation_id):
119162

120163
assert response.status_code == 200
121164
mock_get_user_id.assert_called_once_with("Bearer test_token")
122-
mock_stop_tasks.assert_called_once_with(mock_conversation_id, "test_user_id")
165+
mock_stop_tasks.assert_called_once_with(
166+
mock_conversation_id, "test_user_id")
123167
assert response.json()["status"] == "success"
124168

125169

@@ -128,7 +172,7 @@ def test_agent_stop_api_not_found(mocker, mock_conversation_id):
128172
# Mock the authentication function to return user_id
129173
mock_get_user_id = mocker.patch("apps.agent_app.get_current_user_id")
130174
mock_get_user_id.return_value = ("test_user_id", "test_tenant_id")
131-
175+
132176
mock_stop_tasks = mocker.patch("apps.agent_app.stop_agent_tasks")
133177
mock_stop_tasks.return_value = {"status": "error"} # Simulate not found
134178

@@ -140,7 +184,8 @@ def test_agent_stop_api_not_found(mocker, mock_conversation_id):
140184
# The app should raise HTTPException for non-success status
141185
assert response.status_code == 400
142186
mock_get_user_id.assert_called_once_with("Bearer test_token")
143-
mock_stop_tasks.assert_called_once_with(mock_conversation_id, "test_user_id")
187+
mock_stop_tasks.assert_called_once_with(
188+
mock_conversation_id, "test_user_id")
144189
assert "no running agent or preprocess tasks found" in response.json()[
145190
"detail"]
146191

test/backend/services/test_agent_service.py

Lines changed: 67 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,26 @@
1+
import backend.services.agent_service as agent_service
2+
from backend.services.agent_service import update_agent_info_impl
3+
from backend.services.agent_service import get_creating_sub_agent_info_impl
4+
from backend.services.agent_service import list_all_agent_info_impl
5+
from backend.services.agent_service import get_agent_info_impl
6+
from backend.services.agent_service import get_creating_sub_agent_id_service
7+
from backend.services.agent_service import get_enable_tool_id_by_agent_id
8+
from backend.services.agent_service import (
9+
get_agent_call_relationship_impl,
10+
delete_agent_impl,
11+
export_agent_impl,
12+
export_agent_by_agent_id,
13+
import_agent_by_agent_id,
14+
insert_related_agent_impl,
15+
load_default_agents_json_file,
16+
clear_agent_memory,
17+
import_agent_impl,
18+
get_agent_id_by_name,
19+
save_messages,
20+
prepare_agent_run,
21+
run_agent_stream,
22+
stop_agent_tasks,
23+
)
124
from fastapi import Request
225
from consts.model import ExportAndImportAgentInfo, ExportAndImportDataFormat, MCPInfo, AgentRequest
326
import sys
@@ -37,6 +60,31 @@
3760
sys.modules['nexent.memory'] = MagicMock()
3861
sys.modules['nexent.memory.memory_service'] = MagicMock()
3962

63+
# Mock monitoring modules
64+
monitoring_manager_mock = MagicMock()
65+
66+
# Define a decorator that simply returns the original function unchanged
67+
68+
69+
def pass_through_decorator(*args, **kwargs):
70+
def decorator(func):
71+
return func
72+
return decorator
73+
74+
75+
monitoring_manager_mock.monitor_endpoint = pass_through_decorator
76+
monitoring_manager_mock.monitor_llm_call = pass_through_decorator
77+
monitoring_manager_mock.setup_fastapi_app = MagicMock(return_value=True)
78+
monitoring_manager_mock.configure = MagicMock()
79+
monitoring_manager_mock.add_span_event = MagicMock()
80+
monitoring_manager_mock.set_span_attributes = MagicMock()
81+
82+
# Mock nexent.monitor modules
83+
sys.modules['nexent.monitor'] = MagicMock()
84+
sys.modules['nexent.monitor.monitoring'] = MagicMock()
85+
sys.modules['nexent.monitor'].get_monitoring_manager = lambda: monitoring_manager_mock
86+
sys.modules['nexent.monitor'].monitoring_manager = monitoring_manager_mock
87+
4088
# Mock other dependencies
4189
sys.modules['agents'] = MagicMock()
4290
sys.modules['agents.create_agent_info'] = MagicMock()
@@ -53,32 +101,15 @@
53101
sys.modules['utils.auth_utils'] = MagicMock()
54102
sys.modules['utils.memory_utils'] = MagicMock()
55103
sys.modules['utils.thread_utils'] = MagicMock()
104+
# Mock utils.monitoring to return our monitoring_manager_mock
105+
utils_monitoring_mock = MagicMock()
106+
utils_monitoring_mock.monitoring_manager = monitoring_manager_mock
107+
utils_monitoring_mock.setup_fastapi_app = MagicMock(return_value=True)
108+
sys.modules['utils.monitoring'] = utils_monitoring_mock
56109
sys.modules['agents.agent_run_manager'] = MagicMock()
57110
sys.modules['agents.preprocess_manager'] = MagicMock()
58111
sys.modules['nexent.core.agents.run_agent'] = MagicMock()
59112

60-
from backend.services.agent_service import (
61-
get_agent_call_relationship_impl,
62-
delete_agent_impl,
63-
export_agent_impl,
64-
export_agent_by_agent_id,
65-
import_agent_by_agent_id,
66-
insert_related_agent_impl,
67-
load_default_agents_json_file,
68-
clear_agent_memory,
69-
import_agent_impl,
70-
get_agent_id_by_name,
71-
save_messages,
72-
prepare_agent_run,
73-
run_agent_stream,
74-
stop_agent_tasks,
75-
)
76-
from backend.services.agent_service import get_enable_tool_id_by_agent_id
77-
from backend.services.agent_service import get_creating_sub_agent_id_service
78-
from backend.services.agent_service import get_agent_info_impl
79-
from backend.services.agent_service import list_all_agent_info_impl
80-
from backend.services.agent_service import get_creating_sub_agent_info_impl
81-
from backend.services.agent_service import update_agent_info_impl
82113

83114
original_agent_model = sys.modules['nexent.core.agents.agent_model']
84115
sys.modules['nexent.core.agents.agent_model'] = MagicMock()
@@ -1616,7 +1647,6 @@ async def test_import_agent_impl_mcp_exists_different_url(mock_get_current_user_
16161647
mock_import_agent.assert_called_once()
16171648

16181649

1619-
16201650
@patch('backend.services.agent_service.add_remote_mcp_server_list', new_callable=AsyncMock)
16211651
@patch('backend.services.agent_service.get_mcp_server_by_name_and_tenant')
16221652
@patch('backend.services.agent_service.check_mcp_name_exists')
@@ -1938,7 +1968,8 @@ def test_stop_agent_tasks(mock_preprocess_manager, mock_agent_run_manager):
19381968
assert result["status"] == "success"
19391969
assert "successfully stopped agent run and preprocess tasks" in result["message"]
19401970

1941-
mock_agent_run_manager.stop_agent_run.assert_called_once_with(123, "test_user")
1971+
mock_agent_run_manager.stop_agent_run.assert_called_once_with(
1972+
123, "test_user")
19421973

19431974
# Test only agent stopped
19441975
mock_agent_run_manager.stop_agent_run.return_value = True
@@ -2305,8 +2336,6 @@ def test_get_agent_call_relationship_impl_tool_name_fallback(mock_query_sub_agen
23052336
# Additional tests for newer logic in agent_service.py
23062337
#############################
23072338

2308-
import backend.services.agent_service as agent_service
2309-
23102339

23112340
@pytest.mark.asyncio
23122341
async def test__stream_agent_chunks_persists_and_unregisters(monkeypatch):
@@ -2326,7 +2355,8 @@ async def fake_agent_run(*_, **__):
23262355
yield "chunk1"
23272356
yield "chunk2"
23282357

2329-
monkeypatch.setitem(sys.modules, "nexent.core.agents.run_agent", MagicMock())
2358+
monkeypatch.setitem(
2359+
sys.modules, "nexent.core.agents.run_agent", MagicMock())
23302360
monkeypatch.setattr(
23312361
"backend.services.agent_service.agent_run", fake_agent_run, raising=False
23322362
)
@@ -2674,6 +2704,7 @@ async def test_generate_stream_with_memory_unexpected_exception_emits_error(monk
26742704
assert out and out[0].startswith(
26752705
"data: {") and "\"type\": \"error\"" in out[0]
26762706

2707+
26772708
async def test_generate_stream_no_memory_registers_and_streams(monkeypatch):
26782709
"""generate_stream_no_memory should prepare run info, register it and stream data without memory tokens."""
26792710
# Prepare AgentRequest & Request
@@ -2698,7 +2729,7 @@ async def test_generate_stream_no_memory_registers_and_streams(monkeypatch):
26982729
AsyncMock(return_value=MagicMock()),
26992730
raising=False,
27002731
)
2701-
2732+
27022733
registered = {}
27032734

27042735
def fake_register(conv_id, run_info, user_id):
@@ -2819,7 +2850,8 @@ async def test_generate_stream_with_memory_emits_tokens_and_unregisters(monkeypa
28192850
# Enable memory switch in preview
28202851
monkeypatch.setattr(
28212852
"backend.services.agent_service.build_memory_context",
2822-
MagicMock(return_value=MagicMock(user_config=MagicMock(memory_switch=True))),
2853+
MagicMock(return_value=MagicMock(
2854+
user_config=MagicMock(memory_switch=True))),
28232855
raising=False,
28242856
)
28252857

@@ -2893,7 +2925,8 @@ async def test_generate_stream_with_memory_fallback_on_failure(monkeypatch):
28932925
# Enable memory
28942926
monkeypatch.setattr(
28952927
"backend.services.agent_service.build_memory_context",
2896-
MagicMock(return_value=MagicMock(user_config=MagicMock(memory_switch=True))),
2928+
MagicMock(return_value=MagicMock(
2929+
user_config=MagicMock(memory_switch=True))),
28972930
raising=False,
28982931
)
28992932

@@ -2944,7 +2977,7 @@ def fake_unregister(task_id):
29442977
async def test_list_all_agent_info_impl_with_disabled_agents():
29452978
"""
29462979
Test list_all_agent_info_impl with disabled agents.
2947-
2980+
29482981
This test verifies that:
29492982
1. Agents with enabled=False are skipped and not included in the result
29502983
2. Only enabled agents are processed and returned
@@ -2997,7 +3030,7 @@ async def test_list_all_agent_info_impl_with_disabled_agents():
29973030
assert result[0]["name"] == "Enabled Agent 1"
29983031
assert result[0]["display_name"] == "Display Enabled Agent 1"
29993032
assert result[0]["is_available"] == True
3000-
3033+
30013034
assert result[1]["agent_id"] == 3
30023035
assert result[1]["name"] == "Enabled Agent 2"
30033036
assert result[1]["display_name"] == "Display Enabled Agent 2"
@@ -3019,7 +3052,7 @@ async def test_list_all_agent_info_impl_with_disabled_agents():
30193052
async def test_list_all_agent_info_impl_all_disabled_agents():
30203053
"""
30213054
Test list_all_agent_info_impl with all agents disabled.
3022-
3055+
30233056
This test verifies that:
30243057
1. When all agents are disabled, an empty list is returned
30253058
2. No tool queries are made since no agents are processed
@@ -3059,4 +3092,4 @@ async def test_list_all_agent_info_impl_all_disabled_agents():
30593092
mock_query_agents.assert_called_once_with(tenant_id="test_tenant")
30603093
# No tool queries should be made since no agents are enabled
30613094
mock_search_tools.assert_not_called()
3062-
mock_check_tools.assert_not_called()
3095+
mock_check_tools.assert_not_called()

0 commit comments

Comments
 (0)