Refactor tests: break down single test into focused, modular tests

- Add pytest fixtures for shared data to avoid reparsing YAML files
- Split large test into specific test functions for better modularity
- Add comprehensive test coverage for busy_event, location tracking, and helper functions
- Improve test performance with session-scoped fixtures
- Enable individual test execution with pytest -k

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Edward Betts 2025-07-16 09:04:05 +02:00
parent bdc94f3ebc
commit f89d984623

View file

@ -1,68 +1,223 @@
from datetime import date, datetime, timedelta from datetime import date, datetime
import agenda.busy import agenda.busy
import agenda.travel as travel import agenda.travel as travel
import agenda.trip import agenda.trip
import pytest
from agenda.busy import _parse_datetime_field
from agenda.event import Event
from web_view import app from web_view import app
def test_get_location_for_date() -> None: @pytest.fixture(scope="session")
def app_context():
"""Set up Flask app context for tests."""
app.config["SERVER_NAME"] = "test" app.config["SERVER_NAME"] = "test"
with app.app_context(): with app.app_context():
today = datetime.now().date() yield
trips = agenda.trip.build_trip_list()
data_dir = app.config["PERSONAL_DATA"]
# Parse YAML files once for the test @pytest.fixture(scope="session")
bookings = travel.parse_yaml("flights", data_dir) def trips(app_context):
accommodations = travel.parse_yaml("accommodation", data_dir) """Load trip list once for all tests."""
airports = travel.parse_yaml("airports", data_dir) return agenda.trip.build_trip_list()
for year in range(2023, 2025):
start = date(2023, 1, 1)
busy_events = agenda.busy.get_busy_events(start, app.config, trips)
weekends = agenda.busy.weekends(start, busy_events, trips, data_dir)
for weekend in weekends: @pytest.fixture(scope="session")
for day in "saturday", "sunday": def travel_data(app_context):
# When free (no events), should be home (None) """Load travel data (bookings, accommodations, airports) once for all tests."""
# When traveling (events), should be away (City name) data_dir = app.config["PERSONAL_DATA"]
assert bool(weekend[day + "_location"][0]) == bool(weekend[day]) return {
"bookings": travel.parse_yaml("flights", data_dir),
"accommodations": travel.parse_yaml("accommodation", data_dir),
"airports": travel.parse_yaml("airports", data_dir),
"data_dir": data_dir,
}
# Test some specific cases
april_29_location = agenda.busy.get_location_for_date( def test_weekend_location_consistency(app_context, trips, travel_data):
date(2023, 4, 29), trips, bookings, accommodations, airports """Test that weekend locations are consistent with events (free=home, events=away)."""
for year in range(2023, 2025):
start = date(2023, 1, 1)
busy_events = agenda.busy.get_busy_events(start, app.config, trips)
weekends = agenda.busy.weekends(
start, busy_events, trips, travel_data["data_dir"]
) )
assert not april_29_location[0] # Should be home (None)
l = agenda.busy.get_location_for_date( for weekend in weekends:
date(2025, 2, 15), trips, bookings, accommodations, airports for day in "saturday", "sunday":
# When free (no events), should be home (None)
# When traveling (events), should be away (City name)
location_exists = bool(weekend[day + "_location"][0])
has_events = bool(weekend[day])
assert location_exists == has_events, (
f"Weekend {weekend['date']} {day}: "
f"location_exists={location_exists}, has_events={has_events}"
)
def test_specific_home_dates(travel_data):
"""Test specific dates that should return home (None)."""
trips = agenda.trip.build_trip_list()
home_dates = [
date(2023, 4, 29),
date(2025, 7, 1),
date(2023, 12, 2),
date(2023, 10, 7),
date(2023, 2, 18),
date(2025, 8, 2),
]
for test_date in home_dates:
location = agenda.busy.get_location_for_date(
test_date,
trips,
travel_data["bookings"],
travel_data["accommodations"],
travel_data["airports"],
) )
assert l[0] == "Hackettstown" assert not location[
0
], f"Expected home (None) for {test_date}, got {location[0]}"
l = agenda.busy.get_location_for_date(
date(2025, 7, 1), trips, bookings, accommodations, airports def test_specific_away_dates(travel_data):
"""Test specific dates that should return away locations."""
trips = agenda.trip.build_trip_list()
away_cases = [
(date(2025, 2, 15), "Hackettstown"),
]
for test_date, expected_city in away_cases:
location = agenda.busy.get_location_for_date(
test_date,
trips,
travel_data["bookings"],
travel_data["accommodations"],
travel_data["airports"],
) )
assert not l[0] assert (
location[0] == expected_city
), f"Expected {expected_city} for {test_date}, got {location[0]}"
l = agenda.busy.get_location_for_date(
date(2023, 12, 2), trips, bookings, accommodations, airports
)
assert not l[0]
l = agenda.busy.get_location_for_date( def test_get_location_for_date_basic(travel_data):
date(2023, 10, 7), trips, bookings, accommodations, airports """Test basic functionality of get_location_for_date function."""
) trips = agenda.trip.build_trip_list()
assert not l[0] test_date = date(2023, 1, 1)
l = agenda.busy.get_location_for_date( location = agenda.busy.get_location_for_date(
date(2023, 2, 18), trips, bookings, accommodations, airports test_date,
) trips,
assert not l[0] travel_data["bookings"],
travel_data["accommodations"],
travel_data["airports"],
)
l = agenda.busy.get_location_for_date( # Should return a tuple with (city|None, country)
date(2025, 8, 2), trips, bookings, accommodations, airports assert isinstance(location, tuple)
) assert len(location) == 2
assert not l[0] assert location[1] is not None # Should always have a country
def test_busy_event_classification():
"""Test the busy_event function for different event types."""
# Busy event types
busy_events = [
Event(name="event", title="Test Event", date=date(2023, 1, 1)),
Event(
name="conference",
title="Test Conference",
date=date(2023, 1, 1),
going=True,
),
Event(name="accommodation", title="Hotel", date=date(2023, 1, 1)),
Event(name="transport", title="Flight", date=date(2023, 1, 1)),
]
for event in busy_events:
assert agenda.busy.busy_event(event), f"Event {event.name} should be busy"
# Non-busy events
non_busy_events = [
Event(
name="conference",
title="Test Conference",
date=date(2023, 1, 1),
going=False,
),
Event(name="other", title="Other Event", date=date(2023, 1, 1)),
Event(name="event", title="LHG Run Club", date=date(2023, 1, 1)),
Event(name="event", title="IA UK board meeting", date=date(2023, 1, 1)),
]
for event in non_busy_events:
assert not agenda.busy.busy_event(
event
), f"Event {event.name}/{event.title} should not be busy"
def test_parse_datetime_field():
"""Test the _parse_datetime_field helper function."""
# Test with datetime object
dt = datetime(2023, 1, 1, 12, 0, 0)
parsed_dt, parsed_date = _parse_datetime_field(dt)
assert parsed_dt == dt
assert parsed_date == date(2023, 1, 1)
# Test with ISO string
iso_string = "2023-01-01T12:00:00Z"
parsed_dt, parsed_date = _parse_datetime_field(iso_string)
assert parsed_date == date(2023, 1, 1)
assert parsed_dt.year == 2023
assert parsed_dt.month == 1
assert parsed_dt.day == 1
def test_get_busy_events(app_context, trips):
"""Test get_busy_events function."""
start_date = date(2023, 1, 1)
busy_events = agenda.busy.get_busy_events(start_date, app.config, trips)
# Should return a list
assert isinstance(busy_events, list)
# All events should be Event objects
for event in busy_events:
assert hasattr(event, "name")
assert hasattr(event, "as_date")
# Events should be sorted by date
dates = [event.as_date for event in busy_events]
assert dates == sorted(dates), "Events should be sorted by date"
def test_weekends_function(app_context, trips, travel_data):
"""Test the weekends function."""
start_date = date(2023, 1, 1)
busy_events = agenda.busy.get_busy_events(start_date, app.config, trips)
weekends = agenda.busy.weekends(
start_date, busy_events, trips, travel_data["data_dir"]
)
# Should return a list of weekend info
assert isinstance(weekends, list)
assert len(weekends) == 52 # Should return 52 weekends
# Each weekend should have the required keys
for weekend in weekends[:5]: # Check first 5 weekends
assert "date" in weekend
assert "saturday" in weekend
assert "sunday" in weekend
assert "saturday_location" in weekend
assert "sunday_location" in weekend
# Locations should be tuples
assert isinstance(weekend["saturday_location"], tuple)
assert isinstance(weekend["sunday_location"], tuple)
assert len(weekend["saturday_location"]) == 2
assert len(weekend["sunday_location"]) == 2