"""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