Files
duty-teller/tests/test_utils.py
Nikolay Tatarinov d02d0a1835
All checks were successful
CI / lint-and-test (push) Successful in 21s
refactor: improve language normalization and date handling utilities
- Introduced a new `normalize_lang` function to standardize language codes across the application, ensuring consistent handling of user language preferences.
- Refactored date handling utilities by adding `parse_utc_iso` and `parse_utc_iso_naive` functions for better parsing of ISO 8601 date strings, enhancing timezone awareness.
- Updated various modules to utilize the new language normalization and date parsing functions, improving code clarity and maintainability.
- Enhanced error handling in date validation to raise specific `DateRangeValidationError` exceptions, providing clearer feedback on validation issues.
- Improved test coverage for date range validation and language normalization functionalities, ensuring robustness and reliability.
2026-02-20 22:42:54 +03:00

109 lines
2.9 KiB
Python

"""Unit tests for utils (dates, user, handover)."""
from datetime import date
import pytest
from duty_teller.utils.dates import (
DateRangeValidationError,
day_start_iso,
day_end_iso,
duty_to_iso,
parse_iso_date,
validate_date_range,
)
from duty_teller.utils.user import build_full_name
from duty_teller.utils.handover import parse_handover_time
# --- dates ---
def test_day_start_iso():
assert day_start_iso(date(2026, 2, 18)) == "2026-02-18T00:00:00Z"
def test_day_end_iso():
assert day_end_iso(date(2026, 2, 18)) == "2026-02-18T23:59:59Z"
def test_duty_to_iso():
assert duty_to_iso(date(2026, 2, 18), 6, 0) == "2026-02-18T06:00:00Z"
def test_parse_iso_date_valid():
assert parse_iso_date("2026-02-18") == date(2026, 2, 18)
assert parse_iso_date(" 2026-02-18 ") == date(2026, 2, 18)
def test_parse_iso_date_invalid():
assert parse_iso_date("") is None
assert parse_iso_date("2026-02-31") is None # invalid day
assert parse_iso_date("18-02-2026") is None
assert parse_iso_date("not-a-date") is None
def test_validate_date_range_ok():
validate_date_range("2025-01-01", "2025-01-31") # no raise
def test_validate_date_range_bad_format():
with pytest.raises(DateRangeValidationError) as exc_info:
validate_date_range("01-01-2025", "2025-01-31")
assert exc_info.value.kind == "bad_format"
with pytest.raises(DateRangeValidationError) as exc_info:
validate_date_range("2025-01-01", "invalid")
assert exc_info.value.kind == "bad_format"
def test_validate_date_range_from_after_to():
with pytest.raises(DateRangeValidationError) as exc_info:
validate_date_range("2025-02-01", "2025-01-01")
assert exc_info.value.kind == "from_after_to"
# --- user ---
def test_build_full_name_both():
assert build_full_name("John", "Doe") == "John Doe"
def test_build_full_name_first_only():
assert build_full_name("John", None) == "John"
def test_build_full_name_last_only():
assert build_full_name(None, "Doe") == "Doe"
def test_build_full_name_empty():
assert build_full_name("", "") == "User"
assert build_full_name(None, None) == "User"
# --- handover ---
def test_parse_handover_utc():
assert parse_handover_time("09:00") == (9, 0)
assert parse_handover_time("09:00 UTC") == (9, 0)
assert parse_handover_time(" 06:30 ") == (6, 30)
def test_parse_handover_with_tz():
# Europe/Moscow UTC+3 in winter: 09:00 Moscow = 06:00 UTC
assert parse_handover_time("09:00 Europe/Moscow") == (6, 0)
def test_parse_handover_invalid():
assert parse_handover_time("") is None
assert parse_handover_time("not a time") is None
# 25:00 is normalized to 1:00 by hour % 24; use non-matching string
assert parse_handover_time("12") is None
def test_parse_handover_invalid_timezone_returns_none():
"""Invalid IANA timezone string -> ZoneInfo raises, returns None."""
assert parse_handover_time("09:00 NotAReal/Timezone") is None