150 lines
5.1 KiB
Python
150 lines
5.1 KiB
Python
"""Unit tests for mcp_config_drift.py (#672)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import pytest
|
|
from pathlib import Path
|
|
|
|
from mcp_config_drift import (
|
|
REQUIRED_GITEA_ROLE_SERVERS,
|
|
analyze_config_drift,
|
|
load_mcp_config,
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def sample_global_config() -> dict:
|
|
return {
|
|
"mcpServers": {
|
|
"gitea-author": {
|
|
"command": "python3",
|
|
"args": ["gitea_mcp_server.py"],
|
|
"env": {"GITEA_MCP_PROFILE": "prgs-author", "SENTRY_AUTH_TOKEN": "secret-token-999"},
|
|
},
|
|
"gitea-reviewer": {
|
|
"command": "python3",
|
|
"args": ["gitea_mcp_server.py"],
|
|
"env": {"GITEA_MCP_PROFILE": "prgs-reviewer"},
|
|
},
|
|
"gitea-merger": {
|
|
"command": "python3",
|
|
"args": ["gitea_mcp_server.py"],
|
|
"env": {"GITEA_MCP_PROFILE": "prgs-merger"},
|
|
},
|
|
"gitea-reconciler": {
|
|
"command": "python3",
|
|
"args": ["gitea_mcp_server.py"],
|
|
"env": {"GITEA_MCP_PROFILE": "prgs-reconciler"},
|
|
},
|
|
"gitea-controller": {
|
|
"command": "python3",
|
|
"args": ["gitea_mcp_server.py"],
|
|
"env": {"GITEA_MCP_PROFILE": "prgs-controller"},
|
|
},
|
|
"gitea-tools": {
|
|
"command": "python3",
|
|
"args": ["gitea_mcp_server.py"],
|
|
"env": {"GITEA_MCP_PROFILE": "prgs-author"},
|
|
},
|
|
}
|
|
}
|
|
|
|
|
|
def write_json(path: Path, data: dict) -> str:
|
|
path.write_text(json.dumps(data, indent=2), encoding="utf-8")
|
|
return str(path)
|
|
|
|
|
|
def test_drift_detection_in_sync(tmp_path, sample_global_config):
|
|
glob_file = tmp_path / "global_mcp.json"
|
|
act_file = tmp_path / "active_mcp.json"
|
|
|
|
write_json(glob_file, sample_global_config)
|
|
write_json(act_file, sample_global_config)
|
|
|
|
report = analyze_config_drift(active_config_path=str(act_file), global_config_path=str(glob_file))
|
|
|
|
assert report["in_sync"] is True
|
|
assert report["missing_role_servers"] == []
|
|
assert report["profile_mismatches"] == []
|
|
assert set(report["present_role_servers"]) == set(REQUIRED_GITEA_ROLE_SERVERS)
|
|
|
|
|
|
def test_drift_detection_missing_author(tmp_path, sample_global_config):
|
|
glob_file = tmp_path / "global_mcp.json"
|
|
act_file = tmp_path / "active_mcp.json"
|
|
|
|
active_config = json.loads(json.dumps(sample_global_config))
|
|
del active_config["mcpServers"]["gitea-author"]
|
|
|
|
write_json(glob_file, sample_global_config)
|
|
write_json(act_file, active_config)
|
|
|
|
report = analyze_config_drift(active_config_path=str(act_file), global_config_path=str(glob_file))
|
|
|
|
assert report["in_sync"] is False
|
|
assert "gitea-author" in report["missing_role_servers"]
|
|
assert "gitea-author" not in report["present_role_servers"]
|
|
|
|
|
|
def test_drift_detection_missing_reviewer(tmp_path, sample_global_config):
|
|
glob_file = tmp_path / "global_mcp.json"
|
|
act_file = tmp_path / "active_mcp.json"
|
|
|
|
active_config = json.loads(json.dumps(sample_global_config))
|
|
del active_config["mcpServers"]["gitea-reviewer"]
|
|
|
|
write_json(glob_file, sample_global_config)
|
|
write_json(act_file, active_config)
|
|
|
|
report = analyze_config_drift(active_config_path=str(act_file), global_config_path=str(glob_file))
|
|
|
|
assert report["in_sync"] is False
|
|
assert "gitea-reviewer" in report["missing_role_servers"]
|
|
|
|
|
|
def test_drift_detection_profile_mismatch(tmp_path, sample_global_config):
|
|
glob_file = tmp_path / "global_mcp.json"
|
|
act_file = tmp_path / "active_mcp.json"
|
|
|
|
active_config = json.loads(json.dumps(sample_global_config))
|
|
active_config["mcpServers"]["gitea-author"]["env"]["GITEA_MCP_PROFILE"] = "dadeschools-author"
|
|
|
|
write_json(glob_file, sample_global_config)
|
|
write_json(act_file, active_config)
|
|
|
|
report = analyze_config_drift(active_config_path=str(act_file), global_config_path=str(glob_file))
|
|
|
|
assert report["in_sync"] is False
|
|
assert len(report["profile_mismatches"]) == 1
|
|
mismatch = report["profile_mismatches"][0]
|
|
assert mismatch["server"] == "gitea-author"
|
|
assert mismatch["active_profile"] == "dadeschools-author"
|
|
assert mismatch["global_profile"] == "prgs-author"
|
|
|
|
|
|
def test_secret_redaction_in_drift_report(tmp_path, sample_global_config):
|
|
glob_file = tmp_path / "global_mcp.json"
|
|
act_file = tmp_path / "active_mcp.json"
|
|
|
|
write_json(glob_file, sample_global_config)
|
|
write_json(act_file, sample_global_config)
|
|
|
|
report = analyze_config_drift(active_config_path=str(act_file), global_config_path=str(glob_file))
|
|
serialized = str(report)
|
|
|
|
assert "secret-token-999" not in serialized
|
|
|
|
|
|
def test_sanctioned_runbook_forbids_pkill():
|
|
report = analyze_config_drift(active_config_path="/nonexistent/path/active.json", global_config_path="/nonexistent/path/global.json")
|
|
|
|
runbook_text = " ".join(report["sanctioned_repair_runbook"]).lower()
|
|
forbidden_text = " ".join(report["forbidden_repair_methods"]).lower()
|
|
|
|
assert "pkill" in forbidden_text
|
|
assert "mtime" in forbidden_text
|
|
assert "source" in forbidden_text
|
|
assert "session-state" in forbidden_text
|