Coverage for tests/test_services.py: 100%
56 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-11-13 19:28 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-11-13 19:28 +0000
1"""Tests for domain services."""
3from copy import deepcopy
4from datetime import datetime
6import pytest
8from data_api.data.manager import nthudata
9from data_api.domain.dining import services
10from data_api.domain.dining.enums import DiningScheduleKeyword
11from data_api.domain.dining.services import is_restaurant_open
14class TestDiningService:
15 """Tests for dining service functions."""
17 async def test_is_restaurant_open_no_note(self):
18 """Test restaurant with no note is open."""
19 restaurant = {"name": "Test", "note": ""}
20 assert is_restaurant_open(restaurant, "weekday") is True
22 async def test_is_restaurant_open_break_on_saturday(self):
23 """Test restaurant with break on Saturday is closed."""
24 restaurant = {"name": "Test", "note": "週六休息"}
25 assert is_restaurant_open(restaurant, "saturday") is False
27 async def test_is_restaurant_open_break_different_day(self):
28 """Test restaurant with break on different day is open."""
29 restaurant = {"name": "Test", "note": "週日休息"}
30 assert is_restaurant_open(restaurant, "saturday") is True
32 async def test_is_restaurant_open_weekend(self):
33 """Test restaurant on weekend."""
34 restaurant = {"name": "Test", "note": ""}
35 assert is_restaurant_open(restaurant, "saturday") is True
36 assert is_restaurant_open(restaurant, "sunday") is True
38 async def test_is_restaurant_open_weekday(self):
39 """Test restaurant on weekday."""
40 restaurant = {"name": "Test", "note": ""}
41 assert is_restaurant_open(restaurant, "weekday") is True
44class TestDiningScheduleKeyword:
45 """Tests for DiningScheduleKeyword enum."""
47 async def test_break_keywords_exist(self):
48 """Test break keywords are defined."""
49 assert len(DiningScheduleKeyword.BREAK_KEYWORDS) > 0
51 async def test_day_mapping_exists(self):
52 """Test day mapping is defined."""
53 assert "weekday" in DiningScheduleKeyword.DAY_EN_TO_ZH
54 assert "saturday" in DiningScheduleKeyword.DAY_EN_TO_ZH
55 assert "sunday" in DiningScheduleKeyword.DAY_EN_TO_ZH
58@pytest.mark.parametrize(
59 "instant,closed_day",
60 [
61 ("2026-09-25T15:59:59+00:00", "weekday"),
62 ("2026-09-25T16:00:00+00:00", "saturday"),
63 ("2026-09-26T15:59:59+00:00", "saturday"),
64 ("2026-09-26T16:00:00+00:00", "sunday"),
65 ("2026-09-27T15:59:59+00:00", "sunday"),
66 ("2026-09-27T16:00:00+00:00", "weekday"),
67 ],
68)
69@pytest.mark.parametrize(
70 "method", ["get_dining_data", "fuzzy_search_dining_data", "get_open_restaurants"]
71)
72async def test_today_uses_taiwan_date(monkeypatch, instant, closed_day, method):
73 restaurants = [
74 {"name": "weekday", "note": "平日休息"},
75 {"name": "saturday", "note": "週六休息"},
76 {"name": "sunday", "note": "週日休息"},
77 ]
78 data = [{"building": "Test", "metadata": "preserved", "restaurants": restaurants}]
79 original = deepcopy(data)
80 calls = []
81 now = datetime.fromisoformat(instant)
83 class FrozenDatetime(datetime):
84 @classmethod
85 def now(cls, tz=None):
86 assert str(tz) == "Asia/Taipei"
87 return now.astimezone(tz)
89 async def fake_get(endpoint):
90 calls.append(endpoint)
91 return "hash", data
93 monkeypatch.setattr(services, "datetime", FrozenDatetime)
94 monkeypatch.setattr(nthudata, "get", fake_get)
95 version, result = await getattr(services.dining_service, method)(schedule="today")
96 excluded = {
97 "weekday": {"weekday"},
98 "saturday": {"saturday"},
99 # The legacy note heuristic also treats "平日休息" as a Sunday closure.
100 "sunday": {"weekday", "sunday"},
101 }[closed_day]
102 expected = [r for r in restaurants if r["name"] not in excluded]
103 assert result == (
104 expected if method == "get_open_restaurants" else [{**data[0], "restaurants": expected}]
105 )
106 assert version == "hash"
107 assert calls == ["dining.json"]
108 assert data == original