mirror of
https://github.com/NCBM/plyngent.git
synced 2026-07-25 08:04:57 +08:00
106 lines
3.6 KiB
Python
106 lines
3.6 KiB
Python
|
|
"""CLI ``plyngent plugins`` and slash ``/plugins``."""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from typing import TYPE_CHECKING
|
||
|
|
from unittest.mock import patch
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
import tomlkit
|
||
|
|
from click.testing import CliRunner
|
||
|
|
|
||
|
|
from plyngent.cli.app import main
|
||
|
|
from plyngent.cli.slash import handle_slash
|
||
|
|
from plyngent.cli.state import ReplState
|
||
|
|
from plyngent.config.models import DatabaseConfig, OpenAIProvider
|
||
|
|
from plyngent.config.store import ConfigStore
|
||
|
|
from plyngent.memory import MemoryStore
|
||
|
|
from plyngent.tools.plugins import DiscoveredPlugin, PluginStatus
|
||
|
|
|
||
|
|
if TYPE_CHECKING:
|
||
|
|
from collections.abc import AsyncIterator
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.fixture
|
||
|
|
async def state(tmp_path: Path) -> AsyncIterator[ReplState]:
|
||
|
|
memory = await MemoryStore.open(DatabaseConfig())
|
||
|
|
provider = OpenAIProvider(access_key_or_token="sk-test")
|
||
|
|
config = ConfigStore(path=tmp_path / "plyngent.toml", document=tomlkit.document())
|
||
|
|
config.providers = {"local": provider}
|
||
|
|
st = ReplState(
|
||
|
|
config=config,
|
||
|
|
memory=memory,
|
||
|
|
workspace=tmp_path,
|
||
|
|
provider_name="local",
|
||
|
|
provider=provider,
|
||
|
|
model="gpt-test",
|
||
|
|
tools_enabled=False,
|
||
|
|
)
|
||
|
|
# ReplState builds a real client; slash plugin tests only need config + agent.
|
||
|
|
await st.new_session("t")
|
||
|
|
yield st
|
||
|
|
await memory.close()
|
||
|
|
|
||
|
|
|
||
|
|
def _fake_statuses(*, enable: list[str], disable: list[str]) -> list[PluginStatus]:
|
||
|
|
del enable, disable
|
||
|
|
return [
|
||
|
|
PluginStatus(
|
||
|
|
plugin=DiscoveredPlugin(id="acme", value="acme_pkg:load", package="acme-pkg", version="1.0"),
|
||
|
|
enabled=True,
|
||
|
|
disabled=False,
|
||
|
|
),
|
||
|
|
PluginStatus(
|
||
|
|
plugin=DiscoveredPlugin(id="other", value="other:load", package=None, version=None),
|
||
|
|
enabled=False,
|
||
|
|
disabled=True,
|
||
|
|
),
|
||
|
|
]
|
||
|
|
|
||
|
|
|
||
|
|
def test_plugins_list_cmd(tmp_path: Path) -> None:
|
||
|
|
config = tmp_path / "plyngent.toml"
|
||
|
|
_ = config.write_text(
|
||
|
|
"""
|
||
|
|
[plugins]
|
||
|
|
enable = ["acme"]
|
||
|
|
disable = ["other"]
|
||
|
|
""",
|
||
|
|
encoding="utf-8",
|
||
|
|
)
|
||
|
|
runner = CliRunner()
|
||
|
|
with patch("plyngent.tools.plugins.list_plugin_statuses", side_effect=_fake_statuses):
|
||
|
|
result = runner.invoke(main, ["plugins", "list", "--config", str(config)])
|
||
|
|
assert result.exit_code == 0
|
||
|
|
assert "acme" in result.output
|
||
|
|
assert "enabled" in result.output
|
||
|
|
assert "other" in result.output
|
||
|
|
assert "disabled" in result.output
|
||
|
|
|
||
|
|
|
||
|
|
def test_plugins_enable_disable_write(tmp_path: Path) -> None:
|
||
|
|
config = tmp_path / "plyngent.toml"
|
||
|
|
_ = config.write_text("", encoding="utf-8")
|
||
|
|
runner = CliRunner()
|
||
|
|
result = runner.invoke(main, ["plugins", "enable", "acme", "--config", str(config)])
|
||
|
|
assert result.exit_code == 0
|
||
|
|
text = config.read_text(encoding="utf-8")
|
||
|
|
assert "acme" in text
|
||
|
|
result = runner.invoke(main, ["plugins", "disable", "acme", "--config", str(config)])
|
||
|
|
assert result.exit_code == 0
|
||
|
|
text = config.read_text(encoding="utf-8")
|
||
|
|
assert "disable" in text
|
||
|
|
|
||
|
|
|
||
|
|
async def test_slash_plugins_enable_reload(state: ReplState) -> None:
|
||
|
|
with patch("plyngent.tools.plugins.list_plugin_statuses", return_value=[]):
|
||
|
|
assert await handle_slash(state, "/plugins list") is True
|
||
|
|
assert await handle_slash(state, "/plugins enable acme") is True
|
||
|
|
assert "acme" in state.config.plugins_config.enable
|
||
|
|
assert await handle_slash(state, "/plugins disable acme") is True
|
||
|
|
assert "acme" in state.config.plugins_config.disable
|
||
|
|
assert await handle_slash(state, "/plugins undeny acme") is True
|
||
|
|
assert "acme" not in state.config.plugins_config.disable
|
||
|
|
assert await handle_slash(state, "/plugins reload") is True
|