aps-agent/tests/golden/test_order_management.py

140 lines
4.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ============================================================
# OR-01 订单管理黄金测试(moduleId: golden-order-management, 发版门禁 §14.3)
# 固化订单池写入的最小闭环:
# O1 新增订单进入后续排产输入
# O2 取消订单从后续排产输入排除
# O3 P2 确认卡摘要可生成并校验成品物料
# O4 审计事件同时满足 at/ts 契约且哈希链可校验
# ============================================================
from __future__ import annotations
import pytest
from server.agent_core.audit import write_audit
from server.agent_core.registry import verify_audit_chain
from server.aps_domain.orders import apply_order_action, confirmation_for_order_action
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(world):
"""按当前世界最大 ID 起号,避免新增订单与种子 ID 冲突。"""
tables = {
"audit": "auditEvents",
"conflict": "conflicts",
"log": "logs",
"productionOrder": "productionOrders",
"salesOrder": "salesOrders",
"scheduleVersion": "scheduleVersions",
"workOrder": "workOrders",
}
counters: dict[str, int] = {}
def next_id(kind: str) -> int:
if kind not in counters:
rows = world.get(tables.get(kind, kind + "s"), [])
counters[kind] = max((r.get("id", 0) for r in rows if isinstance(r, dict)), default=0)
counters[kind] += 1
return counters[kind]
return next_id
def _run(world):
"""执行一次规则引擎排产。"""
params = EngineParams(
orderIds=[],
engineType="RULE",
strategyTemplate="COMPREHENSIVE",
planningHorizonDays=14,
startDate=fmt_date(add_minutes(today0(), 24 * 60)),
)
return get_engine("RULE").solve(world, params, _next_id_factory(world))
def test_order_cancel_excludes_from_scheduling():
"""取消订单后,该订单不再生成 PO,参与排产订单数从 7 降为 6。"""
world = seed_world()
target = world["salesOrders"][0]
apply_order_action(world, _next_id_factory(world), "order.cancel", {"id": target["id"]})
result = _run(world)
assert result.orderCount == 6
assert all(po["salesOrderId"] != target["id"] for po in world["productionOrders"])
assert target["status"] == "CANCELLED"
assert all(item["status"] == "CANCELLED" for item in target["items"])
def test_order_upsert_create_enters_scheduling():
"""新增已确认订单后,下一版排产应把它纳入订单池。"""
world = seed_world()
payload = {
"customerName": "试点客户",
"customerLevel": "A",
"deliveryDate": fmt_date(add_minutes(today0(), 9 * 24 * 60)),
"priority": 2,
"status": "CONFIRMED",
"productId": 1,
"quantity": 120,
"isRush": False,
"specialRequirements": "OR-01 golden",
}
applied = apply_order_action(world, _next_id_factory(world), "order.upsert", payload)
order = applied["order"]
result = _run(world)
assert applied["created"] is True
assert len(world["salesOrders"]) == 8
assert result.orderCount == 8
assert any(po["salesOrderId"] == order["id"] for po in world["productionOrders"])
assert order["orderNo"].startswith("SO")
def test_order_confirmation_summary_validates_product():
"""订单确认卡必须可解释影响范围,并拒绝非成品物料。"""
world = seed_world()
title, lines = confirmation_for_order_action(world, "order.upsert", {
"customerName": "试点客户",
"customerLevel": "VIP",
"deliveryDate": fmt_date(add_minutes(today0(), 8 * 24 * 60)),
"priority": 1,
"status": "CONFIRMED",
"productId": 1,
"quantity": 80,
})
summary = "\n".join(lines)
assert "新增" in title
assert "智能控制器A型" in summary
assert "P2" in summary
with pytest.raises(ValueError):
confirmation_for_order_action(world, "order.upsert", {
"customerName": "错误物料客户",
"deliveryDate": fmt_date(add_minutes(today0(), 8 * 24 * 60)),
"productId": 10,
"quantity": 1,
})
def test_audit_event_exposes_at_and_ts_contract():
"""新增审计事件应同时提供 at/ts,前端治理页与旧数据均可读取。"""
world = seed_world()
event = write_audit(
world,
_next_id_factory(world),
actor="tester",
category="GATE",
action="order.upsert.stage",
target={"type": "SALES_ORDER", "id": None},
power="P2",
rationale={"confirmId": "unit-test"},
)
assert event["at"] == event["ts"]
assert verify_audit_chain(world["auditEvents"])["ok"] is True