Add datetime helpers and move all time/date time datetime_handling folder

previous string_handling located datetime and timestamp files have been moved
to the datetime handling folder

Update readme file with more information about currently covered areas
This commit is contained in:
Clemens Schwaighofer
2025-10-24 10:03:04 +09:00
parent 53cf2a6f48
commit a76eae0cc7
19 changed files with 1951 additions and 401 deletions

View File

@@ -1,205 +0,0 @@
"""
Unit tests for convert_to_seconds function from timestamp_strings module.
"""
import pytest
from corelibs.string_handling.timestamp_strings 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'

View File

@@ -1,186 +0,0 @@
"""
PyTest: string_handling/timestamp_strings - seconds_to_string function
"""
from corelibs.string_handling.timestamp_strings import seconds_to_string
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"
# __END__

View File

@@ -1,194 +0,0 @@
"""
PyTest: string_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.string_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.string_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.string_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.string_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.string_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.string_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.string_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.string_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.string_handling.timestamp_strings.ZoneInfo') as mock_zoneinfo:
with patch('corelibs.string_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.string_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.string_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.string_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.string_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__

View File

@@ -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)