aps-agent/tests/golden/test_constraints.py

99 lines
3.8 KiB
Python
Raw Normal View History

# ============================================================
# SC-04 约束配置中心黄金测试
# ============================================================
from __future__ import annotations
from server.agent_core.intent import parse_fast
from server.aps_domain.constraints import (
apply_profile_save, engine_constraint_flags, get_constraint_profile,
hard_blocking_conflicts, material_shortage_severity,
)
from server.engines import get_engine
from server.engines.base import EngineParams
from server.state.seed import seed_world
from server.timeutil import add_minutes, fmt_date, today0
def _next_id_factory():
counters: dict[str, int] = {}
def next_id(kind: str) -> int:
counters[kind] = counters.get(kind, 0) + 1
return counters[kind]
return next_id
def _run(world, strategy="COMPREHENSIVE"):
start = fmt_date(add_minutes(today0(), 24 * 60))
params = EngineParams(
orderIds=[], engineType="RULE", strategyTemplate=strategy,
planningHorizonDays=14, startDate=start,
constraints=engine_constraint_flags(world),
)
return get_engine("RULE").solve(world, params, _next_id_factory())
def _zero_all_stocks(world):
for m in world["materials"]:
if m.get("type") != "FINISHED_PRODUCT":
m["stock"] = 0
m["inTransit"] = 0
def test_default_material_kit_is_soft():
world = seed_world()
assert material_shortage_severity(world) == "MINOR"
c6 = next(c for c in get_constraint_profile(world)["constraints"] if c["id"] == "C6_material_kit")
assert c6["kind"] == "soft" and c6["enabled"]
def test_disable_material_kit_skips_shortage_conflict():
world = seed_world()
_zero_all_stocks(world)
apply_profile_save(world, {"constraints": {"C6_material_kit": {"enabled": False}}})
assert material_shortage_severity(world) is None
_run(world)
shortages = [c for c in world["conflicts"] if c["conflictType"] == "MATERIAL_SHORTAGE"]
assert shortages == []
def test_soft_kit_reports_risk_without_blocking_publish_gate():
world = seed_world()
_zero_all_stocks(world)
# 默认软约束
result = _run(world)
shortages = [c for c in world["conflicts"] if c["conflictType"] == "MATERIAL_SHORTAGE"]
assert shortages
assert all(c["severity"] == "MINOR" for c in shortages)
blockers = hard_blocking_conflicts(world, version_id=result.versionId)
assert not any(b["conflictType"] == "MATERIAL_SHORTAGE" for b in blockers)
def test_hard_kit_blocks_publish_gate():
world = seed_world()
_zero_all_stocks(world)
apply_profile_save(world, {"constraints": {"C6_material_kit": {"kind": "hard", "enabled": True}}})
result = _run(world)
shortages = [c for c in world["conflicts"] if c["conflictType"] == "MATERIAL_SHORTAGE"]
assert shortages and all(c["severity"] == "MAJOR" for c in shortages)
blockers = hard_blocking_conflicts(world, version_id=result.versionId)
assert any(b["conflictType"] == "MATERIAL_SHORTAGE" for b in blockers)
def test_version_snapshots_constraint_profile():
world = seed_world()
apply_profile_save(world, {"constraints": {"C6_material_kit": {"enabled": False}}})
_run(world)
snap = world["scheduleVersions"][-1].get("constraintProfile")
assert snap and snap["constraints"]["C6_material_kit"]["enabled"] is False
def test_intent_constraint_commands():
world = seed_world()
assert parse_fast("查看约束配置", world).intent == "constraint.profile.query"
assert parse_fast("关闭物料齐套", world).intent == "constraint.profile.save"
assert parse_fast("关闭物料齐套", world).params["constraints"]["C6_material_kit"]["enabled"] is False
assert parse_fast("齐套改为硬约束", world).params["constraints"]["C6_material_kit"]["kind"] == "hard"
assert parse_fast("恢复默认约束", world).params.get("resetDefaults") is True