"""parse_util 运费/手续费计算函数以及订单运费路由的单元测试。""" from __future__ import annotations import pytest from app.api.routes.scrape import order_get_shipping_fee from app.models.scrape import OrderItem, OrderShippingFeeRequest from app.utils.parse_util import ( FRANCHISE_SHIPPING_FEE_PER_STORE, HANDLE_FEE_BELOW, OFFICIAL_SHIPPING_FEE_LOW, OFFICIAL_SHIPPING_FEE_MID, get_franchise_shipping_fee, get_handle_fee, get_shipping_fee, ) # --- 官方店铺运费规则: 边界与典型值 --- @pytest.mark.parametrize( "amount, expected", [ (0, OFFICIAL_SHIPPING_FEE_LOW), # < 1000 (999, OFFICIAL_SHIPPING_FEE_LOW), # < 1000 (1000, OFFICIAL_SHIPPING_FEE_MID), # = 1000 -> 385 (1200, OFFICIAL_SHIPPING_FEE_MID), # 1000 <= < 1500 (1499, OFFICIAL_SHIPPING_FEE_MID), # < 1500 (1500, 0), # = 1500 -> 免运费 (2000, 0), # > 1500 ], ) def test_get_shipping_fee_boundaries(amount: int, expected: int) -> None: assert get_shipping_fee(amount) == expected # --- 加盟店运费规则: 按家数 --- @pytest.mark.parametrize( "count, expected", [ (-1, 0), (0, 0), (1, FRANCHISE_SHIPPING_FEE_PER_STORE), (3, 3 * FRANCHISE_SHIPPING_FEE_PER_STORE), ], ) def test_get_franchise_shipping_fee(count: int, expected: int) -> None: assert get_franchise_shipping_fee(count) == expected # --- 通贩手续费规则: 边界与典型值 --- @pytest.mark.parametrize( "amount, expected", [ (0, HANDLE_FEE_BELOW), (4999, HANDLE_FEE_BELOW), (5000, 0), # = 5000 -> 免手续费 (10000, 0), ], ) def test_get_handle_fee_boundaries(amount: int, expected: int) -> None: assert get_handle_fee(amount) == expected # ---- 路由 order_get_shipping_fee 端到端 ---- # 直接调用 async 路由函数,绕开 FastAPI 应用初始化(浏览器/redis 等)。 def _payload(*items: tuple[int, int, str]) -> OrderShippingFeeRequest: """items: (goods_number, goods_price, supplier) 三元组列表""" return OrderShippingFeeRequest( item_list=[ OrderItem( goods_id=f"g{idx}", goods_number=n, goods_price=p, supplier=s, ) for idx, (n, p, s) in enumerate(items) ] ) async def test_route_all_official_below_threshold() -> None: # 1 件 999 元官方商品 -> 官方运费 440, 手续费 240 resp = await order_get_shipping_fee(_payload((1, 999, ""))) data = resp.data assert data.goods_amount == 999 assert data.shipping_fee == 440 assert data.handle_amount == 240 assert data.order_amount == 999 + 440 + 240 async def test_route_all_official_at_free_shipping_boundary() -> None: # 1 件 1500 元官方商品 -> 官方运费 0(>=1500), 手续费 240(<5000) resp = await order_get_shipping_fee(_payload((1, 1500, ""))) data = resp.data assert data.goods_amount == 1500 assert data.shipping_fee == 0 assert data.handle_amount == 240 assert data.order_amount == 1500 + 240 async def test_route_only_franchise_no_official_shipping() -> None: # 全部来自 1 家加盟店 -> 官方运费 0(无官方商品), 加盟运费 800 resp = await order_get_shipping_fee(_payload((2, 400, "ShopA"))) data = resp.data assert data.goods_amount == 800 assert data.shipping_fee == 800 assert data.handle_amount == 240 assert data.order_amount == 800 + 800 + 240 async def test_route_two_franchise_stores_dedup_by_supplier() -> None: # 2 家加盟店各 1 件 + 官方满 1500 -> 官方 0 + 加盟 1600 resp = await order_get_shipping_fee( _payload( (1, 1500, ""), (1, 500, "ShopA"), (1, 500, "ShopA"), # 同店,不重复计费 (1, 500, "ShopB"), ) ) data = resp.data assert data.goods_amount == 3000 assert data.shipping_fee == 0 + 2 * 800 # 官方 0 + 2 家加盟 assert data.handle_amount == 240 # 3000 < 5000 assert data.order_amount == 3000 + 1600 + 240 async def test_route_total_amount_over_5000_handle_fee_free() -> None: # 全部商品总额(官方+加盟)>= 5000 时手续费免 resp = await order_get_shipping_fee( _payload( (1, 3000, ""), # 官方 3000 (1, 2000, "ShopA"), # 加盟 2000 ) ) data = resp.data assert data.goods_amount == 5000 # 官方 3000 -> 1500<=3000 -> 0 运费; 加盟 1 家 -> 800 assert data.shipping_fee == 0 + 800 assert data.handle_amount == 0 # 总额 5000 -> 免 assert data.order_amount == 5000 + 800 async def test_route_backward_compatible_default_supplier() -> None: # 旧调用方不传 supplier 字段 -> 默认空串 -> 全部按官方 payload = OrderShippingFeeRequest.model_validate( {"item_list": [{"goods_id": "x", "goods_number": 1, "goods_price": 800}]} ) resp = await order_get_shipping_fee(payload) data = resp.data # 800 < 1000 -> 官方运费 440, 手续费 240 assert data.shipping_fee == 440 assert data.handle_amount == 240 assert data.order_amount == 800 + 440 + 240