| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 |
- import json
- import os
- import subprocess
- from pathlib import Path
- import pytest
- from app.core.mcp.runtime import identity_from_env
- from app.core.mcp.servers import CONTEXT_TOOL_NAMES, SCHEDULING_TOOL_NAMES
- ROOT = Path(__file__).resolve().parents[2]
- def runtime_env():
- return {
- **os.environ,
- "PYTHONPATH": str(ROOT),
- "DATAOPS_MCP_SUBJECT": "agent-context-test",
- "DATAOPS_MCP_ROLES": "viewer",
- "DATAOPS_MCP_BUSINESS_DOMAINS": "sales",
- "DATAOPS_MCP_ENVIRONMENTS": "test",
- "DATAOPS_MCP_CORRELATION_ID": ("01900000-0000-7000-8000-000000000099"),
- }
- def test_identity_from_env_is_explicit_bounded_and_fail_closed():
- identity = identity_from_env(runtime_env())
- assert identity.subject == "agent-context-test"
- assert identity.roles == frozenset({"viewer"})
- assert identity.business_domains == frozenset({"sales"})
- assert identity.environments == frozenset({"test"})
- incomplete = runtime_env()
- incomplete.pop("DATAOPS_MCP_BUSINESS_DOMAINS")
- with pytest.raises(ValueError, match="BUSINESS_DOMAINS"):
- identity_from_env(incomplete)
- unsafe = runtime_env()
- unsafe["DATAOPS_MCP_ROLES"] = "viewer,root"
- with pytest.raises(ValueError, match="role"):
- identity_from_env(unsafe)
- def test_context_stdio_initializes_and_exposes_only_context_tools():
- messages = [
- {
- "jsonrpc": "2.0",
- "id": 1,
- "method": "initialize",
- "params": {
- "protocolVersion": "2025-06-18",
- "capabilities": {},
- "clientInfo": {"name": "dataops-v53-test", "version": "1.0"},
- },
- },
- {
- "jsonrpc": "2.0",
- "method": "notifications/initialized",
- "params": {},
- },
- {"jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {}},
- ]
- result = subprocess.run(
- [
- str(ROOT / ".venv/bin/python"),
- str(ROOT / "mcp-servers/dataops_mcp_stdio.py"),
- "--kind",
- "context",
- "--factory",
- "tests.mcp.stdio_fixture:build_context_service",
- "--allow-test-factory",
- ],
- cwd=ROOT,
- env=runtime_env(),
- input="\n".join(json.dumps(item) for item in messages) + "\n",
- capture_output=True,
- text=True,
- timeout=30,
- check=True,
- )
- responses = {
- item["id"]: item
- for line in result.stdout.splitlines()
- if line.strip()
- for item in [json.loads(line)]
- if "id" in item
- }
- assert responses[1]["result"]["serverInfo"]["name"] == ("DataOps Context MCP")
- assert {
- tool["name"] for tool in responses[2]["result"]["tools"]
- } == CONTEXT_TOOL_NAMES
- def test_scheduling_stdio_initializes_and_exposes_only_compound_tools():
- messages = [
- {
- "jsonrpc": "2.0",
- "id": 1,
- "method": "initialize",
- "params": {
- "protocolVersion": "2025-06-18",
- "capabilities": {},
- "clientInfo": {"name": "dataops-v53-test", "version": "1.0"},
- },
- },
- {
- "jsonrpc": "2.0",
- "method": "notifications/initialized",
- "params": {},
- },
- {"jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {}},
- ]
- result = subprocess.run(
- [
- str(ROOT / ".venv/bin/python"),
- str(ROOT / "mcp-servers/dataops_mcp_stdio.py"),
- "--kind",
- "scheduling",
- "--factory",
- "tests.mcp.stdio_fixture:build_scheduling_gateway",
- "--allow-test-factory",
- ],
- cwd=ROOT,
- env=runtime_env(),
- input="\n".join(json.dumps(item) for item in messages) + "\n",
- capture_output=True,
- text=True,
- timeout=30,
- )
- assert result.returncode == 0, result.stderr
- responses = {
- item["id"]: item
- for line in result.stdout.splitlines()
- if line.strip()
- for item in [json.loads(line)]
- if "id" in item
- }
- assert {
- tool["name"] for tool in responses[2]["result"]["tools"]
- } == SCHEDULING_TOOL_NAMES
|