diff options
| author | tanaka takashi <isazaka@gmail.com> | 2026-06-26 23:54:17 +0900 |
|---|---|---|
| committer | tanaka takashi <isazaka@gmail.com> | 2026-06-26 23:54:17 +0900 |
| commit | 3a9e8e18df4c596360da54000f65e1bebc6039c2 (patch) | |
| tree | 0c6dc5fd75cc8f751ca6d7dca01c7e7b5fb3c9f5 /tests/test_mcp.py | |
| parent | a296c2c3a294648c04286a5c027deddbbdc8d5ae (diff) | |
MCP機能追加
Diffstat (limited to 'tests/test_mcp.py')
| -rw-r--r-- | tests/test_mcp.py | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/tests/test_mcp.py b/tests/test_mcp.py new file mode 100644 index 0000000..f538a8f --- /dev/null +++ b/tests/test_mcp.py @@ -0,0 +1,142 @@ +"""Tests for MCP client integration and settings API.""" + +from __future__ import annotations + +import tempfile +from pathlib import Path + +import pytest +from starlette.testclient import TestClient + +from ollama_web.app import create_app +from ollama_web.config import settings +from ollama_web.mcp import ( + _clean_schema, + _normalized_tool_name, + _parse_tool_name, + load_mcp_config, + save_mcp_config, +) +from ollama_web.tools.registry import ToolRegistry + + +@pytest.fixture +def client(monkeypatch): + monkeypatch.setattr(settings, "pin", "123456") + monkeypatch.setattr(settings, "secret_key", "test-secret") + monkeypatch.setattr(settings, "allowed_origins", []) + with tempfile.TemporaryDirectory() as tmp: + monkeypatch.setattr(settings, "data_dir", tmp) + client = TestClient(create_app()) + login = client.post("/api/auth/login", json={"pin": "123456"}) + assert login.status_code == 200 + client.csrf = login.headers["x-csrf-token"] + yield client + + +def test_load_save_mcp_config(): + with tempfile.TemporaryDirectory() as tmp: + config = { + "mcpServers": { + "fs": { + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"], + }, + "remote": { + "url": "https://example.com/mcp", + "headers": {"Authorization": "Bearer x"}, + }, + } + } + save_mcp_config(tmp, config) + loaded = load_mcp_config(tmp) + assert loaded == config + assert (Path(tmp) / "mcpServers.json").exists() + + +def test_load_mcp_config_missing_file(): + with tempfile.TemporaryDirectory() as tmp: + loaded = load_mcp_config(tmp) + assert loaded == {"mcpServers": {}} + + +def test_mcp_config_api(client): + res = client.get("/api/mcp/servers") + assert res.status_code == 200 + assert res.json() == {"mcpServers": {}} + + payload = { + "mcpServers": { + "fs": {"command": "npx", "args": ["-y", "server"]}, + "remote": {"url": "https://example.com/mcp", "timeout": 60}, + } + } + res = client.put( + "/api/mcp/servers", + json=payload, + headers={"X-CSRF-Token": client.csrf}, + ) + assert res.status_code == 200 + data = res.json() + assert data["mcpServers"]["fs"]["command"] == "npx" + assert data["mcpServers"]["remote"]["timeout"] == 60.0 + + res = client.get("/api/mcp/servers") + assert res.json() == data + + +def test_mcp_config_api_rejects_unknown_fields(client): + payload = {"mcpServers": {"fs": {"command": "npx", "invalid": 1}}} + res = client.put( + "/api/mcp/servers", + json=payload, + headers={"X-CSRF-Token": client.csrf}, + ) + assert res.status_code == 400 + + +def test_mcp_config_api_requires_transport(client): + payload = {"mcpServers": {"fs": {}}} + res = client.put( + "/api/mcp/servers", + json=payload, + headers={"X-CSRF-Token": client.csrf}, + ) + assert res.status_code == 400 + + +def test_normalized_tool_name(): + assert _normalized_tool_name("server1", "tool_a") == "mcp__server1__tool_a" + + +def test_parse_tool_name(): + assert _parse_tool_name("mcp__server1__tool_a") == ("server1", "tool_a") + assert _parse_tool_name("web_search") is None + + +def test_clean_schema(): + schema = { + "type": "object", + "properties": {"path": {"type": "string"}}, + "required": ["path"], + } + cleaned = _clean_schema(schema) + assert cleaned["type"] == "object" + assert cleaned["required"] == ["path"] + + +def test_tool_registry_mcp_tool(): + reg = ToolRegistry() + + def executor(full_name, arguments=""): + return "ok" + + reg.register_mcp_tool( + "mcp__s__t", {"type": "function", "function": {"name": "mcp__s__t"}}, executor + ) + assert "mcp__s__t" in reg.names + # Executor is registered internally for execution but must not be exposed + # to ollama as a callable (ollama needs the dict definition only). + assert len(reg.ollama_tools) == 1 + result = reg.execute("mcp__s__t", {}) + assert result == "ok"
\ No newline at end of file |
