from datetime import datetime def test_build_targeted_momo_search_terms_keeps_identity_and_spec(): from services.momo_crawler import build_targeted_momo_search_terms terms = build_targeted_momo_search_terms("【LA ROCHE-POSAY 理膚寶水】B5全面修復霜 40ml", max_terms=5) assert terms[0] == "理膚寶水 全面修復霜 b5 40ml" assert any("40ml" in term for term in terms) assert any("b5" in term.lower() for term in terms) def test_momo_next_search_payload_parser_extracts_goods_info_list(): from services.momo_crawler import MomoCrawler html = ( r'{\"goodsInfoList\":[{\"goodsCode\":\"10833188\",' r'\"goodsName\":\"【理膚寶水】B5+全面修復霜輕量版 40ml 年度限定組B\",' r'\"goodsPrice\":\"$$468\",' r'\"goodsPriceOri\":\"$$835\",' r'\"imgUrl\":\"https://img3.momoshop.com.tw/goodsimg/0010/833/188/10833188_OL.jpg\"}]}' ) products = MomoCrawler(timeout=1, delay=0)._parse_next_search_payload_results(html, limit=5) assert len(products) == 1 assert products[0].product_id == "10833188" assert products[0].name == "【理膚寶水】B5+全面修復霜輕量版 40ml 年度限定組B" assert products[0].price == 468 assert products[0].original_price == 835 assert products[0].discount == 44 assert products[0].product_url.endswith("i_code=10833188") def test_search_momo_products_for_pchome_products_uses_each_pchome_product_as_target(): from services.momo_crawler import MomoProduct, search_momo_products_for_pchome_products calls = [] class FakeCrawler: def search_products(self, keyword, limit=10, sort_by="sSaleQty/dc"): calls.append((keyword, limit, sort_by)) if "b5" in keyword.lower() or "全面修復霜" in keyword: return True, "ok", [ MomoProduct( product_id="12345678", name="理膚寶水 B5全面修復霜 40ml", price=890, original_price=990, discount=10, image_url="", product_url="https://www.momoshop.com.tw/goods/GoodsDetail.jsp?i_code=12345678", brand="理膚寶水", crawled_at=datetime.now(), ) ] return True, "ok", [] success, message, products = search_momo_products_for_pchome_products( [ { "product_id": "PCH-1", "name": "【LA ROCHE-POSAY 理膚寶水】B5全面修復霜 40ml", "price": 920, } ], crawler=FakeCrawler(), max_products=5, max_terms_per_product=4, limit_per_product=3, min_score=0.0, ) assert success is True assert "找到 1 筆候選" in message assert calls assert calls[0][1] == 3 assert products[0]["product_id"] == "12345678" assert products[0]["target_pchome_product_id"] == "PCH-1" assert products[0]["target_pchome_name"] == "【LA ROCHE-POSAY 理膚寶水】B5全面修復霜 40ml" assert products[0]["target_pchome_price"] == 920 assert products[0]["target_match_score"] > 0 assert products[0]["source_strategy"] == "pchome_targeted_momo_search" def test_targeted_momo_search_auto_routes_unit_comparable_candidates(): from services.momo_crawler import MomoProduct, search_momo_products_for_pchome_products class FakeCrawler: def search_products(self, keyword, limit=10, sort_by="sSaleQty/dc"): return True, "ok", [ MomoProduct( product_id="10833188", name="【理膚寶水】B5+全面修復霜輕量版 40ml 年度限定組B(萬用修復)", price=468, original_price=835, discount=44, image_url="", product_url="https://www.momoshop.com.tw/goods/GoodsDetail.jsp?i_code=10833188", brand="理膚寶水", crawled_at=datetime.now(), ) ] success, message, products = search_momo_products_for_pchome_products( [ { "product_id": "PCH-1", "name": "【LA ROCHE-POSAY 理膚寶水】B5全面修復霜 40ml", "price": 920, } ], crawler=FakeCrawler(), max_terms_per_product=1, limit_per_product=3, min_score=0.45, ) assert success is True assert "自動單位價比較 1 筆" in message assert products[0]["target_comparison_mode"] == "unit_comparable" assert products[0]["target_hard_veto"] is True assert products[0]["can_auto_compare"] is True assert products[0]["auto_compare_type"] == "unit_price" assert products[0]["target_price_basis"] == "unit_price" assert products[0]["target_review_status"] == "自動單位價比較" assert products[0]["target_unit_price_comparison"]["comparable"] is True assert products[0]["target_unit_price_comparison"]["unit_label"] == "ml" assert products[0]["target_gap_pct"] < 0 def test_targeted_momo_search_keeps_identity_review_candidates_manual(monkeypatch): from services.momo_crawler import MomoProduct, search_momo_products_for_pchome_products class FakeDiagnostics: score = 0.98 hard_veto = False comparison_mode = "exact_identity" price_basis = "manual_review" alert_tier = "identity_review" match_type = "exact" reasons = ("variant_selection_review", "strong_exact_spec_match") class FakeCrawler: def search_products(self, keyword, limit=10, sort_by="sSaleQty/dc"): return True, "ok", [ MomoProduct( product_id="12876190", name="【LAURA MERCIER 蘿拉蜜思】煥顏透明蜜粉 29g(#Rose-國際航空版)", price=809, original_price=999, discount=19, image_url="", product_url="https://www.momoshop.com.tw/goods/GoodsDetail.jsp?i_code=12876190", brand="蘿拉蜜思", crawled_at=datetime.now(), ) ] monkeypatch.setattr( "services.marketplace_product_matcher.score_marketplace_match", lambda *args, **kwargs: FakeDiagnostics(), ) success, message, products = search_momo_products_for_pchome_products( [ { "product_id": "PCH-LAURA", "name": "【Laura Mercier 蘿拉蜜思】煥顏透明蜜粉 29g", "price": 899, } ], crawler=FakeCrawler(), max_terms_per_product=1, limit_per_product=3, min_score=0.45, ) assert success is True assert "需 AI 自動驗證確認 1 筆" in message assert products[0]["auto_compare_type"] == "manual_review" assert products[0]["can_auto_compare"] is False assert products[0]["target_price_basis"] == "none" assert products[0]["target_alert_tier"] == "identity_review"