test(api): Admin sessions — fill gaps + lifecycle test

Fills the session-part coverage gaps in src/routes/admin.ts (cross-user list,
active list, manual create/edit, admin stop/discard) and adds the admin↔worker
lifecycle test.

New route-level tests (apps/api/test/admin.test.ts):
- manual create: 400 on schema-invalid body; 400 when paused_seconds > span.
- edit: 404 non-numeric id; 400 schema-invalid body; 400 unparseable start_time;
  400 paused_seconds > span; 404 unknown activity.
- stop: 404 non-numeric/unknown id; folds an open pause into paused_seconds.
- discard: 404 non-numeric/unknown id; 409 when already closed.

Lifecycle (apps/api/test/integration/admin-worker-session.test.ts): worker
starts → admin stops via the admin endpoint → session lands in /api/admin/report
and /api/admin/export with matching totals (uses resetDb for exact counts).

admin.ts session parts now at 100% stmts/lines; every session-part branch
covered. The one remaining session-part branch (row.pausedSeconds ?? 0 in the
stop route) is c8-ignored — paused_seconds is NOT NULL DEFAULT 0, so the null
branch is DB-impossible. The remaining uncovered branches in admin.ts are the
report/export parts, owned by the Reports domain (B-A6).

No behavior-changing bugs found; all session-part code was already correct.
This commit is contained in:
Bas van Rossem
2026-07-22 12:41:52 +02:00
parent b72076ee15
commit bdf65ae685
4 changed files with 367 additions and 1 deletions

View File

@@ -350,6 +350,7 @@ adminRoutes.post('/api/admin/sessions/:id/stop', async (c) => {
const extraPaused = row.pausedAt
? Math.round((now - new Date(row.pausedAt).getTime()) / 1000)
: 0;
/* c8 ignore next -- pausedSeconds is NOT NULL (schema default 0); the ?? guards a DB-impossible null */
const pausedSeconds = (row.pausedSeconds ?? 0) + extraPaused;
const durationSeconds = computeDuration(new Date(row.startTime).getTime(), now, pausedSeconds);