Files
denya-onecare/tests/test_backdated_reported_date.py
T
root e0a7479c3e feat: backdated reported date for old active tickets
Wahab can now enter historical tickets that keep their true reported date:

- Add nullable tickets.reported_at (DateTime) via alembic d5e0f2a1c3b4;
  backfill existing rows from created_at so nothing shows empty.
- TicketCreate.reported_at (optional) is persisted by create_ticket and
  defaults to now when omitted, so existing create behavior is unchanged.
- New-issue form gains a 'Reported Date' date picker (defaults to today,
  past dates allowed, future blocked) and sends reported_at in the payload.
- List and detail pages show 'Reported' next to 'Created' (date-only,
  labeled) so backdated tickets are obvious; SLA deadline is unchanged and
  still runs from creation time so backfilling never instantly breaches.
- Tests: backdated create persists + is exposed on list/detail; omitted
  reported_at defaults to now; SLA window unchanged; full datetime round trip.
2026-08-03 09:40:23 +00:00

119 lines
4.2 KiB
Python

"""Tests for backdated reported-date support (Wahab demo).
Anchors:
* A ticket created with a past ``reported_at`` persists that date and it is
exposed on list + detail responses — this is how Wahab enters old tickets
that stay active in the normal workflow.
* A ticket created without ``reported_at`` defaults to "now", so existing
create behavior is unchanged.
* The reported date is metadata only: SLA deadlines still run from creation
time and no age/backdate restriction kicks in.
"""
from __future__ import annotations
from datetime import datetime
import pytest
pytestmark = pytest.mark.asyncio
def _naive(iso: str) -> datetime:
"""Parse an ISO datetime and strip any tz offset for safe comparison."""
dt = datetime.fromisoformat(iso)
return dt.replace(tzinfo=None) if dt.tzinfo is not None else dt
async def _login(client, email="wahab@denya.com", password="denya123") -> str:
resp = await client.post(
"/api/auth/login",
json={"email": email, "password": password},
)
assert resp.status_code == 200, resp.text
return resp.json()["access_token"]
async def _create_ticket(client, token: str, **overrides) -> dict:
payload = {
"unit_id": 2,
"category_id": 3,
"priority": "medium",
"reporter": "Backdate Test",
"reported_via": "walk-in",
"description": "backdate test ticket",
**overrides,
}
resp = await client.post(
"/api/tickets",
json=payload,
headers={"Authorization": f"Bearer {token}"},
)
assert resp.status_code == 201, resp.text
return resp.json()
async def test_create_with_backdated_reported_at_persists(client):
"""Wahab (Admin/Wahab) can enter an old ticket and its date sticks."""
token = await _login(client)
ticket = await _create_ticket(
client,
token,
reported_at="2026-07-20",
description="Old plumbing issue reported weeks ago",
)
assert ticket["reported_at"] is not None
assert ticket["reported_at"].startswith("2026-07-20")
# Still an active ticket in the normal workflow — no age restriction.
assert ticket["status"] in {"New", "Logged"}
# Detail endpoint exposes the reported date.
detail = await client.get(f"/api/tickets/{ticket['id']}")
assert detail.status_code == 200
assert detail.json()["reported_at"].startswith("2026-07-20")
# List endpoint exposes it too.
listing = await client.get("/api/tickets")
assert listing.status_code == 200
listed = next(t for t in listing.json()["items"] if t["id"] == ticket["id"])
assert listed["reported_at"].startswith("2026-07-20")
async def test_create_without_reported_at_defaults_to_now(client):
"""Omitting reported_at behaves exactly as before: reported == created."""
token = await _login(client)
ticket = await _create_ticket(client, token, description="normal today ticket")
assert ticket["reported_at"] is not None
reported = _naive(ticket["reported_at"])
created = _naive(ticket["created_at"])
assert abs((reported - created).total_seconds()) < 60
async def test_reported_at_does_not_shift_sla_deadline(client):
"""SLA computation is unchanged: deadlines run from creation time."""
token = await _login(client)
ticket = await _create_ticket(
client,
token,
priority="urgent",
reported_at="2026-01-01",
description="old urgent ticket",
)
assert ticket["sla_deadline"] is not None
created = _naive(ticket["created_at"])
deadline = _naive(ticket["sla_deadline"])
hours = (deadline - created).total_seconds() / 3600
assert 3.5 <= hours <= 4.5 # urgent → 4 h resolution window from creation
async def test_reported_at_round_trips_full_datetime(client):
"""A precise datetime (not just a date) survives the round trip."""
token = await _login(client)
reported = "2026-07-20T14:30:00"
ticket = await _create_ticket(client, token, reported_at=reported, description="datetime round trip")
assert ticket["reported_at"] is not None
parsed = _naive(ticket["reported_at"])
assert parsed.date().isoformat() == "2026-07-20"
assert parsed.hour == 14 and parsed.minute == 30