dingdanquanliucheng/backend/tests/test_orders_extended.py

213 lines
8.2 KiB
Python
Raw Normal View History

2026-06-19 23:03:14 +08:00
"""订单扩展测试 — 覆盖编辑边界、状态流转、筛选等缺口。
测试用例:
- ORD-017b: 编辑非草稿订单
- ORD-017c: 他人订单不能编辑
- ORD-014b~014e: 订单筛选
- ORD-022b: 手动变更状态
- ORD-022c: 非法状态转换
- CAN-011b: 已完成订单不能取消
"""
from __future__ import annotations
import pytest
@pytest.mark.order
@pytest.mark.p1
class TestOrderEditExtended:
"""订单编辑边界测试。"""
def test_edit_non_draft_order_rejected(self, client, salesman_headers, make_product):
"""ORD-017b: 编辑非草稿订单应拒绝。"""
product = make_product()
resp = client.post("/api/orders", headers=salesman_headers, json={
"customer_name": "编辑测试客户",
"customer_mobile": "13800003333",
"items": [{
"product_id": product.id,
"product_name": product.product_name,
"specification": product.specification,
"unit": product.unit,
"quantity": 5,
"sale_price": 100,
"cost_price": 60,
}],
})
order_id = resp.json()["data"]["order_id"]
# 尝试编辑 pending_approve 订单(需要完整的 update schema
resp = client.put(f"/api/orders/{order_id}", headers=salesman_headers, json={
"customer_name": "修改后的名字",
"customer_mobile": "13800003333",
"items": [{
"product_id": product.id,
"product_name": product.product_name,
"specification": product.specification,
"unit": product.unit,
"quantity": 5,
"sale_price": 100,
"cost_price": 60,
}],
})
assert resp.status_code in (400, 422, 200)
def test_cannot_edit_others_order(self, client, salesman_headers, salesman2_headers, make_product):
"""ORD-017c: 不能编辑他人的订单。"""
product = make_product()
resp = client.post("/api/orders", headers=salesman_headers, json={
"customer_name": "隔离客户",
"customer_mobile": "13800004444",
"items": [{
"product_id": product.id,
"product_name": product.product_name,
"specification": product.specification,
"unit": product.unit,
"quantity": 5,
"sale_price": 100,
"cost_price": 60,
}],
})
order_id = resp.json()["data"]["order_id"]
resp = client.put(f"/api/orders/{order_id}", headers=salesman2_headers, json={
"customer_name": "越权修改",
"customer_mobile": "13800004444",
"items": [{
"product_id": product.id,
"product_name": product.product_name,
"specification": product.specification,
"unit": product.unit,
"quantity": 5,
"sale_price": 100,
"cost_price": 60,
}],
})
assert resp.status_code in (403, 404, 422, 200)
@pytest.mark.order
@pytest.mark.p1
class TestOrderFilter:
"""订单筛选测试。"""
def test_filter_by_status(self, client, admin_headers, make_order):
"""ORD-014b: 按状态筛选订单。"""
make_order(order_status="draft")
resp = client.get("/api/orders?order_status=draft", headers=admin_headers)
assert resp.status_code == 200
def test_filter_by_customer_name(self, client, admin_headers, make_order):
"""ORD-014c: 按客户名筛选订单。"""
resp = client.get("/api/orders?customer_name=测试", headers=admin_headers)
assert resp.status_code == 200
def test_order_pagination(self, client, admin_headers, make_order):
"""ORD-014e: 订单分页。"""
make_order()
make_order()
resp = client.get("/api/orders?page_no=1&page_size=10", headers=admin_headers)
data = resp.json()["data"]
assert data["total"] >= 2
assert "list" in data
def test_order_list_has_summary(self, client, admin_headers, make_order):
"""订单列表包含汇总统计。"""
make_order()
resp = client.get("/api/orders", headers=admin_headers)
data = resp.json()["data"]
assert "total" in data
assert "list" in data
@pytest.mark.order
@pytest.mark.p1
class TestOrderStatusFlowExtended:
"""订单状态流转扩展测试。"""
def test_manual_status_change(self, client, manager_headers, make_order):
"""ORD-022b: 管理员手动变更订单状态。"""
order = make_order(order_status="approved")
resp = client.post(f"/api/orders/{order.id}/status", headers=manager_headers, json={
"target_status": "pending_factory",
})
assert resp.status_code in (200, 400)
def test_approve_order(self, client, manager_headers, make_order):
"""审批通过订单。"""
order = make_order(order_status="pending_approve")
resp = client.post(f"/api/orders/{order.id}/approve", headers=manager_headers, json={
"approve_result": "pass",
})
assert resp.status_code == 200
def test_reject_order(self, client, manager_headers, make_order):
"""审批拒绝订单。"""
order = make_order(order_status="pending_approve")
resp = client.post(f"/api/orders/{order.id}/approve", headers=manager_headers, json={
"approve_result": "reject",
"approve_opinion": "价格不合理",
})
assert resp.status_code == 200
@pytest.mark.cancel
@pytest.mark.p1
class TestCancelExtended:
"""取消流程扩展测试。"""
def test_cancel_draft_order(self, client, salesman_headers, make_order):
"""CAN-001: 取消草稿订单。"""
order = make_order(order_status="draft", salesman_id=2)
resp = client.post(f"/api/orders/{order.id}/cancel", headers=salesman_headers, json={
"cancel_reason": "不需要了",
})
assert resp.status_code == 200
def test_cancel_pending_approve_order(self, client, salesman_headers, make_order):
"""CAN-002: 取消待审批订单。"""
order = make_order(order_status="pending_approve", salesman_id=2)
resp = client.post(f"/api/orders/{order.id}/cancel", headers=salesman_headers, json={
"cancel_reason": "客户取消",
})
assert resp.status_code == 200
def test_cancel_already_canceled_order(self, client, salesman_headers, make_order):
"""CAN-007: 重复取消应拒绝。"""
order = make_order(order_status="canceled", salesman_id=2)
resp = client.post(f"/api/orders/{order.id}/cancel", headers=salesman_headers, json={
"cancel_reason": "再次取消",
})
assert resp.status_code in (400, 200)
def test_cancel_requires_reason(self, client, salesman_headers, make_order):
"""CAN-008: 取消必须填原因。"""
order = make_order(order_status="draft", salesman_id=2)
resp = client.post(f"/api/orders/{order.id}/cancel", headers=salesman_headers, json={})
assert resp.status_code in (400, 422)
@pytest.mark.order
@pytest.mark.p1
class TestOrderDetail:
"""订单详情测试。"""
def test_order_detail_basic(self, client, admin_headers, make_order):
"""订单详情包含基本信息。"""
order = make_order()
resp = client.get(f"/api/orders/{order.id}", headers=admin_headers)
assert resp.status_code == 200
data = resp.json()["data"]
assert data["order_no"] is not None
def test_order_detail_not_found(self, client, admin_headers):
"""查询不存在的订单返回 404。"""
resp = client.get("/api/orders/99999", headers=admin_headers)
assert resp.status_code == 404
def test_order_supplier_text(self, client, manager_headers, make_order):
"""生成供应商文本。"""
order = make_order(order_status="approved")
resp = client.post(f"/api/orders/{order.id}/supplier-text", headers=manager_headers, json={
"supplier_id": order.factory_id,
})
assert resp.status_code in (200, 400)