Skip to content

Commit 04cbab6

Browse files
committed
🔨 use arm host for unitest runner and update monitor auto disable
1 parent c6301dc commit 04cbab6

File tree

5 files changed

+92
-58
lines changed

5 files changed

+92
-58
lines changed

‎.github/workflows/auto-unit-test.yml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ on:
1010
runner_label_json:
1111
description: 'runner array in json format (e.g. ["ubuntu-latest"] or ["self-hosted"])'
1212
required: false
13-
default: '["ubuntu-latest"]'
13+
default: '["ubuntu-24.04-arm"]'
1414
pull_request:
1515
branches: [develop]
1616
paths:
@@ -28,7 +28,7 @@ on:
2828

2929
jobs:
3030
test:
31-
runs-on: ${{ fromJson(github.event.inputs.runner_label_json || '["ubuntu-latest"]') }}
31+
runs-on: ${{ fromJson(github.event.inputs.runner_label_json || '["ubuntu-24.04-arm"]') }}
3232
steps:
3333
- name: Checkout code
3434
uses: actions/checkout@v4

‎test/backend/app/test_agent_app.py‎

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import atexit
2-
from unittest.mock import patch, Mock
2+
from unittest.mock import patch, Mock, MagicMock
33
import os
44
import sys
55
import types
@@ -14,15 +14,19 @@
1414
backend_dir = os.path.abspath(os.path.join(current_dir, "../../../backend"))
1515
sys.path.insert(0, backend_dir)
1616

17-
from apps.agent_app import router
17+
# Mock boto3 before importing backend modules
18+
boto3_mock = MagicMock()
19+
sys.modules['boto3'] = boto3_mock
20+
21+
# Import target endpoints with all external dependencies patched
22+
with patch('backend.database.client.MinioClient') as minio_mock:
23+
minio_mock.return_value = MagicMock()
24+
25+
from apps.agent_app import router
1826

1927
# Apply patches before importing any app modules (similar to test_base_app.py)
2028

2129
patches = [
22-
# Mock boto3 client
23-
patch('boto3.client', return_value=Mock()),
24-
# Mock boto3 resource
25-
patch('boto3.resource', return_value=Mock()),
2630
# Mock database sessions
2731
patch('backend.database.client.get_db_session', return_value=Mock()),
2832
# Mock Elasticsearch to prevent connection errors

‎test/backend/services/test_agent_service.py‎

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,11 @@
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-
)
24-
from fastapi import Request
25-
from consts.model import ExportAndImportAgentInfo, ExportAndImportDataFormat, MCPInfo, AgentRequest
261
import sys
272
import asyncio
283
import json
294
from unittest.mock import patch, MagicMock, mock_open, call, Mock, AsyncMock
305

316
import pytest
327
from fastapi.responses import StreamingResponse
8+
from fastapi import Request
339

3410
# Import the actual ToolConfig model for testing before any mocking
3511
from nexent.core.agents.agent_model import ToolConfig
@@ -38,6 +14,35 @@
3814
boto3_mock = MagicMock()
3915
sys.modules['boto3'] = boto3_mock
4016

17+
# Mock MinioClient before importing backend modules that might initialize it
18+
with patch('backend.database.client.MinioClient') as minio_mock:
19+
minio_mock.return_value = MagicMock()
20+
21+
import backend.services.agent_service as agent_service
22+
from backend.services.agent_service import update_agent_info_impl
23+
from backend.services.agent_service import get_creating_sub_agent_info_impl
24+
from backend.services.agent_service import list_all_agent_info_impl
25+
from backend.services.agent_service import get_agent_info_impl
26+
from backend.services.agent_service import get_creating_sub_agent_id_service
27+
from backend.services.agent_service import get_enable_tool_id_by_agent_id
28+
from backend.services.agent_service import (
29+
get_agent_call_relationship_impl,
30+
delete_agent_impl,
31+
export_agent_impl,
32+
export_agent_by_agent_id,
33+
import_agent_by_agent_id,
34+
insert_related_agent_impl,
35+
load_default_agents_json_file,
36+
clear_agent_memory,
37+
import_agent_impl,
38+
get_agent_id_by_name,
39+
save_messages,
40+
prepare_agent_run,
41+
run_agent_stream,
42+
stop_agent_tasks,
43+
)
44+
from consts.model import ExportAndImportAgentInfo, ExportAndImportDataFormat, MCPInfo, AgentRequest
45+
4146
# Mock Elasticsearch
4247
elasticsearch_client_mock = MagicMock()
4348
patch('elasticsearch._sync.client.Elasticsearch',

‎test/run_all_test.py‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import os
22
import subprocess
3-
import glob
43
import sys
54
import logging
65

‎test/sdk/core/tools/test_terminal_tool.py‎

Lines changed: 50 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import pytest
22
import json
33
import time
4-
from unittest.mock import MagicMock, patch, Mock
4+
from unittest.mock import MagicMock, patch
55
import os
66

77
# Create all necessary mocks
@@ -22,7 +22,7 @@
2222
# Apply mocks
2323
with patch.dict('sys.modules', module_mocks):
2424
# Import all required modules
25-
from sdk.nexent.core.utils.observer import MessageObserver, ProcessType
25+
from sdk.nexent.core.utils.observer import MessageObserver
2626
from sdk.nexent.core.utils.tools_common_message import ToolSign
2727
# Import target module
2828
from sdk.nexent.core.tools.terminal_tool import TerminalTool
@@ -68,7 +68,8 @@ def mock_ssh_session():
6868
@pytest.fixture
6969
def terminal_tool(mock_observer):
7070
"""Create a TerminalTool instance for testing"""
71-
with patch('paramiko.SSHClient') as mock_client_class:
71+
with patch('paramiko.SSHClient') as mock_client_class, \
72+
patch('time.sleep'):
7273
mock_client = MagicMock()
7374
mock_client_class.return_value = mock_client
7475

@@ -86,7 +87,8 @@ def terminal_tool(mock_observer):
8687
@pytest.fixture
8788
def terminal_tool_no_observer():
8889
"""Create a TerminalTool instance without observer for testing"""
89-
with patch('paramiko.SSHClient') as mock_client_class:
90+
with patch('paramiko.SSHClient') as mock_client_class, \
91+
patch('time.sleep'):
9092
mock_client = MagicMock()
9193
mock_client_class.return_value = mock_client
9294

@@ -264,7 +266,8 @@ def test_create_session_no_password(self, mock_observer):
264266

265267
def test_get_session_creates_new(self, terminal_tool, mock_ssh_session):
266268
"""Test getting a new session"""
267-
with patch.object(terminal_tool, '_create_session') as mock_create:
269+
with patch.object(terminal_tool, '_create_session') as mock_create, \
270+
patch('time.sleep'):
268271
mock_create.return_value = mock_ssh_session
269272

270273
session = terminal_tool._get_session("test_session")
@@ -274,7 +277,8 @@ def test_get_session_creates_new(self, terminal_tool, mock_ssh_session):
274277

275278
def test_get_session_reuses_existing(self, terminal_tool, mock_ssh_session):
276279
"""Test reusing existing session"""
277-
with patch.object(terminal_tool, '_create_session') as mock_create:
280+
with patch.object(terminal_tool, '_create_session') as mock_create, \
281+
patch('time.sleep'):
278282
mock_create.return_value = mock_ssh_session
279283
with patch.object(terminal_tool, '_is_session_alive') as mock_alive:
280284
mock_alive.return_value = True
@@ -289,7 +293,8 @@ def test_get_session_reuses_existing(self, terminal_tool, mock_ssh_session):
289293

290294
def test_get_session_recreates_dead_session(self, terminal_tool, mock_ssh_session):
291295
"""Test recreating dead session"""
292-
with patch.object(terminal_tool, '_create_session') as mock_create:
296+
with patch.object(terminal_tool, '_create_session') as mock_create, \
297+
patch('time.sleep'):
293298
mock_create.return_value = mock_ssh_session
294299
with patch.object(terminal_tool, '_is_session_alive') as mock_alive:
295300
mock_alive.return_value = False
@@ -418,8 +423,12 @@ def test_execute_command_success(self, terminal_tool, mock_ssh_session):
418423
mock_channel.recv_ready.return_value = True
419424
mock_channel.recv.return_value = b"test output\n$ "
420425

421-
with patch.object(terminal_tool, '_clean_output') as mock_clean:
426+
with patch.object(terminal_tool, '_clean_output') as mock_clean, \
427+
patch('sdk.nexent.core.tools.terminal_tool.time.sleep'), \
428+
patch('sdk.nexent.core.tools.terminal_tool.time.time') as mock_time:
422429
mock_clean.return_value = "cleaned output"
430+
# Mock time progression to avoid infinite loop
431+
mock_time.side_effect = [0, 0, 0, 31] # Simulate timeout after a few iterations
423432

424433
result = terminal_tool._execute_command(mock_channel, "ls", 30)
425434

@@ -432,7 +441,8 @@ def test_execute_command_timeout(self, terminal_tool, mock_ssh_session):
432441
mock_channel = mock_ssh_session["channel"]
433442
mock_channel.recv_ready.return_value = False # No output
434443

435-
with patch('time.time') as mock_time:
444+
with patch('time.time') as mock_time, \
445+
patch('time.sleep'):
436446
mock_time.side_effect = [0, 0, 35] # Timeout after 35 seconds
437447

438448
result = terminal_tool._execute_command(mock_channel, "sleep 60", 30)
@@ -444,10 +454,11 @@ def test_execute_command_exception(self, terminal_tool, mock_ssh_session):
444454
mock_channel = mock_ssh_session["channel"]
445455
mock_channel.send.side_effect = Exception("Send failed")
446456

447-
result = terminal_tool._execute_command(mock_channel, "ls", 30)
448-
449-
assert "Error executing command" in result
450-
assert "Send failed" in result
457+
with patch('time.sleep'):
458+
result = terminal_tool._execute_command(mock_channel, "ls", 30)
459+
460+
assert "Error executing command" in result
461+
assert "Send failed" in result
451462

452463
def test_execute_command_prompt_detection_with_no_more_data(self, terminal_tool, mock_ssh_session):
453464
"""Test command execution with prompt detection and no more data after prompt"""
@@ -458,7 +469,8 @@ def test_execute_command_prompt_detection_with_no_more_data(self, terminal_tool,
458469
mock_channel.recv_ready.side_effect = [True, False]
459470
mock_channel.recv.return_value = b"file1.txt\nfile2.txt\n$ "
460471

461-
with patch.object(terminal_tool, '_clean_output') as mock_clean:
472+
with patch.object(terminal_tool, '_clean_output') as mock_clean, \
473+
patch('time.sleep'):
462474
mock_clean.return_value = "cleaned output"
463475

464476
result = terminal_tool._execute_command(mock_channel, "ls", 30)
@@ -477,7 +489,8 @@ def test_execute_command_multiple_prompt_types(self, terminal_tool, mock_ssh_ses
477489
mock_channel.recv_ready.side_effect = [True, False]
478490
mock_channel.recv.return_value = b"root@server:~# "
479491

480-
with patch.object(terminal_tool, '_clean_output') as mock_clean:
492+
with patch.object(terminal_tool, '_clean_output') as mock_clean, \
493+
patch('time.sleep'):
481494
mock_clean.return_value = "cleaned output"
482495

483496
result = terminal_tool._execute_command(mock_channel, "whoami", 30)
@@ -494,7 +507,8 @@ def test_execute_command_windows_prompt(self, terminal_tool, mock_ssh_session):
494507
mock_channel.recv_ready.side_effect = [True, False]
495508
mock_channel.recv.return_value = b"C:\\Users\\test> "
496509

497-
with patch.object(terminal_tool, '_clean_output') as mock_clean:
510+
with patch.object(terminal_tool, '_clean_output') as mock_clean, \
511+
patch('time.sleep'):
498512
mock_clean.return_value = "cleaned output"
499513

500514
result = terminal_tool._execute_command(mock_channel, "dir", 30)
@@ -510,7 +524,8 @@ def test_execute_command_no_output_timeout(self, terminal_tool, mock_ssh_session
510524
# No data available, should timeout after 2 seconds of no output
511525
mock_channel.recv_ready.return_value = False
512526

513-
with patch('time.time') as mock_time:
527+
with patch('time.time') as mock_time, \
528+
patch('time.sleep'):
514529
# Simulate time progression: start at 0, then 1 second, then 3 seconds (timeout)
515530
mock_time.side_effect = [0, 1, 3]
516531

@@ -525,7 +540,8 @@ class TestForwardMethod:
525540

526541
def test_forward_success(self, terminal_tool, mock_ssh_session):
527542
"""Test successful forward execution"""
528-
with patch.object(terminal_tool, '_get_session') as mock_get_session:
543+
with patch.object(terminal_tool, '_get_session') as mock_get_session, \
544+
patch('time.sleep'):
529545
mock_get_session.return_value = mock_ssh_session
530546
with patch.object(terminal_tool, '_execute_command') as mock_execute:
531547
mock_execute.return_value = "command output"
@@ -543,7 +559,8 @@ def test_forward_success(self, terminal_tool, mock_ssh_session):
543559

544560
def test_forward_with_observer(self, terminal_tool, mock_ssh_session):
545561
"""Test forward execution with observer"""
546-
with patch.object(terminal_tool, '_get_session') as mock_get_session:
562+
with patch.object(terminal_tool, '_get_session') as mock_get_session, \
563+
patch('time.sleep'):
547564
mock_get_session.return_value = mock_ssh_session
548565
with patch.object(terminal_tool, '_execute_command') as mock_execute:
549566
mock_execute.return_value = "command output"
@@ -564,7 +581,8 @@ def test_forward_with_observer(self, terminal_tool, mock_ssh_session):
564581

565582
def test_forward_without_observer(self, terminal_tool_no_observer, mock_ssh_session):
566583
"""Test forward execution without observer"""
567-
with patch.object(terminal_tool_no_observer, '_get_session') as mock_get_session:
584+
with patch.object(terminal_tool_no_observer, '_get_session') as mock_get_session, \
585+
patch('time.sleep'):
568586
mock_get_session.return_value = mock_ssh_session
569587
with patch.object(terminal_tool_no_observer, '_execute_command') as mock_execute:
570588
mock_execute.return_value = "command output"
@@ -577,7 +595,8 @@ def test_forward_without_observer(self, terminal_tool_no_observer, mock_ssh_sess
577595

578596
def test_forward_exception(self, terminal_tool, mock_ssh_session):
579597
"""Test forward execution with exception"""
580-
with patch.object(terminal_tool, '_get_session') as mock_get_session:
598+
with patch.object(terminal_tool, '_get_session') as mock_get_session, \
599+
patch('time.sleep'):
581600
mock_get_session.side_effect = Exception("Session failed")
582601

583602
result = terminal_tool.forward("ls -la", "test_session", 30)
@@ -590,7 +609,8 @@ def test_forward_exception(self, terminal_tool, mock_ssh_session):
590609

591610
def test_forward_default_parameters(self, terminal_tool, mock_ssh_session):
592611
"""Test forward execution with default parameters"""
593-
with patch.object(terminal_tool, '_get_session') as mock_get_session:
612+
with patch.object(terminal_tool, '_get_session') as mock_get_session, \
613+
patch('time.sleep'):
594614
mock_get_session.return_value = mock_ssh_session
595615
with patch.object(terminal_tool, '_execute_command') as mock_execute:
596616
mock_execute.return_value = "output"
@@ -607,7 +627,9 @@ class TestIntegration:
607627

608628
def test_full_workflow(self, terminal_tool, mock_ssh_session):
609629
"""Test complete workflow from initialization to command execution"""
610-
with patch('paramiko.SSHClient') as mock_client_class:
630+
with patch('paramiko.SSHClient') as mock_client_class, \
631+
patch('sdk.nexent.core.tools.terminal_tool.time.sleep'), \
632+
patch('sdk.nexent.core.tools.terminal_tool.time.time') as mock_time:
611633
mock_client = MagicMock()
612634
mock_client_class.return_value = mock_client
613635
mock_client.connect.return_value = None
@@ -617,6 +639,9 @@ def test_full_workflow(self, terminal_tool, mock_ssh_session):
617639
mock_ssh_session["channel"].recv_ready.return_value = True
618640
mock_ssh_session["channel"].recv.return_value = b"file1.txt\nfile2.txt\n$ "
619641

642+
# Mock time progression to avoid infinite loop
643+
mock_time.side_effect = [0, 0, 0, 31, 1000, 1001, 1002, 1003, 1004, 1005] # More values for other time.time() calls
644+
620645
# Execute command
621646
result = terminal_tool.forward("ls", "integration_test", 30)
622647

@@ -627,7 +652,8 @@ def test_full_workflow(self, terminal_tool, mock_ssh_session):
627652

628653
def test_multiple_commands_same_session(self, terminal_tool, mock_ssh_session):
629654
"""Test multiple commands using the same session"""
630-
with patch.object(terminal_tool, '_get_session') as mock_get_session:
655+
with patch.object(terminal_tool, '_get_session') as mock_get_session, \
656+
patch('time.sleep'):
631657
mock_get_session.return_value = mock_ssh_session
632658
with patch.object(terminal_tool, '_execute_command') as mock_execute:
633659
mock_execute.return_value = "output"

0 commit comments

Comments
 (0)