Files
Agentic-OS/backend/tests/test_mcp_approvals.py
nearxos 0375b20bb4 Enhance MCP development features and introduce skills management
- Added configuration options for requiring human approval before applying LLM-generated MCP patches.
- Updated Docker setup to include skills directory.
- Integrated skills management into the backend, allowing for procedural guides and skill matching.
- Refactored database initialization to apply Alembic migrations.
- Enhanced task approval process to handle MCP patch applications with optional approval.
- Introduced new schemas for skills and updated existing APIs to support skills functionality.

This commit lays the groundwork for improved agent capabilities and better management of MCP development processes.
2026-06-14 22:27:24 +03:00

107 lines
3.5 KiB
Python

"""Tests for MCP patch approval gating."""
from __future__ import annotations
import asyncio
import os
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
os.environ.setdefault("SECRET_KEY", "test-secret-key-for-unit-tests-only")
def test_repair_queues_apply_patch_when_approval_required(monkeypatch):
from app.config import get_settings, settings
get_settings.cache_clear()
proposal = {
"ok": True,
"summary": "Add missing tool",
"commit_message": "fix: add tool",
"files": [{"path": "server.py", "content": "# patched"}],
}
mock_db = MagicMock()
mock_db.commit = AsyncMock()
mock_session = MagicMock()
mock_session.__aenter__ = AsyncMock(return_value=mock_db)
mock_session.__aexit__ = AsyncMock(return_value=None)
async def _run():
with (
patch.object(settings, "mcp_dev_require_approval", True),
patch("app.agent.mcp_development.propose_mcp_patch", new=AsyncMock(return_value=proposal)),
patch(
"app.agent.mcp_development.backup_proposal_files",
new=AsyncMock(return_value={"server.py": "# old"}),
),
patch("app.agent.mcp_development.execute_approved_patch", new=AsyncMock()) as mock_apply,
patch("app.database.SessionLocal", return_value=mock_session),
):
from app.agent.mcp_development import attempt_mcp_repair
emit = AsyncMock()
result = await attempt_mcp_repair(
task_id=42,
server="pfsense-mcp",
tool="pfsense_list_rules",
args={"interface": "wan"},
issue="tool missing",
error="unknown tool",
output=None,
manager=AsyncMock(),
emit=emit,
device="pfsense",
)
return result, mock_apply, emit, mock_db
result, mock_apply, emit, mock_db = asyncio.run(_run())
assert result.get("pending_approval") is True
assert result.get("approval_type") == "mcp_apply_patch"
mock_apply.assert_not_called()
mock_db.add.assert_called_once()
mock_db.commit.assert_awaited_once()
assert any(c.args[1] == "approval" for c in emit.call_args_list)
def test_repair_applies_immediately_when_approval_not_required(monkeypatch):
from app.config import get_settings, settings
get_settings.cache_clear()
proposal = {
"ok": True,
"summary": "Fix crash",
"files": [{"path": "server.py", "content": "# patched"}],
}
async def _run():
with (
patch.object(settings, "mcp_dev_require_approval", False),
patch("app.agent.mcp_development.propose_mcp_patch", new=AsyncMock(return_value=proposal)),
patch(
"app.agent.mcp_development.execute_approved_patch",
new=AsyncMock(return_value={"ok": True, "tools": ["a"]}),
) as mock_apply,
):
from app.agent.mcp_development import attempt_mcp_repair
return await attempt_mcp_repair(
task_id=1,
server="pfsense-mcp",
tool="t",
args={},
issue="x",
error="err",
output=None,
manager=AsyncMock(),
emit=AsyncMock(),
), mock_apply
result, mock_apply = asyncio.run(_run())
assert result.get("ok") is True
mock_apply.assert_awaited_once()