fastmcp/tests/cli/test_config_encoding.py

102 lines
3.3 KiB
Python

"""Regression tests for GH-5317: CLI config files are UTF-8 regardless of locale."""
import builtins
import io
import json
from pathlib import Path
from typing import Any
import pytest
from fastmcp.cli.client import _resolve_json_spec
from fastmcp.cli.install.claude_desktop import install_claude_desktop
from fastmcp.cli.install.shared import process_common_args
from fastmcp.cli.run import create_mcp_config_server
from fastmcp.utilities.cli import load_and_merge_config
ARABIC_PATH = "C:\\Users\\فاطمة\\notes.js"
GREETING = "Olá José"
@pytest.fixture
def cp1252_locale(monkeypatch: pytest.MonkeyPatch) -> None:
"""Make text reads without an explicit encoding decode as cp1252, as they
do on Windows with a non-UTF-8 system code page."""
real_open = builtins.open
def locale_open(
file: Any,
mode: str = "r",
buffering: int = -1,
encoding: str | None = None,
*args: Any,
**kwargs: Any,
) -> Any:
if "b" not in mode and encoding in (None, "locale"):
encoding = "cp1252"
return real_open(file, mode, buffering, encoding, *args, **kwargs)
def text_encoding(encoding: str | None, stacklevel: int = 2) -> str:
return encoding or "cp1252"
monkeypatch.setattr(builtins, "open", locale_open)
monkeypatch.setattr(io, "text_encoding", text_encoding)
def _write_utf8_json(path: Path, data: dict[str, Any]) -> None:
path.write_bytes(json.dumps(data, ensure_ascii=False).encode("utf-8"))
def _mcp_config() -> dict[str, Any]:
return {
"mcpServers": {
"demo": {
"command": "node",
"args": [ARABIC_PATH],
"env": {"GREETING": GREETING},
}
}
}
@pytest.mark.usefixtures("cp1252_locale")
class TestConfigFilesReadAsUtf8:
def test_client_resolves_mcp_config(self, tmp_path: Path):
path = tmp_path / "mcp.json"
_write_utf8_json(path, _mcp_config())
assert _resolve_json_spec(path) == _mcp_config()
def test_run_creates_mcp_config_server(self, tmp_path: Path):
path = tmp_path / "mcp.json"
_write_utf8_json(path, _mcp_config())
create_mcp_config_server(path)
def test_load_and_merge_fastmcp_json(self, tmp_path: Path):
path = tmp_path / "fastmcp.json"
_write_utf8_json(path, {"source": {"path": ARABIC_PATH}})
config, _ = load_and_merge_config(str(path))
assert config.source.path == ARABIC_PATH
async def test_install_reads_fastmcp_json(self, tmp_path: Path):
path = tmp_path / "fastmcp.json"
(tmp_path / "فاطمة.py").write_text("", encoding="utf-8")
_write_utf8_json(path, {"source": {"path": "فاطمة.py"}})
file, *_ = await process_common_args(str(path), None, None, None, None)
assert file == tmp_path / "فاطمة.py"
def test_install_claude_desktop_merges_existing_config(self, tmp_path: Path):
config_file = tmp_path / "claude_desktop_config.json"
_write_utf8_json(config_file, _mcp_config())
assert install_claude_desktop(
tmp_path / "server.py", None, "demo", config_path=tmp_path
)
config = json.loads(config_file.read_bytes().decode("utf-8"))
assert config["mcpServers"]["demo"]["env"] == {"GREETING": GREETING}