"""Unit tests for time rounding functionality""" import pytest from app.utils.time_rounding import ( round_time_duration, apply_user_rounding, format_rounding_interval, get_available_rounding_intervals, get_available_rounding_methods, get_user_rounding_settings ) class TestRoundTimeDuration: """Test the core time rounding function""" def test_no_rounding_when_interval_is_one(self): """Test that rounding_minutes=1 returns exact duration""" assert round_time_duration(3720, 1, 'nearest') == 3720 assert round_time_duration(3722, 1, 'up') == 3722 assert round_time_duration(3718, 1, 'down') == 3718 def test_round_to_nearest_5_minutes(self): """Test rounding to nearest 5 minute interval""" # 62 minutes should round to 60 minutes (nearest 5-min interval) assert round_time_duration(3720, 5, 'nearest') == 3600 # 63 minutes should round to 65 minutes assert round_time_duration(3780, 5, 'nearest') == 3900 # 2 minutes should round to 0 assert round_time_duration(120, 5, 'nearest') == 0 # 3 minutes should round to 5 assert round_time_duration(180, 5, 'nearest') == 300 def test_round_to_nearest_15_minutes(self): """Test rounding to nearest 15 minute interval""" # 62 minutes should round to 60 minutes assert round_time_duration(3720, 15, 'nearest') == 3600 # 68 minutes should round to 75 minutes assert round_time_duration(4080, 15, 'nearest') == 4500 # 7 minutes should round to 0 assert round_time_duration(420, 15, 'nearest') == 0 # 8 minutes should round to 15 assert round_time_duration(480, 15, 'nearest') == 900 def test_round_up(self): """Test always rounding up (ceiling)""" # 62 minutes with 15-min interval rounds up to 75 assert round_time_duration(3720, 15, 'up') == 4500 # 60 minutes with 15-min interval stays 60 (exact match) assert round_time_duration(3600, 15, 'up') == 3600 # 61 minutes with 15-min interval rounds up to 75 assert round_time_duration(3660, 15, 'up') == 4500 # 1 minute with 5-min interval rounds up to 5 assert round_time_duration(60, 5, 'up') == 300 def test_round_down(self): """Test always rounding down (floor)""" # 62 minutes with 15-min interval rounds down to 60 assert round_time_duration(3720, 15, 'down') == 3600 # 74 minutes with 15-min interval rounds down to 60 assert round_time_duration(4440, 15, 'down') == 3600 # 75 minutes with 15-min interval stays 75 (exact match) assert round_time_duration(4500, 15, 'down') == 4500 def test_round_to_hour(self): """Test rounding to 1 hour intervals""" # 62 minutes rounds to 60 minutes (nearest hour) assert round_time_duration(3720, 60, 'nearest') == 3600 # 90 minutes rounds to 120 minutes (nearest hour) assert round_time_duration(5400, 60, 'nearest') == 7200 # 89 minutes rounds to 60 minutes (nearest hour) assert round_time_duration(5340, 60, 'nearest') == 3600 def test_invalid_rounding_method_defaults_to_nearest(self): """Test that invalid rounding method falls back to 'nearest'""" result = round_time_duration(3720, 15, 'invalid') expected = round_time_duration(3720, 15, 'nearest') assert result == expected def test_zero_duration(self): """Test handling of zero duration""" assert round_time_duration(0, 15, 'nearest') == 0 assert round_time_duration(0, 15, 'up') == 0 assert round_time_duration(0, 15, 'down') == 0 def test_very_small_durations(self): """Test rounding of very small durations""" # 30 seconds with 5-min rounding assert round_time_duration(30, 5, 'nearest') == 0 assert round_time_duration(30, 5, 'up') == 300 # Rounds up to 5 minutes assert round_time_duration(30, 5, 'down') == 0 def test_very_large_durations(self): """Test rounding of large durations""" # 8 hours 7 minutes (487 minutes) with 15-min rounding # 487 / 15 = 32.47 -> rounds to 32 intervals = 480 minutes = 28800 seconds assert round_time_duration(29220, 15, 'nearest') == 28800 # 480 minutes (8 hours) # 8 hours 8 minutes (488 minutes) with 15-min rounding # 488 / 15 = 32.53 -> rounds to 33 intervals = 495 minutes = 29700 seconds assert round_time_duration(29280, 15, 'nearest') == 29700 # 495 minutes (8 hours 15 min) class TestApplyUserRounding: """Test applying user-specific rounding preferences""" def test_with_rounding_disabled(self): """Test that rounding is skipped when disabled for user""" class MockUser: time_rounding_enabled = False time_rounding_minutes = 15 time_rounding_method = 'nearest' user = MockUser() assert apply_user_rounding(3720, user) == 3720 def test_with_rounding_enabled(self): """Test that rounding is applied when enabled""" class MockUser: time_rounding_enabled = True time_rounding_minutes = 15 time_rounding_method = 'nearest' user = MockUser() # 62 minutes should round to 60 with 15-min interval assert apply_user_rounding(3720, user) == 3600 def test_different_user_preferences(self): """Test that different users can have different rounding settings""" class MockUser1: time_rounding_enabled = True time_rounding_minutes = 5 time_rounding_method = 'up' class MockUser2: time_rounding_enabled = True time_rounding_minutes = 15 time_rounding_method = 'down' duration = 3720 # 62 minutes # User 1: 5-min up -> 65 minutes assert apply_user_rounding(duration, MockUser1()) == 3900 # User 2: 15-min down -> 60 minutes assert apply_user_rounding(duration, MockUser2()) == 3600 def test_get_user_rounding_settings(self): """Test retrieving user rounding settings""" class MockUser: time_rounding_enabled = True time_rounding_minutes = 10 time_rounding_method = 'up' settings = get_user_rounding_settings(MockUser()) assert settings['enabled'] is True assert settings['minutes'] == 10 assert settings['method'] == 'up' def test_get_user_rounding_settings_with_defaults(self): """Test default values when attributes don't exist""" class MockUser: pass settings = get_user_rounding_settings(MockUser()) assert settings['enabled'] is True assert settings['minutes'] == 1 assert settings['method'] == 'nearest' class TestFormattingFunctions: """Test formatting and helper functions""" def test_format_rounding_interval(self): """Test formatting of rounding intervals""" assert format_rounding_interval(1) == 'No rounding (exact time)' assert format_rounding_interval(5) == '5 minutes' assert format_rounding_interval(15) == '15 minutes' assert format_rounding_interval(30) == '30 minutes' assert format_rounding_interval(60) == '1 hour' assert format_rounding_interval(120) == '2 hours' def test_get_available_rounding_intervals(self): """Test getting available rounding intervals""" intervals = get_available_rounding_intervals() assert len(intervals) == 6 assert (1, 'No rounding (exact time)') in intervals assert (5, '5 minutes') in intervals assert (60, '1 hour') in intervals def test_get_available_rounding_methods(self): """Test getting available rounding methods""" methods = get_available_rounding_methods() assert len(methods) == 3 method_values = [m[0] for m in methods] assert 'nearest' in method_values assert 'up' in method_values assert 'down' in method_values