left4me/l4d2web/tests/test_timeago.py
mwiegand 263a9a9f27
feat(app): register timeago Jinja filter
Templates can now call {{ ts | timeago }} directly without route-side
precomputation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 11:10:59 +02:00

133 lines
4.5 KiB
Python

from datetime import UTC, datetime, timedelta
import pytest
from markupsafe import Markup
from l4d2web.services.timeago import format_time_html, humanize_delta
NOW = datetime(2026, 5, 16, 12, 0, 0, tzinfo=UTC)
@pytest.mark.parametrize(
("delta", "expected"),
[
(timedelta(0), "now"),
(timedelta(seconds=1), "1 second ago"),
(timedelta(seconds=2), "2 seconds ago"),
(timedelta(seconds=59), "59 seconds ago"),
(timedelta(seconds=60), "1 minute ago"),
(timedelta(minutes=1), "1 minute ago"),
(timedelta(minutes=2), "2 minutes ago"),
(timedelta(minutes=59), "59 minutes ago"),
(timedelta(minutes=60), "1 hour ago"),
(timedelta(hours=1), "1 hour ago"),
(timedelta(hours=2), "2 hours ago"),
(timedelta(hours=23), "23 hours ago"),
(timedelta(hours=24), "1 day ago"),
(timedelta(days=1), "1 day ago"),
(timedelta(days=2), "2 days ago"),
(timedelta(days=6), "6 days ago"),
(timedelta(days=7), "9 May"),
(timedelta(days=30), "16 Apr"),
(timedelta(days=120), "16 Jan"),
(timedelta(days=365), "16 May 2025"),
(timedelta(days=400), "11 Apr 2025"),
],
)
def test_humanize_delta_past(delta, expected):
then = NOW - delta
assert humanize_delta(then, now=NOW) == expected
@pytest.mark.parametrize(
("delta", "expected"),
[
(timedelta(seconds=1), "in 1 second"),
(timedelta(seconds=2), "in 2 seconds"),
(timedelta(seconds=59), "in 59 seconds"),
(timedelta(seconds=60), "in 1 minute"),
(timedelta(minutes=2), "in 2 minutes"),
(timedelta(minutes=59), "in 59 minutes"),
(timedelta(hours=1), "in 1 hour"),
(timedelta(hours=23), "in 23 hours"),
(timedelta(days=1), "in 1 day"),
(timedelta(days=6), "in 6 days"),
(timedelta(days=7), "23 May"),
(timedelta(days=30), "15 Jun"),
(timedelta(days=365), "16 May 2027"),
],
)
def test_humanize_delta_future(delta, expected):
then = NOW + delta
assert humanize_delta(then, now=NOW) == expected
def test_humanize_delta_accepts_naive_input_as_utc():
then_naive = (NOW - timedelta(minutes=5)).replace(tzinfo=None)
assert humanize_delta(then_naive, now=NOW) == "5 minutes ago"
def test_humanize_delta_accepts_naive_now_as_utc():
then = NOW - timedelta(minutes=5)
now_naive = NOW.replace(tzinfo=None)
assert humanize_delta(then, now=now_naive) == "5 minutes ago"
def test_humanize_delta_default_now_is_datetime_now_utc():
then = datetime.now(UTC) - timedelta(seconds=3)
assert humanize_delta(then) in {"3 seconds ago", "2 seconds ago", "4 seconds ago"}
def test_humanize_delta_year_boundary_includes_year_when_years_differ():
now = datetime(2026, 1, 15, 12, 0, 0, tzinfo=UTC)
then = datetime(2025, 11, 15, 12, 0, 0, tzinfo=UTC)
assert humanize_delta(then, now=now) == "15 Nov 2025"
def test_format_time_html_returns_markup():
then = NOW - timedelta(minutes=5)
out = format_time_html(then, now=NOW)
assert isinstance(out, Markup)
def test_format_time_html_contains_time_element_with_attrs():
then = datetime(2026, 5, 16, 14, 32, 11, tzinfo=UTC)
now = then + timedelta(minutes=5)
out = str(format_time_html(then, now=now))
assert out.startswith("<time ")
assert out.endswith("</time>")
assert 'datetime="2026-05-16T14:32:11+00:00"' in out
assert 'title="2026-05-16 14:32:11 UTC"' in out
assert ">5 minutes ago<" in out
def test_format_time_html_label_matches_humanize_delta():
then = NOW - timedelta(hours=2)
label = humanize_delta(then, now=NOW)
out = str(format_time_html(then, now=NOW))
assert f">{label}<" in out
def test_format_time_html_normalises_naive_input_to_utc():
then_naive = datetime(2026, 5, 16, 14, 32, 11)
now = datetime(2026, 5, 16, 14, 37, 11, tzinfo=UTC)
out = str(format_time_html(then_naive, now=now))
assert 'datetime="2026-05-16T14:32:11+00:00"' in out
assert 'title="2026-05-16 14:32:11 UTC"' in out
def test_timeago_filter_registered_on_app():
from flask import render_template_string
from l4d2web.app import create_app
app = create_app({"TESTING": True, "SECRET_KEY": "test"})
with app.app_context():
rendered = render_template_string(
"{{ ts | timeago }}",
ts=datetime.now(UTC) - timedelta(minutes=3),
)
assert "<time " in rendered
assert "&lt;time" not in rendered
assert "3 minutes ago" in rendered