247 lines
8.3 KiB
Python
247 lines
8.3 KiB
Python
"""@file test_verification.py
|
|
@brief The comparison of the bot's results with the My Bookings page, and its text.
|
|
"""
|
|
|
|
import pytest
|
|
from tests.unit.test_booking_entry import make_entry
|
|
from tests.unit.test_models import make_card
|
|
|
|
from snack_and_lift.domain.errors import PageFormatError
|
|
from snack_and_lift.domain.models import ClassResult, Outcome
|
|
from snack_and_lift.domain.verification import build_verification, format_verification
|
|
|
|
BOOKED = ClassResult(make_card("Tue, Aug 12"), Outcome.NEW_BOOKING)
|
|
WAITLISTED = ClassResult(make_card("Thu, Aug 14", card_id="2"), Outcome.NEW_WAITLIST)
|
|
|
|
|
|
def test_everything_found_is_a_success() -> None:
|
|
result = build_verification(
|
|
[BOOKED, WAITLISTED],
|
|
[make_entry("Tue, Aug 12, 6:00 PM"), make_entry("Thu, Aug 14, 6:00 PM", waitlisted=True)],
|
|
)
|
|
|
|
assert result.is_success
|
|
assert result.verified == ["Spin Class", "Spin Class (Waitlist)"]
|
|
assert (result.expected, result.found) == (2, 2)
|
|
|
|
|
|
def test_success_text_is_exact() -> None:
|
|
result = build_verification(
|
|
[BOOKED, WAITLISTED],
|
|
[make_entry("Tue, Aug 12, 6:00 PM"), make_entry("Thu, Aug 14, 6:00 PM", waitlisted=True)],
|
|
)
|
|
|
|
assert format_verification(result) == (
|
|
"--- VERIFYING ON MY BOOKINGS PAGE ---\n"
|
|
" ✓ Verified: Spin Class\n"
|
|
" ✓ Verified: Spin Class (Waitlist)\n"
|
|
"\n"
|
|
"--- VERIFICATION RESULT ---\n"
|
|
"Expected: 2 bookings\n"
|
|
"Found: 2 bookings\n"
|
|
"✅ SUCCESS: All bookings verified!"
|
|
)
|
|
|
|
|
|
def test_one_booking_is_singular() -> None:
|
|
text = format_verification(build_verification([BOOKED], [make_entry("Tue, Aug 12, 6:00 PM")]))
|
|
|
|
assert "Expected: 1 booking\nFound: 1 booking\n" in text
|
|
|
|
|
|
def test_missing_entry_is_a_mismatch_with_a_line() -> None:
|
|
result = build_verification([BOOKED, WAITLISTED], [make_entry("Tue, Aug 12, 6:00 PM")])
|
|
|
|
text = format_verification(result)
|
|
|
|
assert not result.is_success
|
|
assert result.missing == ["Spin Class (Waitlist) on Thu, Aug 14"]
|
|
assert "Expected: 2 bookings\nFound: 1 booking\n❌ MISMATCH: Missing 1 bookings" in text
|
|
assert text.endswith(" ✗ Missing: Spin Class (Waitlist) on Thu, Aug 14")
|
|
|
|
|
|
def test_unexpected_entry_inside_the_window_is_a_mismatch() -> None:
|
|
result = build_verification(
|
|
[BOOKED, WAITLISTED],
|
|
[
|
|
make_entry("Tue, Aug 12, 6:00 PM"),
|
|
make_entry("Thu, Aug 14, 6:00 PM", waitlisted=True),
|
|
make_entry("Thu, Aug 14, 6:00 PM", name="Yoga Class"),
|
|
],
|
|
)
|
|
|
|
text = format_verification(result)
|
|
|
|
assert not result.is_success
|
|
assert result.unexpected == ["Yoga Class on Thu, Aug 14"]
|
|
assert "❌ MISMATCH: Missing 0 bookings" in text
|
|
assert text.endswith(" ? Unexpected: Yoga Class on Thu, Aug 14")
|
|
|
|
|
|
def test_entries_outside_the_window_are_a_note_and_a_success() -> None:
|
|
result = build_verification(
|
|
[BOOKED, WAITLISTED],
|
|
[
|
|
make_entry("Tue, Aug 12, 6:00 PM"),
|
|
make_entry("Thu, Aug 14, 6:00 PM", waitlisted=True),
|
|
make_entry("Thu, Aug 7, 6:00 PM"),
|
|
make_entry("Tue, Aug 19, 6:00 PM", waitlisted=True),
|
|
],
|
|
)
|
|
|
|
assert result.is_success
|
|
assert (result.expected, result.found) == (2, 2)
|
|
assert result.also_booked == [
|
|
"Spin Class on Thu, Aug 7",
|
|
"Spin Class (Waitlist) on Tue, Aug 19",
|
|
]
|
|
assert format_verification(result) == (
|
|
"--- VERIFYING ON MY BOOKINGS PAGE ---\n"
|
|
" ✓ Verified: Spin Class\n"
|
|
" ✓ Verified: Spin Class (Waitlist)\n"
|
|
"\n"
|
|
"--- VERIFICATION RESULT ---\n"
|
|
"Expected: 2 bookings\n"
|
|
"Found: 2 bookings\n"
|
|
" i Also booked (not checked): Spin Class on Thu, Aug 7\n"
|
|
" i Also booked (not checked): Spin Class (Waitlist) on Tue, Aug 19\n"
|
|
"✅ SUCCESS: All bookings verified!"
|
|
)
|
|
|
|
|
|
def test_window_ends_are_inclusive() -> None:
|
|
result = build_verification(
|
|
[BOOKED, WAITLISTED],
|
|
[make_entry("Tue, Aug 12, 6:00 PM"), make_entry("Thu, Aug 14, 6:00 PM", waitlisted=True)],
|
|
)
|
|
|
|
assert result.also_booked == []
|
|
|
|
|
|
def test_missing_count_is_the_number_of_missing_classes_with_notes_outside() -> None:
|
|
result = build_verification(
|
|
[BOOKED, WAITLISTED],
|
|
[make_entry("Tue, Aug 12, 6:00 PM"), make_entry("Tue, Aug 26, 6:00 PM")],
|
|
)
|
|
|
|
text = format_verification(result)
|
|
|
|
assert result.missing == ["Spin Class (Waitlist) on Thu, Aug 14"]
|
|
assert "Expected: 2 bookings\nFound: 1 booking\n" in text
|
|
assert (
|
|
" i Also booked (not checked): Spin Class on Tue, Aug 26\n❌ MISMATCH: Missing 1 bookings"
|
|
in text
|
|
)
|
|
|
|
|
|
def test_missing_count_is_never_negative() -> None:
|
|
result = build_verification(
|
|
[BOOKED, WAITLISTED],
|
|
[
|
|
make_entry("Tue, Aug 12, 6:00 PM"),
|
|
make_entry("Wed, Aug 13, 6:00 PM"),
|
|
make_entry("Wed, Aug 13, 6:00 PM", name="Yoga Class"),
|
|
],
|
|
)
|
|
|
|
assert "Missing 1 bookings" in format_verification(result)
|
|
assert len(result.unexpected) == 2
|
|
|
|
|
|
def test_real_run_with_bookings_after_the_window_is_a_success() -> None:
|
|
first = ClassResult(make_card("Thu, Oct 29"), Outcome.ALREADY_BOOKED)
|
|
second = ClassResult(make_card("Tue, Nov 3", card_id="2"), Outcome.ALREADY_BOOKED)
|
|
entries = [
|
|
make_entry("Thu, Oct 29, 6:00 PM"),
|
|
make_entry("Tue, Nov 3, 6:00 PM"),
|
|
make_entry("Thu, Nov 12, 6:00 PM"),
|
|
make_entry("Tue, Nov 17, 6:00 PM"),
|
|
make_entry("Tue, Nov 24, 6:00 PM"),
|
|
]
|
|
|
|
result = build_verification([first, second], entries)
|
|
|
|
assert result.is_success
|
|
assert (result.expected, result.found) == (2, 2)
|
|
assert result.also_booked == [
|
|
"Spin Class on Thu, Nov 12",
|
|
"Spin Class on Tue, Nov 17",
|
|
"Spin Class on Tue, Nov 24",
|
|
]
|
|
|
|
|
|
def test_no_results_makes_every_entry_a_note_and_no_mismatch() -> None:
|
|
result = build_verification([], [make_entry("Tue, Aug 12, 6:00 PM")])
|
|
|
|
assert result.is_success
|
|
assert (result.expected, result.found) == (0, 0)
|
|
assert result.also_booked == ["Spin Class on Tue, Aug 12"]
|
|
|
|
|
|
def test_window_holds_over_the_turn_of_the_year() -> None:
|
|
december = ClassResult(make_card("Tue, Dec 30"), Outcome.NEW_BOOKING)
|
|
january = ClassResult(make_card("Thu, Jan 1", card_id="2"), Outcome.NEW_BOOKING)
|
|
|
|
result = build_verification(
|
|
[december, january],
|
|
[
|
|
make_entry("Tue, Dec 30, 6:00 PM"),
|
|
make_entry("Thu, Jan 1, 6:00 PM"),
|
|
make_entry("Thu, Jan 8, 6:00 PM"),
|
|
make_entry("Thu, Dec 18, 6:00 PM"),
|
|
],
|
|
)
|
|
|
|
assert result.is_success
|
|
assert result.also_booked == ["Spin Class on Thu, Jan 8", "Spin Class on Thu, Dec 18"]
|
|
|
|
|
|
def test_leap_day_is_a_valid_date() -> None:
|
|
leap = ClassResult(make_card("Thu, Feb 29"), Outcome.NEW_BOOKING)
|
|
|
|
assert build_verification([leap], [make_entry("Thu, Feb 29, 6:00 PM")]).is_success
|
|
|
|
|
|
@pytest.mark.parametrize("date_text", ["Tue, Foo 12", "Tue, Feb 30"])
|
|
def test_an_invalid_date_is_a_page_format_error(date_text: str) -> None:
|
|
bad = ClassResult(make_card(date_text), Outcome.NEW_BOOKING)
|
|
|
|
with pytest.raises(PageFormatError):
|
|
build_verification([bad], [])
|
|
|
|
|
|
def test_waitlist_flag_must_match() -> None:
|
|
result = build_verification([BOOKED], [make_entry("Tue, Aug 12, 6:00 PM", waitlisted=True)])
|
|
|
|
assert result.missing == ["Spin Class on Tue, Aug 12"]
|
|
assert result.unexpected == ["Spin Class (Waitlist) on Tue, Aug 12"]
|
|
assert "Missing 1 bookings" in format_verification(result)
|
|
|
|
|
|
def test_date_must_match() -> None:
|
|
result = build_verification([BOOKED], [make_entry("Tue, Aug 19, 6:00 PM")])
|
|
|
|
assert not result.is_success
|
|
|
|
|
|
def test_already_waitlisted_is_expected_as_waitlist() -> None:
|
|
results = [ClassResult(make_card("Thu, Aug 14"), Outcome.ALREADY_WAITLISTED)]
|
|
|
|
assert build_verification(
|
|
results, [make_entry("Thu, Aug 14, 6:00 PM", waitlisted=True)]
|
|
).is_success
|
|
|
|
|
|
def test_nothing_expected_and_nothing_found_is_a_success() -> None:
|
|
result = build_verification([], [])
|
|
|
|
assert result.is_success
|
|
assert "Expected: 0 bookings\nFound: 0 bookings\n✅ SUCCESS" in format_verification(result)
|
|
|
|
|
|
def test_same_class_twice_is_matched_one_to_one() -> None:
|
|
result = build_verification([BOOKED, BOOKED], [make_entry("Tue, Aug 12, 6:00 PM")])
|
|
|
|
assert result.verified == ["Spin Class"]
|
|
assert result.missing == ["Spin Class on Tue, Aug 12"]
|