File size: 3,222 Bytes
60e41bb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
from datetime import date, time

from waterleaf.calendar import CalendarPlant, build_garden_ics
from waterleaf.models import WateringEvent


def test_calendar_contains_timed_events_alarm_profile_and_attachment():
    plant = CalendarPlant(
        id="plant-1",
        nickname="Front rose",
        common_name="Dog rose",
        scientific_name="Rosa canina",
        timezone="Europe/Stockholm",
        preferred_time=time(7, 30),
        profile_url="https://waterleaf.example/plants/rose-abc",
        image_url="https://waterleaf.example/media/image-1",
        events=[
            WateringEvent(
                date=date(2026, 6, 12),
                reason="Deferred after forecast rain",
                confidence="forecast",
            )
        ],
    )

    content = build_garden_ics([plant], generated_at="20260608T100000Z")

    assert "X-WR-CALNAME:Waterleaf" in content
    assert "SUMMARY:Water Front rose" in content
    assert "DTSTART;TZID=Europe/Stockholm:20260612T073000" in content
    assert "DTEND;TZID=Europe/Stockholm:20260612T074500" in content
    assert "TRIGGER:-PT30M" in content
    assert "URL:https://waterleaf.example/plants/rose-abc" in content
    assert "ATTACH;FMTTYPE=image/jpeg:https://waterleaf.example/media/image-1" in content
    assert "Dog rose (Rosa canina)" in content


def test_calendar_uid_is_stable_for_same_plant_and_date():
    plant = CalendarPlant(
        id="plant-1",
        nickname="Mint",
        common_name="Spearmint",
        scientific_name="Mentha spicata",
        timezone="UTC",
        preferred_time=time(8, 0),
        profile_url="https://example.com/plants/mint",
        image_url="https://example.com/media/mint",
        events=[
            WateringEvent(
                date=date(2026, 6, 12),
                reason="Species care baseline",
                confidence="baseline",
            )
        ],
    )

    first = build_garden_ics([plant], generated_at="20260608T100000Z")
    second = build_garden_ics([plant], generated_at="20260609T100000Z")

    first_uid = next(line for line in first.splitlines() if line.startswith("UID:"))
    second_uid = next(line for line in second.splitlines() if line.startswith("UID:"))
    assert first_uid == second_uid


def test_calendar_escapes_text_and_folds_long_lines():
    plant = CalendarPlant(
        id="plant-2",
        nickname="Herbs, north bed",
        common_name="A very long common plant name used to force an RFC line fold",
        scientific_name="Mentha longifolia",
        timezone="UTC",
        preferred_time=time(9, 0),
        profile_url="https://example.com/plants/" + ("very-long-segment-" * 8),
        image_url="https://example.com/media/image-2",
        events=[
            WateringEvent(
                date=date(2026, 6, 14),
                reason="Line one\nLine two; check",
                confidence="seasonal",
            )
        ],
    )

    content = build_garden_ics([plant], generated_at="20260608T100000Z")

    assert "SUMMARY:Water Herbs\\, north bed" in content
    assert "Line one\\nLine two\\; check" in content.replace("\r\n ", "")
    for line in content.split("\r\n"):
        assert len(line.encode("utf-8")) <= 75