50da718e35
失败记录不再一律「网络开小差」:新增记录级 fail_reason 派生列——information 具体则直出,笼统则从 platform_results 救出业务原因(找不到店/菜、未起送、打烊、 单点不配送等),纯系统失败为 None → 端侧品牌兜底。store_closed/no_delivery 被 pricebot 漏成 status=failed 的按 reason 补判,打烊脏店名统一简短模板。接入 harvest_done 与灰度期 upsert_record 两条写路径。 - models: comparison_record.fail_reason 列 - repositories: _derive_fail_display + 补判/清洗 helper,两条写路径接入 - schemas: ComparisonRecordOut 暴露 fail_reason - alembic: 加列 + 回填老 specific 失败记录 - tests: _derive_fail_display 单测(8 例)+ harvest 失败落库集成测试 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: guke <guke@autohome.com.cn> Reviewed-on: #189
123 lines
5.6 KiB
Python
123 lines
5.6 KiB
Python
"""失败卡展示原因派生(repositories.comparison._derive_fail_display)单元测试。
|
|
|
|
用例取自线上真实 failed 记录(platform_results 形态),覆盖:
|
|
- information 具体 → 直出
|
|
- information 笼统 + platform_results 有干净业务结局 → 救援出该原因(id 3030/2964 型)
|
|
- information 笼统 + 仅系统失败(搜索失败等黑话)→ None(端侧品牌兜底,id 3027 型)
|
|
- store_closed / no_delivery 被 pricebot 漏成 status=failed → 按 reason 关键字补判
|
|
- 打烊类脏店名 blob → 统一简短模板
|
|
- platform_results 为空 / 非对象 → None
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from app.repositories import comparison as crud
|
|
|
|
|
|
def test_specific_information_passthrough() -> None:
|
|
# information 本身具体(未达起送/找不到菜等)→ 直出,不看 platform_results
|
|
assert (
|
|
crud._derive_fail_display("淘宝闪购未达起送门槛,可加菜凑单后下单", {})
|
|
== "淘宝闪购未达起送门槛,可加菜凑单后下单"
|
|
)
|
|
assert crud._derive_fail_display("未识别到商品", {}) == "未识别到商品"
|
|
|
|
|
|
def test_generic_info_rescued_from_items_not_found() -> None:
|
|
# id 3030 型:美团系统失败 + 京东 items_not_found,记录级 information 笼统 → 救出京东那条
|
|
pr = {
|
|
"eleme": {"is_source": True, "status": "source", "price": 23.04},
|
|
"meituan_waimai": {
|
|
"is_source": False, "status": "failed",
|
|
"reason": "搜索店铺失败, 无法跳转到搜索页",
|
|
},
|
|
"jd_waimai_standalone": {
|
|
"is_source": False, "status": "items_not_found",
|
|
"reason": "京东外卖此店内未找到这些菜品",
|
|
},
|
|
}
|
|
assert (
|
|
crud._derive_fail_display("比价过程出错,请稍后重试", pr)
|
|
== "京东外卖此店内未找到这些菜品"
|
|
)
|
|
|
|
|
|
def test_generic_info_rescued_from_store_not_found() -> None:
|
|
# id 2964 型:美团系统失败 + 京东 store_not_found → 救出京东相似店铺文案
|
|
pr = {
|
|
"taobao_flash": {"is_source": True, "status": "source", "price": 127.98},
|
|
"meituan": {
|
|
"is_source": False, "status": "failed",
|
|
"reason": "比价过程出错,请稍后重试",
|
|
},
|
|
"jd_waimai": {
|
|
"is_source": False, "status": "store_not_found",
|
|
"reason": "未在京东找到「黔珍味·贵州牛肉蘸水健康菜 (望京店)」相似店铺",
|
|
},
|
|
}
|
|
assert (
|
|
crud._derive_fail_display("比价过程出错,请稍后重试", pr)
|
|
== "未在京东找到「黔珍味·贵州牛肉蘸水健康菜 (望京店)」相似店铺"
|
|
)
|
|
|
|
|
|
def test_generic_info_pure_system_failure_returns_none() -> None:
|
|
# id 3027 型:唯一目标平台是自动化黑话失败 → 不给用户看 → None(端侧品牌兜底)
|
|
pr = {
|
|
"eleme": {"is_source": True, "status": "source", "price": 18.83},
|
|
"meituan_waimai": {
|
|
"is_source": False, "status": "failed",
|
|
"reason": "搜索店铺失败, 无法跳转到搜索页",
|
|
},
|
|
}
|
|
assert crud._derive_fail_display("比价过程出错,请稍后重试", pr) is None
|
|
|
|
|
|
def test_store_closed_leaked_to_failed_is_rescued_and_cleaned() -> None:
|
|
# 打烊被漏成 status=failed;reason 常带脏店名 blob → 统一简短模板
|
|
pr = {
|
|
"jd_waimai": {"is_source": True, "status": "source", "price": 25},
|
|
"taobao_flash": {
|
|
"is_source": False, "status": "failed",
|
|
"reason": "淘宝闪购「沙胆彪炭炉牛杂煲(...),蜂鸟准时达,月售300+,起送¥20」本店已休息,无法比价",
|
|
},
|
|
}
|
|
assert crud._derive_fail_display("比价过程出错,请稍后重试", pr) == "门店休息中,无法比价"
|
|
|
|
pr2 = {
|
|
"taobao_flash": {"is_source": True, "status": "source", "price": 31.83},
|
|
"meituan": {
|
|
"is_source": False, "status": "failed",
|
|
"reason": "美团「奈雪的茶(北京王府井奥莱·香江」门店已打烊,无法比价",
|
|
},
|
|
}
|
|
assert crud._derive_fail_display("比价出错", pr2) == "门店已打烊,无法比价"
|
|
|
|
|
|
def test_no_delivery_leaked_to_failed_is_rescued() -> None:
|
|
# 单点不配送被漏成 status=failed;reason 本身干净 → 直接用
|
|
reason = "京东外卖该商家所选商品单点不配送,无法进入结算比价"
|
|
pr = {
|
|
"taobao_flash": {"is_source": True, "status": "source", "price": 20.1},
|
|
"jd_waimai_standalone": {
|
|
"is_source": False, "status": "failed", "reason": reason,
|
|
},
|
|
}
|
|
assert crud._derive_fail_display("比价过程出错,请稍后重试", pr) == reason
|
|
|
|
|
|
def test_empty_or_missing_platform_results_returns_none() -> None:
|
|
# 「比价出错」+ 空 {} / None / 非对象:引擎早夭,无可展示原因 → None
|
|
assert crud._derive_fail_display("比价出错", {}) is None
|
|
assert crud._derive_fail_display("比价过程出错,请稍后重试", None) is None
|
|
assert crud._derive_fail_display("比价出错", []) is None # 老 array 形态,防御
|
|
|
|
|
|
def test_clean_status_wins_over_priority_order() -> None:
|
|
# 多个业务结局同现时按 _BIZ_STATUS_PRIORITY 选(below_minimum 优先于 store_not_found)
|
|
pr = {
|
|
"src": {"is_source": True, "status": "source", "price": 30},
|
|
"a": {"is_source": False, "status": "store_not_found", "reason": "未找到店铺A"},
|
|
"b": {"is_source": False, "status": "below_minimum", "reason": "B未达起送门槛"},
|
|
}
|
|
assert crud._derive_fail_display("比价过程出错,请稍后重试", pr) == "B未达起送门槛"
|