from flask import Flask def test_compare_prices_auto_fetches_targeted_momo_candidates_when_pchome_products_exist(monkeypatch): from routes import price_comparison_routes as routes captured = {} def fake_targeted_search(pchome_products, **kwargs): captured["targeted_momo_search"] = (pchome_products, kwargs) return True, "ok", [ { "name": "理膚寶水 B5 修復霜", "price": 890, "product_id": "12345678", "product_url": "https://www.momoshop.com.tw/goods/GoodsDetail.jsp?i_code=12345678", "can_auto_compare": True, } ] def fake_search_momo(keyword, limit=10): captured["fallback_momo_search"] = (keyword, limit) return True, "fallback", [] def fake_compare(brand, pchome_products, momo_products): captured["compare"] = (brand, pchome_products, momo_products) return { "pchome_count": len(pchome_products), "momo_count": len(momo_products), "matched_count": 0, "matches": [], "stats": {}, } monkeypatch.setattr(routes, "search_momo_products_for_pchome_products", fake_targeted_search) monkeypatch.setattr(routes, "search_momo_products", fake_search_momo) monkeypatch.setattr(routes, "compare_brand_prices", fake_compare) app = Flask(__name__) with app.test_request_context( "/api/price_comparison/compare", method="POST", json={ "brand": "理膚寶水", "pchome_products": [ {"name": "理膚寶水 B5 修復霜", "price": 900, "product_id": "PCHOME-1"}, ], }, ): response = routes.compare_prices.__wrapped__() payload = response.get_json() assert payload["success"] is True assert payload["data"]["momo_count"] == 1 assert payload["data"]["momo_targeted_search"] == { "message": "ok", "candidate_count": 1, "auto_compare_count": 1, "exact_compare_count": 1, "unit_compare_count": 0, "review_count": 0, } assert captured["targeted_momo_search"][0][0]["name"] == "理膚寶水 B5 修復霜" assert captured["targeted_momo_search"][1]["max_products"] == 30 assert "fallback_momo_search" not in captured assert captured["compare"][0] == "理膚寶水" assert captured["compare"][2][0]["product_id"] == "12345678" def test_compare_prices_falls_back_to_brand_momo_search_without_pchome_products(monkeypatch): from routes import price_comparison_routes as routes captured = {} def fake_search_momo(keyword, limit=10): captured["momo_search"] = (keyword, limit) return True, "ok", [ {"name": "理膚寶水 B5 修復霜", "price": 890, "product_id": "12345678"} ] def fake_search_pchome(keyword, limit=100): captured["pchome_search"] = (keyword, limit) return True, "ok", [] def fake_compare(brand, pchome_products, momo_products): captured["compare"] = (brand, pchome_products, momo_products) return { "pchome_count": len(pchome_products), "momo_count": len(momo_products), "matched_count": 0, "matches": [], "stats": {}, } monkeypatch.setattr(routes, "search_pchome_products", fake_search_pchome) monkeypatch.setattr(routes, "search_momo_products", fake_search_momo) monkeypatch.setattr(routes, "compare_brand_prices", fake_compare) app = Flask(__name__) with app.test_request_context( "/api/price_comparison/compare", method="POST", json={"brand": "理膚寶水"}, ): response = routes.compare_prices.__wrapped__() payload = response.get_json() assert payload["success"] is True assert payload["data"]["momo_count"] == 1 assert captured["pchome_search"] == ("理膚寶水", 100) assert captured["momo_search"] == ("理膚寶水", 100) def test_fetch_momo_for_pchome_endpoint_splits_auto_and_review_candidates(monkeypatch): from routes import price_comparison_routes as routes def fake_targeted_search(pchome_products, **kwargs): return True, "找到候選", [ { "name": "可直接比價商品", "price": 890, "product_id": "AUTO-1", "can_auto_compare": True, }, { "name": "組合需確認商品", "price": 468, "product_id": "UNIT-1", "auto_compare_type": "unit_price", "can_auto_compare": True, "target_review_status": "自動單位價比較", "target_unit_price_comparison": { "comparable": True, "unit_label": "ml", "momo_unit_price": 11.7, "competitor_unit_price": 23.0, "unit_gap_pct": -49.13, }, }, { "name": "組合需確認商品", "price": 468, "product_id": "REVIEW-1", "product_url": "https://www.momoshop.com.tw/goods/GoodsDetail.jsp?i_code=REVIEW-1", "can_auto_compare": False, "target_review_status": "需人工確認", "target_pchome_product_id": "PCH-1", "target_pchome_name": "PChome B5 修復霜", "target_pchome_price": 920, "target_match_score": 0.97, "target_match_reasons": ["variant_selection_review"], }, ] monkeypatch.setattr(routes, "search_momo_products_for_pchome_products", fake_targeted_search) app = Flask(__name__) with app.test_request_context( "/api/price_comparison/fetch_momo_for_pchome", method="POST", json={ "pchome_products": [ {"name": "理膚寶水 B5 修復霜", "price": 920, "product_id": "PCH-1"}, ], }, ): response = routes.fetch_momo_for_pchome_products.__wrapped__() payload = response.get_json() assert payload["success"] is True assert payload["message"] == "找到候選" assert payload["data"]["count"] == 1 assert payload["data"]["exact_compare_count"] == 1 assert payload["data"]["unit_compare_count"] == 1 assert payload["data"]["auto_compare_count"] == 2 assert payload["data"]["review_count"] == 1 assert payload["data"]["candidate_count"] == 3 assert payload["data"]["products"][0]["product_id"] == "AUTO-1" assert payload["data"]["unit_compare_candidates"][0]["product_id"] == "UNIT-1" review_candidate = payload["data"]["review_candidates"][0] assert review_candidate["product_id"] == "REVIEW-1" assert review_candidate["target_pchome_product_id"] == "PCH-1" assert review_candidate["target_pchome_name"] == "PChome B5 修復霜" assert review_candidate["target_pchome_price"] == 920 assert review_candidate["target_pchome_url"] == "https://24h.pchome.com.tw/prod/PCH-1" assert review_candidate["target_match_reason_labels"] == ["可信度 97%", "需確認色號"] assert "target_match_reasons" not in review_candidate assert payload["data"]["external_offer_sync"]["status"] == "not_requested" def test_fetch_momo_for_pchome_endpoint_syncs_auto_candidates_when_requested(monkeypatch): from routes import price_comparison_routes as routes captured = {} def fake_targeted_search(pchome_products, **kwargs): return True, "找到候選", [ { "name": "可直接比價商品", "price": 890, "product_id": "AUTO-1", "auto_compare_type": "total_price", "can_auto_compare": True, }, { "name": "自動單位價商品", "price": 468, "product_id": "UNIT-1", "auto_compare_type": "unit_price", "can_auto_compare": True, }, { "name": "需確認商品", "price": 468, "product_id": "REVIEW-1", "can_auto_compare": False, }, ] def fake_sync(candidates): captured["sync_candidates"] = candidates return { "success": True, "status": "synced", "written_count": len(candidates), "unit_price_count": 1, "total_price_count": 1, } monkeypatch.setattr(routes, "search_momo_products_for_pchome_products", fake_targeted_search) monkeypatch.setattr(routes, "_sync_targeted_candidates_to_external_offers", fake_sync) app = Flask(__name__) with app.test_request_context( "/api/price_comparison/fetch_momo_for_pchome", method="POST", json={ "sync_external_offers": True, "pchome_products": [ {"name": "理膚寶水 B5 修復霜", "price": 920, "product_id": "PCH-1"}, ], }, ): response = routes.fetch_momo_for_pchome_products.__wrapped__() payload = response.get_json() assert payload["success"] is True assert payload["data"]["external_offer_sync"]["status"] == "synced" assert payload["data"]["external_offer_sync"]["written_count"] == 2 assert [item["product_id"] for item in captured["sync_candidates"]] == ["AUTO-1", "UNIT-1"]