File size: 2,061 Bytes
90b4eb9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
"""Tests for the text rendering layer (render.py).

The critical one is `test_no_none_leaks_*`: if a missing value ever renders as
the literal "None", it silently poisons the model's prompt.
"""

from datetime import date, timedelta

from rate_my_run.models import WeekStats, TrainingSummary
from rate_my_run.render import format_pace, summary_to_text


def _empty_weeks(n=8):
    """n empty weekly buckets (oldest -> newest)."""
    return [WeekStats(date.today() - timedelta(days=7 * (n - i)), 0, 0.0, 0.0, None)
            for i in range(n)]


def _summary(weeks, days_since=1, mileage=None, trend="insufficient_data", signals=None):
    return TrainingSummary(
        weeks=weeks,
        days_since_last_run=days_since,
        mileage_change_pct=mileage,
        pace_trend=trend,
        signals=signals or [],
    )


# --- format_pace ------------------------------------------------------------

def test_format_pace_basic():
    assert format_pace(5.0) == "5:00/km"
    assert format_pace(5.5) == "5:30/km"


def test_format_pace_none():
    assert format_pace(None) == "n/a"


def test_format_pace_rounds_60s_up():
    # 5.999 min would round to 5:60 -> must become 6:00
    assert format_pace(5.999) == "6:00/km"


# --- summary_to_text --------------------------------------------------------

def test_no_none_leaks_with_missing_data():
    # All-empty history: pace None, mileage None, insufficient pace trend.
    text = summary_to_text(_summary(_empty_weeks()))
    assert "None" not in text


def test_goal_is_included_when_given():
    text = summary_to_text(_summary(_empty_weeks()), goal="run a sub-25 5K")
    assert "sub-25 5K" in text


def test_goal_omitted_when_absent():
    text = summary_to_text(_summary(_empty_weeks()))
    assert "stated goal" not in text


def test_signals_render_as_descriptions_not_raw_keys():
    text = summary_to_text(_summary(_empty_weeks(), signals=["mileage_spike"]))
    assert "Weekly mileage rose more than 10%" in text
    assert "mileage_spike" not in text  # raw enum key must not leak through