Files
wehub-resource-sync da6a09ff09
Auto Bump and Release / auto-bump-and-release (push) Has been cancelled
style-check / pre-commit (push) Has been cancelled
unit-test / unit testing with python (push) Has been cancelled
unit-test / unit testing with python 3.10 (push) Has been cancelled
unit-test / unit testing with python 3.11 (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:39:49 +08:00

161 lines
5.4 KiB
Python

from __future__ import annotations
from typing import TYPE_CHECKING
import pytest
from ktem.mcp.db import MCPTable
from sqlalchemy import create_engine
from sqlalchemy.orm import Session
if TYPE_CHECKING:
from ktem.mcp.manager import MCPManager
from pytest_mock.plugin import MockerFixture
# ---------------------------------------------------------------------------
# Fixtures
# ---------------------------------------------------------------------------
@pytest.fixture
def test_engine():
"""Create an in-memory SQLite engine with MCPTable schema."""
engine = create_engine("sqlite:///:memory:")
MCPTable.metadata.create_all(engine)
return engine
@pytest.fixture
def manager(test_engine, mocker: MockerFixture) -> MCPManager:
"""Fresh MCPManager with a mocked in-memory DB for each test."""
mocker.patch("ktem.mcp.manager.engine", test_engine)
from ktem.mcp.manager import MCPManager
return MCPManager()
# ---------------------------------------------------------------------------
# MCPManager.add tests
# ---------------------------------------------------------------------------
def test_add_and_retrieve(manager: MCPManager) -> None:
"""add() persists data; get() and info() reflect it."""
manager.add("server1", {"command": "uvx", "args": ["mcp-server-fetch"]})
assert manager.info()["server1"]["config"]["command"] == "uvx"
entry = manager.get("server1")
assert entry is not None
assert entry["name"] == "server1"
def test_add_multiple(manager: MCPManager) -> None:
"""Multiple servers can be added."""
manager.add("s1", {"command": "cmd1"})
manager.add("s2", {"command": "cmd2"})
assert set(manager.info().keys()) == {"s1", "s2"}
@pytest.mark.parametrize("name", ["", " "])
def test_add_empty_or_whitespace_name_raises(
manager: MCPManager,
name: str,
) -> None:
"""Empty or whitespace-only name raises ValueError."""
with pytest.raises(ValueError, match="Name must not be empty"):
manager.add(name, {})
def test_add_whitespace_name_is_stripped(manager: MCPManager) -> None:
"""Whitespace in name is stripped."""
manager.add(" server1 ", {"command": "uvx"})
assert "server1" in manager.info()
def test_add_complex_config_stored_correctly(manager: MCPManager) -> None:
"""Complex config with nested data is stored correctly."""
config = {
"command": "uvx",
"env": {"JIRA_URL": "https://example.atlassian.net"},
"enabled_tools": ["jira_search"],
}
manager.add("atlassian", config)
entry = manager.get("atlassian")
assert entry is not None
stored = entry["config"]
assert stored["env"]["JIRA_URL"] == "https://example.atlassian.net"
assert stored["enabled_tools"] == ["jira_search"]
# ---------------------------------------------------------------------------
# MCPManager.update and delete tests
# ---------------------------------------------------------------------------
def test_update_changes_config(manager: MCPManager) -> None:
"""update() changes config for specified server only."""
manager.add("s1", {"command": "cmd1"})
manager.add("s2", {"command": "cmd2"})
manager.update("s1", {"command": "updated"})
assert manager.info()["s1"]["config"]["command"] == "updated"
assert manager.info()["s2"]["config"]["command"] == "cmd2" # untouched
def test_update_nonexistent_raises(manager: MCPManager) -> None:
"""Updating nonexistent server raises ValueError."""
with pytest.raises(ValueError, match="not found"):
manager.update("ghost", {})
def test_delete_removes_entry(manager: MCPManager) -> None:
"""delete() removes the specified entry."""
manager.add("s1", {})
manager.add("s2", {})
manager.delete("s1")
assert "s1" not in manager.info()
assert "s2" in manager.info()
def test_delete_nonexistent_is_noop(manager: MCPManager) -> None:
"""Deleting nonexistent server is a no-op."""
manager.delete("ghost") # must not raise
assert len(manager.info()) == 0
# ---------------------------------------------------------------------------
# MCPManager.list_registered_mcp_servers tests
# ---------------------------------------------------------------------------
def test_list_registered_mcp_servers(manager: MCPManager) -> None:
"""All configured servers are listed with the MCP prefix."""
manager.add("no_filter", {"command": "uvx"})
manager.add("with_filter", {"command": "uvx", "enabled_tools": ["tool_a"]})
choices = manager.list_registered_mcp_servers()
assert "[MCP] no_filter" in choices
assert "[MCP] with_filter" in choices
def test_list_registered_mcp_servers_empty_when_no_servers(manager: MCPManager) -> None:
"""Returns empty list when no servers are configured."""
assert manager.list_registered_mcp_servers() == []
# ---------------------------------------------------------------------------
# MCPManager.load tests
# ---------------------------------------------------------------------------
def test_load_picks_up_external_db_changes(
manager: MCPManager,
test_engine,
) -> None:
"""load() refreshes from database, picking up external changes."""
manager.add("server1", {})
with Session(test_engine) as session:
session.add(MCPTable(name="external", config={"command": "ext"}))
session.commit()
assert "external" not in manager.info() # not yet refreshed
manager.load()
assert "external" in manager.info()