BuyingAgent/backend/tests/unit/test_price_score.py

104 lines
3.8 KiB
Python
Raw Permalink Normal View History

from datetime import datetime, timezone
from decimal import Decimal
from uuid import uuid4
from app.domain.models import Budget
from app.scoring.price import daily_series, median, score_price, select_current_observation
from tests.conftest import observation, utc
def test_budget_and_history_points(scoring_model):
budget = Budget(Decimal("1500.00"), Decimal("2000.00"))
score, budget_score, history_score, basis, warning = score_price(
current_price=Decimal("1500.00"),
reference_history=(Decimal("1500"), Decimal("1500"), Decimal("1500")),
budget=budget,
model=scoring_model,
)
assert budget_score == Decimal("100")
assert history_score == Decimal("50")
assert basis.value == "BUDGET_AND_HISTORY"
assert warning is None
over, over_budget, _, _, _ = score_price(
current_price=Decimal("2000.00"),
reference_history=(Decimal("1000"), Decimal("1000"), Decimal("1000")),
budget=budget,
model=scoring_model,
)
assert over_budget == Decimal("0")
cheap, _, hist, _, _ = score_price(
current_price=Decimal("800.00"),
reference_history=(Decimal("1000"), Decimal("1000"), Decimal("1000")),
budget=budget,
model=scoring_model,
)
assert hist == Decimal("100")
expensive, _, hist2, _, _ = score_price(
current_price=Decimal("1200.00"),
reference_history=(Decimal("1000"), Decimal("1000"), Decimal("1000")),
budget=budget,
model=scoring_model,
)
assert hist2 == Decimal("0")
def test_budget_only_with_short_history(scoring_model):
budget = Budget(Decimal("1500.00"), Decimal("2000.00"))
for days in ((), (Decimal("10"),), (Decimal("10"), Decimal("11"))):
score, budget_score, history_score, basis, warning = score_price(
current_price=Decimal("1298.00"),
reference_history=days,
budget=budget,
model=scoring_model,
)
assert score == budget_score == Decimal("100")
assert history_score is None
assert basis.value == "BUDGET_ONLY"
assert warning == "INSUFFICIENT_HISTORY"
def test_median_even_and_odd():
assert median((Decimal("1"), Decimal("3"), Decimal("5"))) == Decimal("3")
assert median((Decimal("1"), Decimal("2"), Decimal("3"), Decimal("4"))) == Decimal("2.5")
def test_daily_series_latest_then_quality_filter(scoring_model):
offer = uuid4()
morning = observation((10, 1), "10.00", offer_id=offer, hour=8)
later_unknown = observation((10, 1), "9.00", offer_id=offer, hour=12, availability="unknown")
values, _ = daily_series(
(morning, later_unknown),
window_start=datetime(2026, 9, 1, tzinfo=timezone.utc).date(),
window_end_exclusive=datetime(2026, 10, 3, tzinfo=timezone.utc).date(),
minimum_confidence=scoring_model.minimum_confidence,
)
assert values == ()
def test_current_observation_does_not_fall_back():
cheap_old = observation((10, 2), "10.00")
unknown_new = observation((10, 3), "99.00", availability="unknown")
current = select_current_observation(
(cheap_old, unknown_new),
as_of=utc(2026, 10, 3, 12),
snapshot_cutoff=utc(2026, 10, 3, 12),
)
assert current is not None
assert current.availability.value == "unknown"
assert current.item_price == Decimal("99.00")
def test_no_offer_object_required_for_price_function(scoring_model):
score, _, _, basis, _ = score_price(
current_price=Decimal("1298.00"),
reference_history=(Decimal("1599"), Decimal("1599"), Decimal("1599")),
budget=Budget(Decimal("1500"), Decimal("2000")),
model=scoring_model,
)
from app.scoring.mathutil import round_score
assert basis.value == "BUDGET_AND_HISTORY"
assert round_score(score) == Decimal("98.24")