|
| 1 | +""" |
| 2 | +Unit tests for edge cases in parse_to_utc in backend/secuscan/time_utils.py. |
| 3 | +
|
| 4 | +These complement the happy-path tests in test_time_utils.py which cover |
| 5 | +common ISO 8601 formats. This file focuses on malformed inputs, None, |
| 6 | +non-string types, and other edge cases. |
| 7 | +""" |
| 8 | + |
| 9 | +from __future__ import annotations |
| 10 | + |
| 11 | +from datetime import datetime, timezone |
| 12 | + |
| 13 | +import pytest |
| 14 | + |
| 15 | +from backend.secuscan.time_utils import parse_to_utc |
| 16 | + |
| 17 | + |
| 18 | +class TestParseToUtcEdgeCases: |
| 19 | + """Edge case tests for parse_to_utc.""" |
| 20 | + |
| 21 | + def test_none_returns_none(self): |
| 22 | + """parse_to_utc(None) returns None without raising.""" |
| 23 | + result = parse_to_utc(None) |
| 24 | + assert result is None |
| 25 | + |
| 26 | + def test_empty_string_returns_none(self): |
| 27 | + """parse_to_utc('') returns None without raising.""" |
| 28 | + result = parse_to_utc("") |
| 29 | + assert result is None |
| 30 | + |
| 31 | + def test_whitespace_only_string_returns_none(self): |
| 32 | + """parse_to_utc(' ') (whitespace only) returns None.""" |
| 33 | + result = parse_to_utc(" ") |
| 34 | + assert result is None |
| 35 | + |
| 36 | + def test_garbage_string_returns_none(self): |
| 37 | + """parse_to_utc('not-a-date') returns None without raising.""" |
| 38 | + result = parse_to_utc("not-a-date") |
| 39 | + assert result is None |
| 40 | + |
| 41 | + def test_random_text_returns_none(self): |
| 42 | + """parse_to_utc('hello world') returns None.""" |
| 43 | + result = parse_to_utc("hello world") |
| 44 | + assert result is None |
| 45 | + |
| 46 | + def test_integer_timestamp_returns_utc(self): |
| 47 | + """parse_to_utc(1762108800) treats the integer as a Unix timestamp.""" |
| 48 | + result = parse_to_utc(1762108800) |
| 49 | + assert result is not None |
| 50 | + assert result.tzinfo is not None |
| 51 | + # 1762108800 is a valid Unix timestamp for 2025-11-02 UTC |
| 52 | + assert result.year == 2025 |
| 53 | + |
| 54 | + def test_float_timestamp_returns_utc(self): |
| 55 | + """parse_to_utc(1762108800.5) treats the float as a Unix timestamp.""" |
| 56 | + result = parse_to_utc(1762108800.5) |
| 57 | + assert result is not None |
| 58 | + assert result.tzinfo is not None |
| 59 | + |
| 60 | + def test_now_string_returns_current_time(self): |
| 61 | + """parse_to_utc('now') returns the current UTC time.""" |
| 62 | + before = datetime.now(timezone.utc) |
| 63 | + result = parse_to_utc("now") |
| 64 | + after = datetime.now(timezone.utc) |
| 65 | + assert result is not None |
| 66 | + assert result.tzinfo is not None |
| 67 | + # Result should be between before and after (within a few seconds) |
| 68 | + assert before <= result <= after or after <= result <= before |
| 69 | + |
| 70 | + def test_list_input_returns_none(self): |
| 71 | + """parse_to_utc([1, 2, 3]) converts to string '[1, 2, 3]' which is not a valid date.""" |
| 72 | + result = parse_to_utc([1, 2, 3]) |
| 73 | + assert result is None |
| 74 | + |
| 75 | + def test_boolean_true_returns_utc_epoch_plus_one_second(self): |
| 76 | + """parse_to_utc(True) treats True as int(1), i.e. Unix epoch + 1 second.""" |
| 77 | + result = parse_to_utc(True) |
| 78 | + assert result is not None |
| 79 | + assert result.tzinfo is not None |
| 80 | + assert result.year == 1970 |
| 81 | + assert result.month == 1 |
| 82 | + assert result.day == 1 |
| 83 | + assert result.hour == 0 |
| 84 | + assert result.minute == 0 |
| 85 | + assert result.second == 1 |
| 86 | + |
| 87 | + def test_boolean_false_returns_utc_epoch(self): |
| 88 | + """parse_to_utc(False) treats False as int(0), i.e. Unix epoch (1970-01-01 00:00:00 UTC).""" |
| 89 | + result = parse_to_utc(False) |
| 90 | + assert result is not None |
| 91 | + assert result.tzinfo is not None |
| 92 | + assert result.year == 1970 |
| 93 | + assert result.month == 1 |
| 94 | + assert result.day == 1 |
| 95 | + assert result.hour == 0 |
| 96 | + assert result.minute == 0 |
| 97 | + assert result.second == 0 |
0 commit comments