"""Tests for the deterministic pricing gate (playbook Definition of Done, ยง13).""" from __future__ import annotations import pytest from config.settings import PricingRules from pricing_agent.schemas import BuyBoxStatus, CompetitiveSnapshot, Decision from pricing_agent.tools.gate import evaluate_gate from pricing_agent.tools.margin_engine import worst_case_stack RULES = PricingRules( margin_floor=0.25, returns_reserve_pct=0.02, storage_alloc=0.25, worst_case_referral_pct=0.15, ) def _stack(price): return worst_case_stack(selling_price=price, landed_cost=8.00, fba_fee=5.50, rules=RULES) def test_healthy_sku_won_buybox_is_approved(): comp = CompetitiveSnapshot(buy_box_status=BuyBoxStatus.WON, competitive_median=24.99) decision, reasons = evaluate_gate(gate_stack=_stack(24.99), competitive=comp, rules=RULES) assert decision == Decision.APPROVED assert reasons def test_below_break_even_is_blocked(): comp = CompetitiveSnapshot(buy_box_status=BuyBoxStatus.WON) decision, reasons = evaluate_gate(gate_stack=_stack(16.00), competitive=comp, rules=RULES) assert decision == Decision.BLOCKED assert "break-even" in " ".join(reasons).lower() def test_below_margin_floor_is_blocked(): # $18.00 is above break-even ($16.76) but its CM (~6.6%) is under the 25% floor, # so it blocks on the margin-floor rule (which is stricter than the MAP guardrail). comp = CompetitiveSnapshot(buy_box_status=BuyBoxStatus.WON) stack = _stack(18.00) decision, reasons = evaluate_gate(gate_stack=stack, competitive=comp, rules=RULES) assert decision == Decision.BLOCKED assert "floor" in " ".join(reasons).lower() # MAP is still computed and available as a downstream guardrail, ~$19. assert round(stack.map_floor, 2) == pytest.approx(19.00, abs=0.25) def test_suppressed_listing_is_blocked_even_if_priced_well(): comp = CompetitiveSnapshot(buy_box_status=BuyBoxStatus.SUPPRESSED, is_suppressed=True) decision, reasons = evaluate_gate(gate_stack=_stack(24.99), competitive=comp, rules=RULES) assert decision == Decision.BLOCKED assert "suppress" in " ".join(reasons).lower() def test_lost_buybox_on_price_needs_review(): comp = CompetitiveSnapshot(buy_box_status=BuyBoxStatus.LOST_PRICE, competitive_median=22.00) decision, reasons = evaluate_gate(gate_stack=_stack(24.99), competitive=comp, rules=RULES) assert decision == Decision.NEEDS_REVIEW def test_lost_buybox_on_eligibility_needs_review_not_silent_approval(): """Previously fell through every branch and was APPROVED with no mention of the loss.""" comp = CompetitiveSnapshot( buy_box_status=BuyBoxStatus.LOST_ELIGIBILITY, competitive_median=26.00 ) decision, reasons = evaluate_gate(gate_stack=_stack(24.99), competitive=comp, rules=RULES) assert decision == Decision.NEEDS_REVIEW assert "eligibility" in " ".join(reasons).lower()