BuyingAgent/backend/tests/unit/test_price_score.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

104 lines
3.8 KiB
Python

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