- Replace _error_code_for_message() string matching with error_code attribute on exception classes - Add error_code to all TwitterError subclasses (AuthenticationError, RateLimitError, etc.) - Add InvalidInputError exception class - TwitterAPIError derives error_code from HTTP status code automatically - auth.py: use AuthenticationError instead of RuntimeError - cli.py: catch (TwitterError, RuntimeError) for backward compat - Extract _fetch_and_display_users() to deduplicate followers/following commands - Add format_iso8601() to timeutil.py - Add createdAtISO field to tweet and user profile serialization - New test files: test_output.py, test_cache.py, test_timeutil.py - Expand test_filter.py (topN, score mode, custom weights, empty input) - Tests: 152 → 194 unit tests, all passing
74 lines
2.3 KiB
Python
74 lines
2.3 KiB
Python
"""Tests for twitter_cli.timeutil module."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from twitter_cli.timeutil import format_iso8601, format_local_time, format_relative_time
|
|
|
|
|
|
SAMPLE_TIMESTAMP = "Sat Mar 08 12:00:00 +0000 2026"
|
|
|
|
|
|
# ── format_local_time ────────────────────────────────────────────────────
|
|
|
|
|
|
def test_format_local_time_valid() -> None:
|
|
result = format_local_time(SAMPLE_TIMESTAMP)
|
|
# Should be in YYYY-MM-DD HH:MM format (local timezone)
|
|
assert result.startswith("2026-03-")
|
|
assert ":" in result
|
|
|
|
|
|
def test_format_local_time_empty() -> None:
|
|
assert format_local_time("") == ""
|
|
|
|
|
|
def test_format_local_time_invalid() -> None:
|
|
assert format_local_time("not a date") == "not a date"
|
|
|
|
|
|
# ── format_relative_time ─────────────────────────────────────────────────
|
|
|
|
|
|
def test_format_relative_time_old() -> None:
|
|
# A timestamp from 2020 should show years ago
|
|
old_ts = "Sat Jan 01 00:00:00 +0000 2020"
|
|
result = format_relative_time(old_ts)
|
|
assert result.endswith("ago")
|
|
assert "y" in result or "mo" in result or "d" in result
|
|
|
|
|
|
def test_format_relative_time_empty() -> None:
|
|
assert format_relative_time("") == ""
|
|
|
|
|
|
def test_format_relative_time_invalid() -> None:
|
|
assert format_relative_time("garbage") == "garbage"
|
|
|
|
|
|
# ── format_iso8601 ───────────────────────────────────────────────────────
|
|
|
|
|
|
def test_format_iso8601_valid() -> None:
|
|
result = format_iso8601(SAMPLE_TIMESTAMP)
|
|
assert result.startswith("2026-03-08T12:00:00")
|
|
assert "+00:00" in result or "Z" in result
|
|
|
|
|
|
def test_format_iso8601_empty() -> None:
|
|
assert format_iso8601("") == ""
|
|
|
|
|
|
def test_format_iso8601_invalid() -> None:
|
|
assert format_iso8601("not a date") == "not a date"
|
|
|
|
|
|
def test_format_iso8601_roundtrip() -> None:
|
|
"""ISO 8601 output should be parseable by datetime.fromisoformat."""
|
|
from datetime import datetime
|
|
|
|
result = format_iso8601(SAMPLE_TIMESTAMP)
|
|
dt = datetime.fromisoformat(result)
|
|
assert dt.year == 2026
|
|
assert dt.month == 3
|
|
assert dt.day == 8
|