Files
rakuten-api/tests/test_trading_api.py
T
q792602257andClaude Opus 4.6 65e3ed31f8 交易服务收敛为仅 rakuten:移除 rakuma 登录态与下单入口
trading 不再管理 ラクマ 的购物车购买、支付与订单监控。删除 auth_site /
auth_session / login_runner / models.AuthSite 中的 rakuma 分支与常量、
account.yaml.example 的 rakuma 段,并清理相关测试。scraping 侧的
ラクマ 抓取 API(/api/rakuma/*)保留不动。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-28 11:50:07 +08:00

158 lines
4.6 KiB
Python

"""交易服务 API 测试:健康检查、登录态查询与重载、鉴权
登录态会话被替换为桩,不触达真实站点、不需要真实账号。
AuthSession 自身的行为(cookie 加载、探测判据)在 tests/test_auth_session.py。
"""
from __future__ import annotations
import pytest
from fastapi.testclient import TestClient
from app.shared.config import get_settings
from app.shared.errors import NotLoggedInError
from app.trading.main import create_app
from app.trading.services.auth_session import AuthStatus
TOKEN = get_settings().bearer_token
AUTH = {"Authorization": f"Bearer {TOKEN}"}
class StubAuthSession:
"""记录调用并返回固定登录态的桩"""
def __init__(self) -> None:
self.checked: list[str] = []
self.reloaded: list[str] = []
self.logged_in = True
@property
def sites(self) -> tuple[str, ...]:
return ("rakuten",)
def _status(self, site: str) -> AuthStatus:
return AuthStatus(
site=site,
state_file_exists=True,
logged_in=self.logged_in,
checked_at=None,
detail="stub",
)
async def check(self, site: str) -> AuthStatus:
self.checked.append(site)
return self._status(site)
def status(self, site: str) -> AuthStatus:
return self._status(site)
def status_all(self) -> dict[str, dict]:
return {site: self._status(site).to_dict() for site in self.sites}
def reload(self, site: str) -> int:
self.reloaded.append(site)
return 3
async def require_logged_in(self, site: str) -> None:
if not self.logged_in:
raise NotLoggedInError(site=site, detail="stub")
async def close(self) -> None:
"""lifespan 收尾会调用;桩没有真实客户端要关"""
@pytest.fixture
def client_and_stub():
app = create_app()
with TestClient(app) as client:
stub = StubAuthSession()
app.state.container.auth_session = stub
yield client, stub
@pytest.fixture
def client(client_and_stub):
return client_and_stub[0]
@pytest.fixture
def stub(client_and_stub):
return client_and_stub[1]
# ---- 健康检查 ----
def test_health_needs_no_token(client):
response = client.get("/health")
assert response.status_code == 200
body = response.json()
assert body["data"]["status"] == "ok"
assert set(body["data"]["auth"]) == {"rakuten"}
def test_health_does_not_probe_the_site(client, stub):
"""健康检查只读缓存:它会被高频轮询,不能每次都去打站点"""
client.get("/health")
assert stub.checked == []
# ---- 鉴权 ----
@pytest.mark.parametrize("path", ["/api/auth/status", "/api/auth/reload"])
def test_auth_endpoints_reject_missing_token(client, path):
response = client.post(path, json={})
assert response.status_code == 401
assert response.json()["code"] == 1001
def test_auth_endpoints_reject_wrong_token(client):
response = client.post(
"/api/auth/status", json={}, headers={"Authorization": "Bearer wrong-token"}
)
assert response.status_code == 401
# ---- 登录态查询 ----
def test_status_probes_site_by_default(client, stub):
response = client.post("/api/auth/status", json={}, headers=AUTH)
assert response.status_code == 200
body = response.json()
assert [item["site"] for item in body["data"]["sites"]] == ["rakuten"]
assert stub.checked == ["rakuten"]
def test_status_can_skip_the_probe(client, stub):
"""refresh=false 时读缓存,不打站点"""
response = client.post("/api/auth/status", json={"refresh": False}, headers=AUTH)
assert response.status_code == 200
assert stub.checked == []
def test_status_accepts_a_single_site(client, stub):
response = client.post("/api/auth/status", json={"site": "rakuten"}, headers=AUTH)
assert response.status_code == 200
assert stub.checked == ["rakuten"]
def test_status_rejects_unknown_site(client):
"""站点名是枚举,未知值应在校验层就被挡下"""
response = client.post("/api/auth/status", json={"site": "mercari"}, headers=AUTH)
assert response.status_code == 422
assert response.json()["code"] == 1002
# ---- 登录态重载 ----
def test_reload_reloads_then_probes(client, stub):
response = client.post("/api/auth/reload", json={"site": "rakuten"}, headers=AUTH)
assert response.status_code == 200
body = response.json()
assert body["data"]["reloaded"] == {"rakuten": 3}
# 重载后必须立刻探测一次,否则调用方拿不到「这次登录到底成没成」
assert stub.reloaded == ["rakuten"]
assert stub.checked == ["rakuten"]