135 lines
4.9 KiB
Python
135 lines
4.9 KiB
Python
"""PROD-001 ~ PROD-008: 产品与产品分类管理测试。
|
|
|
|
覆盖功能点:
|
|
- 产品分类 CRUD
|
|
- 产品 CRUD
|
|
- 产品状态校验
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
|
|
@pytest.mark.product
|
|
@pytest.mark.p1
|
|
class TestProductCategory:
|
|
"""PROD-001 ~ PROD-003: 产品分类测试。"""
|
|
|
|
def test_create_category_success(self, client, admin_headers):
|
|
"""PROD-001: 新增产品分类成功。"""
|
|
resp = client.post("/api/product-categories", headers=admin_headers, json={
|
|
"category_name": "测试分类",
|
|
"category_code": "TEST_CAT_001",
|
|
"sort_no": 1,
|
|
})
|
|
assert resp.status_code == 200
|
|
data = resp.json()
|
|
assert data["code"] == 0
|
|
|
|
def test_duplicate_category_code(self, client, admin_headers, db_session):
|
|
"""PROD-002: 分类编码重复应返回 40006。"""
|
|
from backend.app.models.business import ProductCategory
|
|
cat = ProductCategory(category_name="已有分类", category_code="EXISTING_CODE", status=1)
|
|
db_session.add(cat)
|
|
db_session.flush()
|
|
|
|
resp = client.post("/api/product-categories", headers=admin_headers, json={
|
|
"category_name": "新分类",
|
|
"category_code": "EXISTING_CODE",
|
|
})
|
|
assert resp.status_code in (400, 409)
|
|
|
|
def test_disable_category(self, client, admin_headers, db_session):
|
|
"""PROD-003: 停用分类。"""
|
|
from backend.app.models.business import ProductCategory
|
|
cat = ProductCategory(category_name="待停用", category_code="TO_DISABLE", status=1)
|
|
db_session.add(cat)
|
|
db_session.flush()
|
|
|
|
resp = client.put(f"/api/product-categories/{cat.id}", headers=admin_headers, json={
|
|
"category_name": "待停用",
|
|
"category_code": "TO_DISABLE",
|
|
"status": 0,
|
|
})
|
|
assert resp.status_code == 200
|
|
|
|
|
|
@pytest.mark.product
|
|
@pytest.mark.p1
|
|
class TestProduct:
|
|
"""PROD-004 ~ PROD-008: 产品测试。"""
|
|
|
|
def test_create_product_success(self, client, admin_headers, db_session):
|
|
"""PROD-004: 新增产品成功。
|
|
|
|
注意: CreateProductRequest 需要 specifications 列表(至少一个规格)
|
|
"""
|
|
from backend.app.models.business import ProductCategory
|
|
cat = ProductCategory(category_name="产品测试分类", category_code="PROD_TEST_CAT", status=1)
|
|
db_session.add(cat)
|
|
db_session.flush()
|
|
|
|
resp = client.post("/api/products", headers=admin_headers, json={
|
|
"product_name": "测试产品X",
|
|
"category_id": cat.id,
|
|
"specifications": [{
|
|
"specification": "100x100cm",
|
|
"unit": "张",
|
|
"cost_price": 50,
|
|
"sale_price": 100,
|
|
}],
|
|
})
|
|
assert resp.status_code == 200
|
|
data = resp.json()
|
|
assert data["code"] == 0
|
|
|
|
def test_product_list_filter(self, client, admin_headers, make_product):
|
|
"""PROD-005: 产品列表筛选。"""
|
|
make_product(product_name="铝板产品")
|
|
make_product(product_name="钢材产品")
|
|
resp = client.get("/api/products?keyword=铝板", headers=admin_headers)
|
|
assert resp.status_code == 200
|
|
|
|
def test_product_detail(self, client, admin_headers, make_product):
|
|
"""PROD-006: 产品详情。"""
|
|
product = make_product()
|
|
resp = client.get(f"/api/products/{product.id}", headers=admin_headers)
|
|
assert resp.status_code == 200
|
|
data = resp.json()["data"]
|
|
assert data["product_name"] is not None
|
|
|
|
def test_disabled_product_flag(self, client, admin_headers, make_product):
|
|
"""PROD-007: 禁用产品标识。"""
|
|
product = make_product(status=0)
|
|
resp = client.get(f"/api/products/{product.id}", headers=admin_headers)
|
|
assert resp.status_code == 200
|
|
# 产品详情接口可能过滤已删除(deleted=1)的产品,但 status=0 的产品应该能查到
|
|
data = resp.json()["data"]
|
|
# 确认返回的是该产品
|
|
assert data["product_name"] == product.product_name
|
|
|
|
|
|
@pytest.mark.product
|
|
@pytest.mark.p0
|
|
class TestProductOrderConstraint:
|
|
"""PROD-007: 禁用产品下单约束测试。"""
|
|
|
|
def test_disabled_product_cannot_order(self, client, salesman_headers, make_product):
|
|
"""PROD-007: 禁用产品不能创建订单。"""
|
|
product = make_product(status=0)
|
|
resp = client.post("/api/orders", headers=salesman_headers, json={
|
|
"customer_name": "禁用产品测试客户",
|
|
"customer_mobile": "1380000P007",
|
|
"items": [{
|
|
"product_id": product.id,
|
|
"product_name": product.product_name,
|
|
"specification": product.specification,
|
|
"unit": product.unit,
|
|
"quantity": 1,
|
|
"sale_price": 100,
|
|
"cost_price": 60,
|
|
}],
|
|
})
|
|
# 应被拒绝或有警告
|
|
assert resp.status_code in (400, 200)
|