no-mistakes(review): Harden tech-performance tests, null comparator, and pending label

This commit is contained in:
root
2026-09-10 04:07:31 +00:00
parent 43800345c1
commit b7f36ac3b1
2 changed files with 40 additions and 26 deletions
@@ -52,7 +52,7 @@
<span class="text-2xl font-bold text-red-700" x-text="totals.escalated || 0"></span>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 flex items-center justify-between">
<span class="text-sm font-medium text-gray-600">Pending / Unstarted</span>
<span class="text-sm font-medium text-gray-600">Pending / In review</span>
<span class="text-2xl font-bold text-gray-700" x-text="totals.pending || 0"></span>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-200 p-4 flex items-center justify-between">
@@ -168,10 +168,15 @@
}
let av = a[key];
let bv = b[key];
// Missing resolution times sort last in either direction.
// Missing resolution times sort last in either direction;
// two missing values fall through to the name tiebreak.
if (key === 'avg_resolution_hours') {
if (av === null || av === undefined) return 1;
if (bv === null || bv === undefined) return -1;
const aMissing = av === null || av === undefined;
const bMissing = bv === null || bv === undefined;
if (aMissing || bMissing) {
if (aMissing && bMissing) return a.name.localeCompare(b.name);
return aMissing ? 1 : -1;
}
}
av = av || 0;
bv = bv || 0;
+31 -22
View File
@@ -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 ──────────────────────────────────────────────