116 lines
4.2 KiB
Python
116 lines
4.2 KiB
Python
"""Announcing a scheduled match — ARCH-003.
|
|
|
|
The same twenty lines lived in three routes. They had already drifted:
|
|
create_match formatted the time from the values it had just parsed and
|
|
printed 'TBD' whenever either end was missing, while edit_match re-derived
|
|
them from the saved row and substituted the start time for a missing end.
|
|
A match with a start and no end therefore announced a time in one route and
|
|
'TBD' in the other, for the same data.
|
|
|
|
app/services/scheduling.py keeps the more careful of the two rules.
|
|
"""
|
|
|
|
from datetime import date, time
|
|
|
|
import pytest
|
|
|
|
from app.services import scheduling
|
|
from app.services.scheduling import TIME_UNKNOWN, format_event_time, zip_participants
|
|
|
|
|
|
class TestTimeFormatting:
|
|
def test_a_full_range_is_rendered(self):
|
|
assert format_event_time(time(18, 0), time(20, 30)) == '06:00 PM - 08:30 PM'
|
|
|
|
def test_a_missing_end_falls_back_to_the_start(self):
|
|
"""This is the behaviour create_match did not have: it printed TBD
|
|
and lost the start time it knew perfectly well."""
|
|
assert format_event_time(time(18, 0), None) == '06:00 PM - 06:00 PM'
|
|
|
|
def test_no_start_means_nothing_useful_to_say(self):
|
|
assert format_event_time(None, time(20, 0)) == TIME_UNKNOWN
|
|
assert format_event_time(None, None) == TIME_UNKNOWN
|
|
|
|
|
|
class TestParticipantPairing:
|
|
def test_players_pair_with_their_participant_rows(self):
|
|
assert list(zip_participants([7, 8, 9], [70, 80, 90])) == [(7, 70), (8, 80), (9, 90)]
|
|
|
|
def test_a_short_participant_list_yields_none(self):
|
|
"""The routes append to two lists in step; if they ever fall out of
|
|
step the notification still goes out, against the match itself."""
|
|
assert list(zip_participants([7, 8], [70])) == [(7, 70), (8, None)]
|
|
|
|
def test_no_players_means_no_pairs(self):
|
|
assert list(zip_participants([], [])) == []
|
|
|
|
|
|
class TestNotifying:
|
|
@pytest.fixture
|
|
def sent(self, monkeypatch):
|
|
calls = []
|
|
monkeypatch.setattr(
|
|
scheduling, 'send_schedule_notification', lambda **kwargs: calls.append(kwargs)
|
|
)
|
|
return calls
|
|
|
|
def test_each_participant_is_told(self, sent):
|
|
count = scheduling.notify_participants(
|
|
title='Scrim',
|
|
date=date(2030, 4, 1),
|
|
start_time=time(19, 0),
|
|
end_time=time(21, 0),
|
|
participants=[(1, 11), (2, 22)],
|
|
fallback_id=99,
|
|
)
|
|
|
|
assert count == 2
|
|
assert [call['user_id'] for call in sent] == [1, 2]
|
|
assert [call['reference_id'] for call in sent] == [11, 22]
|
|
assert {call['event_date'] for call in sent} == {'2030-04-01'}
|
|
assert {call['event_time'] for call in sent} == {'07:00 PM - 09:00 PM'}
|
|
assert {call['change_kind'] for call in sent} == {'scheduled'}
|
|
|
|
def test_modified_kind_is_forwarded(self, sent):
|
|
"""A modified notification carries 'modified' to the bot."""
|
|
scheduling.notify_participants(
|
|
title='Scrim',
|
|
date=date(2030, 4, 1),
|
|
start_time=time(19, 0),
|
|
end_time=time(21, 0),
|
|
participants=[(1, 11)],
|
|
fallback_id=99,
|
|
change_kind='modified',
|
|
)
|
|
|
|
assert sent[0]['change_kind'] == 'modified'
|
|
|
|
def test_a_participant_without_a_row_falls_back_to_the_match(self, sent):
|
|
"""A Discord reaction resolves the reference back to a record; the
|
|
match id is the one thing that always exists."""
|
|
scheduling.notify_participants(
|
|
title='Scrim',
|
|
date=date(2030, 4, 1),
|
|
start_time=None,
|
|
end_time=None,
|
|
participants=[(1, None)],
|
|
fallback_id=99,
|
|
)
|
|
|
|
assert sent[0]['reference_id'] == 99
|
|
assert sent[0]['event_time'] == TIME_UNKNOWN
|
|
|
|
def test_a_missing_player_is_skipped(self, sent):
|
|
assert (
|
|
scheduling.notify_participants(
|
|
title='Scrim',
|
|
date=date(2030, 4, 1),
|
|
start_time=None,
|
|
end_time=None,
|
|
participants=[(None, 11), (2, 22)],
|
|
fallback_id=99,
|
|
)
|
|
== 1
|
|
)
|
|
assert [call['user_id'] for call in sent] == [2]
|