"""unit test: build_product_details thresholding, promo read, and combined_discount run from the repo root: uv run pytest tests/test_build_product_details.py pure and offline — no Mongo. Builds a Product from a raw doc dict and checks the discount threshold (with percentage codes compounding onto the sale price), the promo fields read through via .get, and the combined_discount helper. """ from datetime import datetime, timezone from monitor_utils.runner import build_product_details, combined_discount def _doc(**overrides) -> dict: """A well-formed product doc; override prices/promo per test.""" doc = { "_id": "p1_tools", "title": "Widget", "url": "http://x/p1", "currentPrice": 80.0, "originalPrice": 100.0, "imageUrl": "http://img/1", "customizationIndex": None, "memberNonMemberComparison": None, "overstockText": None, "discoveredDate": datetime(2026, 7, 26, tzinfo=timezone.utc), "startDate": None, "endDate": None, "priceBeforeDrop": None, "eduPrice": None, "pastPrices": [ {"price": 80.0, "date": datetime(2026, 7, 26, tzinfo=timezone.utc)} ], "available": True, "condition": None, "appExclusive": None, "memberPick": None, "memberDeal": None, } doc.update(overrides) return doc # ------------------------------------------------------------ combined_discount def test_combined_discount_no_promo_is_base(): assert combined_discount(24, None) == 24 assert combined_discount(24, 0) == 24 def test_combined_discount_compounds_not_adds(): # 20% off then extra 30% code -> 44% off list (customer pays $56), not 50% assert combined_discount(20, 30) == 44 def test_combined_discount_rounds_to_nearest(): # 24% sale + 22% code: 1 - .76*.78 = .4072 -> 41 (rounded, not truncated 40) assert combined_discount(24, 22) == 41 # --------------------------------------------------------- build_product_details def test_promo_fields_read_through(): doc = _doc( promoCode="BIRTHDAY26", promoPercentage=22, promoDollarOff=None, promoMinSpend=None, ) product = build_product_details(doc, min_percentage=0) assert product.metadata.promo_code == "BIRTHDAY26" assert product.metadata.promo_percentage == 22 # base percentage is the raw price delta, unchanged by the code assert product.metadata.percentage == 20 def test_asin_reads_through(): """asin round-trips: convert_product_to_mongo writes it, so the consumer must read it back for the SellerAmp lookup""" product = build_product_details(_doc(asin="B0CHX3QBCH"), min_percentage=0) assert product.metadata.asin == "B0CHX3QBCH" def test_missing_asin_defaults_to_none(): assert build_product_details(_doc(), min_percentage=0).metadata.asin is None def test_missing_promo_keys_default_to_none(): """a doc from a lagging monitor lacks promo keys -> None, not KeyError""" product = build_product_details(_doc(), min_percentage=0) assert product.metadata.promo_code is None assert product.metadata.promo_percentage is None def test_percentage_code_lifts_item_over_threshold(): # 30% base fails min 40, but +22% code -> 45% combined clears it doc = _doc(currentPrice=70.0, originalPrice=100.0, promoPercentage=22) assert build_product_details(doc, min_percentage=40) is not None def test_below_threshold_without_code_is_skipped(): doc = _doc(currentPrice=70.0, originalPrice=100.0) # 30%, no code assert build_product_details(doc, min_percentage=40) is None def test_dollar_code_does_not_affect_threshold(): # a dollar code has no promoPercentage, so 30% base stays below min 40 doc = _doc( currentPrice=70.0, originalPrice=100.0, promoCode="STORE22", promoDollarOff=22, promoMinSpend=100, ) assert build_product_details(doc, min_percentage=40) is None # ----------------------------------------------------------------- pastPrices def test_past_prices_read_through(): """Price history passes through unchanged.""" history = [ {"price": 95.0, "date": datetime(2026, 7, 20, tzinfo=timezone.utc)}, {"price": 90.0, "date": datetime(2026, 7, 24, tzinfo=timezone.utc)}, ] product = build_product_details(_doc(pastPrices=history), min_percentage=0) assert product.pricing.past_prices == history # ----------------------------------------------------------- bypass_thresholds def test_bypass_builds_a_below_threshold_product(): """A new listing sends at any discount when the caller says so.""" doc = _doc(currentPrice=95.0, originalPrice=100.0) # 5%, way under 40 product = build_product_details(doc, min_percentage=40, bypass_thresholds=True) assert product is not None assert product.metadata.percentage == 5 def test_bypass_defaults_off(): doc = _doc(currentPrice=95.0, originalPrice=100.0) assert build_product_details(doc, min_percentage=40) is None def test_bypass_does_not_rescue_broken_prices(): """The missing-price skip is a data guard, not a threshold.""" doc = _doc(currentPrice=95.0, originalPrice=None) assert build_product_details(doc, min_percentage=40, bypass_thresholds=True) is None # --------------------------------------------------------------- newListing def test_new_listing_flag_reads_through(): product = build_product_details(_doc(newListing=True), min_percentage=0) assert product.metadata.new_listing is True def test_legacy_docs_without_the_flag_build_as_none(): """Documents written before newListing existed must not KeyError.""" product = build_product_details(_doc(), min_percentage=0) assert product.metadata.new_listing is None # ------------------------------------------------------------ msrp-less guard def test_msrpless_doc_with_missing_original_price_skips_not_crashes(): """Regression: the guard lived inside the has_msrp branch, so this doc shape raised ValidationError (not an expected error) and killed the consumer.""" doc = _doc(hasMsrp=False, originalPrice=None) assert build_product_details(doc, min_percentage=40) is None def test_msrpless_doc_with_zero_original_price_skips(): doc = _doc(hasMsrp=False, originalPrice=0) assert build_product_details(doc, min_percentage=40) is None def test_bypass_does_not_rescue_msrpless_broken_prices(): doc = _doc(hasMsrp=False, originalPrice=None) assert build_product_details(doc, min_percentage=40, bypass_thresholds=True) is None def test_msrpless_doc_with_prices_builds_unthresholded(): """A priced MSRP-less doc still skips the threshold gate entirely.""" doc = _doc(hasMsrp=False, currentPrice=95.0, originalPrice=100.0) # 5% < 40 product = build_product_details(doc, min_percentage=40) assert product is not None assert product.pricing.has_msrp is False