Skip to content

Commit 593de7e

Browse files
authored
Merge pull request #108 from elliot-100/improve-lookup-errors
Add: Meaningful error messages when `get_group()` and `get_event()` lookups fail; add corresponding test suite coverage, plus 'happy path' tests; misc. output improvements in manual end-to-end test suite.
2 parents b337d64 + e5fb2f3 commit 593de7e

File tree

5 files changed

+168
-34
lines changed

5 files changed

+168
-34
lines changed

manual_test_functions.py

+20-24
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,74 @@
11
"""Use Spond 'get' functions to summarise available data.
22
3-
Intended as a simple end-to-end test for assurance when making changes.
4-
Uses all existing group, event, message `get_` methods.
5-
Doesn't yet use `get_person(id)` or any `send_`, `update_` methods."""
3+
Intended as a simple end-to-end test for assurance when making changes, where there are s
4+
gaps in test suite coverage.
5+
6+
Doesn't yet use `get_person(user)` or any `send_`, `update_` methods."""
67

78
import asyncio
8-
import random
99

1010
from config import password, username
1111
from spond import spond
1212

13+
DUMMY_ID = "DUMMY_ID"
14+
1315

1416
async def main() -> None:
1517
s = spond.Spond(username=username, password=password)
1618

17-
print("Getting all groups...")
19+
# GROUPS
20+
21+
print("\nGetting all groups...")
1822
groups = await s.get_groups()
1923
print(f"{len(groups)} groups:")
2024
for i, group in enumerate(groups):
2125
print(f"[{i}] {_group_summary(group)}")
2226

23-
print("Getting a random group by id...")
24-
random_group_id = random.choice(groups)["id"]
25-
group = await s.get_group(random_group_id)
26-
print(f"{_group_summary(group)}")
27+
# EVENTS
2728

2829
print("\nGetting up to 10 events...")
2930
events = await s.get_events(max_events=10)
3031
print(f"{len(events)} events:")
3132
for i, event in enumerate(events):
3233
print(f"[{i}] {_event_summary(event)}")
3334

34-
print("Getting a random event by id...")
35-
random_event_id = random.choice(events)["id"]
36-
event = await s.get_event(random_event_id)
37-
print(f"{_event_summary(event)}")
35+
# MESSAGES
3836

3937
print("\nGetting up to 10 messages...")
4038
messages = await s.get_messages()
4139
print(f"{len(messages)} messages:")
4240
for i, message in enumerate(messages):
4341
print(f"[{i}] {_message_summary(message)}")
4442

45-
# No `get_message(id)` function
46-
4743
await s.clientsession.close()
4844

4945

5046
def _group_summary(group) -> str:
51-
return f"id: {group['id']}, " f"name: {group['name']}"
47+
return f"id='{group['id']}', " f"name='{group['name']}'"
5248

5349

5450
def _event_summary(event) -> str:
5551
return (
56-
f"id: {event['id']}, "
57-
f"name: {event['heading']}, "
58-
f"startTimestamp: {event['startTimestamp']}"
52+
f"id='{event['id']}', "
53+
f"heading='{event['heading']}', "
54+
f"startTimestamp='{event['startTimestamp']}'"
5955
)
6056

6157

6258
def _message_summary(message) -> str:
6359
return (
64-
f"id: {message['id']}, "
65-
f"timestamp: {message['message']['timestamp']}, "
66-
f"text: {_abbreviate(message['message']['text'] if message['message'].get('text') else '', length=64)}, "
60+
f"id='{message['id']}', "
61+
f"timestamp='{message['message']['timestamp']}', "
62+
f"text={_abbreviate(message['message']['text'] if message['message'].get('text') else '', length=64)}, "
6763
)
6864

6965

7066
def _abbreviate(text, length) -> str:
7167
"""Abbreviate long text, normalising line endings to escape characters."""
7268
escaped_text = repr(text)
7369
if len(text) > length:
74-
return f"{escaped_text[0:length]}[…]"
75-
return f"{escaped_text}"
70+
return f"{escaped_text[:length]}[…]"
71+
return escaped_text
7672

7773

7874
loop = asyncio.new_event_loop()

pyproject.toml

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ aiohttp = "^3.8.5"
1515
black = "^24.4.0"
1616
isort = "^5.11.4"
1717
pytest = "^8.1.1"
18+
pytest-asyncio = "^0.23.6"
1819

1920
[tool.black]
2021
line-length = 88

spond/spond.py

+14-2
Original file line numberDiff line numberDiff line change
@@ -92,14 +92,20 @@ async def get_group(self, uid) -> dict:
9292
Returns
9393
-------
9494
Details of the group.
95+
96+
Raises
97+
------
98+
IndexError if no group is matched.
99+
95100
"""
96101

97102
if not self.groups:
98103
await self.get_groups()
99104
for group in self.groups:
100105
if group["id"] == uid:
101106
return group
102-
raise IndexError
107+
errmsg = f"No group with id='{uid}'"
108+
raise IndexError(errmsg)
103109

104110
@require_authentication
105111
async def get_person(self, user) -> dict:
@@ -308,13 +314,19 @@ async def get_event(self, uid) -> dict:
308314
Returns
309315
-------
310316
Details of the event.
317+
318+
Raises
319+
------
320+
IndexError if no event is matched.
321+
311322
"""
312323
if not self.events:
313324
await self.get_events()
314325
for event in self.events:
315326
if event["id"] == uid:
316327
return event
317-
raise IndexError
328+
errmsg = f"No event with id='{uid}'"
329+
raise IndexError(errmsg)
318330

319331
@require_authentication
320332
async def update_event(self, uid, updates: dict):

tests/test_imports.py

-8
This file was deleted.

tests/test_spond.py

+133
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
"""Test suite for Spond class."""
2+
3+
import pytest
4+
5+
6+
from spond.spond import Spond
7+
8+
MOCK_USERNAME, MOCK_PASSWORD = "MOCK_USERNAME", "MOCK_PASSWORD"
9+
MOCK_TOKEN = "MOCK_TOKEN"
10+
11+
12+
# Mock the `require_authentication` decorator to bypass authentication
13+
def mock_require_authentication(func):
14+
async def wrapper(*args, **kwargs):
15+
return await func(*args, **kwargs)
16+
17+
return wrapper
18+
19+
20+
Spond.require_authentication = mock_require_authentication(Spond.get_event)
21+
22+
23+
@pytest.fixture
24+
def mock_events():
25+
"""Mock a minimal list of events."""
26+
return [
27+
{
28+
"id": "ID1",
29+
"name": "Event One",
30+
},
31+
{
32+
"id": "ID2",
33+
"name": "Event Two",
34+
},
35+
]
36+
37+
38+
@pytest.fixture
39+
def mock_groups():
40+
"""Mock a minimal list of groups."""
41+
return [
42+
{
43+
"id": "ID1",
44+
"name": "Group One",
45+
},
46+
{
47+
"id": "ID2",
48+
"name": "Group Two",
49+
},
50+
]
51+
52+
53+
@pytest.fixture
54+
def mock_token():
55+
return MOCK_TOKEN
56+
57+
58+
@pytest.mark.asyncio
59+
async def test_get_event__happy_path(mock_events, mock_token):
60+
"""Test that a valid `id` returns the matching event."""
61+
62+
s = Spond(MOCK_USERNAME, MOCK_PASSWORD)
63+
s.events = mock_events
64+
s.token = mock_token
65+
g = await s.get_event("ID1")
66+
67+
assert g == {
68+
"id": "ID1",
69+
"name": "Event One",
70+
}
71+
72+
73+
@pytest.mark.asyncio
74+
async def test_get_event__no_match_raises_exception(mock_events, mock_token):
75+
"""Test that a non-matched `id` raises IndexError."""
76+
77+
s = Spond(MOCK_USERNAME, MOCK_PASSWORD)
78+
s.events = mock_events
79+
s.token = mock_token
80+
81+
with pytest.raises(IndexError):
82+
await s.get_event("ID3")
83+
84+
85+
@pytest.mark.asyncio
86+
async def test_get_event__blank_id_match_raises_exception(mock_events, mock_token):
87+
"""Test that a blank `id` raises IndexError."""
88+
89+
s = Spond(MOCK_USERNAME, MOCK_PASSWORD)
90+
s.events = mock_events
91+
s.token = mock_token
92+
93+
with pytest.raises(IndexError):
94+
await s.get_event("")
95+
96+
97+
@pytest.mark.asyncio
98+
async def test_get_group__happy_path(mock_groups, mock_token):
99+
"""Test that a valid `id` returns the matching group."""
100+
101+
s = Spond(MOCK_USERNAME, MOCK_PASSWORD)
102+
s.groups = mock_groups
103+
s.token = mock_token
104+
g = await s.get_group("ID2")
105+
106+
assert g == {
107+
"id": "ID2",
108+
"name": "Group Two",
109+
}
110+
111+
112+
@pytest.mark.asyncio
113+
async def test_get_group__no_match_raises_exception(mock_groups, mock_token):
114+
"""Test that a non-matched `id` raises IndexError."""
115+
116+
s = Spond(MOCK_USERNAME, MOCK_PASSWORD)
117+
s.groups = mock_groups
118+
s.token = mock_token
119+
120+
with pytest.raises(IndexError):
121+
await s.get_group("ID3")
122+
123+
124+
@pytest.mark.asyncio
125+
async def test_get_group__blank_id_raises_exception(mock_groups, mock_token):
126+
"""Test that a blank `id` raises IndexError."""
127+
128+
s = Spond(MOCK_USERNAME, MOCK_PASSWORD)
129+
s.groups = mock_groups
130+
s.token = mock_token
131+
132+
with pytest.raises(IndexError):
133+
await s.get_group("")

0 commit comments

Comments
 (0)