fix(growth): normalize nested marketplace packs
Some checks failed
CD Pipeline / deploy (push) Has been cancelled

This commit is contained in:
ogt
2026-07-15 10:01:55 +08:00
parent 05c7cd8421
commit 954c729da2
4 changed files with 115 additions and 14 deletions

View File

@@ -3443,6 +3443,7 @@ def test_marketplace_search_terms_prefer_readable_brand_core_spec():
def test_marketplace_sheet_count_is_structured_for_search_and_identity():
from services.marketplace_product_matcher import (
build_unit_price_comparison,
build_search_terms,
parse_product_identity,
score_marketplace_match,
@@ -3450,15 +3451,21 @@ def test_marketplace_sheet_count_is_structured_for_search_and_identity():
pchome_name = "GATSBY 潔面濕紙巾超值包42張入"
same_pack = "【GATSBY】潔面濕紙巾超值包42張入(2款任選)"
multi_pack = "【GATSBY】潔面濕紙巾超值包42張入*4包(4款任選)"
different_pack = "【GATSBY】潔面濕紙巾15張入(3款任選)"
identity = parse_product_identity(pchome_name)
multi_pack_identity = parse_product_identity(multi_pack)
terms = build_search_terms(pchome_name, max_terms=5)
exact = score_marketplace_match(same_pack, pchome_name)
normalized = score_marketplace_match(multi_pack, pchome_name)
unit_price = build_unit_price_comparison(multi_pack, pchome_name, 495, 199)
rejected = score_marketplace_match(different_pack, pchome_name)
assert (42, "") in identity.counts
assert identity.total_piece_count == 42
assert (4, "") in multi_pack_identity.counts
assert multi_pack_identity.total_piece_count == 168
assert terms[0] == "gatsby 潔面濕紙巾 42張"
assert not any(term in {"gatsby 張入", "張入"} for term in terms)
assert exact.hard_veto is False
@@ -3467,6 +3474,16 @@ def test_marketplace_sheet_count_is_structured_for_search_and_identity():
assert exact.alert_tier == "price_alert_exact"
assert exact.spec_score == 1.0
assert "spec_name_alignment" in exact.reasons
assert normalized.hard_veto is True
assert normalized.comparison_mode == "unit_comparable"
assert normalized.match_type == "same_product_different_pack"
assert normalized.price_basis == "unit_price"
assert "count_conflict" in normalized.reasons
assert "pack_quantity_difference" in normalized.reasons
assert unit_price["comparable"] is True
assert unit_price["unit_label"] == ""
assert unit_price["momo_total_quantity"] == 168
assert unit_price["competitor_total_quantity"] == 42
assert rejected.hard_veto is True
assert "count_conflict" in rejected.reasons

View File

@@ -117,7 +117,7 @@ def test_independent_verifier_promotes_exact_and_unit_candidates_but_rejects_con
assert result["blocked_candidates"][0]["reasons"]
def test_independent_verifier_promotes_exact_sheet_pack_and_blocks_wrong_count():
def test_independent_verifier_prefers_exact_sheet_pack_over_unit_candidate():
from services.pchome_growth_same_item_reconciliation import (
verify_same_item_candidates,
)
@@ -160,14 +160,63 @@ def test_independent_verifier_promotes_exact_sheet_pack_and_blocks_wrong_count()
assert result["selected_candidate_count"] == 1
assert result["verified_candidates"][0]["product_id"] == "M-GATSBY-42"
assert result["verified_candidates"][0]["auto_compare_type"] == "total_price"
rejected = next(
item
for item in result["blocked_candidates"]
if item["source_product_id"] == "M-GATSBY-15"
assert result["verified_candidate_count"] == 2
assert result["blocked_candidate_count"] == 0
unit_result = verify_same_item_candidates(
[target],
[candidates[1]],
identity={
"trace_id": "trace-gatsby-unit",
"run_id": "run-gatsby-unit",
"work_item_id": "GROWTH-P0-001-B",
},
)
assert rejected["hard_veto"] is True
assert rejected["search_term"] == "gatsby 潔面濕紙巾 42張"
assert "count_conflict" in rejected["reasons"]
assert unit_result["selected_candidate_count"] == 1
unit_candidate = unit_result["verified_candidates"][0]
assert unit_candidate["auto_compare_type"] == "unit_price"
assert unit_candidate["target_price_basis"] == "unit_price"
assert unit_candidate["target_unit_price_comparison"]["comparable"] is True
assert unit_candidate["target_unit_price_comparison"]["unit_label"] == ""
def test_independent_verifier_never_treats_outer_sheet_pack_as_exact_total_price():
from services.pchome_growth_same_item_reconciliation import (
verify_same_item_candidates,
)
target = _target(
"P-GATSBY-42",
"GATSBY 潔面濕紙巾超值包42張入",
199,
1,
)
result = verify_same_item_candidates(
[target],
[{
"product_id": "M-GATSBY-42-X4",
"name": "【GATSBY】潔面濕紙巾超值包42張入*4包(4款任選)",
"price": 495,
"target_pchome_product_id": "P-GATSBY-42",
"target_search_term": "gatsby 潔面濕紙巾 42張",
"auto_compare_type": "total_price",
}],
identity={
"trace_id": "trace-gatsby-outer-pack",
"run_id": "run-gatsby-outer-pack",
"work_item_id": "GROWTH-P0-001-B",
},
)
assert result["selected_candidate_count"] == 1
candidate = result["verified_candidates"][0]
assert candidate["auto_compare_type"] == "unit_price"
assert candidate["same_item_reconciliation"]["candidate_claim_drift"] is True
assert candidate["target_match_type"] == "same_product_different_pack"
assert candidate["target_price_basis"] == "unit_price"
assert candidate["target_unit_price_comparison"]["unit_label"] == ""
assert candidate["target_unit_price_comparison"]["momo_total_quantity"] == 168
assert candidate["target_unit_price_comparison"]["competitor_total_quantity"] == 42
def test_coverage_post_verifier_requires_exact_source_readback_and_reports_revenue_delta():