feat(market-intel): add internal mcp contract
All checks were successful
CD Pipeline / deploy (push) Successful in 1m1s
All checks were successful
CD Pipeline / deploy (push) Successful in 1m1s
This commit is contained in:
@@ -14,6 +14,7 @@ from services.market_intel.adapters import get_adapter, get_adapter_summaries
|
||||
from services.market_intel.candidate_preview import build_candidate_preview_from_discovery
|
||||
from services.market_intel.discovery_runner import ManualDiscoveryRunner
|
||||
from services.market_intel.html_diagnostics import parse_html_diagnostics
|
||||
from services.market_intel.mcp_contract import build_mcp_tool_contract_preview
|
||||
from services.market_intel.mcp_readiness import build_mcp_readiness_plan
|
||||
from services.market_intel.platform_seed_db_diff import build_platform_seed_db_diff_plan
|
||||
from services.market_intel.schema_db_probe import build_schema_db_probe_plan
|
||||
@@ -386,6 +387,7 @@ def test_market_intel_preview_template_uses_safe_fetch_false_endpoint():
|
||||
assert "data-market-intel-mcp-servers" in template
|
||||
assert "data-market-intel-mcp-checks" in template
|
||||
assert "data-market-intel-mcp-tools" in template
|
||||
assert "market_intel_contract" in template
|
||||
assert "data-market-intel-migration" in template
|
||||
assert "data-market-intel-migration-tables" in template
|
||||
assert "data-market-intel-approval" in template
|
||||
@@ -425,7 +427,7 @@ def test_legacy_source_bridge_default_is_planned_only():
|
||||
bridge = MarketIntelService().build_legacy_source_bridge()
|
||||
|
||||
assert bridge["mode"] == "legacy_source_bridge_planned"
|
||||
assert bridge["phase"] == "phase_28_mcp_readiness_preview"
|
||||
assert bridge["phase"] == "phase_29_internal_mcp_contract_preview"
|
||||
assert bridge["execute_requested"] is False
|
||||
assert bridge["read_only_query_executed"] is False
|
||||
assert bridge["database_connection_opened"] is False
|
||||
@@ -579,18 +581,80 @@ def test_legacy_source_bridge_read_only_sqlite_counts_sources():
|
||||
)
|
||||
|
||||
|
||||
def test_mcp_tool_contract_preview_is_read_only_and_whitelisted():
|
||||
contract = MarketIntelService().build_mcp_tool_contract()
|
||||
|
||||
assert contract["mode"] == "mcp_tool_contract_preview"
|
||||
assert contract["phase"] == "phase_29_internal_mcp_contract_preview"
|
||||
assert contract["caller"] == "market_intel"
|
||||
assert contract["contract_ready"] is True
|
||||
assert contract["blocked_reasons"] == []
|
||||
assert contract["tool_count"] == 3
|
||||
assert contract["database_session_created"] is False
|
||||
assert contract["database_write_executed"] is False
|
||||
assert contract["database_commit_executed"] is False
|
||||
assert contract["external_network_executed"] is False
|
||||
assert contract["scheduler_attached"] is False
|
||||
assert contract["writes_executed"] is False
|
||||
assert contract["would_write_database"] is False
|
||||
assert contract["checks"]["market_intel_caller_registered"] is True
|
||||
assert contract["checks"]["all_router_tools_whitelisted"] is True
|
||||
assert contract["checks"]["all_tools_read_only"] is True
|
||||
assert contract["checks"]["no_database_write_allowed"] is True
|
||||
assert contract["checks"]["no_scheduler_attach_allowed"] is True
|
||||
assert {item["name"] for item in contract["tools"]} == {
|
||||
"market_campaign_search",
|
||||
"market_campaign_scrape",
|
||||
"market_product_match_lookup",
|
||||
}
|
||||
assert all(item["router_tools_whitelisted"] is True for item in contract["tools"])
|
||||
assert all(item["write_risk"] is False for item in contract["tools"])
|
||||
|
||||
|
||||
def test_mcp_tool_contract_detects_missing_router_whitelist():
|
||||
contract = build_mcp_tool_contract_preview(tool_registry={})
|
||||
|
||||
assert contract["contract_ready"] is False
|
||||
assert "market_intel_caller_registered" in contract["blocked_reasons"]
|
||||
assert "all_router_tools_whitelisted" in contract["blocked_reasons"]
|
||||
|
||||
|
||||
def test_mcp_tool_contract_route_is_preview_only():
|
||||
from routes.market_intel_routes import market_intel_bp
|
||||
|
||||
app = Flask(__name__)
|
||||
app.secret_key = "test-secret"
|
||||
app.register_blueprint(market_intel_bp)
|
||||
client = app.test_client()
|
||||
with client.session_transaction() as session:
|
||||
session["logged_in"] = True
|
||||
|
||||
response = client.get("/api/market_intel/mcp_tool_contract")
|
||||
data = response.get_json()
|
||||
|
||||
assert response.status_code == 200
|
||||
assert data["mode"] == "mcp_tool_contract_preview"
|
||||
assert data["contract_ready"] is True
|
||||
assert data["database_session_created"] is False
|
||||
assert data["database_write_executed"] is False
|
||||
assert data["database_commit_executed"] is False
|
||||
assert data["external_network_executed"] is False
|
||||
assert data["scheduler_attached"] is False
|
||||
|
||||
|
||||
def test_mcp_readiness_default_is_planned_only(monkeypatch):
|
||||
monkeypatch.delenv("MCP_ROUTER_ENABLED", raising=False)
|
||||
|
||||
readiness = MarketIntelService().build_mcp_readiness()
|
||||
|
||||
assert readiness["mode"] == "mcp_readiness_planned"
|
||||
assert readiness["phase"] == "phase_28_mcp_readiness_preview"
|
||||
assert readiness["phase"] == "phase_29_internal_mcp_contract_preview"
|
||||
assert readiness["execute_requested"] is False
|
||||
assert readiness["router_enabled"] is False
|
||||
assert readiness["external_mcp_complete"] is False
|
||||
assert readiness["internal_mcp_complete"] is False
|
||||
assert readiness["market_intel_mcp_integrated"] is False
|
||||
assert readiness["market_intel_mcp_integrated"] is True
|
||||
assert readiness["mcp_tool_contract"]["contract_ready"] is True
|
||||
assert readiness["database_session_created"] is False
|
||||
assert readiness["database_write_executed"] is False
|
||||
assert readiness["database_commit_executed"] is False
|
||||
@@ -602,12 +666,15 @@ def test_mcp_readiness_default_is_planned_only(monkeypatch):
|
||||
assert readiness["telemetry"]["database_session_created"] is False
|
||||
assert readiness["telemetry"]["database_write_executed"] is False
|
||||
assert readiness["readiness_checks"]["base_callers_registered"] is True
|
||||
assert readiness["readiness_checks"]["market_intel_caller_registered"] is False
|
||||
assert readiness["readiness_checks"]["market_intel_tools_registered"] is False
|
||||
assert readiness["readiness_checks"]["market_intel_caller_registered"] is True
|
||||
assert readiness["readiness_checks"]["market_intel_tools_registered"] is True
|
||||
assert readiness["readiness_checks"]["market_intel_tool_contract_ready"] is True
|
||||
assert len(readiness["server_statuses"]) == 4
|
||||
assert all(item["health_checked"] is False for item in readiness["server_statuses"])
|
||||
assert all(item["healthy"] is False for item in readiness["server_statuses"])
|
||||
assert "execute_false_planned_only" in readiness["blocked_reasons"]
|
||||
assert "market_intel_caller_registered" not in readiness["blocked_reasons"]
|
||||
assert "market_intel_tools_registered" not in readiness["blocked_reasons"]
|
||||
|
||||
|
||||
def test_mcp_readiness_sqlite_read_only_counts_telemetry(monkeypatch):
|
||||
@@ -689,7 +756,9 @@ def test_mcp_readiness_sqlite_read_only_counts_telemetry(monkeypatch):
|
||||
assert readiness["execute_requested"] is True
|
||||
assert readiness["router_enabled"] is False
|
||||
assert readiness["external_mcp_complete"] is False
|
||||
assert readiness["internal_mcp_complete"] is False
|
||||
assert readiness["internal_mcp_complete"] is True
|
||||
assert readiness["market_intel_mcp_integrated"] is True
|
||||
assert readiness["mcp_tool_contract"]["contract_ready"] is True
|
||||
assert readiness["telemetry"]["mode"] == "mcp_telemetry_read_only"
|
||||
assert readiness["telemetry"]["table_exists"] is True
|
||||
assert readiness["telemetry"]["total_calls"] == 3
|
||||
@@ -710,7 +779,7 @@ def test_mcp_readiness_sqlite_read_only_counts_telemetry(monkeypatch):
|
||||
"postgres": 1,
|
||||
}
|
||||
assert "mcp_router_enabled" in readiness["blocked_reasons"]
|
||||
assert "market_intel_caller_registered" in readiness["blocked_reasons"]
|
||||
assert "market_intel_caller_registered" not in readiness["blocked_reasons"]
|
||||
|
||||
|
||||
def test_market_intel_schema_smoke_checks_platform_columns():
|
||||
@@ -962,6 +1031,7 @@ def test_deployment_readiness_reports_app_only_release_gate():
|
||||
assert readiness["checks"]["platform_seed_db_diff_planned_safe"] is True
|
||||
assert readiness["checks"]["legacy_source_bridge_planned_safe"] is True
|
||||
assert readiness["checks"]["mcp_readiness_planned_safe"] is True
|
||||
assert readiness["checks"]["mcp_tool_contract_ready"] is True
|
||||
assert readiness["checks"]["writer_plan_dry_run_only"] is True
|
||||
assert readiness["writer_plan_summary"]["writes_executed"] is False
|
||||
assert "readiness_checks_not_all_passed" not in readiness["blocked_reasons"]
|
||||
@@ -978,6 +1048,7 @@ def test_deployment_readiness_reports_app_only_release_gate():
|
||||
assert "/api/market_intel/platform_seed_db_diff" in readiness["production_smoke_targets"]
|
||||
assert "/api/market_intel/legacy_source_bridge" in readiness["production_smoke_targets"]
|
||||
assert "/api/market_intel/mcp_readiness" in readiness["production_smoke_targets"]
|
||||
assert "/api/market_intel/mcp_tool_contract" in readiness["production_smoke_targets"]
|
||||
assert readiness["write_approval_runbook"]["ready_for_real_write"] is False
|
||||
assert readiness["write_approval_runbook"]["writes_executed"] is False
|
||||
assert readiness["migration_blueprint"]["migration_executed"] is False
|
||||
@@ -988,6 +1059,8 @@ def test_deployment_readiness_reports_app_only_release_gate():
|
||||
assert readiness["legacy_source_bridge"]["read_only_query_executed"] is False
|
||||
assert readiness["mcp_readiness"]["mode"] == "mcp_readiness_planned"
|
||||
assert readiness["mcp_readiness"]["telemetry"]["read_only_query_executed"] is False
|
||||
assert readiness["mcp_tool_contract"]["contract_ready"] is True
|
||||
assert readiness["mcp_tool_contract"]["database_write_executed"] is False
|
||||
|
||||
|
||||
def test_write_approval_runbook_is_read_only_and_blocks_real_write():
|
||||
|
||||
Reference in New Issue
Block a user