no-mistakes(review): Harden tech-performance tests, null comparator, and pending label
This commit is contained in:
@@ -3,9 +3,9 @@
|
||||
Anchors two Wahab-facing defects/requests:
|
||||
|
||||
1. The FM dashboard built its "Technician Workload" card from
|
||||
``Tech #<user id>`` instead of the technician's real name. The template must
|
||||
use ``assigned_technician_name`` (the same field ``/tickets`` already uses),
|
||||
and no template may reintroduce a ``Tech #`` placeholder.
|
||||
``Tech #<user id>`` instead of the technician's real name. The data sources
|
||||
the card consumes must expose the technician's real name via
|
||||
``assigned_technician_name``, never an id placeholder.
|
||||
2. ``GET /api/tickets/tech-performance`` reports per-technician workload and
|
||||
outcomes by real name: totals, per-status buckets, completion rate and
|
||||
``created_at -> closed_at`` resolution times. Everything derives from
|
||||
@@ -15,7 +15,6 @@ Anchors two Wahab-facing defects/requests:
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from sqlalchemy import select
|
||||
@@ -26,8 +25,6 @@ from app.models.user import User
|
||||
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
TEMPLATES_DIR = Path(__file__).resolve().parent.parent / "app" / "templates"
|
||||
|
||||
|
||||
async def _login(client, email: str = "wahab@denya.com", password: str = "denya123") -> str:
|
||||
resp = await client.post("/api/auth/login", json={"email": email, "password": password})
|
||||
@@ -80,24 +77,36 @@ def _ticket(status: str, assigned_to: int | None, *, created_at: datetime | None
|
||||
return spec
|
||||
|
||||
|
||||
# ── 3a. 'Tech #N' regression ─────────────────────────────────────────
|
||||
async def test_fm_dashboard_workload_uses_real_technician_names(client):
|
||||
"""The FM dashboard must build the workload card from the real name field."""
|
||||
resp = await client.get("/dashboard/fm")
|
||||
assert resp.status_code == 200
|
||||
body = resp.text
|
||||
assert "Tech #" not in body
|
||||
assert "assigned_technician_name" in body
|
||||
# ── 3a. Real technician names on the workload data paths ─────────────
|
||||
async def test_workload_sources_report_real_technician_names(client):
|
||||
"""The FM workload card reads ``/api/tickets?page_size=200`` and the
|
||||
performance report reads ``/api/tickets/tech-performance``. For an assigned
|
||||
ticket both must expose the technician's real full name, never a
|
||||
``Tech #<id>`` placeholder.
|
||||
"""
|
||||
prosper = await _user_id("prosper@denya.com")
|
||||
await _seed_tickets([_ticket("In Progress", prosper), _ticket("Escalated", prosper)])
|
||||
|
||||
|
||||
async def test_no_template_reintroduces_tech_id_placeholder():
|
||||
"""Sweep every template for the ``Tech #<id>`` placeholder."""
|
||||
offenders = [
|
||||
str(path.relative_to(TEMPLATES_DIR))
|
||||
for path in TEMPLATES_DIR.rglob("*.html")
|
||||
if "Tech #" in path.read_text(encoding="utf-8")
|
||||
token = await _login(client)
|
||||
list_resp = await client.get("/api/tickets?page_size=200", headers=_auth(token))
|
||||
assert list_resp.status_code == 200
|
||||
active = [
|
||||
t for t in list_resp.json()["items"]
|
||||
if t["status"] not in ("Closed", "Completed", "Cancelled")
|
||||
]
|
||||
assert offenders == []
|
||||
|
||||
# Same mapping the FM workload card builds from active assigned tickets.
|
||||
workload: dict[int, str] = {}
|
||||
for t in active:
|
||||
if t["assigned_to"]:
|
||||
workload.setdefault(t["assigned_to"], t["assigned_technician_name"] or "Unassigned")
|
||||
assert workload == {prosper: "Prosper"}
|
||||
|
||||
perf_resp = await client.get("/api/tickets/tech-performance", headers=_auth(token))
|
||||
assert perf_resp.status_code == 200
|
||||
rows = {t["technician_id"]: t for t in perf_resp.json()["technicians"]}
|
||||
assert rows[prosper]["name"] == "Prosper"
|
||||
assert all(not row["name"].startswith("Tech #") for row in rows.values())
|
||||
|
||||
|
||||
# ── Page + route wiring ──────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user