diff --git a/src/corelibs/config_handling/settings_loader.py b/src/corelibs/config_handling/settings_loader.py index f7257c2..d28f0a7 100644 --- a/src/corelibs/config_handling/settings_loader.py +++ b/src/corelibs/config_handling/settings_loader.py @@ -8,9 +8,9 @@ import re import configparser from typing import Any, Tuple, Sequence, cast from pathlib import Path +from corelibs_var.var_helpers import is_int, is_float, str_to_bool from corelibs.logging_handling.log import Log from corelibs.iterator_handling.list_helpers import convert_to_list, is_list_in_list -from corelibs.var_handling.var_helpers import is_int, is_float, str_to_bool from corelibs.config_handling.settings_loader_handling.settings_loader_check import SettingsLoaderCheck diff --git a/src/corelibs/script_handling/progress.py b/src/corelibs/script_handling/progress.py index a166ecd..be48c47 100644 --- a/src/corelibs/script_handling/progress.py +++ b/src/corelibs/script_handling/progress.py @@ -32,7 +32,7 @@ show_position(file pos optional) import time from typing import Literal from math import floor -from corelibs.datetime_handling.timestamp_convert import convert_timestamp +from corelibs_datetime.timestamp_convert import convert_timestamp from corelibs.string_handling.byte_helpers import format_bytes diff --git a/tests/unit/datetime_handling/__init__.py b/tests/unit/datetime_handling/__init__.py deleted file mode 100644 index a81225c..0000000 --- a/tests/unit/datetime_handling/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -""" -Unit tests for encryption_handling module -""" diff --git a/tests/unit/datetime_handling/test_convert_to_seconds.py b/tests/unit/datetime_handling/test_convert_to_seconds.py deleted file mode 100644 index f2a7325..0000000 --- a/tests/unit/datetime_handling/test_convert_to_seconds.py +++ /dev/null @@ -1,205 +0,0 @@ -""" -Unit tests for convert_to_seconds function from timestamp_strings module. -""" - -import pytest -from corelibs.datetime_handling.timestamp_convert import convert_to_seconds, TimeParseError, TimeUnitError - - -class TestConvertToSeconds: - """Test class for convert_to_seconds function.""" - - def test_numeric_input_int(self): - """Test with integer input.""" - assert convert_to_seconds(42) == 42 - assert convert_to_seconds(0) == 0 - assert convert_to_seconds(-5) == -5 - - def test_numeric_input_float(self): - """Test with float input.""" - assert convert_to_seconds(42.7) == 43 # rounds to 43 - assert convert_to_seconds(42.3) == 42 # rounds to 42 - assert convert_to_seconds(42.5) == 42 # rounds to 42 (banker's rounding) - assert convert_to_seconds(0.0) == 0 - assert convert_to_seconds(-5.7) == -6 - - def test_numeric_string_input(self): - """Test with numeric string input.""" - assert convert_to_seconds("42") == 42 - assert convert_to_seconds("42.7") == 43 - assert convert_to_seconds("42.3") == 42 - assert convert_to_seconds("0") == 0 - assert convert_to_seconds("-5.7") == -6 - - def test_single_unit_seconds(self): - """Test with seconds unit.""" - assert convert_to_seconds("30s") == 30 - assert convert_to_seconds("1s") == 1 - assert convert_to_seconds("0s") == 0 - - def test_single_unit_minutes(self): - """Test with minutes unit.""" - assert convert_to_seconds("5m") == 300 # 5 * 60 - assert convert_to_seconds("1m") == 60 - assert convert_to_seconds("0m") == 0 - - def test_single_unit_hours(self): - """Test with hours unit.""" - assert convert_to_seconds("2h") == 7200 # 2 * 3600 - assert convert_to_seconds("1h") == 3600 - assert convert_to_seconds("0h") == 0 - - def test_single_unit_days(self): - """Test with days unit.""" - assert convert_to_seconds("1d") == 86400 # 1 * 86400 - assert convert_to_seconds("2d") == 172800 # 2 * 86400 - assert convert_to_seconds("0d") == 0 - - def test_single_unit_months(self): - """Test with months unit (30 days * 12 = 1 year).""" - # Note: The code has M: 2592000 * 12 which is 1 year, not 1 month - # This seems like a bug in the original code, but testing what it actually does - assert convert_to_seconds("1M") == 31104000 # 2592000 * 12 - assert convert_to_seconds("2M") == 62208000 # 2 * 2592000 * 12 - - def test_single_unit_years(self): - """Test with years unit.""" - assert convert_to_seconds("1Y") == 31536000 # 365 * 86400 - assert convert_to_seconds("2Y") == 63072000 # 2 * 365 * 86400 - - def test_long_unit_names(self): - """Test with long unit names.""" - assert convert_to_seconds("1year") == 31536000 - assert convert_to_seconds("2years") == 63072000 - assert convert_to_seconds("1month") == 31104000 - assert convert_to_seconds("2months") == 62208000 - assert convert_to_seconds("1day") == 86400 - assert convert_to_seconds("2days") == 172800 - assert convert_to_seconds("1hour") == 3600 - assert convert_to_seconds("2hours") == 7200 - assert convert_to_seconds("1minute") == 60 - assert convert_to_seconds("2minutes") == 120 - assert convert_to_seconds("30min") == 1800 - assert convert_to_seconds("1second") == 1 - assert convert_to_seconds("2seconds") == 2 - assert convert_to_seconds("30sec") == 30 - - def test_multiple_units(self): - """Test with multiple units combined.""" - assert convert_to_seconds("1h30m") == 5400 # 3600 + 1800 - assert convert_to_seconds("1d2h") == 93600 # 86400 + 7200 - assert convert_to_seconds("1h30m45s") == 5445 # 3600 + 1800 + 45 - assert convert_to_seconds("2d3h4m5s") == 183845 # 172800 + 10800 + 240 + 5 - - def test_multiple_units_with_spaces(self): - """Test with multiple units and spaces.""" - assert convert_to_seconds("1h 30m") == 5400 - assert convert_to_seconds("1d 2h") == 93600 - assert convert_to_seconds("1h 30m 45s") == 5445 - assert convert_to_seconds("2d 3h 4m 5s") == 183845 - - def test_mixed_unit_formats(self): - """Test with mixed short and long unit names.""" - assert convert_to_seconds("1hour 30min") == 5400 - assert convert_to_seconds("1day 2hours") == 93600 - assert convert_to_seconds("1h 30minutes 45sec") == 5445 - - def test_negative_values(self): - """Test with negative time strings.""" - assert convert_to_seconds("-30s") == -30 - assert convert_to_seconds("-1h") == -3600 - assert convert_to_seconds("-1h30m") == -5400 - assert convert_to_seconds("-2d3h4m5s") == -183845 - - def test_case_insensitive_long_names(self): - """Test that long unit names are case insensitive.""" - assert convert_to_seconds("1Hour") == 3600 - assert convert_to_seconds("1MINUTE") == 60 - assert convert_to_seconds("1Day") == 86400 - assert convert_to_seconds("2YEARS") == 63072000 - - def test_duplicate_units_error(self): - """Test that duplicate units raise TimeParseError.""" - with pytest.raises(TimeParseError, match="Unit 'h' appears more than once"): - convert_to_seconds("1h2h") - - with pytest.raises(TimeParseError, match="Unit 's' appears more than once"): - convert_to_seconds("30s45s") - - with pytest.raises(TimeParseError, match="Unit 'm' appears more than once"): - convert_to_seconds("1m30m") - - def test_invalid_units_error(self): - """Test that invalid units raise TimeUnitError.""" - with pytest.raises(TimeUnitError, match="Unit 'x' is not a valid unit name"): - convert_to_seconds("30x") - - with pytest.raises(TimeUnitError, match="Unit 'invalid' is not a valid unit name"): - convert_to_seconds("1invalid") - - with pytest.raises(TimeUnitError, match="Unit 'z' is not a valid unit name"): - convert_to_seconds("1h30z") - - def test_empty_string(self): - """Test with empty string.""" - assert convert_to_seconds("") == 0 - - def test_no_matches(self): - """Test with string that has no time units.""" - assert convert_to_seconds("hello") == 0 - assert convert_to_seconds("no time here") == 0 - - def test_zero_values(self): - """Test with zero values for different units.""" - assert convert_to_seconds("0s") == 0 - assert convert_to_seconds("0m") == 0 - assert convert_to_seconds("0h") == 0 - assert convert_to_seconds("0d") == 0 - assert convert_to_seconds("0h0m0s") == 0 - - def test_large_values(self): - """Test with large time values.""" - assert convert_to_seconds("999d") == 86313600 # 999 * 86400 - assert convert_to_seconds("100Y") == 3153600000 # 100 * 31536000 - - def test_order_independence(self): - """Test that order of units doesn't matter.""" - assert convert_to_seconds("30m1h") == 5400 # same as 1h30m - assert convert_to_seconds("45s30m1h") == 5445 # same as 1h30m45s - assert convert_to_seconds("5s4m3h2d") == 183845 # same as 2d3h4m5s - - def test_whitespace_handling(self): - """Test various whitespace scenarios.""" - assert convert_to_seconds("1 h") == 3600 - assert convert_to_seconds("1h 30m") == 5400 - assert convert_to_seconds(" 1h30m ") == 5400 - assert convert_to_seconds("1h\t30m") == 5400 - - def test_mixed_case_short_units(self): - """Test that short units work with different cases.""" - # Note: The regex only matches [a-zA-Z]+ so case matters for the lookup - with pytest.raises(TimeUnitError, match="Unit 'H' is not a valid unit name"): - convert_to_seconds("1H") # 'H' is not in unit_factors, raises error - assert convert_to_seconds("1h") == 3600 # lowercase works - - def test_boundary_conditions(self): - """Test boundary conditions and edge cases.""" - # Test with leading zeros - assert convert_to_seconds("01h") == 3600 - assert convert_to_seconds("001m") == 60 - - # Test very small values - assert convert_to_seconds("1s") == 1 - - def test_negative_with_multiple_units(self): - """Test negative values with multiple units.""" - assert convert_to_seconds("-1h30m45s") == -5445 - assert convert_to_seconds("-2d3h") == -183600 - - def test_duplicate_with_long_names(self): - """Test duplicate detection with long unit names.""" - with pytest.raises(TimeParseError, match="Unit 'h' appears more than once"): - convert_to_seconds("1hour2h") # both resolve to 'h' - - with pytest.raises(TimeParseError, match="Unit 's' appears more than once"): - convert_to_seconds("1second30sec") # both resolve to 's' diff --git a/tests/unit/datetime_handling/test_datetime_helpers.py b/tests/unit/datetime_handling/test_datetime_helpers.py deleted file mode 100644 index f641b7d..0000000 --- a/tests/unit/datetime_handling/test_datetime_helpers.py +++ /dev/null @@ -1,737 +0,0 @@ -""" -PyTest: datetime_handling/datetime_helpers -""" - -from datetime import datetime, time -from zoneinfo import ZoneInfo -import pytest - -from corelibs.datetime_handling.datetime_helpers import ( - create_time, - get_system_timezone, - parse_timezone_data, - get_datetime_iso8601, - validate_date, - parse_flexible_date, - compare_dates, - find_newest_datetime_in_list, - parse_day_of_week_range, - parse_time_range, - times_overlap_or_connect, - is_time_in_range, - reorder_weekdays_from_today, - DAYS_OF_WEEK_LONG_TO_SHORT, - DAYS_OF_WEEK_ISO, - DAYS_OF_WEEK_ISO_REVERSED, -) - - -class TestConstants: - """Test suite for module constants""" - - def test_days_of_week_long_to_short(self): - """Test DAYS_OF_WEEK_LONG_TO_SHORT dictionary""" - assert DAYS_OF_WEEK_LONG_TO_SHORT['Monday'] == 'Mon' - assert DAYS_OF_WEEK_LONG_TO_SHORT['Tuesday'] == 'Tue' - assert DAYS_OF_WEEK_LONG_TO_SHORT['Friday'] == 'Fri' - assert DAYS_OF_WEEK_LONG_TO_SHORT['Sunday'] == 'Sun' - assert len(DAYS_OF_WEEK_LONG_TO_SHORT) == 7 - - def test_days_of_week_iso(self): - """Test DAYS_OF_WEEK_ISO dictionary""" - assert DAYS_OF_WEEK_ISO[1] == 'Mon' - assert DAYS_OF_WEEK_ISO[5] == 'Fri' - assert DAYS_OF_WEEK_ISO[7] == 'Sun' - assert len(DAYS_OF_WEEK_ISO) == 7 - - def test_days_of_week_iso_reversed(self): - """Test DAYS_OF_WEEK_ISO_REVERSED dictionary""" - assert DAYS_OF_WEEK_ISO_REVERSED['Mon'] == 1 - assert DAYS_OF_WEEK_ISO_REVERSED['Fri'] == 5 - assert DAYS_OF_WEEK_ISO_REVERSED['Sun'] == 7 - assert len(DAYS_OF_WEEK_ISO_REVERSED) == 7 - - -class TestCreateTime: - """Test suite for create_time function""" - - def test_create_time_default_format(self): - """Test create_time with default format""" - timestamp = 1609459200.0 # 2021-01-01 00:00:00 UTC - result = create_time(timestamp) - # Result depends on system timezone, so just check format - assert len(result) == 19 - assert '-' in result - assert ':' in result - - def test_create_time_custom_format(self): - """Test create_time with custom format""" - timestamp = 1609459200.0 - result = create_time(timestamp, "%Y/%m/%d") - # Check basic format structure - assert '/' in result - assert len(result) == 10 - - def test_create_time_with_microseconds(self): - """Test create_time with microseconds in format""" - timestamp = 1609459200.123456 - result = create_time(timestamp, "%Y-%m-%d %H:%M:%S") - assert len(result) == 19 - - -class TestGetSystemTimezone: - """Test suite for get_system_timezone function""" - - def test_get_system_timezone_returns_tuple(self): - """Test that get_system_timezone returns a tuple""" - result = get_system_timezone() - assert isinstance(result, tuple) - assert len(result) == 2 - - def test_get_system_timezone_returns_valid_data(self): - """Test that get_system_timezone returns valid timezone info""" - system_tz, timezone_name = get_system_timezone() - assert system_tz is not None - assert isinstance(timezone_name, str) - assert len(timezone_name) > 0 - - -class TestParseTimezoneData: - """Test suite for parse_timezone_data function""" - - def test_parse_timezone_data_valid_timezone(self): - """Test parse_timezone_data with valid timezone string""" - result = parse_timezone_data('Asia/Tokyo') - assert isinstance(result, ZoneInfo) - assert str(result) == 'Asia/Tokyo' - - def test_parse_timezone_data_utc(self): - """Test parse_timezone_data with UTC""" - result = parse_timezone_data('UTC') - assert isinstance(result, ZoneInfo) - assert str(result) == 'UTC' - - def test_parse_timezone_data_empty_string(self): - """Test parse_timezone_data with empty string falls back to system timezone""" - result = parse_timezone_data('') - assert isinstance(result, ZoneInfo) - - def test_parse_timezone_data_invalid_timezone(self): - """Test parse_timezone_data with invalid timezone falls back to system timezone""" - # Invalid timezones fall back to system timezone or UTC - result = parse_timezone_data('Invalid/Timezone') - assert isinstance(result, ZoneInfo) - # Should be either system timezone or UTC - - def test_parse_timezone_data_none(self): - """Test parse_timezone_data with None falls back to system timezone""" - result = parse_timezone_data() - assert isinstance(result, ZoneInfo) - - def test_parse_timezone_data_various_timezones(self): - """Test parse_timezone_data with various timezone strings""" - timezones = ['America/New_York', 'Europe/London', 'Asia/Seoul'] - for tz in timezones: - result = parse_timezone_data(tz) - assert isinstance(result, ZoneInfo) - assert str(result) == tz - - -class TestGetDatetimeIso8601: - """Test suite for get_datetime_iso8601 function""" - - def test_get_datetime_iso8601_default_params(self): - """Test get_datetime_iso8601 with default parameters""" - result = get_datetime_iso8601() - # Should be in ISO 8601 format with T separator and microseconds - assert 'T' in result - assert '.' in result # microseconds - # Check basic ISO 8601 format - datetime.fromisoformat(result) # Should not raise - - def test_get_datetime_iso8601_custom_timezone_string(self): - """Test get_datetime_iso8601 with custom timezone string""" - result = get_datetime_iso8601('UTC') - assert '+00:00' in result or 'Z' in result or result.endswith('+00:00') - - def test_get_datetime_iso8601_custom_timezone_zoneinfo(self): - """Test get_datetime_iso8601 with ZoneInfo object""" - tz = ZoneInfo('Asia/Tokyo') - result = get_datetime_iso8601(tz) - assert 'T' in result - datetime.fromisoformat(result) # Should not raise - - def test_get_datetime_iso8601_custom_separator(self): - """Test get_datetime_iso8601 with custom separator""" - result = get_datetime_iso8601(sep=' ') - assert ' ' in result - assert 'T' not in result - - def test_get_datetime_iso8601_different_timespec(self): - """Test get_datetime_iso8601 with different timespec values""" - result_seconds = get_datetime_iso8601(timespec='seconds') - assert '.' not in result_seconds # No microseconds - - result_milliseconds = get_datetime_iso8601(timespec='milliseconds') - # Should have milliseconds (3 digits after decimal) - assert '.' in result_milliseconds - - -class TestValidateDate: - """Test suite for validate_date function""" - - def test_validate_date_valid_hyphen_format(self): - """Test validate_date with valid Y-m-d format""" - assert validate_date('2023-12-25') is True - assert validate_date('2024-01-01') is True - - def test_validate_date_valid_slash_format(self): - """Test validate_date with valid Y/m/d format""" - assert validate_date('2023/12/25') is True - assert validate_date('2024/01/01') is True - - def test_validate_date_invalid_format(self): - """Test validate_date with invalid format""" - assert validate_date('25-12-2023') is False - assert validate_date('2023.12.25') is False - assert validate_date('invalid') is False - - def test_validate_date_invalid_date(self): - """Test validate_date with invalid date values""" - assert validate_date('2023-13-01') is False # Invalid month - assert validate_date('2023-02-30') is False # Invalid day - - def test_validate_date_with_not_before(self): - """Test validate_date with not_before constraint""" - not_before = datetime(2023, 12, 1) - assert validate_date('2023-12-25', not_before=not_before) is True - assert validate_date('2023-11-25', not_before=not_before) is False - - def test_validate_date_with_not_after(self): - """Test validate_date with not_after constraint""" - not_after = datetime(2023, 12, 31) - assert validate_date('2023-12-25', not_after=not_after) is True - assert validate_date('2024-01-01', not_after=not_after) is False - - def test_validate_date_with_both_constraints(self): - """Test validate_date with both not_before and not_after constraints""" - not_before = datetime(2023, 12, 1) - not_after = datetime(2023, 12, 31) - assert validate_date('2023-12-15', not_before=not_before, not_after=not_after) is True - assert validate_date('2023-11-30', not_before=not_before, not_after=not_after) is False - assert validate_date('2024-01-01', not_before=not_before, not_after=not_after) is False - - -class TestParseFlexibleDate: - """Test suite for parse_flexible_date function""" - - def test_parse_flexible_date_iso8601_full(self): - """Test parse_flexible_date with full ISO 8601 format""" - result = parse_flexible_date('2023-12-25T15:30:45') - assert isinstance(result, datetime) - assert result.year == 2023 - assert result.month == 12 - assert result.day == 25 - assert result.hour == 15 - assert result.minute == 30 - assert result.second == 45 - - def test_parse_flexible_date_iso8601_with_microseconds(self): - """Test parse_flexible_date with microseconds""" - result = parse_flexible_date('2023-12-25T15:30:45.123456') - assert isinstance(result, datetime) - assert result.microsecond == 123456 - - def test_parse_flexible_date_simple_date(self): - """Test parse_flexible_date with simple date format""" - result = parse_flexible_date('2023-12-25') - assert isinstance(result, datetime) - assert result.year == 2023 - assert result.month == 12 - assert result.day == 25 - - def test_parse_flexible_date_with_timezone_string(self): - """Test parse_flexible_date with timezone string""" - result = parse_flexible_date('2023-12-25T15:30:45', timezone_tz='Asia/Tokyo') - assert isinstance(result, datetime) - assert result.tzinfo is not None - - def test_parse_flexible_date_with_timezone_zoneinfo(self): - """Test parse_flexible_date with ZoneInfo object""" - tz = ZoneInfo('UTC') - result = parse_flexible_date('2023-12-25T15:30:45', timezone_tz=tz) - assert isinstance(result, datetime) - assert result.tzinfo is not None - - def test_parse_flexible_date_with_timezone_no_shift(self): - """Test parse_flexible_date with timezone but no shift""" - result = parse_flexible_date('2023-12-25T15:30:45', timezone_tz='UTC', shift_time_zone=False) - assert isinstance(result, datetime) - assert result.hour == 15 # Should not shift - - def test_parse_flexible_date_with_timezone_shift(self): - """Test parse_flexible_date with timezone shift""" - result = parse_flexible_date('2023-12-25T15:30:45+00:00', timezone_tz='Asia/Tokyo', shift_time_zone=True) - assert isinstance(result, datetime) - assert result.tzinfo is not None - - def test_parse_flexible_date_missing_t_with_timezone_shift(self): - """Test parse_flexible_date with timezone shift""" - result = parse_flexible_date('2023-12-25 15:30:45+00:00', timezone_tz='Asia/Tokyo', shift_time_zone=True) - assert isinstance(result, datetime) - assert result.tzinfo is not None - - def test_parse_flexible_date_space_separated_datetime(self): - """Test parse_flexible_date with space-separated datetime format""" - result = parse_flexible_date('2023-12-25 15:30:45') - assert isinstance(result, datetime) - assert result.year == 2023 - assert result.month == 12 - assert result.day == 25 - assert result.hour == 15 - assert result.minute == 30 - assert result.second == 45 - - def test_parse_flexible_date_space_separated_with_microseconds(self): - """Test parse_flexible_date with space-separated datetime and microseconds""" - result = parse_flexible_date('2023-12-25 15:30:45.123456') - assert isinstance(result, datetime) - assert result.year == 2023 - assert result.month == 12 - assert result.day == 25 - assert result.hour == 15 - assert result.minute == 30 - assert result.second == 45 - assert result.microsecond == 123456 - - def test_parse_flexible_date_t_separated_datetime(self): - """Test parse_flexible_date with T-separated datetime (alternative ISO format)""" - result = parse_flexible_date('2023-12-25T15:30:45') - assert isinstance(result, datetime) - assert result.year == 2023 - assert result.month == 12 - assert result.day == 25 - assert result.hour == 15 - assert result.minute == 30 - assert result.second == 45 - - def test_parse_flexible_date_t_separated_with_microseconds(self): - """Test parse_flexible_date with T-separated datetime and microseconds""" - result = parse_flexible_date('2023-12-25T15:30:45.123456') - assert isinstance(result, datetime) - assert result.year == 2023 - assert result.microsecond == 123456 - - def test_parse_flexible_date_invalid_format(self): - """Test parse_flexible_date with invalid format returns None""" - result = parse_flexible_date('invalid-date') - assert result is None - - def test_parse_flexible_date_whitespace(self): - """Test parse_flexible_date with whitespace""" - result = parse_flexible_date(' 2023-12-25 ') - assert isinstance(result, datetime) - assert result.year == 2023 - - -class TestCompareDates: - """Test suite for compare_dates function""" - - def test_compare_dates_first_newer(self): - """Test compare_dates when first date is newer""" - result = compare_dates('2024-01-02', '2024-01-01') - assert result is True - - def test_compare_dates_first_older(self): - """Test compare_dates when first date is older""" - result = compare_dates('2024-01-01', '2024-01-02') - assert result is False - - def test_compare_dates_equal(self): - """Test compare_dates when dates are equal""" - result = compare_dates('2024-01-01', '2024-01-01') - assert result is False - - def test_compare_dates_with_time(self): - """Test compare_dates with time components (should only compare dates)""" - result = compare_dates('2024-01-02T10:00:00', '2024-01-01T23:59:59') - assert result is True - - def test_compare_dates_invalid_first_date(self): - """Test compare_dates with invalid first date""" - result = compare_dates('invalid', '2024-01-01') - assert result is None - - def test_compare_dates_invalid_second_date(self): - """Test compare_dates with invalid second date""" - result = compare_dates('2024-01-01', 'invalid') - assert result is None - - def test_compare_dates_both_invalid(self): - """Test compare_dates with both dates invalid""" - result = compare_dates('invalid1', 'invalid2') - assert result is None - - -class TestFindNewestDatetimeInList: - """Test suite for find_newest_datetime_in_list function""" - - def test_find_newest_datetime_in_list_basic(self): - """Test find_newest_datetime_in_list with basic list""" - dates = [ - '2023-12-25T10:00:00', - '2024-01-01T12:00:00', - '2023-11-15T08:00:00' - ] - result = find_newest_datetime_in_list(dates) - assert result == '2024-01-01T12:00:00' - - def test_find_newest_datetime_in_list_with_timezone(self): - """Test find_newest_datetime_in_list with timezone-aware dates""" - dates = [ - '2025-08-06T16:17:39.747+09:00', - '2025-08-05T16:17:39.747+09:00', - '2025-08-07T16:17:39.747+09:00' - ] - result = find_newest_datetime_in_list(dates) - assert result == '2025-08-07T16:17:39.747+09:00' - - def test_find_newest_datetime_in_list_empty_list(self): - """Test find_newest_datetime_in_list with empty list""" - result = find_newest_datetime_in_list([]) - assert result is None - - def test_find_newest_datetime_in_list_single_date(self): - """Test find_newest_datetime_in_list with single date""" - dates = ['2024-01-01T12:00:00'] - result = find_newest_datetime_in_list(dates) - assert result == '2024-01-01T12:00:00' - - def test_find_newest_datetime_in_list_with_invalid_dates(self): - """Test find_newest_datetime_in_list with some invalid dates""" - dates = [ - '2023-12-25T10:00:00', - 'invalid-date', - '2024-01-01T12:00:00' - ] - result = find_newest_datetime_in_list(dates) - assert result == '2024-01-01T12:00:00' - - def test_find_newest_datetime_in_list_all_invalid(self): - """Test find_newest_datetime_in_list with all invalid dates""" - dates = ['invalid1', 'invalid2', 'invalid3'] - result = find_newest_datetime_in_list(dates) - assert result is None - - def test_find_newest_datetime_in_list_mixed_formats(self): - """Test find_newest_datetime_in_list with mixed date formats""" - dates = [ - '2023-12-25', - '2024-01-01T12:00:00', - '2023-11-15T08:00:00.123456' - ] - result = find_newest_datetime_in_list(dates) - assert result == '2024-01-01T12:00:00' - - -class TestParseDayOfWeekRange: - """Test suite for parse_day_of_week_range function""" - - def test_parse_day_of_week_range_single_day(self): - """Test parse_day_of_week_range with single day""" - result = parse_day_of_week_range('Mon') - assert result == [(1, 'Mon')] - - def test_parse_day_of_week_range_multiple_days(self): - """Test parse_day_of_week_range with multiple days""" - result = parse_day_of_week_range('Mon,Wed,Fri') - assert len(result) == 3 - assert (1, 'Mon') in result - assert (3, 'Wed') in result - assert (5, 'Fri') in result - - def test_parse_day_of_week_range_simple_range(self): - """Test parse_day_of_week_range with simple range""" - result = parse_day_of_week_range('Mon-Fri') - assert len(result) == 5 - assert result[0] == (1, 'Mon') - assert result[-1] == (5, 'Fri') - - def test_parse_day_of_week_range_weekend_spanning(self): - """Test parse_day_of_week_range with weekend-spanning range""" - result = parse_day_of_week_range('Fri-Mon') - assert len(result) == 4 - assert (5, 'Fri') in result - assert (6, 'Sat') in result - assert (7, 'Sun') in result - assert (1, 'Mon') in result - - def test_parse_day_of_week_range_long_names(self): - """Test parse_day_of_week_range with long day names - only works in ranges""" - # Long names only work in ranges, not as standalone days - # This is a limitation of the current implementation - with pytest.raises(ValueError) as exc_info: - parse_day_of_week_range('Monday,Wednesday') - assert 'Invalid day of week entry found' in str(exc_info.value) - - def test_parse_day_of_week_range_mixed_format(self): - """Test parse_day_of_week_range with short names and ranges""" - result = parse_day_of_week_range('Mon,Wed-Fri') - assert len(result) == 4 - assert (1, 'Mon') in result - assert (3, 'Wed') in result - assert (4, 'Thu') in result - assert (5, 'Fri') in result - - def test_parse_day_of_week_range_invalid_day(self): - """Test parse_day_of_week_range with invalid day""" - with pytest.raises(ValueError) as exc_info: - parse_day_of_week_range('InvalidDay') - assert 'Invalid day of week entry found' in str(exc_info.value) - - def test_parse_day_of_week_range_duplicate_days(self): - """Test parse_day_of_week_range with duplicate days""" - with pytest.raises(ValueError) as exc_info: - parse_day_of_week_range('Mon,Mon') - assert 'Duplicate day of week entries found' in str(exc_info.value) - - def test_parse_day_of_week_range_whitespace_handling(self): - """Test parse_day_of_week_range with extra whitespace""" - result = parse_day_of_week_range(' Mon , Wed , Fri ') - assert len(result) == 3 - assert (1, 'Mon') in result - - -class TestParseTimeRange: - """Test suite for parse_time_range function""" - - def test_parse_time_range_valid(self): - """Test parse_time_range with valid time range""" - start, end = parse_time_range('09:00-17:00') - assert start == time(9, 0) - assert end == time(17, 0) - - def test_parse_time_range_different_times(self): - """Test parse_time_range with different time values""" - start, end = parse_time_range('08:30-12:45') - assert start == time(8, 30) - assert end == time(12, 45) - - def test_parse_time_range_invalid_block(self): - """Test parse_time_range with invalid block format""" - with pytest.raises(ValueError) as exc_info: - parse_time_range('09:00') - assert 'Invalid time block' in str(exc_info.value) - - def test_parse_time_range_invalid_format(self): - """Test parse_time_range with invalid time format""" - with pytest.raises(ValueError) as exc_info: - parse_time_range('25:00-26:00') - assert 'Invalid time block format' in str(exc_info.value) - - def test_parse_time_range_start_after_end(self): - """Test parse_time_range with start time after end time""" - with pytest.raises(ValueError) as exc_info: - parse_time_range('17:00-09:00') - assert 'start time after end time' in str(exc_info.value) - - def test_parse_time_range_equal_times(self): - """Test parse_time_range with equal start and end times""" - with pytest.raises(ValueError) as exc_info: - parse_time_range('09:00-09:00') - assert 'start time after end time or equal' in str(exc_info.value) - - def test_parse_time_range_custom_format(self): - """Test parse_time_range with custom time format""" - start, end = parse_time_range('09:00:00-17:00:00', time_format='%H:%M:%S') - assert start == time(9, 0, 0) - assert end == time(17, 0, 0) - - def test_parse_time_range_whitespace(self): - """Test parse_time_range with whitespace""" - start, end = parse_time_range(' 09:00-17:00 ') - assert start == time(9, 0) - assert end == time(17, 0) - - -class TestTimesOverlapOrConnect: - """Test suite for times_overlap_or_connect function""" - - def test_times_overlap_or_connect_clear_overlap(self): - """Test times_overlap_or_connect with clear overlap""" - time1 = (time(9, 0), time(12, 0)) - time2 = (time(10, 0), time(14, 0)) - assert times_overlap_or_connect(time1, time2) is True - - def test_times_overlap_or_connect_no_overlap(self): - """Test times_overlap_or_connect with no overlap""" - time1 = (time(9, 0), time(12, 0)) - time2 = (time(13, 0), time(17, 0)) - assert times_overlap_or_connect(time1, time2) is False - - def test_times_overlap_or_connect_touching_not_allowed(self): - """Test times_overlap_or_connect with touching ranges (not allowed)""" - time1 = (time(8, 0), time(10, 0)) - time2 = (time(10, 0), time(12, 0)) - assert times_overlap_or_connect(time1, time2, allow_touching=False) is True - - def test_times_overlap_or_connect_touching_allowed(self): - """Test times_overlap_or_connect with touching ranges (allowed)""" - time1 = (time(8, 0), time(10, 0)) - time2 = (time(10, 0), time(12, 0)) - assert times_overlap_or_connect(time1, time2, allow_touching=True) is False - - def test_times_overlap_or_connect_one_contains_other(self): - """Test times_overlap_or_connect when one range contains the other""" - time1 = (time(9, 0), time(17, 0)) - time2 = (time(10, 0), time(12, 0)) - assert times_overlap_or_connect(time1, time2) is True - - def test_times_overlap_or_connect_same_start(self): - """Test times_overlap_or_connect with same start time""" - time1 = (time(9, 0), time(12, 0)) - time2 = (time(9, 0), time(14, 0)) - assert times_overlap_or_connect(time1, time2) is True - - def test_times_overlap_or_connect_same_end(self): - """Test times_overlap_or_connect with same end time""" - time1 = (time(9, 0), time(12, 0)) - time2 = (time(10, 0), time(12, 0)) - assert times_overlap_or_connect(time1, time2) is True - - -class TestIsTimeInRange: - """Test suite for is_time_in_range function""" - - def test_is_time_in_range_within_range(self): - """Test is_time_in_range with time within range""" - assert is_time_in_range('10:00:00', '09:00:00', '17:00:00') is True - - def test_is_time_in_range_at_start(self): - """Test is_time_in_range with time at start of range""" - assert is_time_in_range('09:00:00', '09:00:00', '17:00:00') is True - - def test_is_time_in_range_at_end(self): - """Test is_time_in_range with time at end of range""" - assert is_time_in_range('17:00:00', '09:00:00', '17:00:00') is True - - def test_is_time_in_range_before_range(self): - """Test is_time_in_range with time before range""" - assert is_time_in_range('08:00:00', '09:00:00', '17:00:00') is False - - def test_is_time_in_range_after_range(self): - """Test is_time_in_range with time after range""" - assert is_time_in_range('18:00:00', '09:00:00', '17:00:00') is False - - def test_is_time_in_range_crosses_midnight(self): - """Test is_time_in_range with range crossing midnight""" - # Range from 22:00 to 06:00 - assert is_time_in_range('23:00:00', '22:00:00', '06:00:00') is True - assert is_time_in_range('03:00:00', '22:00:00', '06:00:00') is True - assert is_time_in_range('12:00:00', '22:00:00', '06:00:00') is False - - def test_is_time_in_range_midnight_boundary(self): - """Test is_time_in_range at midnight""" - assert is_time_in_range('00:00:00', '22:00:00', '06:00:00') is True - - -class TestReorderWeekdaysFromToday: - """Test suite for reorder_weekdays_from_today function""" - - def test_reorder_weekdays_from_monday(self): - """Test reorder_weekdays_from_today starting from Monday""" - result = reorder_weekdays_from_today('Mon') - values = list(result.values()) - assert values[0] == 'Mon' - assert values[-1] == 'Sun' - assert len(result) == 7 - - def test_reorder_weekdays_from_wednesday(self): - """Test reorder_weekdays_from_today starting from Wednesday""" - result = reorder_weekdays_from_today('Wed') - values = list(result.values()) - assert values[0] == 'Wed' - assert values[1] == 'Thu' - assert values[-1] == 'Tue' - - def test_reorder_weekdays_from_sunday(self): - """Test reorder_weekdays_from_today starting from Sunday""" - result = reorder_weekdays_from_today('Sun') - values = list(result.values()) - assert values[0] == 'Sun' - assert values[-1] == 'Sat' - - def test_reorder_weekdays_from_long_name(self): - """Test reorder_weekdays_from_today with long day name""" - result = reorder_weekdays_from_today('Friday') - values = list(result.values()) - assert values[0] == 'Fri' - assert values[-1] == 'Thu' - - def test_reorder_weekdays_invalid_day(self): - """Test reorder_weekdays_from_today with invalid day name""" - with pytest.raises(ValueError) as exc_info: - reorder_weekdays_from_today('InvalidDay') - assert 'Invalid day name provided' in str(exc_info.value) - - def test_reorder_weekdays_preserves_all_days(self): - """Test that reorder_weekdays_from_today preserves all 7 days""" - for day in ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']: - result = reorder_weekdays_from_today(day) - assert len(result) == 7 - assert set(result.values()) == {'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'} - - -class TestEdgeCases: - """Test suite for edge cases and integration scenarios""" - - def test_parse_flexible_date_with_various_iso_formats(self): - """Test parse_flexible_date handles various ISO format variations""" - formats = [ - '2023-12-25', - '2023-12-25T15:30:45', - '2023-12-25T15:30:45.123456', - ] - for date_str in formats: - result = parse_flexible_date(date_str) - assert result is not None - assert isinstance(result, datetime) - - def test_timezone_consistency_across_functions(self): - """Test timezone handling consistency across functions""" - tz_str = 'Asia/Tokyo' - tz_obj = parse_timezone_data(tz_str) - - # Both should work with get_datetime_iso8601 - result1 = get_datetime_iso8601(tz_str) - result2 = get_datetime_iso8601(tz_obj) - - assert result1 is not None - assert result2 is not None - - def test_date_validation_and_parsing_consistency(self): - """Test that validate_date and parse_flexible_date agree""" - valid_dates = ['2023-12-25', '2024/01/01'] - for date_str in valid_dates: - # normalize format for parse_flexible_date - normalized = date_str.replace('/', '-') - assert validate_date(date_str) is True - assert parse_flexible_date(normalized) is not None - - def test_day_of_week_range_complex_scenario(self): - """Test parse_day_of_week_range with complex mixed input""" - result = parse_day_of_week_range('Mon,Wed-Fri,Sun') - assert len(result) == 5 - assert (1, 'Mon') in result - assert (3, 'Wed') in result - assert (4, 'Thu') in result - assert (5, 'Fri') in result - assert (7, 'Sun') in result - - def test_time_range_boundary_conditions(self): - """Test parse_time_range with boundary times""" - start, end = parse_time_range('00:00-23:59') - assert start == time(0, 0) - assert end == time(23, 59) - -# __END__ diff --git a/tests/unit/datetime_handling/test_seconds_to_string.py b/tests/unit/datetime_handling/test_seconds_to_string.py deleted file mode 100644 index 6169ae8..0000000 --- a/tests/unit/datetime_handling/test_seconds_to_string.py +++ /dev/null @@ -1,462 +0,0 @@ -""" -PyTest: datetime_handling/timestamp_convert - seconds_to_string and convert_timestamp functions -""" - -from corelibs.datetime_handling.timestamp_convert import seconds_to_string, convert_timestamp - - -class TestSecondsToString: - """Test suite for seconds_to_string function""" - - def test_basic_integer_seconds(self): - """Test conversion of basic integer seconds""" - assert seconds_to_string(0) == "0s" - assert seconds_to_string(1) == "1s" - assert seconds_to_string(30) == "30s" - assert seconds_to_string(59) == "59s" - - def test_minutes_conversion(self): - """Test conversion involving minutes""" - assert seconds_to_string(60) == "1m" - assert seconds_to_string(90) == "1m 30s" - assert seconds_to_string(120) == "2m" - assert seconds_to_string(3599) == "59m 59s" - - def test_hours_conversion(self): - """Test conversion involving hours""" - assert seconds_to_string(3600) == "1h" - assert seconds_to_string(3660) == "1h 1m" - assert seconds_to_string(3661) == "1h 1m 1s" - assert seconds_to_string(7200) == "2h" - assert seconds_to_string(7260) == "2h 1m" - - def test_days_conversion(self): - """Test conversion involving days""" - assert seconds_to_string(86400) == "1d" - assert seconds_to_string(86401) == "1d 1s" - assert seconds_to_string(90000) == "1d 1h" - assert seconds_to_string(90061) == "1d 1h 1m 1s" - assert seconds_to_string(172800) == "2d" - - def test_complex_combinations(self): - """Test complex time combinations""" - # 1 day, 2 hours, 3 minutes, 4 seconds - total = 86400 + 7200 + 180 + 4 - assert seconds_to_string(total) == "1d 2h 3m 4s" - - # 5 days, 23 hours, 59 minutes, 59 seconds - total = 5 * 86400 + 23 * 3600 + 59 * 60 + 59 - assert seconds_to_string(total) == "5d 23h 59m 59s" - - def test_fractional_seconds_default_precision(self): - """Test fractional seconds with default precision (3 decimal places)""" - assert seconds_to_string(0.1) == "0.1s" - assert seconds_to_string(0.123) == "0.123s" - assert seconds_to_string(0.1234) == "0.123s" - assert seconds_to_string(1.5) == "1.5s" - assert seconds_to_string(1.567) == "1.567s" - assert seconds_to_string(1.5678) == "1.568s" - - def test_fractional_seconds_microsecond_precision(self): - """Test fractional seconds with microsecond precision""" - assert seconds_to_string(0.1, show_microseconds=True) == "0.1s" - assert seconds_to_string(0.123456, show_microseconds=True) == "0.123456s" - assert seconds_to_string(0.1234567, show_microseconds=True) == "0.123457s" - assert seconds_to_string(1.5, show_microseconds=True) == "1.5s" - assert seconds_to_string(1.567890, show_microseconds=True) == "1.56789s" - - def test_fractional_seconds_with_larger_units(self): - """Test fractional seconds combined with larger time units""" - # 1 minute and 30.5 seconds - assert seconds_to_string(90.5) == "1m 30.5s" - assert seconds_to_string(90.5, show_microseconds=True) == "1m 30.5s" - - # 1 hour, 1 minute, and 1.123 seconds - total = 3600 + 60 + 1.123 - assert seconds_to_string(total) == "1h 1m 1.123s" - assert seconds_to_string(total, show_microseconds=True) == "1h 1m 1.123s" - - def test_negative_values(self): - """Test negative time values""" - assert seconds_to_string(-1) == "-1s" - assert seconds_to_string(-60) == "-1m" - assert seconds_to_string(-90) == "-1m 30s" - assert seconds_to_string(-3661) == "-1h 1m 1s" - assert seconds_to_string(-86401) == "-1d 1s" - assert seconds_to_string(-1.5) == "-1.5s" - assert seconds_to_string(-90.123) == "-1m 30.123s" - - def test_zero_handling(self): - """Test various zero values""" - assert seconds_to_string(0) == "0s" - assert seconds_to_string(0.0) == "0s" - assert seconds_to_string(-0) == "0s" - assert seconds_to_string(-0.0) == "0s" - - def test_float_input_types(self): - """Test various float input types""" - assert seconds_to_string(1.0) == "1s" - assert seconds_to_string(60.0) == "1m" - assert seconds_to_string(3600.0) == "1h" - assert seconds_to_string(86400.0) == "1d" - - def test_large_values(self): - """Test handling of large time values""" - # 365 days (1 year) - year_seconds = 365 * 86400 - assert seconds_to_string(year_seconds) == "365d" - - # 1000 days - assert seconds_to_string(1000 * 86400) == "1000d" - - # Large number with all units - large_time = 999 * 86400 + 23 * 3600 + 59 * 60 + 59.999 - result = seconds_to_string(large_time) - assert result.startswith("999d") - assert "23h" in result - assert "59m" in result - assert "59.999s" in result - - def test_rounding_behavior(self): - """Test rounding behavior for fractional seconds""" - # Default precision (3 decimal places) - values are truncated via rstrip - assert seconds_to_string(1.0004) == "1s" # Truncates trailing zeros after rstrip - assert seconds_to_string(1.0005) == "1s" # Truncates trailing zeros after rstrip - assert seconds_to_string(1.9999) == "2s" # Rounds up and strips .000 - - # Microsecond precision (6 decimal places) - assert seconds_to_string(1.0000004, show_microseconds=True) == "1s" - assert seconds_to_string(1.0000005, show_microseconds=True) == "1.000001s" - - def test_trailing_zero_removal(self): - """Test that trailing zeros are properly removed""" - assert seconds_to_string(1.100) == "1.1s" - assert seconds_to_string(1.120) == "1.12s" - assert seconds_to_string(1.123) == "1.123s" - assert seconds_to_string(1.100000, show_microseconds=True) == "1.1s" - assert seconds_to_string(1.123000, show_microseconds=True) == "1.123s" - - def test_invalid_input_types(self): - """Test handling of invalid input types""" - # String inputs should be returned as-is - assert seconds_to_string("invalid") == "invalid" - assert seconds_to_string("not a number") == "not a number" - assert seconds_to_string("") == "" - - def test_edge_cases_boundary_values(self): - """Test edge cases at unit boundaries""" - # Exactly 1 minute - 1 second - assert seconds_to_string(59) == "59s" - assert seconds_to_string(59.999) == "59.999s" - - # Exactly 1 hour - 1 second - assert seconds_to_string(3599) == "59m 59s" - assert seconds_to_string(3599.999) == "59m 59.999s" - - # Exactly 1 day - 1 second - assert seconds_to_string(86399) == "23h 59m 59s" - assert seconds_to_string(86399.999) == "23h 59m 59.999s" - - def test_very_small_fractional_seconds(self): - """Test very small fractional values""" - assert seconds_to_string(0.001) == "0.001s" - assert seconds_to_string(0.0001) == "0s" # Below default precision - assert seconds_to_string(0.000001, show_microseconds=True) == "0.000001s" - assert seconds_to_string(0.0000001, show_microseconds=True) == "0s" # Below microsecond precision - - def test_precision_consistency(self): - """Test that precision is consistent across different scenarios""" - # With other units present - assert seconds_to_string(61.123456) == "1m 1.123s" - assert seconds_to_string(61.123456, show_microseconds=True) == "1m 1.123456s" - - # Large values with fractional seconds - large_val = 90061.123456 # 1d 1h 1m 1.123456s - assert seconds_to_string(large_val) == "1d 1h 1m 1.123s" - assert seconds_to_string(large_val, show_microseconds=True) == "1d 1h 1m 1.123456s" - - def test_string_numeric_inputs(self): - """Test string inputs that represent numbers""" - # String inputs should be returned as-is, even if they look like numbers - assert seconds_to_string("60") == "60" - assert seconds_to_string("1.5") == "1.5" - assert seconds_to_string("0") == "0" - assert seconds_to_string("-60") == "-60" - - -class TestConvertTimestamp: - """Test suite for convert_timestamp function""" - - def test_basic_integer_seconds(self): - """Test conversion of basic integer seconds""" - assert convert_timestamp(0) == "0s 0ms" - assert convert_timestamp(1) == "1s 0ms" - assert convert_timestamp(30) == "30s 0ms" - assert convert_timestamp(59) == "59s 0ms" - - def test_basic_without_microseconds(self): - """Test conversion without showing microseconds""" - assert convert_timestamp(0, show_microseconds=False) == "0s" - assert convert_timestamp(1, show_microseconds=False) == "1s" - assert convert_timestamp(30, show_microseconds=False) == "30s" - assert convert_timestamp(59, show_microseconds=False) == "59s" - - def test_minutes_conversion(self): - """Test conversion involving minutes""" - assert convert_timestamp(60) == "1m 0s 0ms" - assert convert_timestamp(90) == "1m 30s 0ms" - assert convert_timestamp(120) == "2m 0s 0ms" - assert convert_timestamp(3599) == "59m 59s 0ms" - - def test_minutes_conversion_without_microseconds(self): - """Test conversion involving minutes without microseconds""" - assert convert_timestamp(60, show_microseconds=False) == "1m 0s" - assert convert_timestamp(90, show_microseconds=False) == "1m 30s" - assert convert_timestamp(120, show_microseconds=False) == "2m 0s" - - def test_hours_conversion(self): - """Test conversion involving hours""" - assert convert_timestamp(3600) == "1h 0m 0s 0ms" - assert convert_timestamp(3660) == "1h 1m 0s 0ms" - assert convert_timestamp(3661) == "1h 1m 1s 0ms" - assert convert_timestamp(7200) == "2h 0m 0s 0ms" - assert convert_timestamp(7260) == "2h 1m 0s 0ms" - - def test_hours_conversion_without_microseconds(self): - """Test conversion involving hours without microseconds""" - assert convert_timestamp(3600, show_microseconds=False) == "1h 0m 0s" - assert convert_timestamp(3660, show_microseconds=False) == "1h 1m 0s" - assert convert_timestamp(3661, show_microseconds=False) == "1h 1m 1s" - - def test_days_conversion(self): - """Test conversion involving days""" - assert convert_timestamp(86400) == "1d 0h 0m 0s 0ms" - assert convert_timestamp(86401) == "1d 0h 0m 1s 0ms" - assert convert_timestamp(90000) == "1d 1h 0m 0s 0ms" - assert convert_timestamp(90061) == "1d 1h 1m 1s 0ms" - assert convert_timestamp(172800) == "2d 0h 0m 0s 0ms" - - def test_days_conversion_without_microseconds(self): - """Test conversion involving days without microseconds""" - assert convert_timestamp(86400, show_microseconds=False) == "1d 0h 0m 0s" - assert convert_timestamp(86401, show_microseconds=False) == "1d 0h 0m 1s" - assert convert_timestamp(90000, show_microseconds=False) == "1d 1h 0m 0s" - - def test_complex_combinations(self): - """Test complex time combinations""" - # 1 day, 2 hours, 3 minutes, 4 seconds - total = 86400 + 7200 + 180 + 4 - assert convert_timestamp(total) == "1d 2h 3m 4s 0ms" - - # 5 days, 23 hours, 59 minutes, 59 seconds - total = 5 * 86400 + 23 * 3600 + 59 * 60 + 59 - assert convert_timestamp(total) == "5d 23h 59m 59s 0ms" - - def test_fractional_seconds_with_microseconds(self): - """Test fractional seconds showing microseconds""" - # Note: ms value is the integer of the decimal part string after rounding to 4 places - assert convert_timestamp(0.1) == "0s 1ms" # 0.1 → "0.1" → ms=1 - assert convert_timestamp(0.123) == "0s 123ms" # 0.123 → "0.123" → ms=123 - assert convert_timestamp(0.1234) == "0s 1234ms" # 0.1234 → "0.1234" → ms=1234 - assert convert_timestamp(1.5) == "1s 5ms" # 1.5 → "1.5" → ms=5 - assert convert_timestamp(1.567) == "1s 567ms" # 1.567 → "1.567" → ms=567 - assert convert_timestamp(1.5678) == "1s 5678ms" # 1.5678 rounds to 1.5678 → ms=5678 - - def test_fractional_seconds_rounding(self): - """Test rounding of fractional seconds to 4 decimal places""" - # The function rounds to 4 decimal places before splitting - assert convert_timestamp(0.12345) == "0s 1235ms" # Rounds to 0.1235 - assert convert_timestamp(0.123456) == "0s 1235ms" # Rounds to 0.1235 - assert convert_timestamp(1.99999) == "2s 0ms" # Rounds to 2.0 - - def test_fractional_seconds_with_larger_units(self): - """Test fractional seconds combined with larger time units""" - # 1 minute and 30.5 seconds - assert convert_timestamp(90.5) == "1m 30s 5ms" - - # 1 hour, 1 minute, and 1.123 seconds - total = 3600 + 60 + 1.123 - assert convert_timestamp(total) == "1h 1m 1s 123ms" - - def test_negative_values(self): - """Test negative time values""" - assert convert_timestamp(-1) == "-1s 0ms" - assert convert_timestamp(-60) == "-1m 0s 0ms" - assert convert_timestamp(-90) == "-1m 30s 0ms" - assert convert_timestamp(-3661) == "-1h 1m 1s 0ms" - assert convert_timestamp(-86401) == "-1d 0h 0m 1s 0ms" - assert convert_timestamp(-1.5) == "-1s 5ms" - assert convert_timestamp(-90.123) == "-1m 30s 123ms" - - def test_negative_without_microseconds(self): - """Test negative values without microseconds""" - assert convert_timestamp(-1, show_microseconds=False) == "-1s" - assert convert_timestamp(-60, show_microseconds=False) == "-1m 0s" - assert convert_timestamp(-90.123, show_microseconds=False) == "-1m 30s" - - def test_zero_handling(self): - """Test various zero values""" - assert convert_timestamp(0) == "0s 0ms" - assert convert_timestamp(0.0) == "0s 0ms" - assert convert_timestamp(-0) == "0s 0ms" - assert convert_timestamp(-0.0) == "0s 0ms" - - def test_zero_filling_behavior(self): - """Test that zeros are filled between set values""" - # If we have days and seconds, hours and minutes should be 0 - assert convert_timestamp(86401) == "1d 0h 0m 1s 0ms" - - # If we have hours and seconds, minutes should be 0 - assert convert_timestamp(3601) == "1h 0m 1s 0ms" - - # If we have days and hours, minutes and seconds should be 0 - assert convert_timestamp(90000) == "1d 1h 0m 0s 0ms" - - def test_milliseconds_display(self): - """Test milliseconds are always shown when show_microseconds=True""" - # Even with no fractional part, 0ms should be shown - assert convert_timestamp(1) == "1s 0ms" - assert convert_timestamp(60) == "1m 0s 0ms" - assert convert_timestamp(3600) == "1h 0m 0s 0ms" - - # With fractional part, ms should be shown - assert convert_timestamp(1.001) == "1s 1ms" # "1.001" → ms=1 - assert convert_timestamp(1.0001) == "1s 1ms" # "1.0001" → ms=1 - - def test_float_input_types(self): - """Test various float input types""" - assert convert_timestamp(1.0) == "1s 0ms" - assert convert_timestamp(60.0) == "1m 0s 0ms" - assert convert_timestamp(3600.0) == "1h 0m 0s 0ms" - assert convert_timestamp(86400.0) == "1d 0h 0m 0s 0ms" - - def test_large_values(self): - """Test handling of large time values""" - # 365 days (1 year) - year_seconds = 365 * 86400 - assert convert_timestamp(year_seconds) == "365d 0h 0m 0s 0ms" - - # 1000 days - assert convert_timestamp(1000 * 86400) == "1000d 0h 0m 0s 0ms" - - # Large number with all units - large_time = 999 * 86400 + 23 * 3600 + 59 * 60 + 59.999 - result = convert_timestamp(large_time) - assert result.startswith("999d") - assert "23h" in result - assert "59m" in result - assert "59s" in result - assert "999ms" in result # 59.999 rounds to 59.999, ms=999 - - def test_invalid_input_types(self): - """Test handling of invalid input types""" - # String inputs should be returned as-is - assert convert_timestamp("invalid") == "invalid" - assert convert_timestamp("not a number") == "not a number" - assert convert_timestamp("") == "" - - def test_string_numeric_inputs(self): - """Test string inputs that represent numbers""" - # String inputs should be returned as-is, even if they look like numbers - assert convert_timestamp("60") == "60" - assert convert_timestamp("1.5") == "1.5" - assert convert_timestamp("0") == "0" - assert convert_timestamp("-60") == "-60" - - def test_edge_cases_boundary_values(self): - """Test edge cases at unit boundaries""" - # Exactly 1 minute - 1 second - assert convert_timestamp(59) == "59s 0ms" - assert convert_timestamp(59.999) == "59s 999ms" - - # Exactly 1 hour - 1 second - assert convert_timestamp(3599) == "59m 59s 0ms" - assert convert_timestamp(3599.999) == "59m 59s 999ms" - - # Exactly 1 day - 1 second - assert convert_timestamp(86399) == "23h 59m 59s 0ms" - assert convert_timestamp(86399.999) == "23h 59m 59s 999ms" - - def test_very_small_fractional_seconds(self): - """Test very small fractional values""" - assert convert_timestamp(0.001) == "0s 1ms" # 0.001 → "0.001" → ms=1 - assert convert_timestamp(0.0001) == "0s 1ms" # 0.0001 → "0.0001" → ms=1 - assert convert_timestamp(0.00005) == "0s 1ms" # 0.00005 rounds to 0.0001 → ms=1 - assert convert_timestamp(0.00004) == "0s 0ms" # 0.00004 rounds to 0.0 → ms=0 - - def test_milliseconds_extraction(self): - """Test that milliseconds are correctly extracted from fractional part""" - # The ms value is the integer of the decimal part string, not a conversion - # So 0.1 → "0.1" → ms=1, NOT 100ms as you might expect - assert convert_timestamp(0.1) == "0s 1ms" - # 0.01 seconds → "0.01" → ms=1 (int("01") = 1) - assert convert_timestamp(0.01) == "0s 1ms" - # 0.001 seconds → "0.001" → ms=1 - assert convert_timestamp(0.001) == "0s 1ms" - # 0.0001 seconds → "0.0001" → ms=1 - assert convert_timestamp(0.0001) == "0s 1ms" - # 0.00004 seconds rounds to "0.0" → ms=0 - assert convert_timestamp(0.00004) == "0s 0ms" - - def test_comparison_with_seconds_to_string(self): - """Test differences between convert_timestamp and seconds_to_string""" - # convert_timestamp fills zeros and adds ms - # seconds_to_string omits zeros and no ms - assert convert_timestamp(86401) == "1d 0h 0m 1s 0ms" - assert seconds_to_string(86401) == "1d 1s" - - assert convert_timestamp(3661) == "1h 1m 1s 0ms" - assert seconds_to_string(3661) == "1h 1m 1s" - - # With microseconds disabled, still different due to zero-filling - assert convert_timestamp(86401, show_microseconds=False) == "1d 0h 0m 1s" - assert seconds_to_string(86401) == "1d 1s" - - def test_precision_consistency(self): - """Test that precision is consistent across different scenarios""" - # With other units present - assert convert_timestamp(61.123456) == "1m 1s 1235ms" # Rounds to 61.1235 - - # Large values with fractional seconds - large_val = 90061.123456 # 1d 1h 1m 1.123456s - assert convert_timestamp(large_val) == "1d 1h 1m 1s 1235ms" # Rounds to .1235 - - def test_microseconds_flag_consistency(self): - """Test that show_microseconds flag works consistently""" - test_values = [0, 1, 60, 3600, 86400, 1.5, 90.123, -60] - - for val in test_values: - with_ms = convert_timestamp(val, show_microseconds=True) - without_ms = convert_timestamp(val, show_microseconds=False) - - # With microseconds should contain 'ms', without should not - assert "ms" in with_ms - assert "ms" not in without_ms - - # Both should start with same sign if negative - if val < 0: - assert with_ms.startswith("-") - assert without_ms.startswith("-") - - def test_format_consistency(self): - """Test that output format is consistent""" - # All outputs should have consistent spacing and unit ordering - # Format should be: [d ]h m s[ ms] - result = convert_timestamp(93784.5678) # 1d 2h 3m 4.5678s - # 93784.5678 rounds to 93784.5678, splits to ["93784", "5678"] - assert result == "1d 2h 3m 4s 5678ms" - - # Verify parts are in correct order - parts = result.split() - # Extract units properly: last 1-2 chars that are letters - units: list[str] = [] - for p in parts: - if p.endswith('ms'): - units.append('ms') - elif p[-1].isalpha(): - units.append(p[-1]) - # Should be in order: d, h, m, s, ms - expected_order = ['d', 'h', 'm', 's', 'ms'] - assert units == expected_order - -# __END__ diff --git a/tests/unit/datetime_handling/test_timestamp_strings.py b/tests/unit/datetime_handling/test_timestamp_strings.py deleted file mode 100644 index be025e5..0000000 --- a/tests/unit/datetime_handling/test_timestamp_strings.py +++ /dev/null @@ -1,194 +0,0 @@ -""" -PyTest: datetime_handling/timestamp_strings -""" - -from datetime import datetime -from unittest.mock import patch -from zoneinfo import ZoneInfo -import pytest - -# Assuming the class is in a file called timestamp_strings.py -from corelibs.datetime_handling.timestamp_strings import TimestampStrings - - -class TestTimestampStrings: - """Test suite for TimestampStrings class""" - - def test_default_initialization(self): - """Test initialization with default timezone""" - with patch('corelibs.datetime_handling.timestamp_strings.datetime') as mock_datetime: - mock_now = datetime(2023, 12, 25, 15, 30, 45) - mock_datetime.now.return_value = mock_now - - ts = TimestampStrings() - - assert ts.time_zone == 'Asia/Tokyo' - assert ts.timestamp_now == mock_now - assert ts.today == '2023-12-25' - assert ts.timestamp == '2023-12-25 15:30:45' - assert ts.timestamp_file == '2023-12-25_153045' - - def test_custom_timezone_initialization(self): - """Test initialization with custom timezone""" - custom_tz = 'America/New_York' - - with patch('corelibs.datetime_handling.timestamp_strings.datetime') as mock_datetime: - mock_now = datetime(2023, 12, 25, 15, 30, 45) - mock_datetime.now.return_value = mock_now - - ts = TimestampStrings(time_zone=custom_tz) - - assert ts.time_zone == custom_tz - assert ts.timestamp_now == mock_now - - def test_invalid_timezone_raises_error(self): - """Test that invalid timezone raises ValueError""" - invalid_tz = 'Invalid/Timezone' - - with pytest.raises(ValueError) as exc_info: - TimestampStrings(time_zone=invalid_tz) - - assert 'Zone could not be loaded [Invalid/Timezone]' in str(exc_info.value) - - def test_timestamp_formats(self): - """Test various timestamp format outputs""" - with patch('corelibs.datetime_handling.timestamp_strings.datetime') as mock_datetime: - # Mock both datetime.now() calls - mock_now = datetime(2023, 12, 25, 9, 5, 3) - mock_now_tz = datetime(2023, 12, 25, 23, 5, 3, tzinfo=ZoneInfo('Asia/Tokyo')) - - mock_datetime.now.side_effect = [mock_now, mock_now_tz] - - ts = TimestampStrings() - - assert ts.today == '2023-12-25' - assert ts.timestamp == '2023-12-25 09:05:03' - assert ts.timestamp_file == '2023-12-25_090503' - assert 'JST' in ts.timestamp_tz or 'Asia/Tokyo' in ts.timestamp_tz - - def test_different_timezones_produce_different_results(self): - """Test that different timezones produce different timestamp_tz values""" - with patch('corelibs.datetime_handling.timestamp_strings.datetime') as mock_datetime: - mock_now = datetime(2023, 12, 25, 12, 0, 0) - mock_datetime.now.return_value = mock_now - - # Create instances with different timezones - ts_tokyo = TimestampStrings(time_zone='Asia/Tokyo') - ts_ny = TimestampStrings(time_zone='America/New_York') - - # The timezone-aware timestamps should be different - assert ts_tokyo.time_zone != ts_ny.time_zone - # Note: The actual timestamp_tz values will depend on the mocked datetime - - def test_class_default_timezone(self): - """Test that class default timezone is correctly set""" - assert TimestampStrings.TIME_ZONE == 'Asia/Tokyo' - - def test_none_timezone_uses_default(self): - """Test that passing None for timezone uses class default""" - with patch('corelibs.datetime_handling.timestamp_strings.datetime') as mock_datetime: - mock_now = datetime(2023, 12, 25, 15, 30, 45) - mock_datetime.now.return_value = mock_now - - ts = TimestampStrings(time_zone=None) - - assert ts.time_zone == 'Asia/Tokyo' - - def test_timestamp_file_format_no_colons(self): - """Test that timestamp_file format doesn't contain colons (safe for filenames)""" - with patch('corelibs.datetime_handling.timestamp_strings.datetime') as mock_datetime: - mock_now = datetime(2023, 12, 25, 15, 30, 45) - mock_datetime.now.return_value = mock_now - - ts = TimestampStrings() - - assert ':' not in ts.timestamp_file - assert ' ' not in ts.timestamp_file - assert ts.timestamp_file == '2023-12-25_153045' - - def test_multiple_instances_independent(self): - """Test that multiple instances don't interfere with each other""" - with patch('corelibs.datetime_handling.timestamp_strings.datetime') as mock_datetime: - mock_now = datetime(2023, 12, 25, 15, 30, 45) - mock_datetime.now.return_value = mock_now - - ts1 = TimestampStrings(time_zone='Asia/Tokyo') - ts2 = TimestampStrings(time_zone='Europe/London') - - assert ts1.time_zone == 'Asia/Tokyo' - assert ts2.time_zone == 'Europe/London' - assert ts1.time_zone != ts2.time_zone - - def test_zoneinfo_called_correctly_with_string(self): - """Test that ZoneInfo is called with correct timezone when passing string""" - with patch('corelibs.datetime_handling.timestamp_strings.ZoneInfo') as mock_zoneinfo: - with patch('corelibs.datetime_handling.timestamp_strings.datetime') as mock_datetime: - mock_now = datetime(2023, 12, 25, 15, 30, 45) - mock_datetime.now.return_value = mock_now - - custom_tz = 'Europe/Paris' - ts = TimestampStrings(time_zone=custom_tz) - assert ts.time_zone == custom_tz - - mock_zoneinfo.assert_called_with(custom_tz) - - def test_zoneinfo_object_parameter(self): - """Test that ZoneInfo objects can be passed directly as timezone parameter""" - with patch('corelibs.datetime_handling.timestamp_strings.datetime') as mock_datetime: - mock_now = datetime(2023, 12, 25, 15, 30, 45) - mock_now_tz = datetime(2023, 12, 25, 15, 30, 45, tzinfo=ZoneInfo('Europe/Paris')) - mock_datetime.now.side_effect = [mock_now, mock_now_tz] - - # Create a ZoneInfo object - custom_tz_obj = ZoneInfo('Europe/Paris') - ts = TimestampStrings(time_zone=custom_tz_obj) - - # The time_zone should be the ZoneInfo object itself - assert ts.time_zone_zi is custom_tz_obj - assert isinstance(ts.time_zone_zi, ZoneInfo) - - def test_zoneinfo_object_vs_string_equivalence(self): - """Test that ZoneInfo object and string produce equivalent results""" - with patch('corelibs.datetime_handling.timestamp_strings.datetime') as mock_datetime: - mock_now = datetime(2023, 12, 25, 15, 30, 45) - mock_now_tz = datetime(2023, 12, 25, 15, 30, 45, tzinfo=ZoneInfo('Europe/Paris')) - mock_datetime.now.side_effect = [mock_now, mock_now_tz, mock_now, mock_now_tz] - - # Test with string - ts_string = TimestampStrings(time_zone='Europe/Paris') - - # Test with ZoneInfo object - ts_zoneinfo = TimestampStrings(time_zone=ZoneInfo('Europe/Paris')) - - # Both should produce the same timestamp formats (though time_zone attributes will differ) - assert ts_string.today == ts_zoneinfo.today - assert ts_string.timestamp == ts_zoneinfo.timestamp - assert ts_string.timestamp_file == ts_zoneinfo.timestamp_file - - # The time_zone attributes will be different types but represent the same timezone - assert str(ts_string.time_zone) == 'Europe/Paris' - assert isinstance(ts_zoneinfo.time_zone_zi, ZoneInfo) - - def test_edge_case_midnight(self): - """Test timestamp formatting at midnight""" - with patch('corelibs.datetime_handling.timestamp_strings.datetime') as mock_datetime: - mock_now = datetime(2023, 12, 25, 0, 0, 0) - mock_datetime.now.return_value = mock_now - - ts = TimestampStrings() - - assert ts.timestamp == '2023-12-25 00:00:00' - assert ts.timestamp_file == '2023-12-25_000000' - - def test_edge_case_new_year(self): - """Test timestamp formatting at new year""" - with patch('corelibs.datetime_handling.timestamp_strings.datetime') as mock_datetime: - mock_now = datetime(2024, 1, 1, 0, 0, 0) - mock_datetime.now.return_value = mock_now - - ts = TimestampStrings() - - assert ts.today == '2024-01-01' - assert ts.timestamp == '2024-01-01 00:00:00' - -# __END__ diff --git a/tests/unit/logging_handling/log_testing/test_log_1_settings_parsing_spacers_parameters.py b/tests/unit/logging_handling/log_testing/test_log_1_settings_parsing_spacers_parameters.py index 16aa33d..60b4002 100644 --- a/tests/unit/logging_handling/log_testing/test_log_1_settings_parsing_spacers_parameters.py +++ b/tests/unit/logging_handling/log_testing/test_log_1_settings_parsing_spacers_parameters.py @@ -183,6 +183,64 @@ class TestSpacerConstants: assert LogParent.SPACER_LENGTH == 32 +# MARK: Test ConsoleFormatSettings.from_string +class TestConsoleFormatSettingsFromString: + """Test cases for ConsoleFormatSettings.from_string method""" + + def test_from_string_all(self): + """Test from_string with 'ALL' returns correct format""" + result = ConsoleFormatSettings.from_string('ALL') + assert result == ConsoleFormatSettings.ALL + + def test_from_string_condensed(self): + """Test from_string with 'CONDENSED' returns correct format""" + result = ConsoleFormatSettings.from_string('CONDENSED') + assert result == ConsoleFormatSettings.CONDENSED + + def test_from_string_minimal(self): + """Test from_string with 'MINIMAL' returns correct format""" + result = ConsoleFormatSettings.from_string('MINIMAL') + assert result == ConsoleFormatSettings.MINIMAL + + def test_from_string_bare(self): + """Test from_string with 'BARE' returns correct format""" + result = ConsoleFormatSettings.from_string('BARE') + assert result == ConsoleFormatSettings.BARE + + def test_from_string_invalid_returns_none(self): + """Test from_string with invalid string returns None""" + result = ConsoleFormatSettings.from_string('INVALID') + assert result is None + + def test_from_string_invalid_with_default(self): + """Test from_string with invalid string returns provided default""" + default = ConsoleFormatSettings.ALL + result = ConsoleFormatSettings.from_string('INVALID', default=default) + assert result == default + + def test_from_string_case_sensitive(self): + """Test from_string is case sensitive""" + # Lowercase should not match + result = ConsoleFormatSettings.from_string('all') + assert result is None + + def test_from_string_with_none_default(self): + """Test from_string with explicit None default""" + result = ConsoleFormatSettings.from_string('NONEXISTENT', default=None) + assert result is None + + @pytest.mark.parametrize("setting_name,expected", [ + ("ALL", ConsoleFormatSettings.ALL), + ("CONDENSED", ConsoleFormatSettings.CONDENSED), + ("MINIMAL", ConsoleFormatSettings.MINIMAL), + ("BARE", ConsoleFormatSettings.BARE), + ]) + def test_from_string_all_valid_settings(self, setting_name: str, expected: Any): + """Test from_string with all valid setting names""" + result = ConsoleFormatSettings.from_string(setting_name) + assert result == expected + + # MARK: Parametrized Tests class TestParametrized: """Parametrized tests for comprehensive coverage""" diff --git a/tests/unit/var_handling/__init__.py b/tests/unit/var_handling/__init__.py deleted file mode 100644 index 1bfbaf1..0000000 --- a/tests/unit/var_handling/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -""" -var_handling tests -""" diff --git a/tests/unit/var_handling/test_enum_base.py b/tests/unit/var_handling/test_enum_base.py deleted file mode 100644 index 5ec1dcf..0000000 --- a/tests/unit/var_handling/test_enum_base.py +++ /dev/null @@ -1,546 +0,0 @@ -""" -var_handling.enum_base tests -""" - -from typing import Any -import pytest -from corelibs.var_handling.enum_base import EnumBase - - -class SampleBlock(EnumBase): - """Sample block enum for testing purposes""" - BLOCK_A = "block_a" - BLOCK_B = "block_b" - HAS_NUM = 5 - HAS_FLOAT = 3.14 - LEGACY_KEY = "legacy_value" - - -class SimpleEnum(EnumBase): - """Simple enum with string values""" - OPTION_ONE = "one" - OPTION_TWO = "two" - OPTION_THREE = "three" - - -class NumericEnum(EnumBase): - """Enum with only numeric values""" - FIRST = 1 - SECOND = 2 - THIRD = 3 - - -class TestEnumBaseLookupKey: - """Test cases for lookup_key class method""" - - def test_lookup_key_valid_uppercase(self): - """Test lookup_key with valid uppercase key""" - result = SampleBlock.lookup_key("BLOCK_A") - assert result == SampleBlock.BLOCK_A - assert result.name == "BLOCK_A" - assert result.value == "block_a" - - def test_lookup_key_valid_lowercase(self): - """Test lookup_key with valid lowercase key (should convert to uppercase)""" - result = SampleBlock.lookup_key("block_a") - assert result == SampleBlock.BLOCK_A - assert result.name == "BLOCK_A" - - def test_lookup_key_valid_mixed_case(self): - """Test lookup_key with mixed case key""" - result = SampleBlock.lookup_key("BlOcK_a") - assert result == SampleBlock.BLOCK_A - assert result.name == "BLOCK_A" - - def test_lookup_key_with_numeric_enum(self): - """Test lookup_key with numeric enum member""" - result = SampleBlock.lookup_key("HAS_NUM") - assert result == SampleBlock.HAS_NUM - assert result.value == 5 - - def test_lookup_key_legacy_colon_replacement(self): - """Test lookup_key with legacy colon format (converts : to ___)""" - # This assumes the enum has a key that might be accessed with legacy format - # Should convert : to ___ and look up LEGACY___KEY - # Since we don't have this key, we test the behavior with a valid conversion - # Let's test with a known key that would work - with pytest.raises(ValueError, match="Invalid key"): - SampleBlock.lookup_key("BLOCK:A") # Should fail as BLOCK___A doesn't exist - - def test_lookup_key_invalid_key(self): - """Test lookup_key with invalid key""" - with pytest.raises(ValueError, match="Invalid key: NONEXISTENT"): - SampleBlock.lookup_key("NONEXISTENT") - - def test_lookup_key_empty_string(self): - """Test lookup_key with empty string""" - with pytest.raises(ValueError, match="Invalid key"): - SampleBlock.lookup_key("") - - def test_lookup_key_with_special_characters(self): - """Test lookup_key with special characters that might cause AttributeError""" - with pytest.raises(ValueError, match="Invalid key"): - SampleBlock.lookup_key("@#$%") - - def test_lookup_key_numeric_string(self): - """Test lookup_key with numeric string that isn't a key""" - with pytest.raises(ValueError, match="Invalid key"): - SampleBlock.lookup_key("123") - - -class TestEnumBaseLookupValue: - """Test cases for lookup_value class method""" - - def test_lookup_value_valid_string(self): - """Test lookup_value with valid string value""" - result = SampleBlock.lookup_value("block_a") - assert result == SampleBlock.BLOCK_A - assert result.name == "BLOCK_A" - assert result.value == "block_a" - - def test_lookup_value_valid_integer(self): - """Test lookup_value with valid integer value""" - result = SampleBlock.lookup_value(5) - assert result == SampleBlock.HAS_NUM - assert result.name == "HAS_NUM" - assert result.value == 5 - - def test_lookup_value_valid_float(self): - """Test lookup_value with valid float value""" - result = SampleBlock.lookup_value(3.14) - assert result == SampleBlock.HAS_FLOAT - assert result.name == "HAS_FLOAT" - assert result.value == 3.14 - - def test_lookup_value_invalid_string(self): - """Test lookup_value with invalid string value""" - with pytest.raises(ValueError, match="Invalid value: nonexistent"): - SampleBlock.lookup_value("nonexistent") - - def test_lookup_value_invalid_integer(self): - """Test lookup_value with invalid integer value""" - with pytest.raises(ValueError, match="Invalid value: 999"): - SampleBlock.lookup_value(999) - - def test_lookup_value_case_sensitive(self): - """Test that lookup_value is case-sensitive for string values""" - with pytest.raises(ValueError, match="Invalid value"): - SampleBlock.lookup_value("BLOCK_A") # Value is "block_a", not "BLOCK_A" - - -class TestEnumBaseFromAny: - """Test cases for from_any class method""" - - def test_from_any_with_enum_instance(self): - """Test from_any with an enum instance (should return as-is)""" - enum_instance = SampleBlock.BLOCK_A - result = SampleBlock.from_any(enum_instance) - assert result is enum_instance - assert result == SampleBlock.BLOCK_A - - def test_from_any_with_string_as_key(self): - """Test from_any with string that matches a key""" - result = SampleBlock.from_any("BLOCK_A") - assert result == SampleBlock.BLOCK_A - assert result.name == "BLOCK_A" - assert result.value == "block_a" - - def test_from_any_with_string_as_key_lowercase(self): - """Test from_any with lowercase string key""" - result = SampleBlock.from_any("block_a") - # Should first try as key (convert to uppercase and find BLOCK_A) - assert result == SampleBlock.BLOCK_A - - def test_from_any_with_string_as_value(self): - """Test from_any with string that only matches a value""" - # Use a value that isn't also a valid key - result = SampleBlock.from_any("block_b") - # Should try key first (fail), then value (succeed) - assert result == SampleBlock.BLOCK_B - assert result.value == "block_b" - - def test_from_any_with_integer(self): - """Test from_any with integer value""" - result = SampleBlock.from_any(5) - assert result == SampleBlock.HAS_NUM - assert result.value == 5 - - def test_from_any_with_float(self): - """Test from_any with float value""" - result = SampleBlock.from_any(3.14) - assert result == SampleBlock.HAS_FLOAT - assert result.value == 3.14 - - def test_from_any_with_invalid_string(self): - """Test from_any with string that doesn't match key or value""" - with pytest.raises(ValueError, match="Could not find as key or value: invalid_string"): - SampleBlock.from_any("invalid_string") - - def test_from_any_with_invalid_integer(self): - """Test from_any with integer that doesn't match any value""" - with pytest.raises(ValueError, match="Invalid value: 999"): - SampleBlock.from_any(999) - - def test_from_any_string_key_priority(self): - """Test that from_any tries key lookup before value for strings""" - # Create an enum where a value matches another key - class AmbiguousEnum(EnumBase): - KEY_A = "key_b" # Value is the name of another key - KEY_B = "value_b" - - # When we look up "KEY_B", it should find it as a key, not as value "key_b" - result = AmbiguousEnum.from_any("KEY_B") - assert result == AmbiguousEnum.KEY_B - assert result.value == "value_b" - - -class TestEnumBaseToValue: - """Test cases for to_value instance method""" - - def test_to_value_string_value(self): - """Test to_value with string enum value""" - result = SampleBlock.BLOCK_A.to_value() - assert result == "block_a" - assert isinstance(result, str) - - def test_to_value_integer_value(self): - """Test to_value with integer enum value""" - result = SampleBlock.HAS_NUM.to_value() - assert result == 5 - assert isinstance(result, int) - - def test_to_value_float_value(self): - """Test to_value with float enum value""" - result = SampleBlock.HAS_FLOAT.to_value() - assert result == 3.14 - assert isinstance(result, float) - - def test_to_value_equals_value_attribute(self): - """Test that to_value returns the same as .value""" - enum_instance = SampleBlock.BLOCK_A - assert enum_instance.to_value() == enum_instance.value - - -class TestEnumBaseToLowerCase: - """Test cases for to_lower_case instance method""" - - def test_to_lower_case_uppercase_name(self): - """Test to_lower_case with uppercase enum name""" - result = SampleBlock.BLOCK_A.to_lower_case() - assert result == "block_a" - assert isinstance(result, str) - - def test_to_lower_case_mixed_name(self): - """Test to_lower_case with name containing underscores""" - result = SampleBlock.HAS_NUM.to_lower_case() - assert result == "has_num" - - def test_to_lower_case_consistency(self): - """Test that to_lower_case always returns lowercase""" - for member in SampleBlock: - result = member.to_lower_case() - assert result == result.lower() - assert result == member.name.lower() - - -class TestEnumBaseStrMethod: - """Test cases for __str__ magic method""" - - def test_str_returns_name(self): - """Test that str() returns the enum name""" - result = str(SampleBlock.BLOCK_A) - assert result == "BLOCK_A" - assert result == SampleBlock.BLOCK_A.name - - def test_str_all_members(self): - """Test str() for all enum members""" - for member in SampleBlock: - result = str(member) - assert result == member.name - assert isinstance(result, str) - - def test_str_in_formatting(self): - """Test that str works in string formatting""" - formatted = f"Enum: {SampleBlock.BLOCK_A}" - assert formatted == "Enum: BLOCK_A" - - def test_str_vs_repr(self): - """Test difference between str and repr""" - enum_instance = SampleBlock.BLOCK_A - str_result = str(enum_instance) - repr_result = repr(enum_instance) - - assert str_result == "BLOCK_A" - # repr should include class name - assert "SampleBlock" in repr_result - - -# Parametrized tests for comprehensive coverage -class TestParametrized: - """Parametrized tests for better coverage""" - - @pytest.mark.parametrize("key,expected_member", [ - ("BLOCK_A", SampleBlock.BLOCK_A), - ("block_a", SampleBlock.BLOCK_A), - ("BLOCK_B", SampleBlock.BLOCK_B), - ("HAS_NUM", SampleBlock.HAS_NUM), - ("has_num", SampleBlock.HAS_NUM), - ("HAS_FLOAT", SampleBlock.HAS_FLOAT), - ]) - def test_lookup_key_parametrized(self, key: str, expected_member: EnumBase): - """Test lookup_key with various valid keys""" - result = SampleBlock.lookup_key(key) - assert result == expected_member - - @pytest.mark.parametrize("value,expected_member", [ - ("block_a", SampleBlock.BLOCK_A), - ("block_b", SampleBlock.BLOCK_B), - (5, SampleBlock.HAS_NUM), - (3.14, SampleBlock.HAS_FLOAT), - ("legacy_value", SampleBlock.LEGACY_KEY), - ]) - def test_lookup_value_parametrized(self, value: Any, expected_member: EnumBase): - """Test lookup_value with various valid values""" - result = SampleBlock.lookup_value(value) - assert result == expected_member - - @pytest.mark.parametrize("input_any,expected_member", [ - ("BLOCK_A", SampleBlock.BLOCK_A), - ("block_a", SampleBlock.BLOCK_A), - ("block_b", SampleBlock.BLOCK_B), - (5, SampleBlock.HAS_NUM), - (3.14, SampleBlock.HAS_FLOAT), - (SampleBlock.BLOCK_A, SampleBlock.BLOCK_A), # Pass enum instance - ]) - def test_from_any_parametrized(self, input_any: Any, expected_member: EnumBase): - """Test from_any with various valid inputs""" - result = SampleBlock.from_any(input_any) - assert result == expected_member - - @pytest.mark.parametrize("invalid_key", [ - "NONEXISTENT", - "invalid", - "123", - "", - "BLOCK_C", - ]) - def test_lookup_key_invalid_parametrized(self, invalid_key: str): - """Test lookup_key with various invalid keys""" - with pytest.raises(ValueError, match="Invalid key"): - SampleBlock.lookup_key(invalid_key) - - @pytest.mark.parametrize("invalid_value", [ - "nonexistent", - 999, - -1, - 0.0, - "BLOCK_A", # This is a key name, not a value - ]) - def test_lookup_value_invalid_parametrized(self, invalid_value: Any): - """Test lookup_value with various invalid values""" - with pytest.raises(ValueError, match="Invalid value"): - SampleBlock.lookup_value(invalid_value) - - -# Edge cases and special scenarios -class TestEdgeCases: - """Test edge cases and special scenarios""" - - def test_enum_with_single_member(self): - """Test EnumBase with only one member""" - class SingleEnum(EnumBase): - ONLY_ONE = "single" - - result = SingleEnum.from_any("ONLY_ONE") - assert result == SingleEnum.ONLY_ONE - assert result.to_value() == "single" - - def test_enum_iteration(self): - """Test iterating over enum members""" - members = list(SampleBlock) - assert len(members) == 5 - assert SampleBlock.BLOCK_A in members - assert SampleBlock.BLOCK_B in members - assert SampleBlock.HAS_NUM in members - - def test_enum_membership(self): - """Test checking membership in enum""" - assert SampleBlock.BLOCK_A in SampleBlock - assert SampleBlock.HAS_NUM in SampleBlock - - def test_enum_comparison(self): - """Test comparing enum members""" - assert SampleBlock.BLOCK_A == SampleBlock.BLOCK_A - assert SampleBlock.BLOCK_A != SampleBlock.BLOCK_B - assert SampleBlock.from_any("BLOCK_A") == SampleBlock.BLOCK_A - - def test_enum_identity(self): - """Test enum member identity""" - member1 = SampleBlock.BLOCK_A - member2 = SampleBlock.lookup_key("BLOCK_A") - member3 = SampleBlock.from_any("BLOCK_A") - - assert member1 is member2 - assert member1 is member3 - assert member2 is member3 - - def test_different_enum_classes(self): - """Test that different enum classes are distinct""" - # Even if they have same keys/values, they're different - class OtherEnum(EnumBase): - BLOCK_A = "block_a" - - result1 = SampleBlock.from_any("BLOCK_A") - result2 = OtherEnum.from_any("BLOCK_A") - - assert result1 != result2 - assert not isinstance(result1, type(result2)) - - def test_numeric_enum_operations(self): - """Test operations specific to numeric enums""" - assert NumericEnum.FIRST.to_value() == 1 - assert NumericEnum.SECOND.to_value() == 2 - assert NumericEnum.THIRD.to_value() == 3 - - # Test from_any with integers - assert NumericEnum.from_any(1) == NumericEnum.FIRST - assert NumericEnum.from_any(2) == NumericEnum.SECOND - - def test_mixed_value_types_in_same_enum(self): - """Test enum with mixed value types""" - # SampleBlock already has mixed types (strings, int, float) - assert isinstance(SampleBlock.BLOCK_A.to_value(), str) - assert isinstance(SampleBlock.HAS_NUM.to_value(), int) - assert isinstance(SampleBlock.HAS_FLOAT.to_value(), float) - - def test_from_any_chained_calls(self): - """Test that from_any can be chained (idempotent)""" - result1 = SampleBlock.from_any("BLOCK_A") - result2 = SampleBlock.from_any(result1) - result3 = SampleBlock.from_any(result2) - - assert result1 == result2 == result3 - assert result1 is result2 is result3 - - -# Integration tests -class TestIntegration: - """Integration tests combining multiple methods""" - - def test_round_trip_key_lookup(self): - """Test round-trip from key to enum and back""" - original_key = "BLOCK_A" - enum_member = SampleBlock.lookup_key(original_key) - result_name = str(enum_member) - - assert result_name == original_key - - def test_round_trip_value_lookup(self): - """Test round-trip from value to enum and back""" - original_value = "block_a" - enum_member = SampleBlock.lookup_value(original_value) - result_value = enum_member.to_value() - - assert result_value == original_value - - def test_from_any_workflow(self): - """Test realistic workflow using from_any""" - # Simulate receiving various types of input - inputs = [ - "BLOCK_A", # Key as string - "block_b", # Value as string - 5, # Numeric value - SampleBlock.HAS_FLOAT, # Already an enum - ] - - expected = [ - SampleBlock.BLOCK_A, - SampleBlock.BLOCK_B, - SampleBlock.HAS_NUM, - SampleBlock.HAS_FLOAT, - ] - - for input_val, expected_val in zip(inputs, expected): - result = SampleBlock.from_any(input_val) - assert result == expected_val - - def test_enum_in_dictionary(self): - """Test using enum as dictionary key""" - enum_dict = { - SampleBlock.BLOCK_A: "Value A", - SampleBlock.BLOCK_B: "Value B", - SampleBlock.HAS_NUM: "Value Num", - } - - assert enum_dict[SampleBlock.BLOCK_A] == "Value A" - block_b = SampleBlock.from_any("BLOCK_B") - assert isinstance(block_b, SampleBlock) - assert enum_dict[block_b] == "Value B" - - def test_enum_in_set(self): - """Test using enum in a set""" - enum_set = {SampleBlock.BLOCK_A, SampleBlock.BLOCK_B, SampleBlock.BLOCK_A} - - assert len(enum_set) == 2 # BLOCK_A should be deduplicated - assert SampleBlock.BLOCK_A in enum_set - assert SampleBlock.from_any("BLOCK_B") in enum_set - - -# Real-world usage scenarios -class TestRealWorldScenarios: - """Test real-world usage scenarios from enum_test.py""" - - def test_original_enum_test_scenario(self): - """Test the scenario from the original enum_test.py""" - # BLOCK A: {SampleBlock.from_any('BLOCK_A')} - result_a = SampleBlock.from_any('BLOCK_A') - assert result_a == SampleBlock.BLOCK_A - assert str(result_a) == "BLOCK_A" - - # HAS NUM: {SampleBlock.from_any(5)} - result_num = SampleBlock.from_any(5) - assert result_num == SampleBlock.HAS_NUM - assert result_num.to_value() == 5 - - # DIRECT BLOCK: {SampleBlock.BLOCK_A.name} -> {SampleBlock.BLOCK_A.value} - assert SampleBlock.BLOCK_A.name == "BLOCK_A" - assert SampleBlock.BLOCK_A.value == "block_a" - - def test_config_value_parsing(self): - """Test parsing values from configuration (common use case)""" - # Simulate config values that might come as strings - config_values = ["OPTION_ONE", "option_two", "OPTION_THREE"] - - results = [SimpleEnum.from_any(val) for val in config_values] - - assert results[0] == SimpleEnum.OPTION_ONE - assert results[1] == SimpleEnum.OPTION_TWO - assert results[2] == SimpleEnum.OPTION_THREE - - def test_api_response_mapping(self): - """Test mapping API response values to enum""" - # Simulate API returning numeric codes - api_codes = [1, 2, 3] - - results = [NumericEnum.from_any(code) for code in api_codes] - - assert results[0] == NumericEnum.FIRST - assert results[1] == NumericEnum.SECOND - assert results[2] == NumericEnum.THIRD - - def test_validation_with_error_handling(self): - """Test validation with proper error handling""" - valid_input = "BLOCK_A" - invalid_input = "INVALID" - - # Valid input should work - result = SampleBlock.from_any(valid_input) - assert result == SampleBlock.BLOCK_A - - # Invalid input should raise ValueError - try: - SampleBlock.from_any(invalid_input) - assert False, "Should have raised ValueError" - except ValueError as e: - assert "Could not find as key or value" in str(e) - assert "INVALID" in str(e) diff --git a/tests/unit/var_handling/test_var_helpers.py b/tests/unit/var_handling/test_var_helpers.py deleted file mode 100644 index 588e6ef..0000000 --- a/tests/unit/var_handling/test_var_helpers.py +++ /dev/null @@ -1,241 +0,0 @@ -""" -var helpers -""" - -# ADDED 2025/7/11 Replace 'your_module' with actual module name - -from typing import Any -import pytest -from corelibs.var_handling.var_helpers import is_int, is_float, str_to_bool - - -class TestIsInt: - """Test cases for is_int function""" - - def test_valid_integers(self): - """Test with valid integer strings""" - assert is_int("123") is True - assert is_int("0") is True - assert is_int("-456") is True - assert is_int("+789") is True - assert is_int("000") is True - - def test_invalid_integers(self): - """Test with invalid integer strings""" - assert is_int("12.34") is False - assert is_int("abc") is False - assert is_int("12a") is False - assert is_int("") is False - assert is_int(" ") is False - assert is_int("12.0") is False - assert is_int("1e5") is False - - def test_numeric_types(self): - """Test with actual numeric types""" - assert is_int(123) is True - assert is_int(0) is True - assert is_int(-456) is True - assert is_int(12.34) is True # float can be converted to int - assert is_int(12.0) is True - - def test_other_types(self): - """Test with other data types""" - assert is_int(None) is False - assert is_int([]) is False - assert is_int({}) is False - assert is_int(True) is True # bool is subclass of int - assert is_int(False) is True - - -class TestIsFloat: - """Test cases for is_float function""" - - def test_valid_floats(self): - """Test with valid float strings""" - assert is_float("12.34") is True - assert is_float("0.0") is True - assert is_float("-45.67") is True - assert is_float("+78.9") is True - assert is_float("123") is True # integers are valid floats - assert is_float("0") is True - assert is_float("1e5") is True - assert is_float("1.5e-10") is True - assert is_float("inf") is True - assert is_float("-inf") is True - assert is_float("nan") is True - - def test_invalid_floats(self): - """Test with invalid float strings""" - assert is_float("abc") is False - assert is_float("12.34.56") is False - assert is_float("12a") is False - assert is_float("") is False - assert is_float(" ") is False - assert is_float("12..34") is False - - def test_numeric_types(self): - """Test with actual numeric types""" - assert is_float(123) is True - assert is_float(12.34) is True - assert is_float(0) is True - assert is_float(-45.67) is True - - def test_other_types(self): - """Test with other data types""" - assert is_float(None) is False - assert is_float([]) is False - assert is_float({}) is False - assert is_float(True) is True # bool can be converted to float - assert is_float(False) is True - - -class TestStrToBool: - """Test cases for str_to_bool function""" - - def test_valid_true_strings(self): - """Test with valid true strings""" - assert str_to_bool("True") is True - assert str_to_bool("true") is True - - def test_valid_false_strings(self): - """Test with valid false strings""" - assert str_to_bool("False") is False - assert str_to_bool("false") is False - - def test_invalid_strings(self): - """Test with invalid boolean strings""" - with pytest.raises(ValueError, match="Invalid boolean string"): - str_to_bool("TRUE") - - with pytest.raises(ValueError, match="Invalid boolean string"): - str_to_bool("FALSE") - - with pytest.raises(ValueError, match="Invalid boolean string"): - str_to_bool("yes") - - with pytest.raises(ValueError, match="Invalid boolean string"): - str_to_bool("no") - - with pytest.raises(ValueError, match="Invalid boolean string"): - str_to_bool("1") - - with pytest.raises(ValueError, match="Invalid boolean string"): - str_to_bool("0") - - with pytest.raises(ValueError, match="Invalid boolean string"): - str_to_bool("") - - with pytest.raises(ValueError, match="Invalid boolean string"): - str_to_bool(" True") - - with pytest.raises(ValueError, match="Invalid boolean string"): - str_to_bool("True ") - - def test_error_message_content(self): - """Test that error messages contain the invalid input""" - with pytest.raises(ValueError) as exc_info: - str_to_bool("invalid") - assert "Invalid boolean string: invalid" in str(exc_info.value) - - def test_case_sensitivity(self): - """Test that function is case sensitive""" - with pytest.raises(ValueError): - str_to_bool("TRUE") - - with pytest.raises(ValueError): - str_to_bool("True ") # with space - - with pytest.raises(ValueError): - str_to_bool(" True") # with space - - -# Additional edge case tests -class TestEdgeCases: - """Test edge cases and special scenarios""" - - def test_is_int_with_whitespace(self): - """Test is_int with whitespace (should work due to int() behavior)""" - assert is_int(" 123 ") is True - assert is_int("\t456\n") is True - - def test_is_float_with_whitespace(self): - """Test is_float with whitespace (should work due to float() behavior)""" - assert is_float(" 12.34 ") is True - assert is_float("\t45.67\n") is True - - def test_large_numbers(self): - """Test with very large numbers""" - large_int = "123456789012345678901234567890" - assert is_int(large_int) is True - assert is_float(large_int) is True - - def test_scientific_notation(self): - """Test scientific notation""" - assert is_int("1e5") is False # int() doesn't handle scientific notation - assert is_float("1e5") is True - assert is_float("1.5e-10") is True - assert is_float("2E+3") is True - - -# Parametrized tests for more comprehensive coverage -class TestParametrized: - """Parametrized tests for better coverage""" - - @pytest.mark.parametrize("value,expected", [ - ("123", True), - ("0", True), - ("-456", True), - ("12.34", False), - ("abc", False), - ("", False), - (123, True), - (12.5, True), - (None, False), - ]) - def test_is_int_parametrized(self, value: Any, expected: bool): - """Test""" - assert is_int(value) == expected - - @pytest.mark.parametrize("value,expected", [ - ("12.34", True), - ("123", True), - ("0", True), - ("-45.67", True), - ("inf", True), - ("nan", True), - ("abc", False), - ("", False), - (12.34, True), - (123, True), - (None, False), - ]) - def test_is_float_parametrized(self, value: Any, expected: bool): - """test""" - assert is_float(value) == expected - - @pytest.mark.parametrize("value,expected", [ - ("True", True), - ("true", True), - ("False", False), - ("false", False), - ]) - def test_str_to_bool_valid_parametrized(self, value: Any, expected: bool): - """test""" - assert str_to_bool(value) == expected - - @pytest.mark.parametrize("invalid_value", [ - "TRUE", - "FALSE", - "yes", - "no", - "1", - "0", - "", - " True", - "True ", - "invalid", - ]) - def test_str_to_bool_invalid_parametrized(self, invalid_value: Any): - """test""" - with pytest.raises(ValueError): - str_to_bool(invalid_value)