AI-Pricing-Agent/tests/test_evaluate_gate.py

70 lines
2.9 KiB
Python

"""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()