"""二次分配方案入参校验(schema 层,秒级)。 守的是:比例红线(字符串入参、两位小数、超限拒绝不静默舍入)与 参与人去重——uk(business_id, personnel_id) 的友好前置校验。 明细行同批创建的正常路径由集成测试覆盖,这里集中打边界。 """ import pytest from pydantic import ValidationError from app.schemas.business import SecondaryAllocationIn def _make(personnel_id: int = 1, ratio: str = "50.00") -> dict: return {"personnel_id": personnel_id, "ratio": ratio} class TestRatioBoundary: """比例边界:0 / 满额 / 超限 / 精度 / 非数字。""" @pytest.mark.parametrize( ("ratio", "ok"), [ ("0", False), # 必须大于 0 ("0.01", True), ("33.33", True), ("100", True), ("100.00", True), ("100.01", False), # 超上限 ("-5", False), # 负数 ("33.333", False), # 三位小数:拒绝不静默舍入 ("abc", False), # 非数字 ("", False), # 空串 ], ) def test_比例边界(self, ratio: str, *, ok: bool) -> None: if ok: assert SecondaryAllocationIn(**_make(ratio=ratio)).ratio == ratio else: with pytest.raises(ValidationError): SecondaryAllocationIn(**_make(ratio=ratio)) def test_人员id必须为正整数(self) -> None: with pytest.raises(ValidationError): SecondaryAllocationIn(**_make(personnel_id=0)) with pytest.raises(ValidationError): SecondaryAllocationIn(**_make(personnel_id=-1)) class TestDuplicate: """参与人去重(Create/Update 两个入参模型同款校验)。""" def test_创建入参_重复参与人被拒(self) -> None: from app.schemas.business import BusinessCreateIn payload = { "customer_id": 1, "items": [ { "product_name": "t_产品", "product_category": "municipal_water", "quantity": "1.00", "sale_price": "10.00", } ], "secondary_allocations": [ _make(personnel_id=5, ratio="30.00"), _make(personnel_id=5, ratio="20.00"), ], } with pytest.raises(ValidationError, match="重复"): BusinessCreateIn(**payload) def test_编辑入参_重复参与人被拒(self) -> None: from app.schemas.business import BusinessUpdateIn payload = { "customer_id": 1, "items": [ { "product_name": "t_产品", "product_category": "municipal_water", "quantity": "1.00", "sale_price": "10.00", } ], "secondary_allocations": [_make(personnel_id=5), _make(personnel_id=5)], } with pytest.raises(ValidationError, match="重复"): BusinessUpdateIn(**payload) def test_不同参与人合法(self) -> None: from app.schemas.business import BusinessCreateIn payload = { "customer_id": 1, "items": [ { "product_name": "t_产品", "product_category": "municipal_water", "quantity": "1.00", "sale_price": "10.00", } ], "secondary_allocations": [ _make(personnel_id=5, ratio="30.00"), _make(personnel_id=6, ratio="20.00"), ], } model = BusinessCreateIn(**payload) assert len(model.secondary_allocations) == 2