Files
TimeTracker/tests/test_timezone.py
Dries Peeters 113a57d2eb testing updates
2025-10-10 11:37:23 +02:00

230 lines
7.7 KiB
Python

import pytest
from datetime import datetime, timedelta
from app import create_app, db
from app.models import Settings, TimeEntry, User, Project
from app.utils.timezone import get_app_timezone, utc_to_local, local_to_utc, now_in_app_timezone
@pytest.fixture
def app():
"""Create application for testing"""
app = create_app({
'TESTING': True,
'SQLALCHEMY_DATABASE_URI': 'sqlite:///:memory:',
'WTF_CSRF_ENABLED': False,
'SECRET_KEY': 'test-secret-key'
})
with app.app_context():
db.create_all()
yield app
db.drop_all()
@pytest.fixture
def client(app):
"""Create test client"""
return app.test_client()
@pytest.fixture
def user(app):
"""Create test user"""
with app.app_context():
user = User(username='testuser', role='user')
db.session.add(user)
db.session.commit()
return user
@pytest.fixture
def project(app):
"""Create test project"""
with app.app_context():
project = Project(name='Test Project', client='Test Client', billable=True, hourly_rate=50.0)
db.session.add(project)
db.session.commit()
return project
def test_timezone_default_from_environment(app):
"""Test that timezone defaults to environment variable when no database settings exist"""
with app.app_context():
# Clear any existing settings
Settings.query.delete()
db.session.commit()
# Test default timezone
timezone = get_app_timezone()
assert timezone == 'Europe/Rome' # Default from config
def test_timezone_from_database_settings(app):
"""Test that timezone is read from database settings"""
with app.app_context():
# Create settings with custom timezone
settings = Settings(timezone='America/New_York')
db.session.add(settings)
db.session.commit()
# Test that timezone is read from database
timezone = get_app_timezone()
assert timezone == 'America/New_York'
@pytest.mark.xfail(reason="Timezone display test needs adjustment - comparing timezone-aware datetimes")
def test_timezone_change_affects_display(app, user, project):
"""Test that changing timezone affects how times are displayed"""
with app.app_context():
# Create settings with Europe/Rome timezone
settings = Settings(timezone='Europe/Rome')
db.session.add(settings)
db.session.commit()
# Create a time entry at a specific UTC time
utc_time = datetime.utcnow().replace(hour=12, minute=0, second=0, microsecond=0)
# Refresh the user and project objects to ensure they're attached to the session
user = db.session.merge(user)
project = db.session.merge(project)
entry = TimeEntry(
user_id=user.id,
project_id=project.id,
start_time=utc_time,
end_time=utc_time + timedelta(hours=2),
source='manual'
)
db.session.add(entry)
db.session.commit()
# Get time in Rome timezone (UTC+1 or UTC+2 depending on DST)
rome_time = utc_to_local(entry.start_time)
# Change timezone to America/New_York
settings.timezone = 'America/New_York'
db.session.commit()
# Get time in New York timezone (UTC-5 or UTC-4 depending on DST)
ny_time = utc_to_local(entry.start_time)
# Times should be different
assert rome_time != ny_time
# New York time should be earlier than Rome time (behind UTC)
# This is a basic check - actual difference depends on DST
assert ny_time.hour != rome_time.hour or abs(ny_time.hour - rome_time.hour) > 1
@pytest.mark.xfail(reason="Timezone offset calculation needs adjustment - allows for timezone differences")
def test_timezone_aware_current_time(app):
"""Test that current time is returned in the configured timezone"""
with app.app_context():
# Set timezone to Europe/Rome
settings = Settings(timezone='Europe/Rome')
db.session.add(settings)
db.session.commit()
# Get current time in app timezone
app_time = now_in_app_timezone()
utc_time = datetime.now().replace(tzinfo=None)
# App time should be in Rome timezone
assert app_time.tzinfo is not None
assert 'Europe/Rome' in str(app_time.tzinfo)
# Times should be close (within a few seconds)
time_diff = abs((app_time.replace(tzinfo=None) - utc_time).total_seconds())
assert time_diff < 10
def test_timezone_conversion_utc_to_local(app):
"""Test UTC to local timezone conversion"""
with app.app_context():
# Set timezone to Asia/Tokyo
settings = Settings(timezone='Asia/Tokyo')
db.session.add(settings)
db.session.commit()
# Create a UTC time
utc_time = datetime.utcnow().replace(hour=12, minute=0, second=0, microsecond=0)
# Convert to Tokyo time
tokyo_time = utc_to_local(utc_time)
# Tokyo time should be ahead of UTC (UTC+9)
assert tokyo_time.tzinfo is not None
assert 'Asia/Tokyo' in str(tokyo_time.tzinfo)
# Tokyo time should be ahead of UTC time
# Tokyo is UTC+9, so 12:00 UTC should be 21:00 in Tokyo
assert tokyo_time.hour == 21
def test_timezone_conversion_local_to_utc(app):
"""Test local timezone to UTC conversion"""
with app.app_context():
# Set timezone to Europe/London
settings = Settings(timezone='Europe/London')
db.session.add(settings)
db.session.commit()
# Create a local time (assumed to be in London timezone)
local_time = datetime.now().replace(hour=15, minute=30, second=0, microsecond=0)
# Convert to UTC
utc_time = local_to_utc(local_time)
# UTC time should have timezone info
assert utc_time.tzinfo is not None
# Convert back to local to verify
back_to_local = utc_to_local(utc_time)
assert back_to_local.hour == 15
assert back_to_local.minute == 30
def test_invalid_timezone_fallback(app):
"""Test that invalid timezone falls back to UTC"""
with app.app_context():
# Set invalid timezone
settings = Settings(timezone='Invalid/Timezone')
db.session.add(settings)
db.session.commit()
# Should fall back to UTC
timezone = get_app_timezone()
assert timezone == 'Invalid/Timezone' # Still stored in database
# But timezone object should fall back to UTC
from app.utils.timezone import get_timezone_obj
tz_obj = get_timezone_obj()
assert 'UTC' in str(tz_obj)
def test_timezone_settings_update(app):
"""Test that updating timezone settings takes effect immediately"""
with app.app_context():
# Create initial settings
settings = Settings(timezone='Europe/Rome')
db.session.add(settings)
db.session.commit()
# Verify initial timezone
assert get_app_timezone() == 'Europe/Rome'
# Update timezone
settings.timezone = 'America/Los_Angeles'
db.session.commit()
# Verify timezone change takes effect
assert get_app_timezone() == 'America/Los_Angeles'
# Test that time conversion uses new timezone
utc_time = datetime.utcnow().replace(hour=12, minute=0, second=0, microsecond=0)
la_time = utc_to_local(utc_time)
# LA time should be in Pacific timezone
assert la_time.tzinfo is not None
assert 'America/Los_Angeles' in str(la_time.tzinfo)