36
.github/workflows/test_agent_deep_research.yml
vendored
36
.github/workflows/test_agent_deep_research.yml
vendored
@@ -1,36 +0,0 @@
|
|||||||
name: deep_research_runtime_test
|
|
||||||
on: [push, pull_request]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
python-version: ['3.10']
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Debug directory structure
|
|
||||||
run: |
|
|
||||||
echo "Current directory: $(pwd)"
|
|
||||||
ls -la
|
|
||||||
|
|
||||||
- name: Setup Python
|
|
||||||
uses: actions/setup-python@v4
|
|
||||||
with:
|
|
||||||
python-version: ${{ matrix.python-version }}
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
cd deep_research/agent_deep_research
|
|
||||||
pip install --upgrade pip
|
|
||||||
pip install -r requirements.txt
|
|
||||||
pip install pytest pytest-asyncio pytest-mock
|
|
||||||
|
|
||||||
- name: Run tests
|
|
||||||
env:
|
|
||||||
PYTHONPATH: ${{ github.workspace }}/deep_research/agent_deep_research
|
|
||||||
run: |
|
|
||||||
python -m pytest tests/agent_deep_research_test.py -v
|
|
||||||
46
.github/workflows/test_browser_agent_test.yml
vendored
46
.github/workflows/test_browser_agent_test.yml
vendored
@@ -1,46 +0,0 @@
|
|||||||
name: BrowserAgent Tests
|
|
||||||
|
|
||||||
on: [push]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
name: Run Tests (Python ${{ matrix.python-version }})
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
python-version:
|
|
||||||
- "3.10"
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout Repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Debug directory structure
|
|
||||||
run: |
|
|
||||||
# ✅ Show actual directory structure
|
|
||||||
echo "Current directory: $(pwd)"
|
|
||||||
ls -la
|
|
||||||
|
|
||||||
- name: Setup Python ${{ matrix.python-version }}
|
|
||||||
uses: actions/setup-python@v5
|
|
||||||
with:
|
|
||||||
python-version: ${{ matrix.python-version }}
|
|
||||||
cache: pip
|
|
||||||
|
|
||||||
- name: Install Dependencies
|
|
||||||
run: |
|
|
||||||
cd browser_use/agent_browser
|
|
||||||
python -m pip install --upgrade pip
|
|
||||||
pip install pytest pytest-asyncio
|
|
||||||
pip install -r requirements.txt
|
|
||||||
|
|
||||||
- name: Run Tests
|
|
||||||
env:
|
|
||||||
PYTHONPATH: ${{ github.workspace }}/browser_use/agent_browser
|
|
||||||
DASHSCOPE_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }}
|
|
||||||
run: |
|
|
||||||
# ✅ Ensure test-results directory exists
|
|
||||||
mkdir -p test-results
|
|
||||||
# ✅ Run tests with XML output
|
|
||||||
python -m pytest tests/browser_agent_test.py -v
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
name: browser_use_fullstack_runtime_test
|
|
||||||
on: [push]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
python-version: ['3.10']
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Debug directory structure
|
|
||||||
run: |
|
|
||||||
# ✅ Show actual directory structure
|
|
||||||
echo "Current directory: $(pwd)"
|
|
||||||
ls -la
|
|
||||||
|
|
||||||
- name: Setup Python
|
|
||||||
uses: actions/setup-python@v4
|
|
||||||
with:
|
|
||||||
python-version: ${{ matrix.python-version }}
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
# ✅ Use validated path from debug output
|
|
||||||
cd browser_use/browser_use_fullstack_runtime/backend
|
|
||||||
pip install pytest pytest-asyncio
|
|
||||||
python -m pip install --upgrade pip
|
|
||||||
pip install -r requirements.txt
|
|
||||||
|
|
||||||
- name: Run tests
|
|
||||||
env:
|
|
||||||
DASHSCOPE_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }}
|
|
||||||
PYTHONPATH: ${{ github.workspace }}/browser_use/browser_use_fullstack_runtime/backend
|
|
||||||
run: |
|
|
||||||
# ✅ Use validated path from debug output
|
|
||||||
python -m pytest tests/browser_use_fullstack_runtime_test.py -v
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
name: Conversational Agents Chatbot Test
|
|
||||||
on: [push]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
python-version: ['3.10']
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Setup Python
|
|
||||||
uses: actions/setup-python@v4
|
|
||||||
with:
|
|
||||||
python-version: ${{ matrix.python-version }}
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
# ✅ Use correct relative path
|
|
||||||
cd conversational_agents/chatbot
|
|
||||||
python -m pip install --upgrade pip
|
|
||||||
pip install pytest pytest-asyncio
|
|
||||||
pip install -r requirements.txt
|
|
||||||
|
|
||||||
- name: Run tests
|
|
||||||
env:
|
|
||||||
PYTHONPATH: ${{ github.workspace }}/conversational_agents/chatbot
|
|
||||||
DASHSCOPE_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }}
|
|
||||||
run: |
|
|
||||||
# ✅ Use correct relative path
|
|
||||||
python -m pytest tests/conversational_agents_chatbot_test.py -v
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
name: Flask API Runtime Test
|
|
||||||
on: [push]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
python-version: ['3.10']
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Debug directory structure
|
|
||||||
run: |
|
|
||||||
echo "Current directory: $(pwd)"
|
|
||||||
ls -la
|
|
||||||
|
|
||||||
- name: Setup Python
|
|
||||||
uses: actions/setup-python@v4
|
|
||||||
with:
|
|
||||||
python-version: ${{ matrix.python-version }}
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
cd conversational_agents/chatbot_fullstack_runtime/backend
|
|
||||||
pip install --upgrade pip
|
|
||||||
pip install -r requirements.txt
|
|
||||||
pip install pytest pytest-asyncio
|
|
||||||
|
|
||||||
- name: Run tests
|
|
||||||
env:
|
|
||||||
PYTHONPATH: ${{ github.workspace }}/conversational_agents/chatbot_fullstack_runtime
|
|
||||||
run: |
|
|
||||||
python -m pytest tests/conversational_agents_chatbot_fullstack_runtime_webserver_test.py -v
|
|
||||||
36
.github/workflows/test_evaluation.yml
vendored
36
.github/workflows/test_evaluation.yml
vendored
@@ -1,36 +0,0 @@
|
|||||||
name: ACE Benchmark Evaluation Test
|
|
||||||
on: [push]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
python-version: ['3.10']
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Debug directory structure
|
|
||||||
run: |
|
|
||||||
echo "Current directory: $(pwd)"
|
|
||||||
ls -la
|
|
||||||
|
|
||||||
- name: Setup Python
|
|
||||||
uses: actions/setup-python@v4
|
|
||||||
with:
|
|
||||||
python-version: ${{ matrix.python-version }}
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
pip install --upgrade pip
|
|
||||||
pip install pytest pytest-asyncio pytest-mock
|
|
||||||
pip install agentscope ray
|
|
||||||
|
|
||||||
- name: Run tests
|
|
||||||
env:
|
|
||||||
PYTHONPATH: ${{ env.GITHUB_WORKSPACE }}/evaluation/ace_bench
|
|
||||||
DASHSCOPE_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }}
|
|
||||||
run: |
|
|
||||||
python -m pytest tests/evaluation_test.py -v
|
|
||||||
35
.github/workflows/test_game.yml
vendored
35
.github/workflows/test_game.yml
vendored
@@ -1,35 +0,0 @@
|
|||||||
name: Run test_game.py
|
|
||||||
|
|
||||||
on: [push]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Debug directory structure
|
|
||||||
run: |
|
|
||||||
# ✅ Show actual directory structure
|
|
||||||
echo "Current directory: $(pwd)"
|
|
||||||
ls -la
|
|
||||||
|
|
||||||
- name: Set up Python
|
|
||||||
uses: actions/setup-python@v4
|
|
||||||
with:
|
|
||||||
python-version: "3.10"
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
cd games/game_werewolves
|
|
||||||
pip install pytest pytest-asyncio
|
|
||||||
pip install -r requirements.txt
|
|
||||||
|
|
||||||
- name: Run game_test.py
|
|
||||||
env:
|
|
||||||
DASHSCOPE_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }}
|
|
||||||
PYTHONPATH: $GITHUB_WORKSPACE/games/game_werewolves
|
|
||||||
run: |
|
|
||||||
# ✅ Ensure correct working directory
|
|
||||||
PYTHONPATH=$GITHUB_WORKSPACE/games/game_werewolves python -m pytest tests/game_test.py -v
|
|
||||||
@@ -1,203 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import os
|
|
||||||
import shutil
|
|
||||||
import tempfile
|
|
||||||
from unittest.mock import Mock, AsyncMock, patch, MagicMock
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
from agentscope.formatter import DashScopeChatFormatter
|
|
||||||
from agentscope.mcp import StdIOStatefulClient
|
|
||||||
from agentscope.memory import InMemoryMemory
|
|
||||||
from agentscope.message import Msg
|
|
||||||
from agentscope.model import DashScopeChatModel
|
|
||||||
|
|
||||||
from deep_research.agent_deep_research.deep_research_agent import (
|
|
||||||
DeepResearchAgent,
|
|
||||||
)
|
|
||||||
from deep_research.agent_deep_research.main import main
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_env_vars(monkeypatch):
|
|
||||||
"""Fixture to set required environment variables"""
|
|
||||||
monkeypatch.setenv("TAVILY_API_KEY", "test_tavily_key")
|
|
||||||
monkeypatch.setenv("DASHSCOPE_API_KEY", "test_dashscope_key")
|
|
||||||
return {
|
|
||||||
"TAVILY_API_KEY": "test_tavily_key",
|
|
||||||
"DASHSCOPE_API_KEY": "test_dashscope_key",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def temp_working_dir():
|
|
||||||
"""Create a temporary working directory"""
|
|
||||||
temp_dir = tempfile.mkdtemp()
|
|
||||||
yield temp_dir
|
|
||||||
shutil.rmtree(temp_dir)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_tavily_client():
|
|
||||||
"""Create a mocked Tavily client"""
|
|
||||||
client = AsyncMock(spec=StdIOStatefulClient)
|
|
||||||
client.name = "tavily_mcp"
|
|
||||||
client.connect = AsyncMock()
|
|
||||||
client.close = AsyncMock()
|
|
||||||
return client
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_formatter():
|
|
||||||
"""Create a mocked formatter"""
|
|
||||||
return Mock(spec=DashScopeChatFormatter)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_memory():
|
|
||||||
"""Create a mocked memory instance"""
|
|
||||||
return Mock(spec=InMemoryMemory)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_model():
|
|
||||||
"""Create a mocked model instance"""
|
|
||||||
model = Mock(spec=DashScopeChatModel)
|
|
||||||
model.call = AsyncMock(return_value=Mock(content="test response"))
|
|
||||||
return model
|
|
||||||
|
|
||||||
|
|
||||||
class TestDeepResearchAgent:
|
|
||||||
"""Test suite for Deep Research Agent functionality"""
|
|
||||||
|
|
||||||
def test_agent_initialization(
|
|
||||||
self,
|
|
||||||
mock_model, # pylint: disable=redefined-outer-name
|
|
||||||
mock_tavily_client, # pylint: disable=redefined-outer-name
|
|
||||||
temp_working_dir, # pylint: disable=redefined-outer-name
|
|
||||||
):
|
|
||||||
"""Test agent initialization with valid parameters"""
|
|
||||||
mock_loop = MagicMock()
|
|
||||||
mock_task = AsyncMock()
|
|
||||||
mock_loop.create_task = MagicMock(return_value=mock_task)
|
|
||||||
with patch("asyncio.get_running_loop", return_value=mock_loop):
|
|
||||||
agent = DeepResearchAgent(
|
|
||||||
name="Friday",
|
|
||||||
sys_prompt="You are a helpful assistant named Friday.",
|
|
||||||
model=mock_model,
|
|
||||||
formatter=DashScopeChatFormatter(),
|
|
||||||
memory=InMemoryMemory(),
|
|
||||||
search_mcp_client=mock_tavily_client,
|
|
||||||
tmp_file_storage_dir=temp_working_dir,
|
|
||||||
)
|
|
||||||
|
|
||||||
assert agent.name == "Friday"
|
|
||||||
assert agent.sys_prompt.startswith(
|
|
||||||
"You are a helpful assistant named Friday.",
|
|
||||||
)
|
|
||||||
assert agent.tmp_file_storage_dir == temp_working_dir
|
|
||||||
assert os.path.exists(temp_working_dir)
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_main_function_success(
|
|
||||||
self,
|
|
||||||
mock_tavily_client, # pylint: disable=redefined-outer-name
|
|
||||||
temp_working_dir, # pylint: disable=redefined-outer-name
|
|
||||||
):
|
|
||||||
"""Test main function with successful execution"""
|
|
||||||
with patch(
|
|
||||||
"deep_research.agent_deep_research.main.StdIOStatefulClient",
|
|
||||||
return_value=mock_tavily_client,
|
|
||||||
):
|
|
||||||
with patch(
|
|
||||||
"deep_research.agent_deep_research.main.DeepResearchAgent",
|
|
||||||
autospec=True,
|
|
||||||
) as mock_agent_class:
|
|
||||||
mock_agent = AsyncMock()
|
|
||||||
mock_agent.return_value = Msg(
|
|
||||||
"Friday",
|
|
||||||
"Test response",
|
|
||||||
"assistant",
|
|
||||||
)
|
|
||||||
mock_agent_class.return_value = mock_agent
|
|
||||||
|
|
||||||
with patch("os.makedirs") as mock_makedirs:
|
|
||||||
with patch.dict(
|
|
||||||
os.environ,
|
|
||||||
{"AGENT_OPERATION_DIR": temp_working_dir},
|
|
||||||
):
|
|
||||||
test_query = "Test research question"
|
|
||||||
|
|
||||||
await main(test_query)
|
|
||||||
|
|
||||||
mock_makedirs.assert_called_once_with(
|
|
||||||
temp_working_dir,
|
|
||||||
exist_ok=True,
|
|
||||||
)
|
|
||||||
mock_agent_class.assert_called_once()
|
|
||||||
|
|
||||||
# ✅ Use assert_called_once() + manual argument check
|
|
||||||
mock_agent.assert_called_once()
|
|
||||||
call_arg = mock_agent.call_args[0][0]
|
|
||||||
assert call_arg.name == "Bob"
|
|
||||||
assert call_arg.content == "Test research question"
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_main_function_with_missing_env_vars(self):
|
|
||||||
"""Test main function handles missing environment variables"""
|
|
||||||
with patch.dict(os.environ, clear=True):
|
|
||||||
with pytest.raises(Exception):
|
|
||||||
await main("Test query")
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_agent_cleanup(
|
|
||||||
self,
|
|
||||||
mock_tavily_client, # pylint: disable=redefined-outer-name
|
|
||||||
):
|
|
||||||
"""Test proper cleanup of resources"""
|
|
||||||
with patch(
|
|
||||||
"deep_research.agent_deep_research.main.StdIOStatefulClient",
|
|
||||||
return_value=mock_tavily_client,
|
|
||||||
):
|
|
||||||
with patch.dict(os.environ, {"AGENT_OPERATION_DIR": "/tmp"}):
|
|
||||||
await main("Test query")
|
|
||||||
|
|
||||||
mock_tavily_client.close.assert_called_once()
|
|
||||||
|
|
||||||
def test_working_directory_creation(
|
|
||||||
self,
|
|
||||||
temp_working_dir, # pylint: disable=redefined-outer-name
|
|
||||||
):
|
|
||||||
"""Test working directory is created correctly"""
|
|
||||||
test_dir = os.path.join(temp_working_dir, "test_subdir")
|
|
||||||
os.makedirs(test_dir, exist_ok=True)
|
|
||||||
assert os.path.exists(test_dir)
|
|
||||||
os.makedirs(test_dir, exist_ok=True) # Should not raise error
|
|
||||||
|
|
||||||
|
|
||||||
class TestErrorHandling:
|
|
||||||
"""Test suite for error handling scenarios"""
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_filesystem_errors(
|
|
||||||
self,
|
|
||||||
mock_tavily_client, # pylint: disable=redefined-outer-name
|
|
||||||
):
|
|
||||||
"""Test handling of filesystem errors"""
|
|
||||||
with patch(
|
|
||||||
"deep_research.agent_deep_research.main.StdIOStatefulClient",
|
|
||||||
return_value=mock_tavily_client,
|
|
||||||
):
|
|
||||||
with patch.dict(
|
|
||||||
os.environ,
|
|
||||||
{"AGENT_OPERATION_DIR": "/invalid/path"},
|
|
||||||
):
|
|
||||||
with patch(
|
|
||||||
"os.makedirs",
|
|
||||||
side_effect=PermissionError("Permission denied"),
|
|
||||||
):
|
|
||||||
with pytest.raises(PermissionError):
|
|
||||||
await main("Test query")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
pytest.main(["-v", __file__])
|
|
||||||
@@ -1,171 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from typing import Dict
|
|
||||||
from unittest.mock import AsyncMock, MagicMock, patch
|
|
||||||
import pytest
|
|
||||||
from agentscope.message import Msg
|
|
||||||
from agentscope.tool import Toolkit
|
|
||||||
from agentscope.memory import MemoryBase
|
|
||||||
from agentscope.model import ChatModelBase
|
|
||||||
from agentscope.formatter import FormatterBase
|
|
||||||
from browser_use.agent_browser.browser_agent import BrowserAgent
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_dependencies() -> Dict[str, MagicMock]:
|
|
||||||
return {
|
|
||||||
"model": MagicMock(spec=ChatModelBase),
|
|
||||||
"formatter": MagicMock(spec=FormatterBase),
|
|
||||||
"memory": MagicMock(spec=MemoryBase),
|
|
||||||
"toolkit": MagicMock(spec=Toolkit),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def agent(
|
|
||||||
# pylint: disable=redefined-outer-name
|
|
||||||
mock_dependencies: Dict[str, MagicMock],
|
|
||||||
) -> BrowserAgent:
|
|
||||||
return BrowserAgent(
|
|
||||||
name="TestBot",
|
|
||||||
model=mock_dependencies["model"],
|
|
||||||
formatter=mock_dependencies["formatter"],
|
|
||||||
memory=mock_dependencies["memory"],
|
|
||||||
toolkit=mock_dependencies["toolkit"],
|
|
||||||
start_url="https://test.com",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------
|
|
||||||
# ✅ Hook registration verification (adapted for ReActAgentBase)
|
|
||||||
# -----------------------------
|
|
||||||
def test_hooks_registered(
|
|
||||||
agent: BrowserAgent, # pylint: disable=redefined-outer-name
|
|
||||||
) -> None:
|
|
||||||
"""Verify instance-level hooks are registered"""
|
|
||||||
# Disable pylint warning for protected member access
|
|
||||||
assert hasattr(
|
|
||||||
agent,
|
|
||||||
"_instance_pre_reply_hooks",
|
|
||||||
) # pylint: disable=protected-access
|
|
||||||
assert (
|
|
||||||
"browser_agent_default_url_pre_reply"
|
|
||||||
# pylint: disable=protected-access
|
|
||||||
in agent._instance_pre_reply_hooks
|
|
||||||
)
|
|
||||||
|
|
||||||
assert hasattr(
|
|
||||||
agent,
|
|
||||||
"_instance_pre_reasoning_hooks",
|
|
||||||
) # pylint: disable=protected-access
|
|
||||||
assert (
|
|
||||||
"browser_agent_observe_pre_reasoning"
|
|
||||||
# pylint: disable=protected-access
|
|
||||||
in agent._instance_pre_reasoning_hooks
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------
|
|
||||||
# ✅ Navigation hook test (direct hook invocation)
|
|
||||||
# -----------------------------
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_pre_reply_hook_navigation(
|
|
||||||
agent: BrowserAgent, # pylint: disable=redefined-outer-name
|
|
||||||
) -> None:
|
|
||||||
# pylint: disable=protected-access
|
|
||||||
agent._has_initial_navigated = False
|
|
||||||
|
|
||||||
# Get instance-level hook function
|
|
||||||
# pylint: disable=protected-access
|
|
||||||
hook_func = agent._instance_pre_reply_hooks[
|
|
||||||
"browser_agent_default_url_pre_reply"
|
|
||||||
]
|
|
||||||
await hook_func(agent) # Directly invoke hook function
|
|
||||||
|
|
||||||
# pylint: disable=protected-access
|
|
||||||
assert agent._has_initial_navigated is True
|
|
||||||
assert agent.toolkit.call_tool_function.called
|
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------
|
|
||||||
# ✅ Snapshot hook test (fix content attribute access issue)
|
|
||||||
# -----------------------------
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_observe_pre_reasoning(
|
|
||||||
agent: BrowserAgent, # pylint: disable=redefined-outer-name
|
|
||||||
) -> None:
|
|
||||||
# Mock tool response (fix: use Msg object with content attribute)
|
|
||||||
mock_response = AsyncMock()
|
|
||||||
mock_response.__aiter__.return_value = [
|
|
||||||
Msg("system", [{"text": "Snapshot content"}], "system"),
|
|
||||||
]
|
|
||||||
agent.toolkit.call_tool_function = AsyncMock(
|
|
||||||
return_value=mock_response,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Replace memory add method
|
|
||||||
with patch.object(
|
|
||||||
agent.memory,
|
|
||||||
"add",
|
|
||||||
new_callable=AsyncMock,
|
|
||||||
) as mock_add:
|
|
||||||
# Get instance-level hook function
|
|
||||||
# pylint: disable=protected-access
|
|
||||||
hook_func = agent._instance_pre_reasoning_hooks[
|
|
||||||
"browser_agent_observe_pre_reasoning"
|
|
||||||
]
|
|
||||||
await hook_func(agent) # Directly invoke hook function
|
|
||||||
|
|
||||||
mock_add.assert_awaited_once()
|
|
||||||
added_msg = mock_add.call_args[0][0]
|
|
||||||
assert "Snapshot content" in added_msg.content[0]["text"]
|
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------
|
|
||||||
# ✅ Text filtering test (improved regex)
|
|
||||||
# -----------------------------
|
|
||||||
def test_filter_execution_text(
|
|
||||||
agent: BrowserAgent, # pylint: disable=redefined-outer-name
|
|
||||||
) -> None:
|
|
||||||
text = """
|
|
||||||
### New console messages
|
|
||||||
Some console output
|
|
||||||
###
|
|
||||||
### Page state
|
|
||||||
YAML content here
|
|
||||||
```yaml
|
|
||||||
key: value
|
|
||||||
```
|
|
||||||
Regular text content
|
|
||||||
"""
|
|
||||||
# pylint: disable=protected-access
|
|
||||||
filtered = agent._filter_execution_text(text)
|
|
||||||
|
|
||||||
assert "console output" not in filtered
|
|
||||||
assert "key: value" not in filtered
|
|
||||||
assert "Regular text content" in filtered
|
|
||||||
assert "YAML content" in filtered
|
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------
|
|
||||||
# ✅ Memory summarization test (already passing)
|
|
||||||
# -----------------------------
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_memory_summarizing(
|
|
||||||
agent: BrowserAgent, # pylint: disable=redefined-outer-name
|
|
||||||
) -> None:
|
|
||||||
agent.memory.get_memory = AsyncMock(
|
|
||||||
return_value=[MagicMock(role="user", content="Original question")]
|
|
||||||
* 25,
|
|
||||||
)
|
|
||||||
agent.memory.size = AsyncMock(return_value=25)
|
|
||||||
|
|
||||||
agent.model = AsyncMock()
|
|
||||||
agent.model.return_value = MagicMock(
|
|
||||||
content=[MagicMock(text="Summary text")],
|
|
||||||
)
|
|
||||||
|
|
||||||
# pylint: disable=protected-access
|
|
||||||
await agent._memory_summarizing()
|
|
||||||
|
|
||||||
assert agent.memory.clear.called
|
|
||||||
assert agent.memory.add.call_count == 2 # Original question + summary
|
|
||||||
@@ -1,145 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import asyncio
|
|
||||||
from types import SimpleNamespace
|
|
||||||
from unittest.mock import AsyncMock, MagicMock, patch
|
|
||||||
import pytest
|
|
||||||
import pytest_asyncio
|
|
||||||
|
|
||||||
from quart.testing import QuartClient
|
|
||||||
|
|
||||||
from browser_use.browser_use_fullstack_runtime.backend import (
|
|
||||||
agentscope_browseruse_agent as agent_module,
|
|
||||||
)
|
|
||||||
from browser_use.browser_use_fullstack_runtime.backend import (
|
|
||||||
async_quart_service as service,
|
|
||||||
)
|
|
||||||
|
|
||||||
AgentscopeBrowseruseAgent = agent_module.AgentscopeBrowseruseAgent
|
|
||||||
RunStatus = agent_module.RunStatus
|
|
||||||
app = service.app
|
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------
|
|
||||||
# 🧪 Singleton Test Configuration
|
|
||||||
# -----------------------------
|
|
||||||
@pytest.fixture(scope="session")
|
|
||||||
def event_loop():
|
|
||||||
"""Create an instance of the default event loop for session scope."""
|
|
||||||
loop = asyncio.get_event_loop()
|
|
||||||
yield loop
|
|
||||||
loop.close()
|
|
||||||
|
|
||||||
|
|
||||||
@pytest_asyncio.fixture(scope="session")
|
|
||||||
async def agent_singleton():
|
|
||||||
"""Session-scoped single instance of AgentscopeBrowseruseAgent"""
|
|
||||||
with patch(
|
|
||||||
"browser_use.browser_use_fullstack_runtime."
|
|
||||||
"backend.agentscope_browseruse_agent.SandboxService",
|
|
||||||
) as MockSandboxService, patch(
|
|
||||||
"browser_use.browser_use_fullstack_runtime."
|
|
||||||
"backend.agentscope_browseruse_agent.InMemoryMemoryService",
|
|
||||||
) as MockMemoryService, patch(
|
|
||||||
"browser_use.browser_use_fullstack_runtime."
|
|
||||||
"backend.agentscope_browseruse_agent.InMemorySessionHistoryService",
|
|
||||||
) as MockHistoryService, patch(
|
|
||||||
"agentscope_runtime.common.container_clients.docker_client.docker",
|
|
||||||
) as mock_docker, patch(
|
|
||||||
"agentscope_runtime.sandbox.manager.sandbox_manager.SandboxManager",
|
|
||||||
) as MockSandboxManager:
|
|
||||||
# ✅ Fully mock Docker dependencies
|
|
||||||
mock_api = MagicMock()
|
|
||||||
mock_api.version.return_value = {"ApiVersion": "1.0"}
|
|
||||||
|
|
||||||
mock_client = MagicMock()
|
|
||||||
mock_client.api = mock_api
|
|
||||||
mock_client.from_env.return_value = mock_client
|
|
||||||
mock_client.__enter__.return_value = mock_client
|
|
||||||
|
|
||||||
# ✅ Fully mock APIClient
|
|
||||||
mock_docker.APIClient = MagicMock()
|
|
||||||
mock_docker.from_env.return_value = mock_client
|
|
||||||
|
|
||||||
# ✅ Fully mock SandboxManager
|
|
||||||
MockSandboxManager.return_value = MagicMock()
|
|
||||||
|
|
||||||
# Configure InMemorySessionHistoryService
|
|
||||||
mock_session = MagicMock()
|
|
||||||
mock_session.create_session = AsyncMock()
|
|
||||||
MockHistoryService.return_value = mock_session
|
|
||||||
|
|
||||||
# Configure InMemoryMemoryService
|
|
||||||
mock_memory = MagicMock()
|
|
||||||
mock_memory.start = AsyncMock()
|
|
||||||
MockMemoryService.return_value = mock_memory
|
|
||||||
|
|
||||||
# Configure SandboxService
|
|
||||||
mock_sandbox = MagicMock()
|
|
||||||
mock_sandbox.start = AsyncMock()
|
|
||||||
MockSandboxService.return_value = mock_sandbox
|
|
||||||
|
|
||||||
agent = AgentscopeBrowseruseAgent()
|
|
||||||
await agent.connect()
|
|
||||||
return agent
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
|
||||||
async def test_app():
|
|
||||||
"""Create Quart application test client"""
|
|
||||||
async with QuartClient(app) as client:
|
|
||||||
yield client
|
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------
|
|
||||||
# ✅ AgentscopeBrowseruseAgent Singleton Tests
|
|
||||||
# -----------------------------
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def agent_singleton_singleton_initialization(
|
|
||||||
agent_singleton, # pylint: disable=redefined-outer-name
|
|
||||||
):
|
|
||||||
"""Test agent singleton initialization"""
|
|
||||||
agent = agent_singleton # pylint: disable=redefined-outer-name
|
|
||||||
assert isinstance(agent, AgentscopeBrowseruseAgent)
|
|
||||||
assert hasattr(agent, "agent")
|
|
||||||
assert hasattr(agent, "runner")
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_chat_method(
|
|
||||||
agent_singleton,
|
|
||||||
): # pylint: disable=redefined-outer-name
|
|
||||||
"""Test chat method handles messages"""
|
|
||||||
mock_request = {
|
|
||||||
"messages": [
|
|
||||||
{"role": "user", "content": "Hello"},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
# ✅ Create mock object with object/status properties
|
|
||||||
mock_event = SimpleNamespace(
|
|
||||||
object="message",
|
|
||||||
status=agent_module.RunStatus.Completed,
|
|
||||||
content=[{"type": "text", "text": "Test response"}],
|
|
||||||
)
|
|
||||||
|
|
||||||
with patch.object(
|
|
||||||
agent_singleton.runner, # pylint: disable=redefined-outer-name
|
|
||||||
"stream_query",
|
|
||||||
) as mock_stream:
|
|
||||||
# ✅ Return object with properties
|
|
||||||
async def mock_stream_query(*_args, **_kwargs):
|
|
||||||
yield mock_event
|
|
||||||
|
|
||||||
mock_stream.side_effect = mock_stream_query
|
|
||||||
|
|
||||||
responses = []
|
|
||||||
async for response in agent_singleton.chat(
|
|
||||||
# pylint: disable=redefined-outer-name
|
|
||||||
mock_request["messages"],
|
|
||||||
):
|
|
||||||
responses.append(response)
|
|
||||||
|
|
||||||
assert len(responses) == 1
|
|
||||||
assert (
|
|
||||||
responses[0][0]["text"] == "Test response"
|
|
||||||
) # ✅ Fix property access
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import os
|
|
||||||
import time
|
|
||||||
import tempfile
|
|
||||||
import pytest
|
|
||||||
import conversational_agents.chatbot_fullstack_runtime.backend.web_server as ws
|
|
||||||
|
|
||||||
|
|
||||||
app = ws.app
|
|
||||||
_db = ws.db
|
|
||||||
User = ws.User
|
|
||||||
|
|
||||||
|
|
||||||
def generate_unique_username():
|
|
||||||
return f"testuser_{int(time.time())}"
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def client_and_username():
|
|
||||||
"""Create an Isolated Test Client and Username"""
|
|
||||||
db_fd, db_path = tempfile.mkstemp(suffix=".db")
|
|
||||||
app.config["SQLALCHEMY_DATABASE_URI"] = f"sqlite:///{db_path}"
|
|
||||||
app.config["TESTING"] = True
|
|
||||||
client = app.test_client()
|
|
||||||
|
|
||||||
with app.app_context():
|
|
||||||
_db.drop_all()
|
|
||||||
_db.create_all()
|
|
||||||
|
|
||||||
# Generate Unique Username
|
|
||||||
username = generate_unique_username()
|
|
||||||
password = "testpass"
|
|
||||||
user = User(username=username, name="Test User")
|
|
||||||
user.set_password(password)
|
|
||||||
_db.session.add(user)
|
|
||||||
_db.session.commit()
|
|
||||||
|
|
||||||
yield client, username, password
|
|
||||||
|
|
||||||
os.close(db_fd)
|
|
||||||
os.unlink(db_path)
|
|
||||||
|
|
||||||
|
|
||||||
def test_user_login_success(
|
|
||||||
# pylint: disable=redefined-outer-name
|
|
||||||
client_and_username,
|
|
||||||
):
|
|
||||||
"""Test Successful User Login"""
|
|
||||||
client, username, password = client_and_username
|
|
||||||
|
|
||||||
response = client.post(
|
|
||||||
"/api/login",
|
|
||||||
json={
|
|
||||||
"username": username,
|
|
||||||
"password": password,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert response.status_code == 200
|
|
||||||
data = response.get_json()
|
|
||||||
assert "id" in data
|
|
||||||
assert data["username"] == username
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from unittest.mock import AsyncMock
|
|
||||||
import pytest
|
|
||||||
from agentscope.message import Msg
|
|
||||||
from agentscope.agent import ReActAgent
|
|
||||||
from agentscope.tool import Toolkit
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
class TestReActAgent:
|
|
||||||
"""Test suite for the ReAct agent implementation"""
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def test_agent(self):
|
|
||||||
"""Fixture to create a test ReAct
|
|
||||||
agent with fully mocked dependencies"""
|
|
||||||
|
|
||||||
async def model_response():
|
|
||||||
yield Msg(
|
|
||||||
name="Friday",
|
|
||||||
content="Mocked model response",
|
|
||||||
role="assistant",
|
|
||||||
)
|
|
||||||
|
|
||||||
mock_model = AsyncMock()
|
|
||||||
mock_model.side_effect = model_response
|
|
||||||
|
|
||||||
mock_formatter = AsyncMock()
|
|
||||||
mock_formatter.format = AsyncMock(return_value="Mocked prompt")
|
|
||||||
|
|
||||||
mock_memory = AsyncMock()
|
|
||||||
mock_memory.get_memory = AsyncMock(return_value=[])
|
|
||||||
|
|
||||||
agent = ReActAgent(
|
|
||||||
name="Friday",
|
|
||||||
sys_prompt="You are a helpful assistant named Friday.",
|
|
||||||
model=mock_model,
|
|
||||||
formatter=mock_formatter,
|
|
||||||
toolkit=Toolkit(),
|
|
||||||
memory=mock_memory,
|
|
||||||
)
|
|
||||||
|
|
||||||
# pylint: disable=protected-access
|
|
||||||
agent._reasoning_hint_msgs = AsyncMock()
|
|
||||||
# pylint: disable=protected-access
|
|
||||||
agent._reasoning_hint_msgs.get_memory = AsyncMock(return_value=[])
|
|
||||||
|
|
||||||
return agent
|
|
||||||
|
|
||||||
async def test_exit_command(self, test_agent, monkeypatch):
|
|
||||||
"""Test exit command handling"""
|
|
||||||
|
|
||||||
async def exit_model_response(*_args, **_kwargs):
|
|
||||||
yield Msg(
|
|
||||||
name="Friday",
|
|
||||||
content="exit",
|
|
||||||
role="assistant",
|
|
||||||
)
|
|
||||||
|
|
||||||
test_agent.model.side_effect = exit_model_response
|
|
||||||
|
|
||||||
monkeypatch.setattr("builtins.input", lambda _: "exit")
|
|
||||||
|
|
||||||
msg = Msg(name="User", content="exit", role="user")
|
|
||||||
response = await test_agent(msg)
|
|
||||||
|
|
||||||
assert response.content == "exit"
|
|
||||||
|
|
||||||
async def test_conversation_flow(self, monkeypatch):
|
|
||||||
"""Test full conversation flow"""
|
|
||||||
|
|
||||||
async def model_response(*_args, **_kwargs):
|
|
||||||
yield Msg(
|
|
||||||
name="Friday",
|
|
||||||
content="Thought: I need to use a tool\n"
|
|
||||||
"Action: execute_shell_command\n"
|
|
||||||
"Action Input: echo 'Hello World'",
|
|
||||||
role="assistant",
|
|
||||||
)
|
|
||||||
|
|
||||||
mock_model = AsyncMock()
|
|
||||||
mock_model.side_effect = model_response
|
|
||||||
|
|
||||||
mock_formatter = AsyncMock()
|
|
||||||
mock_formatter.format = AsyncMock(return_value="Mocked prompt")
|
|
||||||
|
|
||||||
mock_memory = AsyncMock()
|
|
||||||
mock_memory.get_memory = AsyncMock(return_value=[])
|
|
||||||
|
|
||||||
agent = ReActAgent(
|
|
||||||
name="Friday",
|
|
||||||
sys_prompt="You are a helpful assistant named Friday.",
|
|
||||||
model=mock_model,
|
|
||||||
formatter=mock_formatter,
|
|
||||||
toolkit=Toolkit(),
|
|
||||||
memory=mock_memory,
|
|
||||||
)
|
|
||||||
|
|
||||||
monkeypatch.setattr("builtins.input", lambda _: "Test command")
|
|
||||||
|
|
||||||
msg = Msg(name="User", content="Test command", role="user")
|
|
||||||
response = await agent(msg)
|
|
||||||
assert "Thought:" in response.content
|
|
||||||
@@ -1,248 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import os
|
|
||||||
from unittest.mock import AsyncMock, Mock, patch
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
from agentscope.agent import ReActAgent
|
|
||||||
from agentscope.model import DashScopeChatModel
|
|
||||||
from agentscope.tool import Toolkit
|
|
||||||
from agentscope.message import Msg
|
|
||||||
from agentscope.formatter import DashScopeChatFormatter
|
|
||||||
from agentscope.memory import InMemoryMemory
|
|
||||||
from agentscope.tool import (
|
|
||||||
view_text_file,
|
|
||||||
write_text_file,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Import the main function and related components
|
|
||||||
from data_juicer_agent.main import main
|
|
||||||
from data_juicer_agent.agent_factory import create_agent
|
|
||||||
from data_juicer_agent.tools import (
|
|
||||||
dj_toolkit,
|
|
||||||
dj_dev_toolkit,
|
|
||||||
dj_tools,
|
|
||||||
dj_dev_tools,
|
|
||||||
mcp_tools,
|
|
||||||
get_mcp_toolkit,
|
|
||||||
execute_safe_command,
|
|
||||||
query_dj_operators,
|
|
||||||
get_basic_files,
|
|
||||||
get_operator_example,
|
|
||||||
configure_data_juicer_path,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_toolkit():
|
|
||||||
"""Create a mocked Toolkit instance"""
|
|
||||||
return Mock(spec=Toolkit)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_model():
|
|
||||||
"""Create a mocked DashScopeChatModel"""
|
|
||||||
model = Mock(spec=DashScopeChatModel)
|
|
||||||
model.call = AsyncMock(
|
|
||||||
return_value=Msg("assistant", "test response", role="assistant"),
|
|
||||||
)
|
|
||||||
return model
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_formatter():
|
|
||||||
"""Create a mocked DashScopeChatFormatter"""
|
|
||||||
return Mock(spec=DashScopeChatFormatter)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_memory():
|
|
||||||
"""Create a mocked InMemoryMemory"""
|
|
||||||
return Mock(spec=InMemoryMemory)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_mcp_client():
|
|
||||||
"""Create a mocked MCP client"""
|
|
||||||
mock_client = Mock()
|
|
||||||
mock_client.name = "DJ_recipe_flow"
|
|
||||||
mock_client.connect = AsyncMock()
|
|
||||||
mock_client.close = AsyncMock()
|
|
||||||
mock_client.get_callable_function = AsyncMock()
|
|
||||||
mock_client.list_tools = AsyncMock()
|
|
||||||
return mock_client
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_agent(
|
|
||||||
mock_model, # pylint: disable=redefined-outer-name
|
|
||||||
mock_formatter, # pylint: disable=redefined-outer-name
|
|
||||||
mock_toolkit, # pylint: disable=redefined-outer-name
|
|
||||||
mock_memory, # pylint: disable=redefined-outer-name
|
|
||||||
):
|
|
||||||
"""Create a mocked ReActAgent instance"""
|
|
||||||
agent = Mock(spec=ReActAgent)
|
|
||||||
agent.model = mock_model
|
|
||||||
agent.formatter = mock_formatter
|
|
||||||
agent.toolkit = mock_toolkit
|
|
||||||
agent.memory = mock_memory
|
|
||||||
agent.__call__ = AsyncMock(
|
|
||||||
return_value=Msg("assistant", "test response", role="assistant"),
|
|
||||||
)
|
|
||||||
return agent
|
|
||||||
|
|
||||||
|
|
||||||
class TestDataJuicerAgent:
|
|
||||||
"""Test suite for the data_juicer_agent functionality"""
|
|
||||||
|
|
||||||
def named_mock_agent(
|
|
||||||
self,
|
|
||||||
name,
|
|
||||||
mock_agent, # pylint: disable=redefined-outer-name
|
|
||||||
):
|
|
||||||
"""Create a named mock agent for testing"""
|
|
||||||
agent_instance = Mock(spec=ReActAgent)
|
|
||||||
agent_instance.model = mock_agent.model
|
|
||||||
agent_instance.formatter = mock_agent.formatter
|
|
||||||
agent_instance.toolkit = mock_agent.toolkit
|
|
||||||
agent_instance.memory = mock_agent.memory
|
|
||||||
agent_instance.__call__ = mock_agent.__call__
|
|
||||||
agent_instance.name = name
|
|
||||||
return agent_instance
|
|
||||||
|
|
||||||
def _named_mock_agent_side_effect(
|
|
||||||
self,
|
|
||||||
mock_agent, # pylint: disable=redefined-outer-name
|
|
||||||
):
|
|
||||||
"""Side effect function for creating named mock agents"""
|
|
||||||
return lambda name, *args, **kwargs: self.named_mock_agent(
|
|
||||||
name,
|
|
||||||
mock_agent,
|
|
||||||
*args,
|
|
||||||
**kwargs,
|
|
||||||
)
|
|
||||||
|
|
||||||
async def mock_user_func(self):
|
|
||||||
return Msg("user", "exit", role="user")
|
|
||||||
|
|
||||||
def test_dj_toolkit_initialization(self):
|
|
||||||
"""Test DJ toolkit initialization and tool registration"""
|
|
||||||
assert dj_toolkit.tools.get("execute_safe_command") is not None
|
|
||||||
assert dj_toolkit.tools.get("view_text_file") is not None
|
|
||||||
assert dj_toolkit.tools.get("write_text_file") is not None
|
|
||||||
assert dj_toolkit.tools.get("query_dj_operators") is not None
|
|
||||||
|
|
||||||
# Verify tool list contains expected tools
|
|
||||||
expected_tools = [
|
|
||||||
execute_safe_command,
|
|
||||||
view_text_file,
|
|
||||||
write_text_file,
|
|
||||||
query_dj_operators,
|
|
||||||
]
|
|
||||||
assert len(dj_tools) == len(expected_tools)
|
|
||||||
for tool in expected_tools:
|
|
||||||
assert tool in dj_tools
|
|
||||||
|
|
||||||
def test_dj_dev_toolkit_initialization(self):
|
|
||||||
"""Test DJ development toolkit initialization and tool registration"""
|
|
||||||
assert dj_dev_toolkit.tools.get("view_text_file") is not None
|
|
||||||
assert dj_dev_toolkit.tools.get("write_text_file") is not None
|
|
||||||
assert dj_dev_toolkit.tools.get("get_basic_files") is not None
|
|
||||||
assert dj_dev_toolkit.tools.get("get_operator_example") is not None
|
|
||||||
assert (
|
|
||||||
dj_dev_toolkit.tools.get("configure_data_juicer_path") is not None
|
|
||||||
)
|
|
||||||
|
|
||||||
# Verify tool list contains expected tools
|
|
||||||
expected_tools = [
|
|
||||||
view_text_file,
|
|
||||||
write_text_file,
|
|
||||||
get_basic_files,
|
|
||||||
get_operator_example,
|
|
||||||
configure_data_juicer_path,
|
|
||||||
]
|
|
||||||
assert len(dj_dev_tools) == len(expected_tools)
|
|
||||||
for tool in expected_tools:
|
|
||||||
assert tool in dj_dev_tools
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_mcp_tools_list(
|
|
||||||
self,
|
|
||||||
mock_mcp_client, # pylint: disable=redefined-outer-name
|
|
||||||
):
|
|
||||||
"""Test MCP tools list contains expected tools"""
|
|
||||||
with patch(
|
|
||||||
"agentscope.mcp.HttpStatefulClient",
|
|
||||||
return_value=mock_mcp_client,
|
|
||||||
) as mock_client_cls:
|
|
||||||
await get_mcp_toolkit()
|
|
||||||
assert mock_client_cls.assert_called_once
|
|
||||||
|
|
||||||
expected_tools = [view_text_file, write_text_file]
|
|
||||||
assert len(mcp_tools) == len(expected_tools)
|
|
||||||
for tool in expected_tools:
|
|
||||||
assert tool in mcp_tools
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_agent_initialization(
|
|
||||||
self,
|
|
||||||
mock_model, # pylint: disable=redefined-outer-name
|
|
||||||
mock_formatter, # pylint: disable=redefined-outer-name
|
|
||||||
mock_toolkit, # pylint: disable=redefined-outer-name
|
|
||||||
mock_memory, # pylint: disable=redefined-outer-name
|
|
||||||
):
|
|
||||||
"""Test ReActAgent initialization"""
|
|
||||||
with patch.dict(os.environ, {"DASHSCOPE_API_KEY": "test_key"}):
|
|
||||||
agent = create_agent(
|
|
||||||
name="DataJuicer",
|
|
||||||
sys_prompt="You are {name}, a agent.",
|
|
||||||
toolkit=mock_toolkit,
|
|
||||||
description="test description",
|
|
||||||
model=mock_model,
|
|
||||||
formatter=mock_formatter,
|
|
||||||
memory=mock_memory,
|
|
||||||
)
|
|
||||||
|
|
||||||
assert agent.name == "DataJuicer"
|
|
||||||
assert "DataJuicer" in agent.sys_prompt
|
|
||||||
assert "test" in agent.__doc__
|
|
||||||
assert agent.model == mock_model
|
|
||||||
assert agent.formatter == mock_formatter
|
|
||||||
assert agent.toolkit == mock_toolkit
|
|
||||||
assert agent.memory == mock_memory
|
|
||||||
assert isinstance(agent, ReActAgent)
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_main_with_multiple_agents_loading(
|
|
||||||
self,
|
|
||||||
mock_agent, # pylint: disable=redefined-outer-name
|
|
||||||
mock_mcp_client, # pylint: disable=redefined-outer-name
|
|
||||||
):
|
|
||||||
"""Test main function loads multiple agents successfully"""
|
|
||||||
with patch.dict(os.environ, {"DASHSCOPE_API_KEY": "test_key"}):
|
|
||||||
mock_mcp_clients = [mock_mcp_client]
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"data_juicer_agent.tools.mcp_helpers._create_clients",
|
|
||||||
return_value=mock_mcp_clients,
|
|
||||||
):
|
|
||||||
with patch(
|
|
||||||
"data_juicer_agent.main.create_agent",
|
|
||||||
side_effect=self._named_mock_agent_side_effect(mock_agent),
|
|
||||||
) as mock_create_agent:
|
|
||||||
with patch(
|
|
||||||
"data_juicer_agent.main.user",
|
|
||||||
side_effect=self.mock_user_func,
|
|
||||||
):
|
|
||||||
await main(
|
|
||||||
use_studio=False,
|
|
||||||
available_agents=["dj", "dj_dev", "dj_mcp"],
|
|
||||||
retrieval_mode="auto",
|
|
||||||
)
|
|
||||||
|
|
||||||
# Validate multiple agents are correctly created
|
|
||||||
# (dj, dj_dev, dj_mcp, and router)
|
|
||||||
assert mock_create_agent.call_count == 4
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
pytest.main(["-v", __file__])
|
|
||||||
@@ -1,163 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
import os
|
|
||||||
from unittest.mock import Mock, AsyncMock, patch
|
|
||||||
from typing import List, Dict, Any, Tuple, Callable
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
from agentscope.evaluate import Task, ACEPhone, ACEBenchmark
|
|
||||||
|
|
||||||
# Import the main module from the correct path
|
|
||||||
from evaluation.ace_bench import main as ace_main
|
|
||||||
|
|
||||||
|
|
||||||
class TestReActAgentSolution:
|
|
||||||
"""Test suite for the ReAct agent solution function"""
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_task(self) -> Task:
|
|
||||||
"""Create a mock ACEBench task"""
|
|
||||||
task = Mock(spec=Task)
|
|
||||||
task.input = "Test input query"
|
|
||||||
task.metadata = {
|
|
||||||
"tools": self._create_mock_tools(),
|
|
||||||
"phone": Mock(spec=ACEPhone),
|
|
||||||
}
|
|
||||||
return task
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_pre_hook(self) -> Mock:
|
|
||||||
"""Create a mock pre-hook function that returns None"""
|
|
||||||
|
|
||||||
def pre_hook_return():
|
|
||||||
"""Mock function that returns None (no modifications)"""
|
|
||||||
return None
|
|
||||||
|
|
||||||
mock = Mock()
|
|
||||||
mock.__name__ = "save_logging"
|
|
||||||
mock.side_effect = (
|
|
||||||
pre_hook_return # ✅ Return None to avoid parameter pollution
|
|
||||||
)
|
|
||||||
return mock
|
|
||||||
|
|
||||||
def _create_mock_tools(self) -> List[Tuple[Callable, Dict[str, Any]]]:
|
|
||||||
"""Create mock tool functions with schemas"""
|
|
||||||
|
|
||||||
def mock_tool():
|
|
||||||
return "tool_response"
|
|
||||||
|
|
||||||
tool_schema = {
|
|
||||||
"type": "function",
|
|
||||||
"function": {
|
|
||||||
"name": "mock_tool",
|
|
||||||
"description": "A mock tool for testing",
|
|
||||||
"parameters": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"param1": {"type": "string"},
|
|
||||||
"param2": {"type": "number"},
|
|
||||||
},
|
|
||||||
"required": ["param1"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return [(mock_tool, tool_schema)]
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_error_handling(
|
|
||||||
self,
|
|
||||||
mock_task: Task,
|
|
||||||
mock_pre_hook: Mock,
|
|
||||||
) -> None:
|
|
||||||
"""Test error handling in the solution function"""
|
|
||||||
with patch.dict(os.environ, {"DASHSCOPE_API_KEY": "test_key"}):
|
|
||||||
# Mock a failure case
|
|
||||||
with patch(
|
|
||||||
"evaluation.ace_bench.main.Toolkit.register_tool_function",
|
|
||||||
side_effect=Exception("Registration error"),
|
|
||||||
):
|
|
||||||
with pytest.raises(Exception) as exc_info:
|
|
||||||
await ace_main.react_agent_solution(
|
|
||||||
mock_task,
|
|
||||||
mock_pre_hook,
|
|
||||||
)
|
|
||||||
|
|
||||||
assert "Registration error" in str(exc_info.value)
|
|
||||||
|
|
||||||
|
|
||||||
class TestMainFunction:
|
|
||||||
"""Test suite for the main function"""
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_args(self, tmpdir) -> Mock:
|
|
||||||
"""Create mock command-line arguments with temporary directories"""
|
|
||||||
args = Mock()
|
|
||||||
args.data_dir = str(tmpdir / "data")
|
|
||||||
args.result_dir = str(tmpdir / "results")
|
|
||||||
args.n_workers = 2
|
|
||||||
return args
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_evaluator_initialization(self, mock_args: Mock) -> None:
|
|
||||||
"""Test evaluator initialization"""
|
|
||||||
with patch(
|
|
||||||
"evaluation.ace_bench.main.ArgumentParser.parse_args",
|
|
||||||
return_value=mock_args,
|
|
||||||
):
|
|
||||||
with patch(
|
|
||||||
"evaluation.ace_bench.main.RayEvaluator",
|
|
||||||
) as mock_evaluator_class:
|
|
||||||
mock_evaluator = AsyncMock()
|
|
||||||
mock_evaluator_class.return_value = mock_evaluator
|
|
||||||
|
|
||||||
# ✅ Simulate _download_data and _load_data
|
|
||||||
with patch(
|
|
||||||
"agentscope.evaluate._ace_benchmark."
|
|
||||||
"_ace_benchmark.ACEBenchmark._download_data",
|
|
||||||
):
|
|
||||||
with patch(
|
|
||||||
"agentscope.evaluate._ace_benchmark."
|
|
||||||
"_ace_benchmark.ACEBenchmark._load_data",
|
|
||||||
return_value=[],
|
|
||||||
):
|
|
||||||
# Run main function
|
|
||||||
await ace_main.main()
|
|
||||||
|
|
||||||
# Verify evaluator initialization
|
|
||||||
mock_evaluator_class.assert_called_once()
|
|
||||||
call_args = mock_evaluator_class.call_args[1]
|
|
||||||
assert call_args["n_workers"] == 2
|
|
||||||
assert isinstance(call_args["benchmark"], ACEBenchmark)
|
|
||||||
assert call_args["benchmark"].data_dir == mock_args.data_dir
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_evaluation_execution(self, mock_args: Mock) -> None:
|
|
||||||
"""Test evaluation execution"""
|
|
||||||
with patch(
|
|
||||||
"evaluation.ace_bench.main.ArgumentParser.parse_args",
|
|
||||||
return_value=mock_args,
|
|
||||||
):
|
|
||||||
with patch(
|
|
||||||
"evaluation.ace_bench.main.RayEvaluator",
|
|
||||||
) as mock_evaluator_class:
|
|
||||||
mock_evaluator = AsyncMock()
|
|
||||||
mock_evaluator.run = AsyncMock()
|
|
||||||
mock_evaluator_class.return_value = mock_evaluator
|
|
||||||
|
|
||||||
# ✅ Simulate _download_data and _load_data
|
|
||||||
with patch(
|
|
||||||
"agentscope.evaluate._ace_benchmark._ace_benchmark."
|
|
||||||
"ACEBenchmark._download_data",
|
|
||||||
):
|
|
||||||
with patch(
|
|
||||||
"agentscope.evaluate._ace_benchmark."
|
|
||||||
"_ace_benchmark.ACEBenchmark._load_data",
|
|
||||||
return_value=[],
|
|
||||||
):
|
|
||||||
# Run main function
|
|
||||||
await ace_main.main()
|
|
||||||
|
|
||||||
# Verify evaluation execution
|
|
||||||
mock_evaluator.run.assert_called_once_with(
|
|
||||||
ace_main.react_agent_solution,
|
|
||||||
)
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from unittest.mock import AsyncMock, patch, MagicMock
|
|
||||||
import pytest
|
|
||||||
from agentscope.agent import ReActAgent
|
|
||||||
from agentscope.model import ChatModelBase
|
|
||||||
from agentscope.formatter import FormatterBase
|
|
||||||
|
|
||||||
# Import modules to test
|
|
||||||
from games.game_werewolves import game, utils, structured_model
|
|
||||||
|
|
||||||
|
|
||||||
class HunterModelMock:
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
self._data = {
|
|
||||||
"name": kwargs.get("name", None),
|
|
||||||
"shoot": kwargs.get("shoot", False),
|
|
||||||
}
|
|
||||||
self.metadata = {"shoot": self._data["name"] is not None}
|
|
||||||
|
|
||||||
def model_dump(self):
|
|
||||||
return self._data
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self):
|
|
||||||
return self._data["name"]
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_werewolves_discussion() -> None:
|
|
||||||
mock_hub = AsyncMock()
|
|
||||||
mock_hub.__aenter__.return_value = mock_hub
|
|
||||||
mock_hub.__aexit__.return_value = AsyncMock()
|
|
||||||
|
|
||||||
with patch("games.game_werewolves.game.MsgHub", return_value=mock_hub):
|
|
||||||
mock_agent = AsyncMock()
|
|
||||||
mock_agent.name = "Player1"
|
|
||||||
|
|
||||||
agents = [mock_agent for _ in range(9)]
|
|
||||||
await game.werewolves_game(agents)
|
|
||||||
assert True
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_witch_resurrect() -> None:
|
|
||||||
async def mock_model(**kwargs):
|
|
||||||
return {"resurrect": kwargs.get("resurrect", False)}
|
|
||||||
|
|
||||||
with patch(
|
|
||||||
"games.game_werewolves.game.WitchResurrectModel",
|
|
||||||
side_effect=mock_model,
|
|
||||||
):
|
|
||||||
result = await game.WitchResurrectModel(**{"resurrect": True})
|
|
||||||
assert result["resurrect"] is True
|
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------
|
|
||||||
# Test: utils.py
|
|
||||||
# -----------------------------
|
|
||||||
def test_majority_vote() -> None:
|
|
||||||
votes = ["Player1", "Player1", "Player2"]
|
|
||||||
result, _ = utils.majority_vote(votes)
|
|
||||||
assert result == "Player1"
|
|
||||||
|
|
||||||
|
|
||||||
def test_names_to_str_single() -> None:
|
|
||||||
assert utils.names_to_str(["Player1"]) == "Player1"
|
|
||||||
|
|
||||||
|
|
||||||
def test_players_role_mapping() -> None:
|
|
||||||
players = utils.Players()
|
|
||||||
mock_agent = utils.EchoAgent()
|
|
||||||
mock_agent.name = "Player1"
|
|
||||||
|
|
||||||
players.add_player(mock_agent, "werewolf")
|
|
||||||
assert players.name_to_role["Player1"] == "werewolf"
|
|
||||||
assert len(players.werewolves) == 1
|
|
||||||
|
|
||||||
|
|
||||||
def test_vote_model_generation() -> None:
|
|
||||||
mock_model = MagicMock(spec=ChatModelBase)
|
|
||||||
mock_formatter = MagicMock(spec=FormatterBase)
|
|
||||||
|
|
||||||
agents = [
|
|
||||||
ReActAgent(
|
|
||||||
name=f"Player{i}",
|
|
||||||
sys_prompt=f"Vote system prompt {i}",
|
|
||||||
model=mock_model,
|
|
||||||
formatter=mock_formatter,
|
|
||||||
)
|
|
||||||
for i in range(3)
|
|
||||||
]
|
|
||||||
|
|
||||||
VoteModel = structured_model.get_vote_model(agents)
|
|
||||||
assert "vote" in VoteModel.model_fields
|
|
||||||
assert (
|
|
||||||
VoteModel.model_fields["vote"].description
|
|
||||||
== "The name of the player you want to vote for"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_witch_poison_model_fields() -> None:
|
|
||||||
mock_model = MagicMock(spec=ChatModelBase)
|
|
||||||
mock_formatter = MagicMock(spec=FormatterBase)
|
|
||||||
|
|
||||||
agents = [
|
|
||||||
ReActAgent(
|
|
||||||
name="Player1",
|
|
||||||
sys_prompt="Poison system prompt",
|
|
||||||
model=mock_model,
|
|
||||||
formatter=mock_formatter,
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
||||||
PoisonModel = structured_model.get_poison_model(agents)
|
|
||||||
assert "poison" in PoisonModel.model_fields
|
|
||||||
assert "name" in PoisonModel.model_fields
|
|
||||||
Reference in New Issue
Block a user