|
3 | 3 | import os |
4 | 4 | from pathlib import Path |
5 | 5 | from typing import Any, cast |
| 6 | +from unittest.mock import AsyncMock |
6 | 7 |
|
7 | 8 | import pytest |
8 | 9 | import yaml |
9 | 10 |
|
| 11 | +from astrbot.core.agent.tool import FunctionTool |
10 | 12 | from astrbot.core.star import star_manager as star_manager_module |
11 | 13 | from astrbot.core.star.star_manager import PluginDependencyInstallError, PluginManager |
12 | 14 | from astrbot.core.utils.pip_installer import PipInstallError |
@@ -162,6 +164,120 @@ def _clear_star_runtime_state(): |
162 | 164 | star_manager_module.star_handlers_registry.clear() |
163 | 165 |
|
164 | 166 |
|
| 167 | +def _plugin_toggle_manager(plugin): |
| 168 | + manager = object.__new__(PluginManager) |
| 169 | + manager.context = type( |
| 170 | + "Context", |
| 171 | + (), |
| 172 | + {"get_registered_star": lambda self, name: plugin if name == plugin.name else None}, |
| 173 | + )() |
| 174 | + manager._pm_lock = asyncio.Lock() |
| 175 | + manager._terminate_plugin = AsyncMock() |
| 176 | + manager.reload = AsyncMock(return_value=(True, "")) |
| 177 | + return manager |
| 178 | + |
| 179 | + |
| 180 | +def _tool(name: str, module_path: str, active: bool = True) -> FunctionTool: |
| 181 | + return FunctionTool( |
| 182 | + name=name, |
| 183 | + description=name, |
| 184 | + parameters={"type": "object", "properties": {}}, |
| 185 | + handler_module_path=module_path, |
| 186 | + active=active, |
| 187 | + ) |
| 188 | + |
| 189 | + |
| 190 | +@pytest.fixture |
| 191 | +def plugin_toggle_preferences(monkeypatch): |
| 192 | + values = { |
| 193 | + "inactivated_plugins": [], |
| 194 | + "inactivated_llm_tools": [], |
| 195 | + } |
| 196 | + |
| 197 | + async def global_get(key, default): |
| 198 | + return values.get(key, default) |
| 199 | + |
| 200 | + async def global_put(key, value): |
| 201 | + values[key] = value |
| 202 | + |
| 203 | + monkeypatch.setattr(star_manager_module.sp, "global_get", global_get) |
| 204 | + monkeypatch.setattr(star_manager_module.sp, "global_put", global_put) |
| 205 | + return values |
| 206 | + |
| 207 | + |
| 208 | +@pytest.mark.asyncio |
| 209 | +async def test_turn_off_plugin_deactivates_tools_under_plugin_module( |
| 210 | + monkeypatch, |
| 211 | + plugin_toggle_preferences, |
| 212 | +): |
| 213 | + plugin = star_manager_module.StarMetadata( |
| 214 | + name="demo", |
| 215 | + module_path="data.plugins.demo.main", |
| 216 | + activated=True, |
| 217 | + ) |
| 218 | + plugin_tool = _tool( |
| 219 | + "demo_tool", |
| 220 | + "data.plugins.demo.main.tools", |
| 221 | + ) |
| 222 | + other_tool = _tool( |
| 223 | + "other_tool", |
| 224 | + "data.plugins.other.main.tools", |
| 225 | + ) |
| 226 | + monkeypatch.setattr( |
| 227 | + star_manager_module.llm_tools, |
| 228 | + "func_list", |
| 229 | + [plugin_tool, other_tool], |
| 230 | + ) |
| 231 | + manager = _plugin_toggle_manager(plugin) |
| 232 | + |
| 233 | + await manager.turn_off_plugin("demo") |
| 234 | + |
| 235 | + assert plugin_tool.active is False |
| 236 | + assert other_tool.active is True |
| 237 | + assert plugin_toggle_preferences["inactivated_plugins"] == [ |
| 238 | + "data.plugins.demo.main" |
| 239 | + ] |
| 240 | + assert plugin_toggle_preferences["inactivated_llm_tools"] == ["demo_tool"] |
| 241 | + |
| 242 | + |
| 243 | +@pytest.mark.asyncio |
| 244 | +async def test_turn_on_plugin_reactivates_tools_under_plugin_module( |
| 245 | + monkeypatch, |
| 246 | + plugin_toggle_preferences, |
| 247 | +): |
| 248 | + plugin = star_manager_module.StarMetadata( |
| 249 | + name="demo", |
| 250 | + module_path="data.plugins.demo.main", |
| 251 | + activated=False, |
| 252 | + ) |
| 253 | + plugin_toggle_preferences["inactivated_plugins"] = ["data.plugins.demo.main"] |
| 254 | + plugin_toggle_preferences["inactivated_llm_tools"] = ["demo_tool", "manual_tool"] |
| 255 | + plugin_tool = _tool( |
| 256 | + "demo_tool", |
| 257 | + "data.plugins.demo.main.tools", |
| 258 | + active=False, |
| 259 | + ) |
| 260 | + manual_tool = _tool( |
| 261 | + "manual_tool", |
| 262 | + "data.plugins.other.main.tools", |
| 263 | + active=False, |
| 264 | + ) |
| 265 | + monkeypatch.setattr( |
| 266 | + star_manager_module.llm_tools, |
| 267 | + "func_list", |
| 268 | + [plugin_tool, manual_tool], |
| 269 | + ) |
| 270 | + manager = _plugin_toggle_manager(plugin) |
| 271 | + |
| 272 | + await manager.turn_on_plugin("demo") |
| 273 | + |
| 274 | + assert plugin_tool.active is True |
| 275 | + assert manual_tool.active is False |
| 276 | + assert plugin_toggle_preferences["inactivated_plugins"] == [] |
| 277 | + assert plugin_toggle_preferences["inactivated_llm_tools"] == ["manual_tool"] |
| 278 | + manager.reload.assert_awaited_once_with("demo") |
| 279 | + |
| 280 | + |
165 | 281 | def _build_load_mock(events): |
166 | 282 | async def mock_load(specified_dir_name=None, ignore_version_check=False): |
167 | 283 | del ignore_version_check |
|
0 commit comments