213 lines
8.2 KiB
Python
213 lines
8.2 KiB
Python
"""订单扩展测试 — 覆盖编辑边界、状态流转、筛选等缺口。
|
||
|
||
测试用例:
|
||
- 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)
|