BuyingAgent/backend/tests/unit/test_engine_cases.py
Lars b276758ee1
All checks were successful
Deploy Development / deploy (push) Successful in 1m19s
Test Suite / pytest-backend (push) Successful in 31s
Test Suite / smoke-dev (push) Successful in 0s
Test Suite / frontend-checks (push) Successful in 28s
Stelle den Frontend-MVP und die v0.3-API auf develop bereit, damit Dev API und PWA zusammen deployen.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-04 13:04:59 +02:00

166 lines
7.1 KiB
Python

from datetime import timedelta
from decimal import Decimal
from uuid import uuid4
from app.domain.enums import Eligibility, PriceReferenceScope
from app.scoring.deal import classify_deal_score
from app.scoring.engine import evaluate_deal
from app.scoring.mathutil import round_score
from tests.conftest import (
AS_OF,
CUTOFF,
case_a_attributes,
case_a_prices,
offer_a,
observation,
product_a,
statement,
variant_a,
)
def _run(profile, scoring_model, definitions, attributes=None, observations=None, product=None, offer=None):
return evaluate_deal(
profile=profile,
scoring_model=scoring_model,
product=product or product_a(),
variant=variant_a(),
offer=offer or offer_a(),
attributes=attributes or case_a_attributes(),
observations=observations or case_a_prices(),
definitions=definitions,
as_of=AS_OF,
snapshot_cutoff=CUTOFF,
engine_build="test",
)
def test_reference_case_a(profile, scoring_model, definitions):
result = _run(profile, scoring_model, definitions)
assert result.product_eligibility == Eligibility.ELIGIBLE
assert result.offer_eligibility == Eligibility.ELIGIBLE
assert result.overall_eligibility == Eligibility.ELIGIBLE
assert result.eligible is True
assert result.product_score == Decimal("90.21")
assert result.price_statistics.median_30d == Decimal("1599.00")
assert result.price_statistics.median_30d_days == 2
assert result.price_statistics.median_90d == Decimal("1599.00")
assert result.price_statistics.median_90d_days == 3
assert result.price_statistics.lowest_365d == Decimal("1599.00")
assert result.price_statistics.lowest_365d_days == 3
assert result.price_score == Decimal("98.24")
assert result.deal_score == Decimal("93.42")
assert result.classification == "EXCEPTIONAL"
assert result.product_coverage == Decimal("1")
assert result.price_reference_scope == PriceReferenceScope.OFFER
assert result.price_basis.value == "BUDGET_AND_HISTORY"
assert any(reason.code == "PRICE_BELOW_MEDIAN" for reason in result.reasons)
def test_case_b_ineligible_product(profile, scoring_model, definitions):
attrs = (
statement("ram_gb", 8),
statement("storage_gb", 1024),
statement("weight_kg", Decimal("1.2")),
statement("battery_score", 90),
statement("display_score", 90),
statement("cpu_score", 90),
)
result = _run(profile, scoring_model, definitions, attributes=attrs)
assert result.product_eligibility == Eligibility.INELIGIBLE
assert result.offer_eligibility == Eligibility.ELIGIBLE
assert result.overall_eligibility == Eligibility.INELIGIBLE
assert result.eligible is False
assert result.product_score is not None
assert result.deal_score is None
assert result.classification == "INELIGIBLE"
assert any(item.constraint_id == "min_ram" and item.status == "FAIL" for item in result.constraint_results)
def test_case_c_unknown_weight(profile, scoring_model, definitions):
attrs = (
statement("ram_gb", 32),
statement("storage_gb", 1024),
statement("weight_kg", None, status="unknown"),
statement("battery_score", 90),
statement("display_score", 90),
statement("cpu_score", 90),
)
result = _run(profile, scoring_model, definitions, attributes=attrs)
assert result.product_eligibility == Eligibility.UNKNOWN
assert result.offer_eligibility == Eligibility.ELIGIBLE
assert result.overall_eligibility == Eligibility.UNKNOWN
assert result.eligible is None
assert result.product_score is not None
assert result.deal_score is None
assert result.classification == "INSUFFICIENT_DATA"
def test_case_d_over_budget_keeps_product_fit(profile, scoring_model, definitions):
prices = (
observation((9, 1), "1599.00"),
observation((9, 15), "1599.00"),
observation((10, 1), "1599.00"),
observation((10, 3), "2049.00"),
)
result = _run(profile, scoring_model, definitions, observations=prices)
assert result.product_eligibility == Eligibility.ELIGIBLE
assert result.offer_eligibility == Eligibility.INELIGIBLE
assert result.overall_eligibility == Eligibility.INELIGIBLE
assert result.product_score == Decimal("90.21")
assert result.price_score is not None
assert result.deal_score is None
def test_freshness_boundary(profile, scoring_model, definitions):
fresh = observation((10, 3), "1298.00")
fresh = fresh.__class__(**{**fresh.__dict__, "observed_at": AS_OF - timedelta(hours=48)})
result = _run(profile, scoring_model, definitions, observations=(fresh,))
assert result.offer_eligibility in {Eligibility.ELIGIBLE, Eligibility.UNKNOWN}
stale = fresh.__class__(**{**fresh.__dict__, "observed_at": AS_OF - timedelta(hours=48, seconds=1)})
result_stale = _run(profile, scoring_model, definitions, observations=(stale,))
assert result_stale.offer_eligibility == Eligibility.UNKNOWN
assert result_stale.price_score is None
def test_unknown_shipping(profile, scoring_model, definitions):
price = observation((10, 3), "1298.00")
price = price.__class__(**{**price.__dict__, "shipping_price": None})
result = _run(profile, scoring_model, definitions, observations=(price,))
assert result.offer_eligibility == Eligibility.UNKNOWN
assert result.price_score is None
def test_classification_boundaries(scoring_model):
assert classify_deal_score(Decimal("59.999"), scoring_model) == "IGNORE"
assert classify_deal_score(Decimal("60"), scoring_model) == "WATCH"
assert classify_deal_score(Decimal("74.999"), scoring_model) == "WATCH"
assert classify_deal_score(Decimal("75"), scoring_model) == "GOOD"
assert classify_deal_score(Decimal("85"), scoring_model) == "VERY_GOOD"
assert classify_deal_score(Decimal("92"), scoring_model) == "VERY_GOOD"
assert classify_deal_score(Decimal("92.01"), scoring_model) == "EXCEPTIONAL"
def test_rounding_does_not_shift_class(scoring_model):
raw = Decimal("59.996")
assert classify_deal_score(raw, scoring_model) == "IGNORE"
assert round_score(raw) == Decimal("60.00")
def test_other_category_without_engine_change(profile, scoring_model, definitions):
camera = product_a().__class__(**{**product_a().__dict__, "category": "camera"})
result = _run(profile, scoring_model, definitions, product=camera)
assert result.product_eligibility == Eligibility.INELIGIBLE
assert any(gate.reason_code == "CATEGORY_MISMATCH" for gate in result.product_gates)
def test_does_not_mix_other_offer_history(profile, scoring_model, definitions):
other = observation((9, 1), "100.00", offer_id=uuid4())
mixed = case_a_prices() + (other,)
result = _run(profile, scoring_model, definitions, observations=mixed)
# Engine is called with the provided list; the assembler in v0.1 should only
# receive one offer. Guard the function by using only same-offer rows.
same_offer = tuple(item for item in mixed if item.offer_id == offer_a().id)
result = _run(profile, scoring_model, definitions, observations=same_offer)
assert result.price_statistics.median_90d == Decimal("1599.00")