Skip to content

Commit cc7bc8f

Browse files
Merge pull request #234 from Doist/scottl/reminders
2 parents f882644 + 33a6894 commit cc7bc8f

8 files changed

Lines changed: 1270 additions & 0 deletions

File tree

tests/conftest.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,12 @@
1313
DEFAULT_COMPLETED_TASKS_RESPONSE,
1414
DEFAULT_LABEL_RESPONSE,
1515
DEFAULT_LABELS_RESPONSE,
16+
DEFAULT_LOCATION_REMINDER_RESPONSE,
17+
DEFAULT_LOCATION_REMINDERS_RESPONSE,
1618
DEFAULT_PROJECT_RESPONSE,
1719
DEFAULT_PROJECTS_RESPONSE,
20+
DEFAULT_REMINDER_RESPONSE,
21+
DEFAULT_REMINDERS_RESPONSE,
1822
DEFAULT_REQUEST_ID,
1923
DEFAULT_SECTION_RESPONSE,
2024
DEFAULT_SECTIONS_RESPONSE,
@@ -32,7 +36,9 @@
3236
Collaborator,
3337
Comment,
3438
Label,
39+
LocationReminder,
3540
Project,
41+
Reminder,
3642
Section,
3743
Task,
3844
)
@@ -210,6 +216,52 @@ def default_labels_list() -> list[list[Label]]:
210216
]
211217

212218

219+
@pytest.fixture
220+
def default_reminder_response() -> dict[str, Any]:
221+
return DEFAULT_REMINDER_RESPONSE
222+
223+
224+
@pytest.fixture
225+
def default_reminder() -> Reminder:
226+
return Reminder.from_dict(DEFAULT_REMINDER_RESPONSE)
227+
228+
229+
@pytest.fixture
230+
def default_reminders_response() -> list[PaginatedResults]:
231+
return DEFAULT_REMINDERS_RESPONSE
232+
233+
234+
@pytest.fixture
235+
def default_reminders_list() -> list[list[Reminder]]:
236+
return [
237+
[Reminder.from_dict(result) for result in response["results"]]
238+
for response in DEFAULT_REMINDERS_RESPONSE
239+
]
240+
241+
242+
@pytest.fixture
243+
def default_location_reminder_response() -> dict[str, Any]:
244+
return DEFAULT_LOCATION_REMINDER_RESPONSE
245+
246+
247+
@pytest.fixture
248+
def default_location_reminder() -> LocationReminder:
249+
return LocationReminder.from_dict(DEFAULT_LOCATION_REMINDER_RESPONSE)
250+
251+
252+
@pytest.fixture
253+
def default_location_reminders_response() -> list[PaginatedResults]:
254+
return DEFAULT_LOCATION_REMINDERS_RESPONSE
255+
256+
257+
@pytest.fixture
258+
def default_location_reminders_list() -> list[list[LocationReminder]]:
259+
return [
260+
[LocationReminder.from_dict(result) for result in response["results"]]
261+
for response in DEFAULT_LOCATION_REMINDERS_RESPONSE
262+
]
263+
264+
213265
@pytest.fixture
214266
def default_quick_add_response() -> dict[str, Any]:
215267
return DEFAULT_TASK_RESPONSE

tests/data/test_defaults.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,69 @@ class PaginatedItems(TypedDict):
268268
},
269269
]
270270

271+
DEFAULT_REMINDER_RESPONSE: dict[str, Any] = {
272+
"id": "6X7rM8997g3RQmvh",
273+
"item_id": "6Jf8VQXxpwv56VQ7",
274+
"notify_uid": "34567",
275+
"type": "relative",
276+
"is_deleted": False,
277+
"is_urgent": False,
278+
"minute_offset": 30,
279+
"due": DEFAULT_DUE_RESPONSE,
280+
"service": "push",
281+
}
282+
283+
DEFAULT_REMINDER_RESPONSE_2 = dict(DEFAULT_REMINDER_RESPONSE)
284+
DEFAULT_REMINDER_RESPONSE_2["id"] = "6X7rfFVPjhvv84XG"
285+
286+
DEFAULT_REMINDER_RESPONSE_3 = dict(DEFAULT_REMINDER_RESPONSE)
287+
DEFAULT_REMINDER_RESPONSE_3["id"] = "6X7rfEVP8hvv25ZQ"
288+
289+
DEFAULT_REMINDERS_RESPONSE: list[PaginatedResults] = [
290+
{
291+
"results": [DEFAULT_REMINDER_RESPONSE, DEFAULT_REMINDER_RESPONSE_2],
292+
"next_cursor": "next",
293+
},
294+
{
295+
"results": [DEFAULT_REMINDER_RESPONSE_3],
296+
"next_cursor": None,
297+
},
298+
]
299+
300+
DEFAULT_LOCATION_REMINDER_RESPONSE: dict[str, Any] = {
301+
"id": "6X7rM8997g3RQmvh",
302+
"item_id": "6Jf8VQXxpwv56VQ7",
303+
"project_id": "6Jf8VQXxpwv56VQ7",
304+
"notify_uid": "34567",
305+
"name": "Office",
306+
"loc_lat": "51.5074",
307+
"loc_long": "-0.1278",
308+
"loc_trigger": "on_enter",
309+
"radius": 100,
310+
"type": "location",
311+
"is_deleted": False,
312+
}
313+
314+
DEFAULT_LOCATION_REMINDER_RESPONSE_2 = dict(DEFAULT_LOCATION_REMINDER_RESPONSE)
315+
DEFAULT_LOCATION_REMINDER_RESPONSE_2["id"] = "6X7rfFVPjhvv84XG"
316+
317+
DEFAULT_LOCATION_REMINDER_RESPONSE_3 = dict(DEFAULT_LOCATION_REMINDER_RESPONSE)
318+
DEFAULT_LOCATION_REMINDER_RESPONSE_3["id"] = "6X7rfEVP8hvv25ZQ"
319+
320+
DEFAULT_LOCATION_REMINDERS_RESPONSE: list[PaginatedResults] = [
321+
{
322+
"results": [
323+
DEFAULT_LOCATION_REMINDER_RESPONSE,
324+
DEFAULT_LOCATION_REMINDER_RESPONSE_2,
325+
],
326+
"next_cursor": "next",
327+
},
328+
{
329+
"results": [DEFAULT_LOCATION_REMINDER_RESPONSE_3],
330+
"next_cursor": None,
331+
},
332+
]
333+
271334
DEFAULT_AUTH_RESPONSE = {
272335
"access_token": "123456789",
273336
"state": "somestate",
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
from __future__ import annotations
2+
3+
from typing import TYPE_CHECKING, Any
4+
5+
import pytest
6+
7+
from tests.data.test_defaults import (
8+
DEFAULT_API_URL,
9+
PaginatedResults,
10+
)
11+
from tests.utils.test_utils import api_headers, enumerate_async, mock_route
12+
from todoist_api_python.models import LocationReminder
13+
14+
if TYPE_CHECKING:
15+
import respx
16+
17+
from todoist_api_python.api import TodoistAPI
18+
from todoist_api_python.api_async import TodoistAPIAsync
19+
20+
21+
@pytest.mark.asyncio
22+
async def test_get_location_reminder(
23+
todoist_api: TodoistAPI,
24+
todoist_api_async: TodoistAPIAsync,
25+
respx_mock: respx.MockRouter,
26+
default_location_reminder_response: dict[str, Any],
27+
default_location_reminder: LocationReminder,
28+
) -> None:
29+
location_reminder_id = "6X7rM8997g3RQmvh"
30+
endpoint = f"{DEFAULT_API_URL}/location_reminders/{location_reminder_id}"
31+
32+
mock_route(
33+
respx_mock,
34+
method="GET",
35+
url=endpoint,
36+
request_headers=api_headers(),
37+
response_json=default_location_reminder_response,
38+
response_status=200,
39+
)
40+
41+
reminder = todoist_api.get_location_reminder(location_reminder_id)
42+
43+
assert len(respx_mock.calls) == 1
44+
assert reminder == default_location_reminder
45+
46+
reminder = await todoist_api_async.get_location_reminder(location_reminder_id)
47+
48+
assert len(respx_mock.calls) == 2
49+
assert reminder == default_location_reminder
50+
51+
52+
@pytest.mark.asyncio
53+
async def test_get_location_reminders(
54+
todoist_api: TodoistAPI,
55+
todoist_api_async: TodoistAPIAsync,
56+
respx_mock: respx.MockRouter,
57+
default_location_reminders_response: list[PaginatedResults],
58+
default_location_reminders_list: list[list[LocationReminder]],
59+
) -> None:
60+
task_id = "6X7rM8997g3RQmvh"
61+
endpoint = f"{DEFAULT_API_URL}/location_reminders"
62+
63+
cursor: str | None = None
64+
for page in default_location_reminders_response:
65+
mock_route(
66+
respx_mock,
67+
method="GET",
68+
url=endpoint,
69+
request_params={"task_id": task_id}
70+
| ({"cursor": cursor} if cursor else {}),
71+
request_headers=api_headers(),
72+
response_json=page,
73+
response_status=200,
74+
)
75+
cursor = page["next_cursor"]
76+
77+
count = 0
78+
79+
reminders_iter = todoist_api.get_location_reminders(task_id=task_id)
80+
81+
for i, reminders in enumerate(reminders_iter):
82+
assert len(respx_mock.calls) == count + 1
83+
assert reminders == default_location_reminders_list[i]
84+
count += 1
85+
86+
reminders_async_iter = await todoist_api_async.get_location_reminders(
87+
task_id=task_id
88+
)
89+
90+
async for i, reminders in enumerate_async(reminders_async_iter):
91+
assert len(respx_mock.calls) == count + 1
92+
assert reminders == default_location_reminders_list[i]
93+
count += 1
94+
95+
96+
@pytest.mark.asyncio
97+
async def test_add_location_reminder(
98+
todoist_api: TodoistAPI,
99+
todoist_api_async: TodoistAPIAsync,
100+
respx_mock: respx.MockRouter,
101+
default_location_reminder_response: dict[str, Any],
102+
default_location_reminder: LocationReminder,
103+
) -> None:
104+
task_id = "6X7rM8997g3RQmvh"
105+
106+
mock_route(
107+
respx_mock,
108+
method="POST",
109+
url=f"{DEFAULT_API_URL}/location_reminders",
110+
request_headers=api_headers(),
111+
request_json={
112+
"task_id": task_id,
113+
"name": "Office",
114+
"loc_lat": "51.5074",
115+
"loc_long": "-0.1278",
116+
"loc_trigger": "on_enter",
117+
"radius": 200,
118+
},
119+
response_json=default_location_reminder_response,
120+
response_status=200,
121+
)
122+
123+
new_reminder = todoist_api.add_location_reminder(
124+
task_id=task_id,
125+
name="Office",
126+
loc_lat="51.5074",
127+
loc_long="-0.1278",
128+
loc_trigger="on_enter",
129+
radius=200,
130+
)
131+
132+
assert len(respx_mock.calls) == 1
133+
assert new_reminder == default_location_reminder
134+
135+
new_reminder = await todoist_api_async.add_location_reminder(
136+
task_id=task_id,
137+
name="Office",
138+
loc_lat="51.5074",
139+
loc_long="-0.1278",
140+
loc_trigger="on_enter",
141+
radius=200,
142+
)
143+
144+
assert len(respx_mock.calls) == 2
145+
assert new_reminder == default_location_reminder
146+
147+
148+
@pytest.mark.asyncio
149+
async def test_update_location_reminder(
150+
todoist_api: TodoistAPI,
151+
todoist_api_async: TodoistAPIAsync,
152+
respx_mock: respx.MockRouter,
153+
default_location_reminder: LocationReminder,
154+
) -> None:
155+
args = {
156+
"name": "Home Office",
157+
"radius": 150,
158+
}
159+
updated_dict = default_location_reminder.to_dict() | args
160+
161+
mock_route(
162+
respx_mock,
163+
method="POST",
164+
url=f"{DEFAULT_API_URL}/location_reminders/{default_location_reminder.id}",
165+
request_headers=api_headers(),
166+
request_json=args,
167+
response_json=updated_dict,
168+
response_status=200,
169+
)
170+
171+
response = todoist_api.update_location_reminder(
172+
location_reminder_id=default_location_reminder.id, **args
173+
)
174+
175+
assert len(respx_mock.calls) == 1
176+
assert response == LocationReminder.from_dict(updated_dict)
177+
178+
response = await todoist_api_async.update_location_reminder(
179+
location_reminder_id=default_location_reminder.id, **args
180+
)
181+
182+
assert len(respx_mock.calls) == 2
183+
assert response == LocationReminder.from_dict(updated_dict)
184+
185+
186+
@pytest.mark.asyncio
187+
async def test_delete_location_reminder(
188+
todoist_api: TodoistAPI,
189+
todoist_api_async: TodoistAPIAsync,
190+
respx_mock: respx.MockRouter,
191+
) -> None:
192+
location_reminder_id = "6X7rM8997g3RQmvh"
193+
endpoint = f"{DEFAULT_API_URL}/location_reminders/{location_reminder_id}"
194+
195+
mock_route(
196+
respx_mock,
197+
method="DELETE",
198+
url=endpoint,
199+
request_headers=api_headers(),
200+
response_status=204,
201+
)
202+
203+
response = todoist_api.delete_location_reminder(location_reminder_id)
204+
205+
assert len(respx_mock.calls) == 1
206+
assert response is True
207+
208+
response = await todoist_api_async.delete_location_reminder(location_reminder_id)
209+
210+
assert len(respx_mock.calls) == 2
211+
assert response is True

0 commit comments

Comments
 (0)