mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2026-01-04 02:30:01 -06:00
230 lines
7.7 KiB
Python
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)
|