96 lines
3.5 KiB
Python
96 lines
3.5 KiB
Python
"""多比比新增端点的冒烟测试。LLM 调用被 monkeypatch 成固定 JSON,避免真打智谱。"""
|
|
from __future__ import annotations
|
|
|
|
from unittest.mock import patch
|
|
|
|
from fastapi.testclient import TestClient
|
|
|
|
from app.main import app
|
|
|
|
client = TestClient(app)
|
|
|
|
ARENA_JSON = (
|
|
'{"title":"iPhone 15 Pro 256GB","typical_price":8299.0,'
|
|
'"quotes":[{"platform":"淘宝","price":8099.0,"note":""},'
|
|
'{"platform":"京东","price":8299.0,"note":"自营"},'
|
|
'{"platform":"拼多多","price":7888.0,"note":"百亿补贴"},'
|
|
'{"platform":"抖音","price":8190.0,"note":""}]}'
|
|
)
|
|
|
|
WORTH_JSON = (
|
|
'{"score":78,"verdict":"buy","headline":"现在入手划算",'
|
|
'"reasons":["低于市场常见价约 2%","距下次大促还有一段时间"],'
|
|
'"typical_price":8299.0,"best_time":"现在就合适"}'
|
|
)
|
|
|
|
|
|
def test_arena_quote_basic() -> None:
|
|
with patch("app.api.arena.chat", return_value=ARENA_JSON):
|
|
r = client.post("/api/v1/arena-quote", json={"title": "iPhone 15 Pro 256GB"})
|
|
assert r.status_code == 200, r.text
|
|
body = r.json()
|
|
assert body["typical_price"] > 0
|
|
assert len(body["quotes"]) == 4
|
|
assert body["lowest_platform"] == "拼多多" # 7888 最低
|
|
|
|
|
|
def test_arena_quote_synthesizes_missing_platforms() -> None:
|
|
"""LLM 只给 1 个平台,其余平台应被确定性合成,保证每行有值。"""
|
|
only_one = '{"title":"x","typical_price":100.0,"quotes":[{"platform":"淘宝","price":95.0}]}'
|
|
with patch("app.api.arena.chat", return_value=only_one):
|
|
r = client.post(
|
|
"/api/v1/arena-quote",
|
|
json={"title": "x", "platforms": ["淘宝", "京东", "拼多多"]},
|
|
)
|
|
assert r.status_code == 200, r.text
|
|
quotes = r.json()["quotes"]
|
|
assert len(quotes) == 3
|
|
assert {q["platform"] for q in quotes} == {"淘宝", "京东", "拼多多"}
|
|
|
|
|
|
def test_arena_quote_empty_title() -> None:
|
|
r = client.post("/api/v1/arena-quote", json={"title": " "})
|
|
assert r.status_code == 422
|
|
|
|
|
|
def test_worth_buy_basic() -> None:
|
|
with patch("app.api.worth_buy.chat", return_value=WORTH_JSON):
|
|
r = client.post(
|
|
"/api/v1/worth-buy",
|
|
json={"title": "iPhone 15 Pro 256GB", "price": 8099.0},
|
|
)
|
|
assert r.status_code == 200, r.text
|
|
body = r.json()
|
|
assert 0 <= body["score"] <= 100
|
|
assert body["verdict"] in {"buy", "wait", "neutral"}
|
|
assert isinstance(body["reasons"], list) and body["reasons"]
|
|
|
|
|
|
def test_worth_buy_fallback_on_garbage() -> None:
|
|
"""LLM 返回非 JSON 垃圾时,接口仍给中性结论而非 5xx。"""
|
|
with patch("app.api.worth_buy.chat", return_value="不是 JSON 的废话"):
|
|
r = client.post("/api/v1/worth-buy", json={"title": "x", "price": 50.0})
|
|
assert r.status_code == 200, r.text
|
|
body = r.json()
|
|
assert body["typical_price"] > 0
|
|
assert body["verdict"] in {"buy", "wait", "neutral"}
|
|
|
|
|
|
def test_worth_buy_invalid_price() -> None:
|
|
r = client.post("/api/v1/worth-buy", json={"title": "x", "price": 0})
|
|
assert r.status_code == 422
|
|
|
|
|
|
def test_parse_ocr_boots_and_works() -> None:
|
|
"""parse-ocr 端点存在(修复了缺失 import),mock 延迟被关掉后能正常返回。"""
|
|
async def _instant(_seconds):
|
|
return None
|
|
|
|
with patch("app.mock_extractor.asyncio.sleep", _instant):
|
|
r = client.post(
|
|
"/api/v1/parse-ocr",
|
|
json={"ocr_text": "iPhone 15 Pro Max 256GB ¥8099 京东自营", "clusters": []},
|
|
)
|
|
assert r.status_code == 200, r.text
|
|
assert r.json()["price"] > 0
|