Files
ewoooc/tests/test_marketplace_product_matcher.py
OoO 8e4d7d306e
All checks were successful
CD Pipeline / deploy (push) Successful in 1m3s
強化 Dashing Diva 美甲片同款比對
2026-05-21 12:39:23 +08:00

1010 lines
38 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from sqlalchemy import create_engine, text
def test_marketplace_matcher_accepts_same_product_identity():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"理膚寶水 B5 修復霜 40ml",
"理膚寶水 全面修復霜 B5 40ml",
momo_price=699,
competitor_price=679,
)
assert diagnostics.score >= 0.9
assert diagnostics.hard_veto is False
assert "brand_match" in diagnostics.tags
assert "spec_match" in diagnostics.tags
def test_marketplace_matcher_rejects_brand_conflict_even_when_volume_matches():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"【蘭蔻】官方直營 玫瑰霜60ml+玫瑰精露150ml",
"LOREAL Paris 巴黎萊雅 金致臻顏花蜜奢養膠原輕盈乳霜_60ml",
momo_price=18765,
competitor_price=1249,
)
assert diagnostics.score < 0.76
assert diagnostics.hard_veto is True
assert "brand_conflict" in diagnostics.reasons
def test_marketplace_matcher_rejects_volume_conflict_for_same_brand():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"【CLARINS 克蘭詩】黃金亮眼萃20mlX2囤貨組",
"【CLARINS克蘭詩】黃金雙萃精華 75ml",
momo_price=4500,
competitor_price=3549,
)
assert diagnostics.score < 0.76
assert diagnostics.hard_veto is True
assert "volume_conflict" in diagnostics.reasons
def test_marketplace_matcher_handles_bundle_piece_count():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"【Relove】馬甲纖纖飲-莓果風味X3盒 共72包",
"【3入超值組】Relove 馬甲纖纖飲 24包/7克",
momo_price=4590,
competitor_price=4590,
)
assert diagnostics.score >= 0.76
assert diagnostics.hard_veto is False
def test_marketplace_matcher_accepts_strong_model_line_without_specs():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"【Stadler Form】Sophie 無線香氛水氧機 水氧機 香氛機",
"【瑞士Stadler Form】無線香氛水氧機 露營燈造型 Sophie",
momo_price=3780,
competitor_price=3980,
)
assert diagnostics.score >= 0.76
assert diagnostics.hard_veto is False
assert "strong_product_line_match" in diagnostics.reasons
def test_marketplace_matcher_rejects_bundle_to_single_even_when_brand_matches():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"【NARS】小白餅閨蜜分享組(裸光蜜粉餅/定妝蜜粉)",
"【NARS】裸光蜜粉餅(小白餅) 10g",
momo_price=3300,
competitor_price=1099,
)
assert diagnostics.score < 0.76
assert diagnostics.hard_veto is True
assert "bundle_offer_conflict" in diagnostics.reasons
assert diagnostics.comparison_mode == "not_comparable"
def test_marketplace_matcher_marks_bundle_single_as_unit_comparable_not_exact():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"理膚寶水 B5 全面修復霜 40ml x2 超值組",
"理膚寶水 全面修復霜 B5 40ml",
momo_price=1199,
competitor_price=679,
)
assert diagnostics.score < 0.76
assert diagnostics.hard_veto is True
assert diagnostics.comparison_mode == "unit_comparable"
assert "unit_comparable" in diagnostics.reasons
assert "comparison_unit_comparable" in diagnostics.tags
def test_unit_price_comparison_builds_normalized_evidence():
from services.marketplace_product_matcher import build_unit_price_comparison
comparison = build_unit_price_comparison(
"理膚寶水 B5 全面修復霜 40ml x2 超值組",
"理膚寶水 全面修復霜 B5 40ml",
momo_price=1199,
competitor_price=679,
)
assert comparison["comparable"] is True
assert comparison["unit_label"] == "ml"
assert comparison["momo_total_quantity"] == 80
assert comparison["competitor_total_quantity"] == 40
assert comparison["momo_unit_price"] == 14.9875
assert comparison["competitor_unit_price"] == 16.975
assert comparison["unit_gap_pct"] < 0
def test_marketplace_matcher_does_not_unit_compare_multi_component_set():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"【蘭蔻】官方直營 玫瑰霜60ml+玫瑰精露150ml",
"【蘭蔻】絕對完美玫瑰霜 60ml",
momo_price=18765,
competitor_price=5349,
)
assert diagnostics.hard_veto is True
assert diagnostics.comparison_mode == "not_comparable"
assert "unit_comparable" not in diagnostics.reasons
comparison = score_marketplace_match(
"【蘭蔻】官方直營 玫瑰霜60ml+玫瑰精露150ml",
"【蘭蔻】絕對完美玫瑰霜 60ml",
momo_price=18765,
competitor_price=5349,
)
assert comparison.comparison_mode == "not_comparable"
def test_marketplace_matcher_does_not_promote_wide_price_refill_candidate():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"【蘭蔻】官方直營 絕對完美永生玫瑰逆齡乳霜60ml補充瓶",
"LANCOME蘭蔻 絕對完美永生玫瑰逆齡乳霜 60ml",
momo_price=11205,
competitor_price=5349,
)
assert diagnostics.score < 0.76
assert diagnostics.hard_veto is True
assert "refill_pack_conflict" in diagnostics.reasons
assert "strong_product_line_match" not in diagnostics.reasons
def test_marketplace_matcher_rejects_partial_overlap_in_multi_spec_set():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"【蘭蔻】玫瑰霜60ml+玫瑰精露150ml",
"【蘭蔻】玫瑰霜60ml+玫瑰精露20ml",
momo_price=18765,
competitor_price=7999,
)
assert diagnostics.score < 0.76
assert diagnostics.hard_veto is True
assert "volume_conflict" in diagnostics.reasons
assert diagnostics.comparison_mode == "not_comparable"
def test_marketplace_matcher_rejects_dosage_conflict():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"品牌 葉黃素 10mg 60錠",
"品牌 葉黃素 20mg 60錠",
momo_price=990,
competitor_price=890,
)
assert diagnostics.score < 0.76
assert diagnostics.hard_veto is True
assert "dosage_conflict" in diagnostics.reasons
def test_marketplace_matcher_rejects_product_type_conflict_even_when_line_matches():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"理膚寶水 MelaB3 淡斑精華 30ml",
"理膚寶水 MelaB3 淡斑化妝水 30ml",
momo_price=1280,
competitor_price=1180,
)
assert diagnostics.score < 0.76
assert diagnostics.hard_veto is True
assert "type_conflict" in diagnostics.reasons
assert diagnostics.comparison_mode == "not_comparable"
def test_marketplace_matcher_rejects_foundation_stick_vs_foundation_liquid():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"【蘭蔻】零粉感超持久粉底棒9.5g",
"【LANCOME 蘭蔻】零粉感超持久粉底 30ml",
momo_price=1620,
competitor_price=1580,
)
assert diagnostics.score < 0.76
assert diagnostics.hard_veto is True
assert "type_conflict" in diagnostics.reasons
def test_marketplace_matcher_promotes_nivea_deodorant_spray_identity():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"【NIVEA 妮維雅】男士 止汗爽身噴霧 無印乾爽-清新海洋",
"NIVEA 妮維雅 男士止汗爽身噴霧 無印乾爽-清新海洋150ml",
momo_price=159,
competitor_price=169,
)
assert diagnostics.score >= 0.76
assert diagnostics.hard_veto is False
def test_marketplace_matcher_promotes_packaging_variant_for_same_nars_powder():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"【NARS】官方直營 裸光蜜粉餅(璀璨奢金限定版/星沙金小白餅)",
"【NARS】裸光蜜粉餅(小白餅) 10g",
momo_price=1050,
competitor_price=1050,
)
assert diagnostics.score >= 0.76
assert diagnostics.hard_veto is False
assert "shared_identity_anchor_packaging_variant" in diagnostics.reasons
def test_marketplace_matcher_promotes_private_wash_same_identity():
from services.marketplace_product_matcher import score_marketplace_match
summer = score_marketplace_match(
"【Summers Eve 舒摩兒】浴潔露237ml 單入任選(私密清潔 經典防護王)",
"eve舒摩兒 賦活美學浴潔露-全肌防護 237ml",
momo_price=441,
competitor_price=441,
)
femfresh = score_marketplace_match(
"【femfresh 芳芯】弱酸性植萃複方溫和潤澤護理私密肌潔膚露250ml/瓶(pH值平衡護潔露淨味沐浴乳香氛凝膠)",
"【femfresh芳芯 官方直營】私密潔膚露250ml (任選)",
momo_price=399,
competitor_price=399,
)
for diagnostics in (summer, femfresh):
assert diagnostics.score >= 0.76
assert diagnostics.hard_veto is False
def test_marketplace_matcher_rejects_private_spray_vs_private_gel():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"【isLeaf】韓國isLeaf男性私密醒肌抑菌噴霧60ml-夏夜微醺(SGS 24小時抑菌)",
"韓國 isLeaf 男性私密激淨凝露 湛藍海洋 60ml",
momo_price=299,
competitor_price=299,
)
assert diagnostics.score < 0.76
assert diagnostics.hard_veto is True
assert "type_conflict" in diagnostics.reasons
def test_marketplace_matcher_promotes_marketing_variant_same_maybelline_lip_tint():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"【MAYBELLINE 媚比琳】超持久水光鎖吻唇釉(絲絨甜點新色/鎖吻棒/水光持色)",
"MAYBELLINE 媚比琳 超持久水光鎖吻唇釉 4.2ml",
momo_price=399,
competitor_price=399,
)
assert diagnostics.score >= 0.76
assert diagnostics.hard_veto is False
assert "shared_identity_anchor_marketing_variant" in diagnostics.reasons
def test_marketplace_matcher_promotes_besthot_aroma_machine_marketing_variant():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"【BESTHOT】天然陶瓷精油香薰機-贈精油一瓶(水氧機 加濕器 精油機 香薰機 擴香機 小夜燈)",
"Besthot 天然陶瓷超聲波大噴霧精油香薰機-贈送薰衣草精油 水氧機 加濕器 精油機",
momo_price=899,
competitor_price=899,
)
assert diagnostics.score >= 0.76
assert diagnostics.hard_veto is False
def test_marketplace_matcher_promotes_precise_cosmetics_and_skincare_lines():
from services.marketplace_product_matcher import score_marketplace_match
samples = [
(
"【NARS】官方直營 裸光幻閃亮采餅",
"NARS 裸光幻閃亮采餅 2g #Heavenly",
),
(
"【PONY EFFECT】絕對持久定妝噴霧 100ml(持久定妝/控油特霧)",
"【PONY EFFECT】絕對持久定妝噴霧(控油特霧版)",
),
(
"【植村秀】官方直營 自動武士刀眉筆(Shu uemura/眉筆)",
"《Shu Uemura 植村秀》自動武士刀眉筆 0.3g",
),
(
"【SHISEIDO 資生堂國際櫃】超進化光感輕潤遮瑕棒(遮瑕/修容/打亮/新品)",
"【資生堂國際櫃】超進化光感輕潤遮瑕棒2.7g",
),
(
"【LANCOME 蘭蔻】唯我玫瑰裸光潤唇膏(多款任選 國際航空版 送禮)",
"LANCOME 唯我玫瑰裸光潤唇膏 3g",
),
(
"【Les nez 香鼻子】晨曦冷香儀贈複方擴香精油 30ml(無水擴香 智能霧化)",
"les nez 晨曦冷香儀(無水擴香、智能霧化)",
),
(
"【Mustela 慕之恬廊】舒恬良 修護霜 40ml(Vit.B5 嬰兒界萬用霜 醫師好辣推薦)",
"Mustela 慕之恬廊舒恬良修護霜40ml",
),
]
for left, right in samples:
diagnostics = score_marketplace_match(left, right, momo_price=399, competitor_price=399)
assert diagnostics.score >= 0.76
assert diagnostics.hard_veto is False
def test_marketplace_matcher_rejects_same_count_different_unit_family():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"品牌 保濕面膜10片",
"品牌 保濕面膜10盒",
momo_price=399,
competitor_price=1990,
)
assert diagnostics.score < 0.76
assert diagnostics.hard_veto is True
assert "count_conflict" in diagnostics.reasons
def test_marketplace_matcher_marks_generic_bundle_words_as_unit_comparable():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"【NARS】裸光蜜粉餅 雙件組 10g",
"【NARS】裸光蜜粉餅 10g",
momo_price=1999,
competitor_price=1099,
)
assert diagnostics.score < 0.76
assert diagnostics.hard_veto is True
assert "bundle_offer_conflict" in diagnostics.reasons
assert diagnostics.comparison_mode == "unit_comparable"
def test_marketplace_matcher_ignores_non_brand_bracket_copy():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"【保濕組】理膚寶水 B5 修復霜 40ml",
"理膚寶水 B5 修復霜 40ml",
momo_price=699,
competitor_price=679,
)
assert diagnostics.score >= 0.76
assert diagnostics.hard_veto is False
assert "brand_conflict" not in diagnostics.reasons
def test_marketplace_matcher_uses_brand_alias_and_chinese_line_signal():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"【CLARINS 克蘭詩】黃金雙激萃50ml",
"克蘭詩 黃金雙萃精華 50ml",
momo_price=4500,
competitor_price=3549,
)
assert diagnostics.score >= 0.76
assert diagnostics.hard_veto is False
assert "brand_match" in diagnostics.tags
assert "spec_match" in diagnostics.tags
def test_marketplace_matcher_matches_cetaphil_alias():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"舒特膚 AD 乳液 200ml",
"Cetaphil AD 乳液 200ml",
momo_price=980,
competitor_price=899,
)
assert diagnostics.score >= 0.9
assert diagnostics.hard_veto is False
assert "brand_match" in diagnostics.tags
def test_marketplace_matcher_treats_piece_unit_synonyms_as_same_pack():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"品牌 葉黃素 10mg 60粒",
"品牌 葉黃素 10毫克 60錠",
momo_price=990,
competitor_price=890,
)
assert diagnostics.score >= 0.76
assert diagnostics.hard_veto is False
assert "count_conflict" not in diagnostics.reasons
def test_marketplace_matcher_normalizes_buy_get_pack_evidence():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"NARS 裸光蜜粉餅 買1送1 10g",
"NARS 裸光蜜粉餅 10g x2",
momo_price=1999,
competitor_price=1099,
)
assert diagnostics.comparison_mode == "unit_comparable"
assert "unit_comparable" in diagnostics.reasons
assert "count_conflict" not in diagnostics.reasons
def test_marketplace_matcher_accepts_strong_multi_component_line_without_full_specs():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"【Gennies 奇妮】COSVITAL乳頭霜50ml+滋潤彈力霜100ml+修復彈力霜100ml(金燦極緻法國頂級孕期產後保養組)",
"Gennies奇妮 COSVITAL 金燦極緻法國頂級保養(乳頭霜+滋潤彈力霜+修復彈力霜)(801062+801081+801082)",
momo_price=6980,
competitor_price=6980,
)
assert diagnostics.score >= 0.76
assert diagnostics.hard_veto is False
assert diagnostics.comparison_mode == "exact_identity"
assert "strong_component_line_match" in diagnostics.reasons
def test_marketplace_matcher_accepts_known_brand_alias_and_option_copy():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"【OBgE】韓國 OBgE 男士自然遮瑕粉底棒13g",
"OBgE/自然遮瑕粉底棒13g - 多款可選",
momo_price=765,
competitor_price=1099,
)
assert diagnostics.score >= 0.76
assert diagnostics.hard_veto is False
assert diagnostics.comparison_mode == "exact_identity"
def test_marketplace_matcher_accepts_same_pack_with_chinese_count_wording():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"【SEBAMED】潔膚露1000ml共2入(大容量 平行輸入)",
"Sebamed施巴 潔膚露1000ml 兩入組",
momo_price=799,
competitor_price=899,
)
assert diagnostics.score >= 0.76
assert diagnostics.hard_veto is False
assert "brand_match" in diagnostics.tags
def test_marketplace_matcher_rejects_razor_series_and_blade_count_conflict():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"【Gillette 吉列】SkinGuard 紳適系列刮鬍刀頭(4刀頭)",
"【Gillette 吉列 】Fusion鋒隱系列刮鬍刀頭(8刀頭)",
momo_price=499,
competitor_price=906,
)
assert diagnostics.score < 0.76
assert diagnostics.hard_veto is True
assert diagnostics.comparison_mode == "not_comparable"
assert "count_conflict" in diagnostics.reasons
assert "model_line_conflict" in diagnostics.reasons
def test_marketplace_matcher_promotes_safe_exact_spec_near_threshold():
from services.marketplace_product_matcher import score_marketplace_match
opi = score_marketplace_match(
"【O.P.I】Top Coat 持久閃耀保色護甲油15mL-IST31(類光繚指甲油專用亮油/小銀蓋/如膠似漆/美甲/官方直營)",
"OPI 官方直營持久閃耀保色護甲油15mL-IST31如膠似漆閃耀系列",
momo_price=578,
competitor_price=680,
)
mustela = score_marketplace_match(
"【Mustela 慕之恬廊】慕之幼 免用水潔淨液 300ml(外出清潔 卸除髒汙 卸除防曬 卸防曬)",
"Mustela慕之恬廊 慕之幼免用水潔淨液300ml",
momo_price=275,
competitor_price=398,
)
romand = score_marketplace_match(
"【rom&nd】韓國 Rom&nd 果汁唇釉 5.5g(韓國彩妝 唇釉 唇彩 果汁 水光感)",
"rom&nd 果汁唇釉5.5g_多款可選",
momo_price=211,
competitor_price=319,
)
for diagnostics in (opi, mustela, romand):
assert diagnostics.score >= 0.76
assert diagnostics.hard_veto is False
assert "strong_exact_spec_match" in diagnostics.reasons
def test_marketplace_matcher_does_not_promote_different_option_without_spec():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"【DASHING DIVA】MAGICPRESS時尚潮流美甲片_極光之藍",
"Dashing Diva/F 時尚潮流美甲片-月光銀影 MDF5F010AG",
momo_price=331,
competitor_price=420,
)
assert diagnostics.score < 0.76
assert "strong_exact_spec_match" not in diagnostics.reasons
assert "variant_descriptor_conflict" in diagnostics.reasons
def test_marketplace_matcher_promotes_variant_safe_exact_option():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"【DASHING DIVA】MAGICPRESS時尚潮流美甲片_極光之藍",
"Dashing Diva/F 時尚潮流美甲片-極光之藍 MDF5F001AG",
momo_price=331,
competitor_price=420,
)
assert diagnostics.score >= 0.76
assert diagnostics.hard_veto is False
assert "shared_identity_anchor_variant_safe" in diagnostics.reasons
def test_marketplace_matcher_allows_catalog_piece_count_for_exact_dashing_diva_variant():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"【DASHING DIVA】MAGICPRESS 時尚潮流美甲片-銀河綠波(貓眼 漸層)",
"Dashing Diva/F 時尚潮流美甲片-銀河綠波 30片/盒 MDF5F012AG",
momo_price=399,
competitor_price=420,
)
assert diagnostics.score >= 0.76
assert diagnostics.hard_veto is False
assert "count_conflict" not in diagnostics.reasons
def test_marketplace_matcher_rejects_explicit_shade_option_mismatch():
from services.marketplace_product_matcher import score_marketplace_match
lipstick = score_marketplace_match(
"【Maybelline 媚比琳】超持久水光鎖吻唇釉#62 4.2ml",
"MAYBELLINE 媚比琳 超持久水光鎖吻唇釉 #120 4.2ml",
momo_price=399,
competitor_price=399,
)
primer = score_marketplace_match(
"【植村秀】水凝光透妝前防護乳 紫色 30ml",
"植村秀 水凝光透妝前防護乳 粉色 30ml",
momo_price=1200,
competitor_price=1100,
)
for diagnostics in (lipstick, primer):
assert diagnostics.hard_veto is True
assert diagnostics.comparison_mode == "not_comparable"
assert diagnostics.score < 0.76
assert "variant_option_conflict" in diagnostics.reasons
def test_marketplace_matcher_accepts_same_explicit_shade_option():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"【Maybelline 媚比琳】超持久水光鎖吻唇釉#62 4.2ml",
"MAYBELLINE 媚比琳 超持久水光鎖吻唇釉 #62 4.2ml",
momo_price=399,
competitor_price=399,
)
assert diagnostics.hard_veto is False
assert diagnostics.comparison_mode == "exact_identity"
assert diagnostics.score >= 0.76
assert "variant_option_conflict" not in diagnostics.reasons
def test_marketplace_matcher_promotes_shared_identity_anchor_near_threshold():
from services.marketplace_product_matcher import score_marketplace_match
obge = score_marketplace_match(
"【OBgE】自然遮瑕素顏霜 50g",
"OBgE/自然遮瑕素顏霜50g",
momo_price=699,
competitor_price=699,
)
unicat = score_marketplace_match(
"【UNICAT 變臉貓】超持久細滑眼線筆1.5ml",
"【UNICAT】超持久細滑眼線筆 1.5ml 新品搶先優惠",
momo_price=399,
competitor_price=399,
)
for diagnostics in (obge, unicat):
assert diagnostics.score >= 0.76
assert diagnostics.hard_veto is False
assert "shared_identity_anchor" in diagnostics.reasons
def test_marketplace_matcher_promotes_contained_identity_anchor_with_same_spec():
from services.marketplace_product_matcher import score_marketplace_match
sebamed = score_marketplace_match(
"【Sebamed】女性私密處護潔露200ml",
"Sebamed私密護潔露(pH6.8)黃金女郎型200ml",
momo_price=399,
competitor_price=399,
)
lab52 = score_marketplace_match(
"【Lab52 齒妍堂】兒童口腔清潔棒30入/盒",
"Lab52齒妍堂 兒童口腔清潔棒30入",
momo_price=299,
competitor_price=299,
)
for diagnostics in (sebamed, lab52):
assert diagnostics.score >= 0.76
assert diagnostics.hard_veto is False
assert "shared_identity_anchor" in lab52.reasons or "shared_identity_anchor" in sebamed.reasons
def test_marketplace_matcher_promotes_same_spec_name_alignment_near_threshold():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"【Sebamed】女性私密處護潔露200ml(國際航空版)",
"Sebamed私密護潔露(pH6.8)黃金女郎型200ml",
momo_price=399,
competitor_price=399,
)
assert diagnostics.score >= 0.76
assert diagnostics.hard_veto is False
assert "spec_name_alignment" in diagnostics.reasons or "shared_identity_anchor" in diagnostics.reasons
def test_marketplace_matcher_promotes_shared_anchor_without_spec_conflict():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"【Les nez 香鼻子】水晶香氛能量寶盒禮盒組(運財、智慧、愛情、守護、和諧、療癒、悟性 聖誕禮物)",
"【Les nez 香鼻子】水晶香氛能量寶盒禮盒組",
momo_price=2480,
competitor_price=2480,
)
assert diagnostics.score >= 0.76
assert diagnostics.hard_veto is False
assert "shared_identity_anchor_no_spec" in diagnostics.reasons
def test_marketplace_matcher_promotes_recipe_box_near_threshold_with_variant_safe_anchor():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"【Recipebox】Recipe Box兒童防曬氣墊粉餅(兒童化妝品/無毒防曬粉餅/天然彩妝)",
"Recipe Box 韓兔 兒童防曬氣墊粉餅",
momo_price=699,
competitor_price=699,
)
assert diagnostics.score >= 0.76
assert diagnostics.hard_veto is False
assert "shared_identity_anchor_variant_safe" in diagnostics.reasons
def test_marketplace_matcher_promotes_romand_palette_exact_line():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"【rom&nd】勝過眼皮十色眼影盤",
"rom&nd X ZO&FRIENDS 勝過眼皮十色眼影盤 8g/7g",
momo_price=499,
competitor_price=499,
)
assert diagnostics.score >= 0.76
assert diagnostics.hard_veto is False
assert "shared_identity_anchor_variant_safe" in diagnostics.reasons
def test_marketplace_matcher_promotes_lactacyd_private_wash_exact_line():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"【Lactacyd 立朵舒】私密潔浴露250ml - 滋潤緊緻/加倍修護(私密清潔 產後私密保養 更年期乾癢修護)",
"Lactacyd 立朵舒私密潔浴露-滋潤緊緻250ml",
momo_price=399,
competitor_price=399,
)
assert diagnostics.score >= 0.76
assert diagnostics.hard_veto is False
assert "shared_identity_anchor" in diagnostics.reasons
def test_marketplace_matcher_promotes_eaoron_classic_tone_up_cream_exact_line():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"【Eaoron】澳洲 經典素顏霜 50ml#0000199#(面霜 素顏霜 懶人霜 打造素顏女神)",
"【澳洲 EAORON】第三代經典版白素顏霜 50ml",
momo_price=399,
competitor_price=399,
)
assert diagnostics.score >= 0.76
assert diagnostics.hard_veto is False
assert "strong_exact_spec_match" in diagnostics.reasons
def test_marketplace_matcher_promotes_shared_model_token_for_exact_model():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"【Panasonic 國際牌】男士身體除毛器 2025新款 ER-GK83 日版 日本直送",
"Panasonic 國際牌 男仕防水美體除毛器 國際版 (ER-GK83)",
momo_price=2490,
competitor_price=2290,
)
assert diagnostics.score >= 0.76
assert diagnostics.hard_veto is False
assert "shared_model_token" in diagnostics.reasons
def test_marketplace_matcher_keeps_variant_warmer_low_score():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"【Les nez 香鼻子】香氛融蠟燈 閃耀琥珀(附燈泡 定時 調節亮度 聖誕節禮物)",
"【Les nez 香鼻子】香氛融蠟燈 雲石玫瑰琥珀 贈燈泡(定時 可調光 香氛蠟燭 香氛)",
momo_price=1980,
competitor_price=1980,
)
assert diagnostics.score < 0.76
assert diagnostics.hard_veto is False
def test_marketplace_matcher_keeps_generic_vs_specific_warmer_low_score():
from services.marketplace_product_matcher import score_marketplace_match
diagnostics = score_marketplace_match(
"【Les nez 香鼻子】北歐香氛融蠟燈(附燈泡 定時 調節亮度)",
"【Les nez 香鼻子】香氛融蠟燈 星夜款",
momo_price=1980,
competitor_price=1980,
)
assert diagnostics.score < 0.76
assert diagnostics.hard_veto is False
assert "variant_descriptor_conflict" in diagnostics.reasons
def test_marketplace_search_terms_prefer_readable_brand_core_spec():
from services.marketplace_product_matcher import build_search_terms
terms = build_search_terms("【LA ROCHE-POSAY 理膚寶水】B5全面修復霜 40ml")
assert terms[0] == "理膚寶水 全面修復霜 b5 40ml"
assert not any(term.endswith(" l") for term in terms)
def test_marketplace_search_terms_prioritize_identity_phrase_over_ambiguous_copy():
from services.marketplace_product_matcher import build_search_terms
terms = build_search_terms("【TAICEND 泰陞】寶貝液體保護膜 屁屁噴 100ml", max_terms=5)
assert terms[0] == "泰陞 屁屁噴 100ml"
assert "保護膜" not in terms[0]
assert "屁屁噴" in " ".join(terms[:3])
def test_marketplace_search_terms_drop_option_and_marketing_noise():
from services.marketplace_product_matcher import build_search_terms
terms = build_search_terms("【YSL】情挑誘光嫩唇凍6ml(任選一款/新品上市)", max_terms=5)
assert terms[0] == "ysl 情挑誘光嫩唇凍 6ml"
assert not any("一款" in term or "上市" in term for term in terms)
def test_marketplace_search_terms_keep_professional_product_phrase():
from services.marketplace_product_matcher import build_search_terms
abysse_terms = build_search_terms("【Abysse】天然植萃身體按摩精油550ml", max_terms=5)
mustela_terms = build_search_terms(
"【Mustela 慕之恬廊】慕之幼 免用水潔淨液 300ml(外出清潔 卸除髒汙 卸除防曬 卸防曬)",
max_terms=5,
)
assert abysse_terms[0] == "abysse 身體按摩精油 550ml"
assert mustela_terms[0] == "慕之恬廊 免用水潔淨液 300ml"
assert not any("卸除防曬" in term or "外出清潔" in term for term in mustela_terms)
def test_marketplace_search_terms_keep_variant_descriptor_for_sensitive_lines():
from services.marketplace_product_matcher import build_search_terms
dashing_terms = build_search_terms("【DASHING DIVA】MAGICPRESS時尚潮流美甲片_極光之藍", max_terms=5)
romand_terms = build_search_terms("【rom&nd】勝過眼皮十色眼影盤", max_terms=5)
assert dashing_terms[0] == "dashing diva 時尚潮流美甲片 極光之藍"
assert romand_terms[0] == "romand 勝過眼皮十色眼影盤"
def test_marketplace_search_terms_prioritize_exact_dashing_diva_variant_name():
from services.marketplace_product_matcher import build_search_terms
moon_terms = build_search_terms(
"【DASHING DIVA】MAGICPRESS 時尚潮流美甲片-月影柔霧(貓眼 金色 漸層)",
max_terms=5,
)
silk_terms = build_search_terms(
"【DASHING DIVA】MAGICPRESS 時尚潮流美甲片-銀絲柔彩(紫色 漸層 貓眼)",
max_terms=5,
)
assert moon_terms[0] == "dashing diva 時尚潮流美甲片 月影柔霧"
assert silk_terms[0] == "dashing diva 時尚潮流美甲片 銀絲柔彩"
def test_marketplace_search_terms_prioritize_private_wash_identity_phrase():
from services.marketplace_product_matcher import build_search_terms
terms = build_search_terms(
"【Lactacyd 立朵舒】私密潔浴露250ml - 滋潤緊緻/加倍修護(私密清潔 產後私密保養 更年期乾癢修護)",
max_terms=5,
)
assert terms[0] == "立朵舒 私密潔浴露 250ml"
assert "滋潤緊緻" in terms[1]
def test_marketplace_search_terms_prioritize_precise_primer_identity_phrase():
from services.marketplace_product_matcher import build_search_terms
shu_terms = build_search_terms(
"【植村秀】官方直營 無極限保濕防曬妝前乳30ml(Shu uemura/防曬/保濕/校色",
max_terms=5,
)
meme_terms = build_search_terms(
"【im meme】我愛修膚/水凝光透/控油/好氣色妝前防護乳(SPF50+PA++++)",
max_terms=5,
)
eaoron_terms = build_search_terms(
"【Eaoron】澳洲 經典素顏霜 50ml#0000199#(面霜 素顏霜 懶人霜 打造素顏女神)",
max_terms=5,
)
assert shu_terms[0] == "植村秀 無極限保濕防曬妝前乳 30ml"
assert "校色" not in " ".join(shu_terms[:3])
assert any("水凝光透 妝前防護乳" in term for term in meme_terms[:3])
assert "好氣色" not in " ".join(meme_terms[:3])
assert eaoron_terms[0] == "eaoron 素顏霜 50ml"
assert "懶人霜" not in " ".join(eaoron_terms[:3])
def test_marketplace_search_terms_prefer_exact_identity_for_nail_foam_and_foot_mask():
from services.marketplace_product_matcher import build_search_terms
opi_terms = build_search_terms(
"【O.P.I】Top Coat 持久閃耀保色護甲油15mL-IST31(類光繚指甲油專用亮油/小銀蓋/如膠似漆/美甲/官方直營)",
max_terms=5,
)
arau_terms = build_search_terms(
"【arau baby】愛樂寶 溫和洗手慕斯300ml (溫和不乾澀;寶寶共和國)",
max_terms=5,
)
kameria_terms = build_search_terms(
"【KAMERIA】凱蜜菈 足足稱奇足膜17ml*2枚入(任選三款)",
max_terms=5,
)
assert opi_terms[0] == "ist31 閃耀保色護甲油 15ml"
assert "小銀蓋" not in " ".join(opi_terms[:3])
assert arau_terms[0] == "愛樂寶 溫和洗手慕斯 300ml"
assert "溫和不乾澀" not in " ".join(arau_terms[:3])
assert kameria_terms[0] == "凱蜜菈 足足稱奇足膜 17ml 2包"
assert "枚入" not in " ".join(kameria_terms[:3])
def test_marketplace_search_terms_preserve_decimal_spec_and_shade_option():
from services.marketplace_product_matcher import build_search_terms
terms = build_search_terms(
"【Maybelline 媚比琳】超持久水光鎖吻唇釉#62 4.2ml",
max_terms=5,
)
assert terms[0] == "媚比琳 超持久水光鎖吻唇釉 62 4.2ml"
assert "4 2ml" not in " ".join(terms)
def test_marketplace_search_terms_prefer_specific_line_over_generic_usage_words():
from services.marketplace_product_matcher import build_search_terms
mask_terms = build_search_terms("【SK-II】官方直營 青春敷面膜10+6+6送12(保濕/面膜/超品日)", max_terms=5)
bottle_terms = build_search_terms(
"【LANCOME 蘭蔻】官方直營 經典款小黑瓶100ml雙入組(LANCOME/經典款/抗老)",
max_terms=5,
)
rose_terms = build_search_terms(
"【蘭蔻】官方直營 玫瑰霜60ml+玫瑰精露150ml(LANCOME/永生玫瑰霜/抗老/PDRN)",
max_terms=5,
)
assert mask_terms[0] == "sk ii 青春敷面膜"
assert bottle_terms[0] == "蘭蔻 小黑瓶 100ml"
assert rose_terms[0] == "蘭蔻 永生玫瑰霜 60ml"
assert "抗老" not in " ".join(bottle_terms[:3] + rose_terms[:3])
assert "pdrn" not in " ".join(term.lower() for term in rose_terms[:3])
def test_batch_compare_top_uses_latest_momo_price_not_revenue(monkeypatch):
from services import pchome_crawler
engine = create_engine("sqlite:///:memory:")
with engine.begin() as conn:
conn.execute(text("CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT, i_code TEXT)"))
conn.execute(text("CREATE TABLE daily_sales (product_id INTEGER, revenue NUMERIC, date TEXT)"))
conn.execute(text("CREATE TABLE price_records (id INTEGER PRIMARY KEY, product_id INTEGER, price NUMERIC, timestamp TEXT)"))
conn.execute(text("INSERT INTO products VALUES (1, '理膚寶水 B5 修復霜 40ml', 'SKU001')"))
conn.execute(text("INSERT INTO daily_sales VALUES (1, 999999, '2026-05-19')"))
conn.execute(text("INSERT INTO price_records VALUES (1, 1, 699, '2026-05-19 10:00:00')"))
calls = []
def fake_compare_product(name, momo_price, momo_icode=""):
calls.append((name, momo_price, momo_icode))
return {"found": True, "momo_price": momo_price}
monkeypatch.setattr(pchome_crawler, "compare_product", fake_compare_product)
results = pchome_crawler.batch_compare_top(engine, top_n=1)
assert results == [{"found": True, "momo_price": 699.0}]
assert calls == [("理膚寶水 B5 修復霜 40ml", 699.0, "SKU001")]