import { describe, it, expect, beforeAll, afterAll } from "vitest"; import Fastify from "fastify"; import jwt from "jsonwebtoken"; import prisma from "../config/database"; import { config } from "../config/env"; import dashboardRoutes from "../routes/admin/dashboard"; // --------------------------------------------------------------------------- // Regression test for the "Docházka dnes" / "Přítomní dnes" dashboard window. // // attendance.shift_date is @db.Date and Prisma truncates a filter Date to its // UTC date part. The dashboard used local-midnight boundaries // (new Date(y, m, d) = 22:00/23:00 UTC of the PREVIOUS day under // TZ=Europe/Prague), which truncated to [yesterday, today) — today's punches // matched zero rows and everyone showed as absent. The boundaries must be // UTC-midnight instants of the LOCAL calendar day. // // Unlike the other suites this one has to use the REAL current date (the // route computes "today" internally), so fixtures are tracked by id and // deleted in afterAll. // --------------------------------------------------------------------------- let app: Awaited>; let adminUserId: number; let adminToken: string; const createdIds: number[] = []; async function buildApp() { const a = Fastify({ logger: false }); await a.register(dashboardRoutes, { prefix: "/api/admin/dashboard" }); return a; } beforeAll(async () => { app = await buildApp(); const admin = await prisma.users.findFirst({ where: { roles: { name: "admin" } }, include: { roles: true }, }); if (!admin) throw new Error("Test setup: admin user not found"); adminUserId = admin.id; adminToken = jwt.sign( { sub: admin.id, username: admin.username, role: "admin" }, config.jwt.secret, { expiresIn: "15m" }, ); }); afterAll(async () => { if (createdIds.length) { await prisma.attendance.deleteMany({ where: { id: { in: createdIds } } }); } if (app) await app.close(); }); describe("GET /api/admin/dashboard — today's attendance window", () => { it("counts a punched-in user as present (local-noon @db.Date row)", async () => { const now = new Date(); // The attendance regime stores shift_date at LOCAL NOON (so the UTC date // part equals the Prague calendar date) — same as punchAction does. const row = await prisma.attendance.create({ data: { user_id: adminUserId, shift_date: new Date( now.getFullYear(), now.getMonth(), now.getDate(), 12, 0, 0, ), leave_type: "work", arrival_time: now, departure_time: null, notes: "dashboard_window_test", }, }); createdIds.push(row.id); const res = await app.inject({ method: "GET", url: "/api/admin/dashboard", headers: { Authorization: `Bearer ${adminToken}` }, }); expect(res.statusCode).toBe(200); const body = res.json(); expect(body.success).toBe(true); const attendance = body.data.attendance; expect(attendance).toBeTruthy(); const me = attendance.users.find( (u: { user_id: number }) => u.user_id === adminUserId, ); expect(me).toBeTruthy(); expect(me.status).toBe("in"); expect(attendance.present_today).toBeGreaterThanOrEqual(1); }); });