1+ import Fastify from 'fastify' ;
12import { describe , it , expect , beforeEach , afterEach , vi } from 'vitest' ;
2- import Fastify , { FastifyInstance } from 'fastify' ;
3- import { PrismaClient } from '@prisma/client' ;
3+
44import { eventRoutes } from '../routes/event' ;
55
6+ import type { PrismaClient } from '@prisma/client' ;
7+ import type { FastifyInstance , LightMyRequestResponse } from 'fastify' ;
8+
69// ─── Shared mock data ────────────────────────────────────────────────────────
710
811const MOCK_USER_ID = 'user-uuid-001' ;
@@ -64,7 +67,7 @@ const prismaMock = {
6467//
6568// This mirrors the real app setup without touching a real DB or real JWT keys.
6669
67- let mockJwtVerify = vi . fn ( ) ;
70+ const mockJwtVerify = vi . fn ( ) ;
6871
6972async function buildApp ( ) : Promise < FastifyInstance > {
7073 const app = Fastify ( { logger : false } ) ;
@@ -77,7 +80,14 @@ async function buildApp(): Promise<FastifyInstance> {
7780 app . decorateRequest ( 'jwtVerify' , function ( ) {
7881 return mockJwtVerify ( ) ;
7982 } ) ;
80-
83+ app . decorate ( 'authenticate' , async function ( request , reply ) {
84+ try {
85+ const payload = await request . jwtVerify ( ) ;
86+ if ( payload ) { request . user = payload as typeof request . user ; }
87+ } catch {
88+ return reply . status ( 401 ) . send ( { error : 'Unauthorized' } ) ;
89+ }
90+ } ) ;
8191 // Register with the same prefix used in production (app.ts) so that
8292 // tests exercise routes at their real paths — /api/events, /api/events/:slug, etc.
8393 await app . register ( eventRoutes , { prefix : '/api/events' } ) ;
@@ -97,7 +107,7 @@ async function createEvent(
97107 app : FastifyInstance ,
98108 body : Record < string , unknown > ,
99109 authenticated = true ,
100- ) {
110+ ) : Promise < LightMyRequestResponse > {
101111 return app . inject ( {
102112 method : 'POST' ,
103113 url : '/api/events' ,
@@ -251,14 +261,15 @@ describe('Events API', () => {
251261 it ( '200 — returns event info with attendee count' , async ( ) => {
252262 prismaMock . event . findUnique . mockResolvedValue ( {
253263 ...MOCK_EVENT ,
264+ organizer : { username : 'johndoe' , displayName : 'John Doe' } ,
254265 _count : { attendees : 42 } ,
255266 } ) ;
256267
257268 const res = await app . inject ( {
258269 method : 'GET' ,
259270 url : '/api/events/devcard-conf-2025' ,
260271 } ) ;
261-
272+ console . log ( JSON . stringify ( res . json ( ) , null , 2 ) ) ;
262273 expect ( res . statusCode ) . toBe ( 200 ) ;
263274 const body = res . json ( ) ;
264275 expect ( body . slug ) . toBe ( 'devcard-conf-2025' ) ;
@@ -275,7 +286,7 @@ describe('Events API', () => {
275286 method : 'GET' ,
276287 url : '/api/events/ghost-event' ,
277288 } ) ;
278-
289+
279290 expect ( res . statusCode ) . toBe ( 404 ) ;
280291 expect ( res . json ( ) ) . toMatchObject ( { error : 'Event not found' } ) ;
281292 } ) ;
@@ -285,6 +296,7 @@ describe('Events API', () => {
285296 mockJwtVerify . mockRejectedValue ( new Error ( 'Should not be called' ) ) ;
286297 prismaMock . event . findUnique . mockResolvedValue ( {
287298 ...MOCK_EVENT ,
299+ organizer : { username : 'johndoe' , displayName : 'John Doe' } ,
288300 _count : { attendees : 0 } ,
289301 } ) ;
290302
@@ -476,7 +488,13 @@ describe('Events API', () => {
476488 /** Builds a raw EventAttendee row as Prisma returns it (with nested user) */
477489 function makeAttendeeRow (
478490 profile : typeof MOCK_USER_PROFILE | typeof MOCK_OTHER_USER_PROFILE ,
479- ) {
491+ ) : {
492+ id : string ;
493+ userId : string ;
494+ eventId : string ;
495+ joinedAt : Date ;
496+ user : typeof MOCK_USER_PROFILE | typeof MOCK_OTHER_USER_PROFILE ;
497+ } {
480498 return {
481499 id : `attendee-${ profile . id } ` ,
482500 userId : profile . id ,
@@ -495,6 +513,7 @@ describe('Events API', () => {
495513 prismaMock . event . findUnique . mockResolvedValue ( {
496514 ...MOCK_EVENT ,
497515 attendees : attendeeRows ,
516+ _count : { attendees : 2 } ,
498517 } ) ;
499518
500519 const res = await app . inject ( {
@@ -523,6 +542,7 @@ describe('Events API', () => {
523542 prismaMock . event . findUnique . mockResolvedValue ( {
524543 ...MOCK_EVENT ,
525544 attendees : [ makeAttendeeRow ( MOCK_OTHER_USER_PROFILE ) ] ,
545+ _count : { attendees : 1 } ,
526546 } ) ;
527547
528548 const res = await app . inject ( {
@@ -545,6 +565,7 @@ describe('Events API', () => {
545565 prismaMock . event . findUnique . mockResolvedValue ( {
546566 ...MOCK_EVENT ,
547567 attendees : [ ] ,
568+ _count : { attendees : 0 } ,
548569 } ) ;
549570
550571 const res = await app . inject ( {
@@ -561,6 +582,7 @@ describe('Events API', () => {
561582 prismaMock . event . findUnique . mockResolvedValue ( {
562583 ...MOCK_EVENT ,
563584 attendees : [ ] ,
585+ _count : { attendees : 0 } ,
564586 } ) ;
565587
566588 const res = await app . inject ( {
@@ -577,6 +599,7 @@ describe('Events API', () => {
577599 prismaMock . event . findUnique . mockResolvedValue ( {
578600 ...MOCK_EVENT ,
579601 attendees : [ ] ,
602+ _count : { attendees : 0 } ,
580603 } ) ;
581604
582605 const res = await app . inject ( {
@@ -594,6 +617,7 @@ describe('Events API', () => {
594617 prismaMock . event . findUnique . mockResolvedValue ( {
595618 ...MOCK_EVENT ,
596619 attendees : [ makeAttendeeRow ( MOCK_USER_PROFILE ) ] ,
620+ _count : { attendees : 1 } ,
597621 } ) ;
598622
599623 const res = await app . inject ( {
@@ -683,4 +707,4 @@ describe('Events API', () => {
683707 expect ( slug ) . not . toMatch ( / - - / ) ;
684708 } ) ;
685709 } ) ;
686- } ) ;
710+ } ) ;
0 commit comments