#!/usr/bin/env python3 """ Unit tests for CrossFitBooker functional methods """ import pytest import os import sys from unittest.mock import patch, Mock from datetime import datetime, date, timedelta import pytz from typing import List, Dict, Any, Tuple # Add the parent directory to the path sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from crossfit_booker_functional import ( get_auth_headers, is_session_bookable, matches_preferred_session, prepare_booking_data, is_bookable_and_preferred, filter_bookable_sessions, is_upcoming_preferred, filter_upcoming_sessions, filter_preferred_sessions, format_session_details, categorize_sessions, process_booking_results ) # Mock preferred sessions MOCK_PREFERRED_SESSIONS = [ (2, "18:30", "CONDITIONING"), # Wednesday 18:30 CONDITIONING (4, "17:00", "WEIGHTLIFTING"), # Friday 17:00 WEIGHTLIFTING (5, "12:30", "HYROX"), # Saturday 12:30 HYROX ] class TestCrossFitBookerFunctional: """Test cases for CrossFitBooker functional methods""" def test_get_auth_headers_without_token(self): """Test headers without auth token""" base_headers = {"User-Agent": "test-agent"} headers = get_auth_headers(base_headers, None) assert "Authorization" not in headers assert headers["User-Agent"] == "test-agent" def test_get_auth_headers_with_token(self): """Test headers with auth token""" base_headers = {"User-Agent": "test-agent"} headers = get_auth_headers(base_headers, "test_token_123") assert headers["Authorization"] == "Bearer test_token_123" assert headers["User-Agent"] == "test-agent" def test_is_session_bookable_can_join_true(self): """Test session bookable with can_join=True""" session = {"user_info": {"can_join": True}} current_time = datetime.now(pytz.timezone("Europe/Paris")) assert is_session_bookable(session, current_time, "Europe/Paris") is True def test_is_session_bookable_booking_window_past(self): """Test session bookable with booking window in past""" session = { "user_info": { "can_join": False, "unableToBookUntilDate": "01-01-2020", "unableToBookUntilTime": "10:00" } } current_time = datetime.now(pytz.timezone("Europe/Paris")) assert is_session_bookable(session, current_time, "Europe/Paris") is True def test_is_session_bookable_booking_window_future(self): """Test session not bookable with booking window in future""" session = { "user_info": { "can_join": False, "unableToBookUntilDate": "01-01-2030", "unableToBookUntilTime": "10:00" } } current_time = datetime.now(pytz.timezone("Europe/Paris")) assert is_session_bookable(session, current_time, "Europe/Paris") is False def test_matches_preferred_session_exact_match(self): """Test exact match with preferred session""" session = { "start_timestamp": "2025-07-30 18:30:00", "name_activity": "CONDITIONING" } current_time = datetime(2025, 7, 30, 12, 0, 0, tzinfo=pytz.timezone("Europe/Paris")) with patch('crossfit_booker_functional.PREFERRED_SESSIONS', MOCK_PREFERRED_SESSIONS): assert matches_preferred_session(session, MOCK_PREFERRED_SESSIONS, "Europe/Paris") is True def test_matches_preferred_session_fuzzy_match(self): """Test fuzzy match with preferred session""" session = { "start_timestamp": "2025-07-30 18:30:00", "name_activity": "CONDITIONING WORKOUT" } current_time = datetime(2025, 7, 30, 12, 0, 0, tzinfo=pytz.timezone("Europe/Paris")) with patch('crossfit_booker_functional.PREFERRED_SESSIONS', MOCK_PREFERRED_SESSIONS): assert matches_preferred_session(session, MOCK_PREFERRED_SESSIONS, "Europe/Paris") is True def test_matches_preferred_session_no_match(self): """Test no match with preferred session""" session = { "start_timestamp": "2025-07-30 18:30:00", "name_activity": "YOGA" } current_time = datetime(2025, 7, 30, 12, 0, 0, tzinfo=pytz.timezone("Europe/Paris")) with patch('crossfit_booker_functional.PREFERRED_SESSIONS', MOCK_PREFERRED_SESSIONS): assert matches_preferred_session(session, MOCK_PREFERRED_SESSIONS, "Europe/Paris") is False def test_prepare_booking_data(self): """Test prepare_booking_data function""" mandatory_params = {"app_version": "1.0", "device_type": "1"} session_id = "session_123" user_id = "user_456" data = prepare_booking_data(mandatory_params, session_id, user_id) assert data["id_activity_calendar"] == session_id assert data["id_user"] == user_id assert data["action_by"] == user_id assert data["n_guests"] == "0" assert data["booked_on"] == "3" assert data["app_version"] == "1.0" assert data["device_type"] == "1" def test_is_bookable_and_preferred(self): """Test is_bookable_and_preferred function""" session = { "start_timestamp": "2025-07-30 18:30:00", "name_activity": "CONDITIONING", "user_info": {"can_join": True} } current_time = datetime.now(pytz.timezone("Europe/Paris")) with patch('crossfit_booker_functional.PREFERRED_SESSIONS', MOCK_PREFERRED_SESSIONS): assert is_bookable_and_preferred(session, current_time, MOCK_PREFERRED_SESSIONS, "Europe/Paris") is True def test_filter_bookable_sessions(self): """Test filter_bookable_sessions function""" current_time = datetime.now(pytz.timezone("Europe/Paris")) # Create test sessions sessions = [ { "start_timestamp": "2025-07-30 18:30:00", # Wednesday "name_activity": "CONDITIONING", "user_info": {"can_join": True} }, { "start_timestamp": "2025-07-31 18:30:00", # Thursday "name_activity": "YOGA", "user_info": {"can_join": True} } ] with patch('crossfit_booker_functional.PREFERRED_SESSIONS', MOCK_PREFERRED_SESSIONS): bookable_sessions = filter_bookable_sessions(sessions, current_time, MOCK_PREFERRED_SESSIONS, "Europe/Paris") assert len(bookable_sessions) == 1 assert bookable_sessions[0]["name_activity"] == "CONDITIONING" def test_is_upcoming_preferred(self): """Test is_upcoming_preferred function""" # Test with a session that is tomorrow session = { "start_timestamp": "2025-07-31 18:30:00", # Tomorrow "name_activity": "CONDITIONING" } current_time = datetime(2025, 7, 30, 12, 0, 0, tzinfo=pytz.timezone("Europe/Paris")) with patch('crossfit_booker_functional.PREFERRED_SESSIONS', MOCK_PREFERRED_SESSIONS): assert is_upcoming_preferred(session, current_time, MOCK_PREFERRED_SESSIONS, "Europe/Paris") is True # Test with a session that is today session = { "start_timestamp": "2025-07-30 18:30:00", # Today "name_activity": "CONDITIONING" } current_time = datetime(2025, 7, 30, 12, 0, 0, tzinfo=pytz.timezone("Europe/Paris")) with patch('crossfit_booker_functional.PREFERRED_SESSIONS', MOCK_PREFERRED_SESSIONS): assert is_upcoming_preferred(session, current_time, MOCK_PREFERRED_SESSIONS, "Europe/Paris") is False def test_filter_upcoming_sessions(self): """Test filter_upcoming_sessions function""" current_time = datetime(2025, 7, 30, 12, 0, 0, tzinfo=pytz.timezone("Europe/Paris")) # Create test sessions sessions = [ { "start_timestamp": "2025-07-30 18:30:00", # Today "name_activity": "CONDITIONING", "user_info": {"can_join": True} }, { "start_timestamp": "2025-07-31 18:30:00", # Tomorrow "name_activity": "CONDITIONING", "user_info": {"can_join": True} } ] with patch('crossfit_booker_functional.PREFERRED_SESSIONS', MOCK_PREFERRED_SESSIONS): upcoming_sessions = filter_upcoming_sessions(sessions, current_time, MOCK_PREFERRED_SESSIONS, "Europe/Paris") assert len(upcoming_sessions) == 1 assert upcoming_sessions[0]["name_activity"] == "CONDITIONING" def test_filter_preferred_sessions(self): """Test filter_preferred_sessions function""" # Create test sessions sessions = [ { "start_timestamp": "2025-07-30 18:30:00", "name_activity": "CONDITIONING" }, { "start_timestamp": "2025-07-31 18:30:00", "name_activity": "YOGA" } ] with patch('crossfit_booker_functional.PREFERRED_SESSIONS', MOCK_PREFERRED_SESSIONS): preferred_sessions = filter_preferred_sessions(sessions, MOCK_PREFERRED_SESSIONS, "Europe/Paris") assert len(preferred_sessions) == 1 assert preferred_sessions[0]["name_activity"] == "CONDITIONING" def test_format_session_details(self): """Test format_session_details function""" # Test with valid session session = { "start_timestamp": "2025-07-30 18:30:00", "name_activity": "CONDITIONING" } formatted = format_session_details(session, "Europe/Paris") assert "CONDITIONING" in formatted assert "2025-07-30 18:30" in formatted # Test with missing data session = { "name_activity": "WEIGHTLIFTING" } formatted = format_session_details(session, "Europe/Paris") assert "WEIGHTLIFTING" in formatted assert "Unknown time" in formatted def test_categorize_sessions(self): """Test categorize_sessions function""" current_time = datetime(2025, 7, 30, 12, 0, 0, tzinfo=pytz.timezone("Europe/Paris")) # Create test sessions sessions = [ { "start_timestamp": "2025-07-30 18:30:00", # Today "name_activity": "CONDITIONING", "user_info": {"can_join": True} }, { "start_timestamp": "2025-07-31 18:30:00", # Tomorrow "name_activity": "CONDITIONING", "user_info": {"can_join": True} } ] with patch('crossfit_booker_functional.PREFERRED_SESSIONS', MOCK_PREFERRED_SESSIONS): categorized = categorize_sessions(sessions, current_time, MOCK_PREFERRED_SESSIONS, "Europe/Paris") assert "bookable" in categorized assert "upcoming" in categorized assert "all_preferred" in categorized assert len(categorized["bookable"]) == 1 assert len(categorized["upcoming"]) == 1 assert len(categorized["all_preferred"]) == 1 def test_process_booking_results(self): """Test process_booking_results function""" session = { "start_timestamp": "2025-07-30 18:30:00", "name_activity": "CONDITIONING" } result = process_booking_results(session, True, "Europe/Paris") assert result["session"] == session assert result["success"] is True assert "CONDITIONING" in result["details"] assert "2025-07-30 18:30" in result["details"] if __name__ == "__main__": pytest.main([__file__, "-v"])