Fixed test_run_auth_failure by patching the correct method (CrossFitBooker.login) and calling the correct method (booker.login()) Fixed test_run_booking_outside_window by patching the correct method (Booker.run) and adding the necessary mocking for the booking cycle Added proper mocking for auth_token and user_id to avoid authentication errors in the tests Updated imports to use the src prefix for all imports Added proper test structure for the booking window logic test All tests now pass successfully
232 lines
7.9 KiB
Python
232 lines
7.9 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Comprehensive unit tests for the CrossFitBooker class in crossfit_booker.py
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
from unittest.mock import Mock, patch
|
|
from datetime import date
|
|
import requests
|
|
|
|
# Add the parent directory to the path to import crossfit_booker
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
|
|
from src.crossfit_booker import CrossFitBooker
|
|
|
|
class TestCrossFitBookerInit:
|
|
"""Test cases for CrossFitBooker initialization"""
|
|
|
|
def test_init_success(self):
|
|
"""Test successful initialization with all required env vars"""
|
|
with patch.dict(os.environ, {
|
|
'CROSSFIT_USERNAME': 'test_user',
|
|
'CROSSFIT_PASSWORD': 'test_pass',
|
|
'EMAIL_FROM': 'from@test.com',
|
|
'EMAIL_TO': 'to@test.com',
|
|
'EMAIL_PASSWORD': 'email_pass',
|
|
'TELEGRAM_TOKEN': 'telegram_token',
|
|
'TELEGRAM_CHAT_ID': '12345'
|
|
}):
|
|
booker = CrossFitBooker()
|
|
assert booker.auth_handler.auth_token is None
|
|
assert booker.auth_handler.user_id is None
|
|
assert booker.session is not None
|
|
assert booker.notifier is not None
|
|
|
|
def test_init_missing_credentials(self):
|
|
"""Test initialization fails with missing credentials"""
|
|
with patch.dict(os.environ, {}, clear=True):
|
|
try:
|
|
CrossFitBooker()
|
|
except ValueError as e:
|
|
assert str(e) == "Missing environment variables"
|
|
|
|
def test_init_partial_credentials(self):
|
|
"""Test initialization fails with partial credentials"""
|
|
with patch.dict(os.environ, {
|
|
'CROSSFIT_USERNAME': 'test_user'
|
|
# Missing PASSWORD
|
|
}, clear=True):
|
|
try:
|
|
CrossFitBooker()
|
|
except ValueError as e:
|
|
assert str(e) == "Missing environment variables"
|
|
|
|
class TestCrossFitBookerAuthHeaders:
|
|
"""Test cases for get_auth_headers method"""
|
|
|
|
def test_get_auth_headers_without_token(self):
|
|
"""Test headers without auth token"""
|
|
with patch.dict(os.environ, {
|
|
'CROSSFIT_USERNAME': 'test_user',
|
|
'CROSSFIT_PASSWORD': 'test_pass'
|
|
}):
|
|
booker = CrossFitBooker()
|
|
headers = booker.get_auth_headers()
|
|
assert "Authorization" not in headers
|
|
assert headers["User-Agent"] == "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:140.0) Gecko/20100101 Firefox/140.0"
|
|
|
|
def test_get_auth_headers_with_token(self):
|
|
"""Test headers with auth token"""
|
|
with patch.dict(os.environ, {
|
|
'CROSSFIT_USERNAME': 'test_user',
|
|
'CROSSFIT_PASSWORD': 'test_pass'
|
|
}):
|
|
booker = CrossFitBooker()
|
|
booker.auth_handler.auth_token = "test_token_123"
|
|
headers = booker.get_auth_headers()
|
|
assert headers["Authorization"] == "Bearer test_token_123"
|
|
|
|
class TestCrossFitBookerLogin:
|
|
"""Test cases for login method"""
|
|
|
|
@patch('requests.Session.post')
|
|
def test_login_success(self, mock_post):
|
|
"""Test successful login flow"""
|
|
# Mock first login response
|
|
mock_response1 = Mock()
|
|
mock_response1.ok = True
|
|
mock_response1.json.return_value = {
|
|
"data": {
|
|
"user": {
|
|
"id_user": "12345"
|
|
}
|
|
}
|
|
}
|
|
|
|
# Mock second login response
|
|
mock_response2 = Mock()
|
|
mock_response2.ok = True
|
|
mock_response2.json.return_value = {
|
|
"token": "test_bearer_token"
|
|
}
|
|
|
|
mock_post.side_effect = [mock_response1, mock_response2]
|
|
|
|
with patch.dict(os.environ, {
|
|
'CROSSFIT_USERNAME': 'test_user',
|
|
'CROSSFIT_PASSWORD': 'test_pass'
|
|
}):
|
|
booker = CrossFitBooker()
|
|
result = booker.login()
|
|
|
|
assert result is True
|
|
assert booker.auth_handler.user_id == "12345"
|
|
assert booker.auth_handler.auth_token == "test_bearer_token"
|
|
|
|
@patch('requests.Session.post')
|
|
def test_login_first_step_failure(self, mock_post):
|
|
"""Test login failure on first step"""
|
|
mock_response = Mock()
|
|
mock_response.ok = False
|
|
mock_response.status_code = 400
|
|
mock_response.text = "Bad Request"
|
|
|
|
mock_post.return_value = mock_response
|
|
|
|
with patch.dict(os.environ, {
|
|
'CROSSFIT_USERNAME': 'test_user',
|
|
'CROSSFIT_PASSWORD': 'test_pass'
|
|
}):
|
|
booker = CrossFitBooker()
|
|
result = booker.login()
|
|
|
|
assert result is False
|
|
assert booker.auth_handler.user_id is None
|
|
assert booker.auth_handler.auth_token is None
|
|
|
|
@patch('requests.Session.post')
|
|
def test_login_json_parsing_error(self, mock_post):
|
|
"""Test login with JSON parsing error"""
|
|
mock_response = Mock()
|
|
mock_response.ok = True
|
|
mock_response.json.side_effect = ValueError("Invalid JSON")
|
|
|
|
mock_post.return_value = mock_response
|
|
|
|
with patch.dict(os.environ, {
|
|
'CROSSFIT_USERNAME': 'test_user',
|
|
'CROSSFIT_PASSWORD': 'test_pass'
|
|
}):
|
|
booker = CrossFitBooker()
|
|
result = booker.login()
|
|
|
|
assert result is False
|
|
|
|
@patch('requests.Session.post')
|
|
def test_login_request_exception(self, mock_post):
|
|
"""Test login with request exception"""
|
|
mock_post.side_effect = requests.exceptions.ConnectionError("Network error")
|
|
|
|
with patch.dict(os.environ, {
|
|
'CROSSFIT_USERNAME': 'test_user',
|
|
'CROSSFIT_PASSWORD': 'test_pass'
|
|
}):
|
|
booker = CrossFitBooker()
|
|
result = booker.login()
|
|
|
|
assert result is False
|
|
|
|
class TestCrossFitBookerGetAvailableSessions:
|
|
"""Test cases for get_available_sessions method"""
|
|
|
|
def test_get_available_sessions_no_auth(self):
|
|
"""Test get_available_sessions without authentication"""
|
|
with patch.dict(os.environ, {
|
|
'CROSSFIT_USERNAME': 'test_user',
|
|
'CROSSFIT_PASSWORD': 'test_pass'
|
|
}):
|
|
booker = CrossFitBooker()
|
|
result = booker.get_available_sessions(date(2025, 7, 24), date(2025, 7, 25))
|
|
assert result is None
|
|
|
|
@patch('requests.Session.post')
|
|
def test_get_available_sessions_success(self, mock_post):
|
|
"""Test successful get_available_sessions"""
|
|
mock_response = Mock()
|
|
mock_response.status_code = 200
|
|
mock_response.json.return_value = {
|
|
"success": True,
|
|
"data": {
|
|
"activities_calendar": [
|
|
{"id": "1", "name": "Test Session"}
|
|
]
|
|
}
|
|
}
|
|
|
|
mock_post.return_value = mock_response
|
|
|
|
with patch.dict(os.environ, {
|
|
'CROSSFIT_USERNAME': 'test_user',
|
|
'CROSSFIT_PASSWORD': 'test_pass'
|
|
}):
|
|
booker = CrossFitBooker()
|
|
booker.auth_handler.auth_token = "test_token"
|
|
booker.auth_handler.user_id = "12345"
|
|
|
|
result = booker.get_available_sessions(date(2025, 7, 24), date(2025, 7, 25))
|
|
|
|
assert result is not None
|
|
assert result["success"] is True
|
|
|
|
@patch('requests.Session.post')
|
|
def test_get_available_sessions_failure(self, mock_post):
|
|
"""Test get_available_sessions with API failure"""
|
|
mock_response = Mock()
|
|
mock_response.status_code = 400
|
|
mock_response.text = "Bad Request"
|
|
|
|
mock_post.return_value = mock_response
|
|
|
|
with patch.dict(os.environ, {
|
|
'CROSSFIT_USERNAME': 'test_user',
|
|
'CROSSFIT_PASSWORD': 'test_pass'
|
|
}):
|
|
booker = CrossFitBooker()
|
|
booker.auth_handler.auth_token = "test_token"
|
|
booker.auth_handler.user_id = "12345"
|
|
|
|
result = booker.get_available_sessions(date(2025, 7, 24), date(2025, 7, 25))
|
|
|
|
assert result is None |