118667d01f
/api/order/get_shipping_fee 与购物车加入逻辑此前只认官方自营详情页,加盟店运费分桶 仅靠自由文本 supplier 判断,未与其他店铺列表的 tenpo_cd 打通。补充 OrderItem/ CargoItemRequest 的 tenpo_cd/branch_number 字段,运费计算优先按 tenpo_cd 分桶 (无则回退 supplier 保持兼容),cargo_service 加购 URL 同步带上店铺参数。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
112 lines
4.1 KiB
Python
112 lines
4.1 KiB
Python
"""SurugayaClient 中「指定店铺」相关能力的单元测试:
|
|
|
|
- _build_product_url 携带 tenpo_cd/branch_number 构造跳转到指定店铺报价的详情页 URL
|
|
- fetch_other_shops 独立于 fetch_detail,仅返回 other_shop_list(解耦双倍抓取成本)
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from app.core.config import Settings
|
|
from app.models.scrape import DetailRequest, OtherShopItem
|
|
from app.services.surugaya_client import SurugayaClient
|
|
|
|
|
|
def _make_client() -> SurugayaClient:
|
|
return SurugayaClient(
|
|
settings=Settings(_env_file=None),
|
|
browser_pool=None,
|
|
session_manager=None,
|
|
session_store=None,
|
|
)
|
|
|
|
|
|
class TestBuildProductUrl:
|
|
def test_without_shop_params(self) -> None:
|
|
client = _make_client()
|
|
assert client._build_product_url("220035970") == "https://www.suruga-ya.jp/product/detail/220035970"
|
|
|
|
def test_with_tenpo_cd_and_branch_number(self) -> None:
|
|
client = _make_client()
|
|
url = client._build_product_url("220035970", tenpo_cd="400451", branch_number="0003")
|
|
assert url == "https://www.suruga-ya.jp/product/detail/220035970?tenpo_cd=400451&branch_number=0003"
|
|
|
|
def test_missing_product_id_raises(self) -> None:
|
|
client = _make_client()
|
|
with pytest.raises(Exception):
|
|
client._build_product_url(None)
|
|
|
|
|
|
class TestFetchOtherShops:
|
|
@pytest.mark.asyncio
|
|
async def test_returns_goods_id_and_shop_list(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
client = _make_client()
|
|
fake_items = [OtherShopItem(tenpo_cd="400451", shop_name="駿河屋 山口大学前店", price=1370)]
|
|
|
|
async def fake_fetch_other_shop_list(goods_id: str) -> list[OtherShopItem]:
|
|
assert goods_id == "220035970"
|
|
return fake_items
|
|
|
|
monkeypatch.setattr(client, "_fetch_other_shop_list", fake_fetch_other_shop_list)
|
|
|
|
result = await client.fetch_other_shops(DetailRequest(id="220035970"))
|
|
|
|
assert result.goods_id == "220035970"
|
|
assert result.other_shop_list == fake_items
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_empty_id_raises(self) -> None:
|
|
client = _make_client()
|
|
with pytest.raises(Exception):
|
|
await client.fetch_other_shops(DetailRequest(id=None))
|
|
|
|
|
|
class TestFetchDetailIncludeOtherShops:
|
|
"""DetailRequest.include_other_shops 控制 fetch_detail 是否顺带抓取 other_shop_list。"""
|
|
|
|
MINIMAL_DETAIL_HTML = '<html><body><h1 id="item_title">测试商品</h1></body></html>'
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_flag_false_skips_other_shop_fetch(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
client = _make_client()
|
|
monkeypatch.setattr(client, "_session_manager", _FakeSessionManager(self.MINIMAL_DETAIL_HTML))
|
|
|
|
called = False
|
|
|
|
async def fake_fetch_other_shop_list(goods_id: str) -> list[OtherShopItem]:
|
|
nonlocal called
|
|
called = True
|
|
return []
|
|
|
|
monkeypatch.setattr(client, "_fetch_other_shop_list", fake_fetch_other_shop_list)
|
|
|
|
detail = await client.fetch_detail(DetailRequest(id="220035970"))
|
|
|
|
assert called is False
|
|
assert detail.other_shop_list == []
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_flag_true_includes_other_shop_list(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
client = _make_client()
|
|
monkeypatch.setattr(client, "_session_manager", _FakeSessionManager(self.MINIMAL_DETAIL_HTML))
|
|
|
|
fake_items = [OtherShopItem(tenpo_cd="400451", shop_name="駿河屋 山口大学前店", price=1370)]
|
|
|
|
async def fake_fetch_other_shop_list(goods_id: str) -> list[OtherShopItem]:
|
|
assert goods_id == "220035970"
|
|
return fake_items
|
|
|
|
monkeypatch.setattr(client, "_fetch_other_shop_list", fake_fetch_other_shop_list)
|
|
|
|
detail = await client.fetch_detail(DetailRequest(id="220035970", include_other_shops=True))
|
|
|
|
assert detail.other_shop_list == fake_items
|
|
|
|
|
|
class _FakeSessionManager:
|
|
def __init__(self, html: str) -> None:
|
|
self._html = html
|
|
|
|
async def fetch_html(self, url: str):
|
|
return self._html, None
|