from __future__ import annotations import copy from datetime import date import pytest from fastapi.testclient import TestClient from server.agent_core.async_jobs import JobCancelled, JobQueue from server.aps_domain.cp_marginal import run_cp_marginal_resolve from server.engines import get_engine from server.engines.base import EngineParams from server.engines.cp_engine import CpSatEngine from server.engines.solver_process import ( PROTOCOL_VERSION, SolverProcessError, _validate_result_semantics, run_cp_constraint_diagnostic, ) from server.engines.solver_worker import _request_digest, _validate_request from server.state.seed import build_demo_world, empty_world from tests.auth_provider import install_test_auth def _align_shift_calendar(world: dict, start_date: str) -> None: rows = world.get("shiftCalendar") or [] if not rows: return source_start = min(date.fromisoformat(str(row["date"])) for row in rows) offset = date.fromisoformat(start_date) - source_start for row in rows: row["date"] = (date.fromisoformat(str(row["date"])) + offset).isoformat() def _tight_two_order_world() -> dict: world = build_demo_world() _align_shift_calendar(world, "2026-08-03") orders = [row for row in world["salesOrders"] if row["items"][0]["productId"] == 1][:2] for order in orders: order["deliveryDate"] = "2026-08-03" keep = {row["id"] for row in orders} world["salesOrders"] = [row for row in world["salesOrders"] if row["id"] in keep] world["constraintProfile"] = { "profileId": "test-isolate-c2", "constraints": {"C7_capacity": {"enabled": False}}, } return world def _tight_c2_exact_world() -> dict: world = _tight_two_order_world() routing_id = next( row["id"] for row in world["routings"] if row["productId"] == 1 and row["isDefault"] ) world["routingSteps"] = [ row for row in world["routingSteps"] if row["routingId"] == routing_id and row["sequenceNo"] == 1 ] return world def _counter(): values: dict[str, int] = {} def next_id(kind: str) -> int: values[kind] = values.get(kind, 0) + 1 return values[kind] return next_id def _entries_and_params(world: dict, **overrides): values = { "orderIds": [], "engineType": "CP", "strategyTemplate": "COMPREHENSIVE", "planningHorizonDays": 14, "startDate": "2026-08-03", "timeLimitSeconds": 4, "constraints": {"capacity": False}, } values.update(overrides) params = EngineParams(**values) entries, _, _ = CpSatEngine().collect_and_order(world, params) return entries, params def test_real_c2_removal_improves_optimal_cp_objective_without_mutating_world(): world = _tight_c2_exact_world() before = copy.deepcopy(world) report = run_cp_marginal_resolve( world, start_date="2026-08-03", planning_horizon_days=3, constraint_ids=["C2_no_overlap"], time_limit_seconds=8, ) assert world == before assert report["status"] == "completed" assert report["evaluations"] == 2 assert report["baseline"]["status"] == "OPTIMAL" assert report["baseline"]["operation"] == "diagnose_constraint_baseline" assert report["baseline"]["numSearchWorkers"] == 1 assert report["baseline"]["randomSeed"] == 0 row = report["rows"][0] assert row["status"] == "available" assert row["comparisonQuality"] == "exact-optimal" assert row["objectiveImprovement"] == 2100.0 assert row["relaxed"]["operation"] == "diagnose_constraint_removal" assert row["relaxed"]["relaxedConstraintIds"] == ["C2_no_overlap"] assert row["relaxed"]["invocationId"] != report["baseline"]["invocationId"] assert report["objectiveSpecDigest"] assert report["worldDigest"] and report["entriesDigest"] and report["paramsDigest"] def test_inactive_assumption_has_no_re_solve_row(): world = _tight_two_order_world() for workstation in world["workstations"]: workstation.pop("teamId", None) world["teams"] = [] report = run_cp_marginal_resolve( world, start_date="2026-08-03", constraint_ids=["C12_team"], time_limit_seconds=4, ) assert report["evaluations"] == 1 assert report["rows"][0]["status"] == "inactive" assert report["baseline"]["constraintInstanceCounts"]["C12_team"] == 0 def test_diagnostic_invocation_id_prevents_same_snapshot_response_replay(): world = _tight_two_order_world() entries, params = _entries_and_params(world) _, first = run_cp_constraint_diagnostic( world, entries, params, pipeline_label="REPLAY-BASELINE", ) _, second = run_cp_constraint_diagnostic( world, entries, params, pipeline_label="REPLAY-BASELINE", ) assert first["objective"] == second["objective"] assert first["solverProcess"]["invocationId"] != second["solverProcess"]["invocationId"] assert first["solverProcess"]["requestId"] != second["solverProcess"]["requestId"] assert first["solverProcess"]["protocolVersion"] == "aps.solver-process.v2" def test_c11_removal_disables_start_gate_and_frozen_obstacle_presence(): world = build_demo_world() rule_params = EngineParams( orderIds=[], engineType="RULE", strategyTemplate="COMPREHENSIVE", planningHorizonDays=14, startDate="2026-08-03", ) get_engine("RULE").solve(world, rule_params, _counter()) for work_order in world["workOrders"][:3]: work_order["isFrozen"] = True entries, params = _entries_and_params(world, freezeWindowHours=24.0) _, baseline = run_cp_constraint_diagnostic( world, entries, params, pipeline_label="C11-BASELINE", ) _, relaxed = run_cp_constraint_diagnostic( world, entries, params, pipeline_label="C11-REMOVAL", relaxed_constraint_id="C11_freeze", ) assert baseline["constraintInstanceCounts"]["C11_freeze"] > baseline["frozenCount"] baseline_new = [slot for slot in baseline["operationSlots"] if not slot["isFrozen"]] relaxed_new = [slot for slot in relaxed["operationSlots"] if not slot["isFrozen"]] assert baseline_new and all(slot["startMin"] >= 24 * 60 for slot in baseline_new) assert relaxed_new and any(slot["startMin"] < 24 * 60 for slot in relaxed_new) assert relaxed["relaxedConstraintIds"] == ["C11_freeze"] assert relaxed["frozenCount"] == 0 assert not any(slot["isFrozen"] for slot in relaxed["operationSlots"]) def test_c2_removal_also_removes_frozen_obstacle_no_overlap(): world = build_demo_world() world["workOrders"] = [ { "id": index, "orderNo": f"FROZEN-{index}", "workstationId": 1, "lineId": 1, "plannedStartTime": "2026-08-03 08:00", "plannedEndTime": "2026-08-03 10:00", "isFrozen": True, } for index in (1, 2) ] entries, params = _entries_and_params(world, freezeWindowHours=24.0) _, baseline = run_cp_constraint_diagnostic( world, entries, params, pipeline_label="C2-FROZEN-BASELINE", ) _, relaxed = run_cp_constraint_diagnostic( world, entries, params, pipeline_label="C2-FROZEN-REMOVAL", relaxed_constraint_id="C2_no_overlap", ) assert baseline["status"] == "INFEASIBLE" assert relaxed["status"] in {"OPTIMAL", "FEASIBLE"} assert relaxed["frozenCount"] == 0 assert not any(slot["isFrozen"] for slot in relaxed["operationSlots"]) def test_no_demand_is_explicit_and_does_not_spawn_solver(): world = empty_world() result = run_cp_marginal_resolve( world, start_date="2026-08-03", constraint_ids=["C2_no_overlap"], ) assert result["status"] == "no_demand" assert result["evaluations"] == 0 assert result["rows"] == [] def test_explicit_empty_constraint_list_is_rejected(): with pytest.raises(ValueError, match="不能为空"): run_cp_marginal_resolve( _tight_two_order_world(), start_date="2026-08-03", constraint_ids=[], ) def test_cancellation_contract_exposes_parent_supervision_latency(): report = run_cp_marginal_resolve( empty_world(), start_date="2026-08-03", constraint_ids=["C2_no_overlap"], time_limit_seconds=4, ) assert report["cancellationGranularity"] == "between-solves" assert report["cancellationDoesNotInterruptActiveSolve"] is True assert report["activeSolveSupervisionTimeoutSeconds"] == 16.0 assert report["cancellationCleanupTimeoutSeconds"] == 11.0 assert report["cancellationLatencyUpperBoundSeconds"] == 27.0 def test_cancellation_is_checked_after_baseline_before_partial_rows_escape(): world = _tight_two_order_world() checks = 0 def cancel_after_baseline(): nonlocal checks checks += 1 if checks == 2: raise JobCancelled() with pytest.raises(JobCancelled): run_cp_marginal_resolve( world, start_date="2026-08-03", constraint_ids=["C2_no_overlap"], time_limit_seconds=4, cancel_check=cancel_after_baseline, ) assert checks == 2 def _fake_meta( *, status: str, objective: float | None, bound: float | None, relaxed: list[str], ) -> dict: return { "status": status, "objective": objective, "bestBound": bound, "gap": None if objective is None else round(abs(objective - bound) / abs(objective), 6), "assumptionConstraints": ["C2_no_overlap"], "activeAssumptionConstraints": ["C2_no_overlap"], "enforcedAssumptionConstraints": [] if relaxed else ["C2_no_overlap"], "relaxedConstraintIds": relaxed, "constraintInstanceCounts": {"C2_no_overlap": 1}, "diagnosticMode": True, "numSearchWorkers": 1, "randomSeed": 0, "solverProcess": { "requestId": "req", "invocationId": "inv", "operation": ( "diagnose_constraint_removal" if relaxed else "diagnose_constraint_baseline" ), "runtimeIdentity": {"safe": True}, }, } def _fake_c3_calendar(*, relaxed: bool, digest: str = "0" * 64) -> dict: return { "schemaVersion": "cp-calendar-segmented.v1", "modelMode": "assumption-gated-dual-mode", "pausePolicy": "calendar-boundary-only", "anchor": "2026-08-03 08:00", "horizonMinutes": 40320, "coverageStart": "2026-08-03", "coverageEnd": "2026-08-31", "coverageComplete": True, "normalizedCalendarDigest": digest, "calendarBucketCount": 29, "lineWindowCounts": {"1": 80}, "segmentIntervalCount": 160, "segmentIntervalLimit": 50000, "maxSegmentsPerOperation": 80, "selectedMode": "continuous" if relaxed else "calendar-boundary-only", "active": not relaxed, } def test_c3_calendar_topology_drift_is_solver_error(monkeypatch): from server.aps_domain import cp_marginal def fake_run(*_args, relaxed_constraint_id=None, **_kwargs): relaxed = relaxed_constraint_id is not None meta = _fake_meta( status="OPTIMAL", objective=100.0, bound=100.0, relaxed=["C3_calendar"] if relaxed else [], ) meta["assumptionConstraints"] = ["C3_calendar"] meta["activeAssumptionConstraints"] = ["C3_calendar"] meta["enforcedAssumptionConstraints"] = [] if relaxed else ["C3_calendar"] meta["constraintInstanceCounts"] = {"C3_calendar": 1} meta["c3Calendar"] = _fake_c3_calendar( relaxed=relaxed, digest=("1" * 64 if relaxed else "0" * 64), ) return [], meta monkeypatch.setattr(cp_marginal, "run_cp_constraint_diagnostic", fake_run) report = run_cp_marginal_resolve( _tight_c2_exact_world(), start_date="2026-08-03", constraint_ids=["C3_calendar"], ) assert report["rows"][0]["status"] == "solver_error" assert "模型实例清单不一致" in report["rows"][0]["reason"] def test_feasible_comparison_reports_bounds_not_incumbent_improvement(monkeypatch): from server.aps_domain import cp_marginal calls = 0 def fake_run(*_args, relaxed_constraint_id=None, **_kwargs): nonlocal calls calls += 1 if relaxed_constraint_id is None: return [], _fake_meta(status="FEASIBLE", objective=100.0, bound=80.0, relaxed=[]) return [], _fake_meta( status="FEASIBLE", objective=70.0, bound=50.0, relaxed=[relaxed_constraint_id], ) monkeypatch.setattr(cp_marginal, "run_cp_constraint_diagnostic", fake_run) report = run_cp_marginal_resolve( _tight_two_order_world(), start_date="2026-08-03", constraint_ids=["C2_no_overlap"], ) row = report["rows"][0] assert calls == 2 assert row["status"] == "bounded" assert row["objectiveImprovement"] is None assert row["incumbentDifference"] == 30.0 assert row["improvementLowerBound"] == 10.0 assert row["improvementUpperBound"] == 50.0 def test_feasible_to_infeasible_is_monotonicity_violation(monkeypatch): from server.aps_domain import cp_marginal def fake_run(*_args, relaxed_constraint_id=None, **_kwargs): if relaxed_constraint_id is None: return [], _fake_meta(status="OPTIMAL", objective=100.0, bound=100.0, relaxed=[]) return [], _fake_meta( status="INFEASIBLE", objective=None, bound=None, relaxed=[relaxed_constraint_id], ) monkeypatch.setattr(cp_marginal, "run_cp_constraint_diagnostic", fake_run) report = run_cp_marginal_resolve( _tight_two_order_world(), start_date="2026-08-03", constraint_ids=["C2_no_overlap"], ) assert report["status"] == "monotonicity_violation" assert report["rows"][0]["objectiveImprovement"] is None def test_optimal_negative_improvement_is_monotonicity_violation(monkeypatch): from server.aps_domain import cp_marginal def fake_run(*_args, relaxed_constraint_id=None, **_kwargs): if relaxed_constraint_id is None: return [], _fake_meta(status="OPTIMAL", objective=100.0, bound=100.0, relaxed=[]) return [], _fake_meta( status="OPTIMAL", objective=110.0, bound=110.0, relaxed=[relaxed_constraint_id], ) monkeypatch.setattr(cp_marginal, "run_cp_constraint_diagnostic", fake_run) report = run_cp_marginal_resolve( _tight_two_order_world(), start_date="2026-08-03", constraint_ids=["C2_no_overlap"], ) assert report["status"] == "monotonicity_violation" assert report["rows"][0]["status"] == "monotonicity_violation" assert report["rows"][0]["objectiveImprovement"] is None def test_monotonicity_violation_suppresses_other_available_improvements(monkeypatch): from server.aps_domain import cp_marginal def fake_run(*_args, relaxed_constraint_id=None, **_kwargs): if relaxed_constraint_id is None: meta = _fake_meta(status="OPTIMAL", objective=100.0, bound=100.0, relaxed=[]) meta["assumptionConstraints"] = ["C1_precedence", "C2_no_overlap"] meta["activeAssumptionConstraints"] = ["C1_precedence", "C2_no_overlap"] meta["enforcedAssumptionConstraints"] = ["C1_precedence", "C2_no_overlap"] meta["constraintInstanceCounts"] = {"C1_precedence": 1, "C2_no_overlap": 1} return [], meta objective = 80.0 if relaxed_constraint_id == "C1_precedence" else 110.0 meta = _fake_meta( status="OPTIMAL", objective=objective, bound=objective, relaxed=[relaxed_constraint_id], ) meta["assumptionConstraints"] = ["C1_precedence", "C2_no_overlap"] meta["activeAssumptionConstraints"] = ["C1_precedence", "C2_no_overlap"] meta["enforcedAssumptionConstraints"] = [ value for value in ("C1_precedence", "C2_no_overlap") if value != relaxed_constraint_id ] meta["constraintInstanceCounts"] = {"C1_precedence": 1, "C2_no_overlap": 1} return [], meta monkeypatch.setattr(cp_marginal, "run_cp_constraint_diagnostic", fake_run) report = run_cp_marginal_resolve( _tight_two_order_world(), start_date="2026-08-03", constraint_ids=["C1_precedence", "C2_no_overlap"], ) assert report["status"] == "monotonicity_violation" by_id = {row["constraintId"]: row for row in report["rows"]} assert by_id["C1_precedence"]["status"] == "suppressed" assert by_id["C1_precedence"]["objectiveImprovement"] is None assert by_id["C2_no_overlap"]["status"] == "monotonicity_violation" @pytest.mark.parametrize( ("relaxed_status", "row_status"), [("UNKNOWN", "unavailable"), ("MODEL_INVALID", "solver_error")], ) def test_infeasible_baseline_does_not_overclaim_unknown_or_invalid_variant( monkeypatch, relaxed_status, row_status, ): from server.aps_domain import cp_marginal def fake_run(*_args, relaxed_constraint_id=None, **_kwargs): if relaxed_constraint_id is None: return [], _fake_meta(status="INFEASIBLE", objective=None, bound=None, relaxed=[]) return [], _fake_meta( status=relaxed_status, objective=None, bound=None, relaxed=[relaxed_constraint_id], ) monkeypatch.setattr(cp_marginal, "run_cp_constraint_diagnostic", fake_run) report = run_cp_marginal_resolve( _tight_two_order_world(), start_date="2026-08-03", constraint_ids=["C2_no_overlap"], ) assert report["status"] == "partial" assert report["rows"][0]["status"] == row_status def test_model_instance_signature_mismatch_is_partial_solver_error(monkeypatch): from server.aps_domain import cp_marginal def fake_run(*_args, relaxed_constraint_id=None, **_kwargs): meta = _fake_meta( status="OPTIMAL", objective=100.0, bound=100.0, relaxed=[] if relaxed_constraint_id is None else [relaxed_constraint_id], ) if relaxed_constraint_id is not None: meta["constraintInstanceCounts"] = {"C2_no_overlap": 2} return [], meta monkeypatch.setattr(cp_marginal, "run_cp_constraint_diagnostic", fake_run) report = run_cp_marginal_resolve( _tight_two_order_world(), start_date="2026-08-03", constraint_ids=["C2_no_overlap"], ) assert report["status"] == "partial" assert report["rows"][0]["status"] == "solver_error" assert "实例清单" in report["rows"][0]["reason"] def test_feasible_baseline_to_model_invalid_is_partial_solver_error(monkeypatch): from server.aps_domain import cp_marginal def fake_run(*_args, relaxed_constraint_id=None, **_kwargs): if relaxed_constraint_id is None: return [], _fake_meta(status="OPTIMAL", objective=100.0, bound=100.0, relaxed=[]) return [], _fake_meta( status="MODEL_INVALID", objective=None, bound=None, relaxed=[relaxed_constraint_id], ) monkeypatch.setattr(cp_marginal, "run_cp_constraint_diagnostic", fake_run) report = run_cp_marginal_resolve( _tight_two_order_world(), start_date="2026-08-03", constraint_ids=["C2_no_overlap"], ) assert report["status"] == "partial" assert report["rows"][0]["status"] == "solver_error" def _request(operation: str, relaxed: list[str] | None = None) -> dict: body = { "protocolVersion": PROTOCOL_VERSION, "operation": operation, "world": {}, "entries": [], "params": EngineParams(engineType="CP").model_dump(mode="json"), "warmStart": None, "pipelineLabel": "TEST", } if operation.startswith("diagnose_"): body["relaxedConstraintIds"] = relaxed or [] body["invocationId"] = "a" * 32 return {**body, "requestId": _request_digest(body)} @pytest.mark.parametrize( "payload", [ {**_request("optimize_line_assignment"), "unknown": True}, _request("diagnose_constraint_removal", []), _request("diagnose_constraint_removal", ["C1_precedence", "C2_no_overlap"]), _request("diagnose_constraint_removal", ["UNKNOWN"]), ], ) def test_worker_strict_schema_and_single_relaxation_reject_invalid_requests(payload): payload["requestId"] = _request_digest({k: v for k, v in payload.items() if k != "requestId"}) with pytest.raises((TypeError, ValueError)): _validate_request(payload) def test_worker_rejects_v1_protocol(): request = _request("diagnose_constraint_baseline") request["protocolVersion"] = "aps.solver-process.v1" request["requestId"] = _request_digest({k: v for k, v in request.items() if k != "requestId"}) with pytest.raises(RuntimeError): _validate_request(request) @pytest.mark.parametrize("status", ["INFEASIBLE", "UNKNOWN", "MODEL_INVALID"]) def test_relaxed_response_echo_is_checked_before_nonfeasible_return(status): meta = { "pipeline": "TEST", "status": status, "relaxedConstraintIds": [], "diagnosticMode": True, "assumptionConstraints": ["C2_no_overlap"], "activeAssumptionConstraints": ["C2_no_overlap"], "enforcedAssumptionConstraints": ["C2_no_overlap"], "constraintInstanceCounts": {"C2_no_overlap": 1}, } with pytest.raises(SolverProcessError, match="松弛约束"): _validate_result_semantics( [{"orderNo": "SO-1"}], meta, expected_entries=[{"orderNo": "SO-1"}], pipeline_label="TEST", expected_relaxed_constraint_ids=["C2_no_overlap"], expected_diagnostic_mode=True, ) def test_feasible_response_rejects_invalid_bound_and_gap(): meta = { "pipeline": "TEST", "status": "OPTIMAL", "objective": 10.0, "bestBound": 11.0, "gap": 0.0, "relaxedConstraintIds": [], "diagnosticMode": False, "operationSlots": [{"orderIndex": 0, "isFrozen": False}], } with pytest.raises(SolverProcessError, match="bestBound"): _validate_result_semantics( [{"orderNo": "SO-1"}], meta, expected_entries=[{"orderNo": "SO-1"}], pipeline_label="TEST", expected_relaxed_constraint_ids=[], expected_diagnostic_mode=False, ) def test_optimal_response_requires_zero_gap_and_equal_bound(): meta = { "pipeline": "TEST", "status": "OPTIMAL", "objective": 10.0, "bestBound": 9.0, "gap": 0.1, "relaxedConstraintIds": [], "diagnosticMode": False, "operationSlots": [{"orderIndex": 0, "isFrozen": False}], } with pytest.raises(SolverProcessError, match="OPTIMAL"): _validate_result_semantics( [{"orderNo": "SO-1"}], meta, expected_entries=[{"orderNo": "SO-1"}], pipeline_label="TEST", expected_relaxed_constraint_ids=[], expected_diagnostic_mode=False, ) def test_diagnostic_active_assumptions_must_match_positive_instance_counts(): meta = { "pipeline": "TEST", "status": "INFEASIBLE", "relaxedConstraintIds": [], "diagnosticMode": True, "assumptionConstraints": ["C2_no_overlap"], "activeAssumptionConstraints": [], "enforcedAssumptionConstraints": ["C2_no_overlap"], "constraintInstanceCounts": {"C2_no_overlap": 1}, } with pytest.raises(SolverProcessError, match="实例数"): _validate_result_semantics( [{"orderNo": "SO-1"}], meta, expected_entries=[{"orderNo": "SO-1"}], pipeline_label="TEST", expected_relaxed_constraint_ids=[], expected_diagnostic_mode=True, ) def test_parent_rejects_invalid_c3_calendar_topology(): meta = { "pipeline": "TEST", "status": "INFEASIBLE", "relaxedConstraintIds": [], "diagnosticMode": True, "assumptionConstraints": ["C3_calendar"], "activeAssumptionConstraints": ["C3_calendar"], "enforcedAssumptionConstraints": ["C3_calendar"], "constraintInstanceCounts": {"C3_calendar": 1}, "c3Calendar": _fake_c3_calendar(relaxed=False, digest="not-a-digest"), } with pytest.raises(SolverProcessError, match="C3"): _validate_result_semantics( [{"orderNo": "SO-1"}], meta, expected_entries=[{"orderNo": "SO-1"}], pipeline_label="TEST", expected_relaxed_constraint_ids=[], expected_diagnostic_mode=True, ) class _Store: def __init__(self) -> None: self.data = _tight_c2_exact_world() class _Projects: def active_world_key(self) -> str: return "project-cp-marginal" def require_active_write(self) -> None: return None @pytest.fixture def cp_marginal_client(monkeypatch): import server.gateway.app as gateway from server.agent_core import async_jobs from server.state import projects install_test_auth(monkeypatch, "tenant-cp-marginal") monkeypatch.setattr(gateway, "get_store", lambda: _Store()) monkeypatch.setattr(projects, "get_project_store", lambda: _Projects()) queue = JobQueue() monkeypatch.setattr(async_jobs, "_queue", queue) client = TestClient(gateway.create_app()) assert client.post( "/api/auth/login", json={"username": "planner", "password": "test"}, ).status_code == 200 return client, queue @pytest.mark.parametrize( "params", [ {"startDate": "2026-08-03", "track": "flex"}, {"startDate": "2026-08-03", "planningHorizonDays": 0}, {"startDate": "2026-08-03", "timeLimitSeconds": "4"}, {"startDate": "2026-08-03", "constraintIds": []}, {"startDate": "2026-08-03", "constraintIds": ["UNKNOWN"]}, {"startDate": "20260803"}, ], ) def test_gateway_rejects_invalid_cp_marginal_before_submit(cp_marginal_client, params): client, queue = cp_marginal_client response = client.post( "/api/jobs", json={"kind": "cp-marginal.recompute", "params": params}, ) assert response.status_code == 422, response.text assert queue.stats()["total"] == 0 def test_gateway_submit_poll_is_tenant_project_bound(monkeypatch, cp_marginal_client): from server.aps_domain import cp_marginal client, queue = cp_marginal_client def fast_report(_world, **options): options["cancel_check"]() return {"method": "cp-one-constraint-at-a-time-resolve.v1", "rows": []} monkeypatch.setattr(cp_marginal, "run_cp_marginal_resolve", fast_report) response = client.post("/api/jobs", json={ "kind": "cp-marginal.recompute", "params": { "track": "fixed", "startDate": "2026-08-03", "constraintIds": ["C2_no_overlap"], "timeLimitSeconds": 1, }, }) assert response.status_code == 200, response.text job_id = response.json()["jobId"] record = queue.wait(job_id, timeout=10) assert record["status"] == "done" assert record["tenant_uuid"] == "tenant-cp-marginal" assert record["project_id"] == "project-cp-marginal" assert client.get(f"/api/jobs/{job_id}").json()["job"]["result"]["method"].startswith("cp-") def test_gateway_real_cp_marginal_submit_and_poll(cp_marginal_client): client, queue = cp_marginal_client response = client.post("/api/jobs", json={ "kind": "cp-marginal.recompute", "params": { "track": "fixed", "startDate": "2026-08-03", "planningHorizonDays": 3, "constraintIds": ["C2_no_overlap"], "timeLimitSeconds": 8, }, }) assert response.status_code == 200, response.text job_id = response.json()["jobId"] record = queue.wait(job_id, timeout=20) assert record["status"] == "done" result = record["result"] assert result["status"] == "completed" assert result["evaluations"] == 2 assert result["rows"][0]["objectiveImprovement"] == 2100.0