feat(market-intel): add scheduler attach plan preview
Some checks failed
CD Pipeline / deploy (push) Failing after 45s

This commit is contained in:
OoO
2026-05-18 15:48:59 +08:00
parent d2d8dbab65
commit a3fb476eed
8 changed files with 335 additions and 11 deletions

View File

@@ -443,6 +443,11 @@ def test_market_intel_preview_template_uses_safe_fetch_false_endpoint():
assert "data-market-intel-mcp-fetch-gate-checks" in template
assert "data-market-intel-mcp-fetch-gate-sequence" in template
assert "data-market-intel-mcp-fetch-gate-readiness" in template
assert "data-market-intel-scheduler" in template
assert "data-market-intel-scheduler-checks" in template
assert "data-market-intel-scheduler-jobs" in template
assert "data-market-intel-scheduler-sequence" in template
assert "data-market-intel-scheduler-fallback" in template
assert "data-market-intel-migration" in template
assert "data-market-intel-migration-tables" in template
assert "data-market-intel-approval" in template
@@ -460,6 +465,7 @@ def test_market_intel_preview_template_uses_safe_fetch_false_endpoint():
assert "market_intel.market_intel_mcp_deploy_preflight" in template
assert "market_intel.market_intel_mcp_activation_runbook" in template
assert "market_intel.market_intel_mcp_fetch_gate" in template
assert "market_intel.market_intel_scheduler_plan" in template
assert "market_intel.market_intel_migration_blueprint" in template
assert "market_intel.market_intel_write_approval_runbook" in template
assert "market_intel.market_intel_deployment_readiness" in template
@@ -485,7 +491,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_32_mcp_fetch_gate_preview"
assert bridge["phase"] == "phase_33_scheduler_plan_preview"
assert bridge["execute_requested"] is False
assert bridge["read_only_query_executed"] is False
assert bridge["database_connection_opened"] is False
@@ -643,7 +649,7 @@ 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_32_mcp_fetch_gate_preview"
assert contract["phase"] == "phase_33_scheduler_plan_preview"
assert contract["caller"] == "market_intel"
assert contract["contract_ready"] is True
assert contract["blocked_reasons"] == []
@@ -776,7 +782,7 @@ def test_mcp_activation_runbook_route_is_preview_only():
assert response.status_code == 200
assert data["mode"] == "mcp_activation_runbook_preview"
assert data["phase"] == "phase_32_mcp_fetch_gate_preview"
assert data["phase"] == "phase_33_scheduler_plan_preview"
assert data["deployment_actions_executed"] is False
assert data["docker_command_executed"] is False
assert data["ssh_command_executed"] is False
@@ -789,7 +795,7 @@ def test_mcp_fetch_gate_default_blocks_external_fetch():
gate = MarketIntelService().build_mcp_fetch_gate(fetch_requested=True)
assert gate["mode"] == "mcp_fetch_gate_planned"
assert gate["phase"] == "phase_32_mcp_fetch_gate_preview"
assert gate["phase"] == "phase_33_scheduler_plan_preview"
assert gate["fetch_requested"] is True
assert gate["manual_fetch_gate_open"] is False
assert gate["network_request_allowed"] is False
@@ -859,7 +865,7 @@ def test_mcp_fetch_gate_route_is_preview_only():
assert response.status_code == 200
assert data["mode"] == "mcp_fetch_gate_planned"
assert data["phase"] == "phase_32_mcp_fetch_gate_preview"
assert data["phase"] == "phase_33_scheduler_plan_preview"
assert data["fetch_requested"] is False
assert data["network_request_allowed"] is False
assert data["external_network_executed"] is False
@@ -867,6 +873,55 @@ def test_mcp_fetch_gate_route_is_preview_only():
assert data["scheduler_attached"] is False
def test_scheduler_plan_preview_blocks_job_attachment():
plan = MarketIntelService().build_scheduler_plan()
assert plan["mode"] == "scheduler_attach_plan_preview"
assert plan["phase"] == "phase_33_scheduler_plan_preview"
assert plan["ready_to_attach_scheduler"] is False
assert plan["scheduler_attached"] is False
assert plan["scheduler_registration_executed"] is False
assert plan["crawler_job_started"] is False
assert plan["external_network_executed"] is False
assert plan["database_session_created"] is False
assert plan["database_write_executed"] is False
assert plan["database_commit_executed"] is False
assert plan["writes_executed"] is False
assert plan["would_write_database"] is False
assert plan["job_count"] == 3
assert {item["key"] for item in plan["jobs"]} == {
"campaign_discovery_daily",
"campaign_product_probe",
"product_match_review_seed",
}
assert "market_intel_enabled" in plan["blocked_reasons"]
assert "mcp_fetch_gate_open" in plan["blocked_reasons"]
assert "manual_operator_approval" in plan["blocked_reasons"]
assert "do_not_attach_scheduler_from_api" in plan["safe_boundaries"]
def test_scheduler_plan_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/scheduler_plan")
data = response.get_json()
assert response.status_code == 200
assert data["mode"] == "scheduler_attach_plan_preview"
assert data["phase"] == "phase_33_scheduler_plan_preview"
assert data["scheduler_registration_executed"] is False
assert data["crawler_job_started"] is False
assert data["external_network_executed"] is False
assert data["database_write_executed"] is False
def test_mcp_deploy_preflight_blocks_without_required_env():
preflight = build_mcp_deploy_preflight_plan(env={})
@@ -930,7 +985,7 @@ def test_mcp_deploy_preflight_route_is_preview_only():
assert response.status_code == 200
assert data["mode"] == "mcp_external_deploy_preflight_preview"
assert data["phase"] == "phase_32_mcp_fetch_gate_preview"
assert data["phase"] == "phase_33_scheduler_plan_preview"
assert data["deployment_actions_executed"] is False
assert data["docker_command_executed"] is False
assert data["ssh_command_executed"] is False
@@ -945,7 +1000,7 @@ def test_mcp_readiness_default_is_planned_only(monkeypatch):
readiness = MarketIntelService().build_mcp_readiness()
assert readiness["mode"] == "mcp_readiness_planned"
assert readiness["phase"] == "phase_32_mcp_fetch_gate_preview"
assert readiness["phase"] == "phase_33_scheduler_plan_preview"
assert readiness["execute_requested"] is False
assert readiness["router_enabled"] is False
assert readiness["external_mcp_complete"] is False
@@ -1332,6 +1387,7 @@ def test_deployment_readiness_reports_app_only_release_gate():
assert readiness["checks"]["mcp_deploy_preflight_preview_safe"] is True
assert readiness["checks"]["mcp_activation_runbook_preview_safe"] is True
assert readiness["checks"]["mcp_fetch_gate_preview_safe"] is True
assert readiness["checks"]["scheduler_plan_preview_safe"] 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"]
@@ -1352,6 +1408,7 @@ def test_deployment_readiness_reports_app_only_release_gate():
assert "/api/market_intel/mcp_deploy_preflight" in readiness["production_smoke_targets"]
assert "/api/market_intel/mcp_activation_runbook" in readiness["production_smoke_targets"]
assert "/api/market_intel/mcp_fetch_gate" in readiness["production_smoke_targets"]
assert "/api/market_intel/scheduler_plan" 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
@@ -1370,6 +1427,9 @@ def test_deployment_readiness_reports_app_only_release_gate():
assert readiness["mcp_activation_runbook"]["docker_command_executed"] is False
assert readiness["mcp_fetch_gate"]["network_request_allowed"] is False
assert readiness["mcp_fetch_gate"]["external_network_executed"] is False
assert readiness["scheduler_plan"]["scheduler_registration_executed"] is False
assert readiness["scheduler_plan"]["crawler_job_started"] is False
assert readiness["scheduler_plan"]["database_write_executed"] is False
def test_write_approval_runbook_is_read_only_and_blocks_real_write():