baseline: 批次A-D 成果 + membership 半成品(测试红)
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
"""商品中心集成测试:CRUD + 多租户隔离。
|
||||
|
||||
使用同步 APIClient(走 WSGI handler + adrf view),因为:
|
||||
- DRF/adrf 的 auth/permissions/pagination 都是同步触发的
|
||||
- 测试 client 同步请求 adrf 的 async view 时 Django 自动用 async_to_sync 包装
|
||||
- 比 AsyncClient 稳定,async_to_sync 不会触发 RuntimeError
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from model_bakery import baker
|
||||
from rest_framework.test import APIClient
|
||||
from rest_framework_simplejwt.tokens import RefreshToken
|
||||
|
||||
from apps.catalog.models import Product, Unit, UnitConversion
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def auth_client(db, user, tenant):
|
||||
client = APIClient()
|
||||
refresh = RefreshToken.for_user(user)
|
||||
client.credentials(
|
||||
HTTP_AUTHORIZATION=f"Bearer {refresh.access_token}",
|
||||
HTTP_X_TENANT_ID=tenant.code,
|
||||
)
|
||||
return client
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def anon(db):
|
||||
return APIClient()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_create_product(auth_client, tenant, org):
|
||||
payload = {
|
||||
"code": "P001",
|
||||
"name": "测试商品",
|
||||
"spec": "500ml",
|
||||
"barcode": "6901234567890",
|
||||
"cost_price": "10.0000",
|
||||
"sale_price": "15.0000",
|
||||
"status": "active",
|
||||
}
|
||||
resp = auth_client.post("/api/v1/catalog/products/", payload, format="json")
|
||||
assert resp.status_code == 201, resp.content
|
||||
data = resp.json()
|
||||
assert data["code"] == "P001"
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_list_products(auth_client, tenant):
|
||||
baker.make(Product, tenant=tenant, code="P001", name="商品1")
|
||||
baker.make(Product, tenant=tenant, code="P002", name="商品2")
|
||||
resp = auth_client.get("/api/v1/catalog/products/")
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["count"] == 2
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_tenant_isolation(auth_client, tenant, other_tenant):
|
||||
baker.make(Product, tenant=other_tenant, code="OTHER_P1")
|
||||
baker.make(Product, tenant=tenant, code="MY_P1")
|
||||
|
||||
resp = auth_client.get("/api/v1/catalog/products/")
|
||||
codes = {p["code"] for p in resp.json()["results"]}
|
||||
assert "MY_P1" in codes
|
||||
assert "OTHER_P1" not in codes
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_soft_delete_excluded(auth_client, tenant):
|
||||
baker.make(Product, tenant=tenant, code="DEL_P", is_deleted=True)
|
||||
resp = auth_client.get("/api/v1/catalog/products/")
|
||||
codes = {x["code"] for x in resp.json()["results"]}
|
||||
assert "DEL_P" not in codes
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_search(auth_client, tenant):
|
||||
baker.make(Product, tenant=tenant, code="P001", name="苹果")
|
||||
baker.make(Product, tenant=tenant, code="P002", name="香蕉")
|
||||
resp = auth_client.get("/api/v1/catalog/products/?search=苹果")
|
||||
assert resp.json()["count"] == 1
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_anon_cannot_access(anon, tenant):
|
||||
anon.credentials(HTTP_X_TENANT_ID=tenant.code)
|
||||
resp = anon.get("/api/v1/catalog/products/")
|
||||
assert resp.status_code == 401
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_product_units_expose_sale_and_cost_price_per_source_unit(
|
||||
auth_client, tenant, org
|
||||
):
|
||||
base = baker.make(Unit, tenant=tenant, code="bottle", name="瓶", is_base=True)
|
||||
box = baker.make(Unit, tenant=tenant, code="box", name="箱", is_base=False)
|
||||
product = baker.make(
|
||||
Product,
|
||||
tenant=tenant,
|
||||
code="PRICE-UNIT",
|
||||
name="单价换算测试",
|
||||
base_unit=base,
|
||||
cost_price="3.0000",
|
||||
sale_price="5.0000",
|
||||
)
|
||||
UnitConversion.objects.create(
|
||||
tenant=tenant, product=product, unit=box, rate="24"
|
||||
)
|
||||
|
||||
response = auth_client.get(f"/api/v1/catalog/products/{product.id}/units/")
|
||||
|
||||
assert response.status_code == 200
|
||||
by_unit = {item["unit_code"]: item for item in response.json()["units"]}
|
||||
assert by_unit["bottle"]["cost_price"] == "3.0000"
|
||||
assert by_unit["box"]["cost_price"] == "72.0000"
|
||||
assert by_unit["box"]["price"] == "120.0000"
|
||||
Reference in New Issue
Block a user