Fix: Consolidate divergent _since_unix implementations (T-09)

Consolidate the two divergent implementations of _since_unix from ban_service.py
and history_service.py into a single shared utility function in time_utils.py.

Changes:
- Move _since_unix to app/utils/time_utils.py with consistent time.time() approach
- Move TIME_RANGE_SLACK_SECONDS constant to app/utils/constants.py
- Update ban_service.py to import since_unix from time_utils
- Update history_service.py to import since_unix from time_utils
- Both services now use the same window boundary calculation with 60-second slack
- Add comprehensive tests for the shared since_unix function
- Document timestamp handling rationale in Backend-Development.md

This ensures dashboard and history queries return consistent row counts for the
same time range by using the same timestamp calculation and slack window across
all services.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-04-25 18:44:59 +02:00
parent 420ea18fd9
commit ac2028e1c2
6 changed files with 132 additions and 51 deletions

View File

@@ -1,8 +1,17 @@
"""Tests for app.utils.time_utils."""
import datetime
import time
from app.utils.time_utils import add_minutes, hours_ago, is_expired, utc_from_timestamp, utc_now
from app.utils.time_utils import (
add_minutes,
hours_ago,
is_expired,
since_unix,
utc_from_timestamp,
utc_now,
)
from app.utils.constants import TIME_RANGE_SLACK_SECONDS
class TestUtcNow:
@@ -77,3 +86,51 @@ class TestHoursAgo:
expected_min = before - datetime.timedelta(hours=1, seconds=1)
expected_max = after - datetime.timedelta(hours=1) + datetime.timedelta(seconds=1)
assert expected_min <= result <= expected_max
class TestSinceUnix:
"""Tests for :func:`since_unix`."""
def test_since_unix_24h_returns_unix_timestamp(self) -> None:
"""Verify since_unix returns an integer timestamp."""
result = since_unix("24h")
assert isinstance(result, int)
def test_since_unix_24h_is_roughly_24_hours_ago(self) -> None:
"""Verify 24h preset returns a timestamp ~24 hours in the past."""
before = int(time.time())
result = since_unix("24h")
after = int(time.time())
# Allow 1 second tolerance for execution time
expected_min = before - (24 * 3600) - TIME_RANGE_SLACK_SECONDS - 1
expected_max = after - (24 * 3600) - TIME_RANGE_SLACK_SECONDS + 1
assert expected_min <= result <= expected_max
def test_since_unix_7d_is_roughly_7_days_ago(self) -> None:
"""Verify 7d preset returns a timestamp ~7 days in the past."""
before = int(time.time())
result = since_unix("7d")
after = int(time.time())
# Allow 1 second tolerance for execution time
expected_min = before - (7 * 24 * 3600) - TIME_RANGE_SLACK_SECONDS - 1
expected_max = after - (7 * 24 * 3600) - TIME_RANGE_SLACK_SECONDS + 1
assert expected_min <= result <= expected_max
def test_since_unix_includes_slack_window(self) -> None:
"""Verify 60-second slack is included in all presets."""
now = int(time.time())
result = since_unix("24h")
# Verify slack is included: result should be (now - 24h - 60s)
# within tolerance
diff_without_slack = now - result
expected_without_slack = 24 * 3600
actual_slack = diff_without_slack - expected_without_slack
# The slack should be ~60 seconds
assert actual_slack >= TIME_RANGE_SLACK_SECONDS - 1
assert actual_slack <= TIME_RANGE_SLACK_SECONDS + 1