mirror of
https://github.com/AstrBotDevs/AstrBot
synced 2026-07-17 01:49:15 +08:00
- ToolSet.add() now protects active tools from inactive overrides - Add missing _has_meaningful_content() method in RespondStage - Remove is_local=True from ExecuteShellTool (no longer supported) - Fix ToolSet() missing namespace argument in get_full_tool_set() - Rewrite tests to match new tool architecture (cron_tools, func_tool_manager, tool_conflict_resolution)
180 lines
5.1 KiB
Python
180 lines
5.1 KiB
Python
"""Tests for cron tool metadata."""
|
|
|
|
from types import SimpleNamespace
|
|
from unittest.mock import AsyncMock
|
|
|
|
import pytest
|
|
|
|
from astrbot.core.tools.cron_tools import (
|
|
CreateActiveCronTool,
|
|
DeleteCronJobTool,
|
|
ListCronJobsTool,
|
|
)
|
|
|
|
|
|
def test_create_future_task_tool_has_correct_name():
|
|
"""The create tool should have correct name."""
|
|
tool = CreateActiveCronTool()
|
|
|
|
assert tool.name == "create_future_task"
|
|
|
|
|
|
def test_create_future_task_tool_requires_note():
|
|
"""The create tool should require note field."""
|
|
tool = CreateActiveCronTool()
|
|
|
|
assert "note" in tool.parameters["required"]
|
|
|
|
|
|
def test_create_future_task_tool_has_cron_guidance():
|
|
"""The create tool should have cron guidance in description."""
|
|
tool = CreateActiveCronTool()
|
|
|
|
description = tool.parameters["properties"]["cron_expression"]["description"]
|
|
|
|
assert "mon-fri" in description
|
|
assert "sat,sun" in description
|
|
|
|
|
|
def test_delete_future_task_tool_has_job_id_required():
|
|
"""The delete tool should require job_id."""
|
|
tool = DeleteCronJobTool()
|
|
|
|
assert tool.name == "delete_future_task"
|
|
assert "job_id" in tool.parameters["required"]
|
|
|
|
|
|
def test_list_future_tasks_tool_name():
|
|
"""The list tool should have correct name."""
|
|
tool = ListCronJobsTool()
|
|
|
|
assert tool.name == "list_future_tasks"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_delete_future_task_requires_job_id():
|
|
"""Delete should require job_id."""
|
|
tool = DeleteCronJobTool()
|
|
context = SimpleNamespace(
|
|
context=SimpleNamespace(
|
|
context=SimpleNamespace(cron_manager=SimpleNamespace()),
|
|
event=SimpleNamespace(
|
|
unified_msg_origin="test:private:session",
|
|
get_sender_id=lambda: "user-1",
|
|
),
|
|
)
|
|
)
|
|
|
|
result = await tool.call(context, job_id=None)
|
|
|
|
assert "job_id is required" in result
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_delete_future_task_verifies_ownership():
|
|
"""Delete should verify the job belongs to current umo."""
|
|
tool = DeleteCronJobTool()
|
|
cron_mgr = SimpleNamespace(
|
|
db=SimpleNamespace(get_cron_job=AsyncMock(return_value=SimpleNamespace(
|
|
job_id="job-1",
|
|
name="test job",
|
|
payload={"session": "other:private:session"},
|
|
))),
|
|
)
|
|
context = SimpleNamespace(
|
|
context=SimpleNamespace(
|
|
context=SimpleNamespace(cron_manager=cron_mgr),
|
|
event=SimpleNamespace(
|
|
unified_msg_origin="test:private:session",
|
|
get_sender_id=lambda: "user-1",
|
|
),
|
|
)
|
|
)
|
|
|
|
result = await tool.call(context, job_id="job-1")
|
|
|
|
assert "only delete" in result
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_delete_future_task_success():
|
|
"""Delete should successfully delete job and return message."""
|
|
tool = DeleteCronJobTool()
|
|
cron_mgr = SimpleNamespace(
|
|
db=SimpleNamespace(get_cron_job=AsyncMock(return_value=SimpleNamespace(
|
|
job_id="job-1",
|
|
name="test job",
|
|
payload={"session": "test:private:session"},
|
|
))),
|
|
delete_job=AsyncMock(return_value=True),
|
|
)
|
|
context = SimpleNamespace(
|
|
context=SimpleNamespace(
|
|
context=SimpleNamespace(cron_manager=cron_mgr),
|
|
event=SimpleNamespace(
|
|
unified_msg_origin="test:private:session",
|
|
get_sender_id=lambda: "user-1",
|
|
),
|
|
)
|
|
)
|
|
|
|
result = await tool.call(context, job_id="job-1")
|
|
|
|
cron_mgr.delete_job.assert_awaited_once_with("job-1")
|
|
assert "Deleted cron job job-1" in result
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_list_future_tasks_returns_jobs():
|
|
"""List should return formatted job list."""
|
|
tool = ListCronJobsTool()
|
|
cron_mgr = SimpleNamespace(
|
|
list_jobs=AsyncMock(return_value=[
|
|
SimpleNamespace(
|
|
job_id="job-1",
|
|
name="test job",
|
|
job_type="active_agent",
|
|
run_once=False,
|
|
enabled=True,
|
|
next_run_time="2026-04-16T08:00:00",
|
|
payload={"session": "test:private:session"},
|
|
),
|
|
]),
|
|
)
|
|
context = SimpleNamespace(
|
|
context=SimpleNamespace(
|
|
context=SimpleNamespace(cron_manager=cron_mgr),
|
|
event=SimpleNamespace(
|
|
unified_msg_origin="test:private:session",
|
|
get_sender_id=lambda: "user-1",
|
|
),
|
|
)
|
|
)
|
|
|
|
result = await tool.call(context)
|
|
|
|
assert "job-1" in result
|
|
assert "test job" in result
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_list_future_tasks_empty():
|
|
"""List should return message when no jobs."""
|
|
tool = ListCronJobsTool()
|
|
cron_mgr = SimpleNamespace(
|
|
list_jobs=AsyncMock(return_value=[]),
|
|
)
|
|
context = SimpleNamespace(
|
|
context=SimpleNamespace(
|
|
context=SimpleNamespace(cron_manager=cron_mgr),
|
|
event=SimpleNamespace(
|
|
unified_msg_origin="test:private:session",
|
|
get_sender_id=lambda: "user-1",
|
|
),
|
|
)
|
|
)
|
|
|
|
result = await tool.call(context)
|
|
|
|
assert "No cron jobs found" in result
|