-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_basic.py
More file actions
195 lines (140 loc) · 7 KB
/
test_basic.py
File metadata and controls
195 lines (140 loc) · 7 KB
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
import json
import os
import pytest
import datetime
# Import the function parse_time to use the real logic for comparison
from assistant import get_routine, get_task_by_time, add_routine_entry, remove_routine_entry, parse_time
# --- Setup Fixtures (Mock Data) ---
@pytest.fixture
def mock_routine_data():
"""Provides a sample routine structure for testing."""
return [
{"start": "09:00", "end": "10:00", "activity": "Wake up and meditate"},
{"start": "10:00", "end": "11:00", "activity": "Breakfast and check emails"},
{"start": "11:00", "end": "12:30", "activity": "Morning walk or yoga"},
{"start": "12:30", "end": "13:30", "activity": "Lunch break"},
{"start": "13:30", "end": "15:30", "activity": "Work on personal project X"},
]
@pytest.fixture(autouse=True)
def mock_json_file(tmp_path, mock_routine_data, monkeypatch):
"""
CRITICAL FIX: Creates a temporary routine.json file for tests to use in the FLAT structure.
Uses monkeypatch to directly override the variable in assistant.py.
"""
# Create the mock routine.json file directly in the temporary root path
file_path = tmp_path / "routine.json"
with open(file_path, "w") as f:
json.dump(mock_routine_data, f, indent=4)
# 1. Patch the ROUTINE_FILE_PATH variable in assistant.py to point to the mock file
# This ensures the assistant uses the mock file instead of the real one.
monkeypatch.setattr('assistant.ROUTINE_FILE_PATH', str(file_path))
# 2. Mock the favorites file path similarly for safety
favorites_path = tmp_path / "favorites.json"
monkeypatch.setattr('assistant.FAVORITES_FILE_PATH', str(favorites_path))
yield
# --- Test Cases ---
def test_get_routine_success():
"""Test that get_routine loads data successfully."""
routine_json_string = get_routine()
routine = json.loads(routine_json_string)
assert isinstance(routine, list)
assert len(routine) > 0
assert "activity" in routine[0]
def test_get_task_by_time_current_task(mocker):
"""Test finding a task currently in progress."""
# Setup mock for datetime.now() to return a time within the 10:00-11:00 slot (10:30)
mock_dt_class = mocker.MagicMock(spec=datetime.datetime)
mock_now_dt = datetime.datetime.combine(datetime.date.today(), datetime.time(10, 30))
# Configure the Mock Class
mock_dt_class.now.return_value = mock_now_dt
mock_dt_class.today.return_value = mock_now_dt
mock_dt_class.combine = datetime.datetime.combine # Use real combine method
# Patch the imported 'datetime' object in assistant.py
mocker.patch('assistant.datetime', mock_dt_class)
# get_task_by_time() with no argument uses the mocked current time (10:30)
result = get_task_by_time()
result_data = json.loads(result)
assert result_data["status"] == "found"
assert "Breakfast and check emails" in result_data["activity"]
def test_get_task_by_time_next_task(mocker):
"""Test finding a task by explicit time, and a next task."""
# --- Test 1: Querying a time *inside* an activity ---
result_in_task = get_task_by_time(query_time="10:00")
result_data_in_task = json.loads(result_in_task)
assert result_data_in_task["status"] == "found"
assert "Breakfast and check emails" in result_data_in_task["activity"]
# --- Test 2: Querying a time *after* the last entry (wrap around) ---
# Setup mock environment for explicit query (required for the datetime.combine path)
mock_dt_class = mocker.MagicMock(spec=datetime.datetime)
mock_today_date = datetime.date(2025, 12, 13)
mock_dt_class.today.return_value = mocker.MagicMock(date=lambda: mock_today_date)
mock_dt_class.combine = datetime.datetime.combine
mocker.patch('assistant.datetime', mock_dt_class)
# Time 15:35 is after the last entry (15:30) in the mock data
result_gap = get_task_by_time(query_time="15:35")
result_data_gap = json.loads(result_gap)
# The logic should wrap around and find the first task of the day (09:00)
assert result_data_gap["status"] == "next_found"
assert "Wake up and meditate" in result_data_gap["activity"]
def test_get_task_by_time_invalid_time():
"""Test handling of invalid time format."""
result = get_task_by_time(query_time="4 PM")
assert '"status": "error"' in result
assert "Invalid time format" in result
# --- New Routine Modification Tests ---
def test_add_routine_entry():
"""Test adding a new entry and verifying its presence and sorting."""
start_time = "12:15"
end_time = "12:20"
activity = "Quick Snack Break"
# 1. Add the entry
result_json = add_routine_entry(start_time, end_time, activity)
result_data = json.loads(result_json)
assert result_data["status"] == "success"
assert "Added Quick Snack Break" in result_data["message"]
# 2. Verify it was written to the mock file
routine_json_string = get_routine()
routine = json.loads(routine_json_string)
# Check total count (original 5 + 1 new)
assert len(routine) == 6
# Check for the new entry
new_entry_found = any(
entry["activity"] == activity for entry in routine
)
assert new_entry_found
# Check if the list is still sorted by time (12:15 should be between 11:00 and 12:30)
# Find the index of the new entry
new_entry_index = [i for i, entry in enumerate(routine) if entry['activity'] == activity][0]
# The task before the new one (11:00) should start before 12:15
assert parse_time(routine[new_entry_index - 1]['start']) < parse_time(start_time)
# The task after the new one (12:30) should start after 12:15
assert parse_time(routine[new_entry_index + 1]['start']) > parse_time(start_time)
def test_remove_routine_entry():
"""Test removing an entry based on a keyword match."""
keyword = "breakfast"
# 1. Remove the entry
result_json = remove_routine_entry(keyword)
result_data = json.loads(result_json)
assert result_data["status"] == "success"
assert result_data["removed_count"] == 1
# 2. Verify it was removed from the mock file
routine_json_string = get_routine()
routine = json.loads(routine_json_string)
# Check total count (original 5 - 1 removed)
assert len(routine) == 4
# Check that the removed activity is definitely gone
removed_found = any(
keyword.lower() in entry["activity"].lower() for entry in routine
)
assert not removed_found
def test_remove_routine_entry_not_found():
"""Test removal when the keyword does not match any entry."""
keyword = "nonexistent activity"
# 1. Attempt to remove the entry
result_json = remove_routine_entry(keyword)
result_data = json.loads(result_json)
assert result_data["status"] == "not_found"
# 2. Verify the list size is unchanged (original 5)
routine_json_string = get_routine()
routine = json.loads(routine_json_string)
assert len(routine) == 5