const request = require('supertest'); const app = require('../app'); const { prisma } = require('../utils/db'); const { generateToken } = require('../utils/auth'); let testUser; let testEvent; let authToken; beforeAll(async () => { // Clean up database await prisma.eventParticipant.deleteMany({}); await prisma.eventCheckinToken.deleteMany({}); await prisma.chatRoom.deleteMany({}); await prisma.event.deleteMany({}); await prisma.user.deleteMany({}); // Create test user testUser = await prisma.user.create({ data: { username: 'checkintest', email: 'checkin@test.com', passwordHash: 'hashedpassword', emailVerified: true, }, }); // Generate auth token authToken = generateToken(testUser.id); // Create test event testEvent = await prisma.event.create({ data: { slug: 'test-event-slug', name: 'Test Event', location: 'Test Location', startDate: new Date('2025-06-01'), endDate: new Date('2025-06-03'), description: 'Test event description', participantsCount: 0, }, }); // Create chat room for event await prisma.chatRoom.create({ data: { eventId: testEvent.id, type: 'event', }, }); }); afterAll(async () => { // Clean up await prisma.eventParticipant.deleteMany({}); await prisma.eventCheckinToken.deleteMany({}); await prisma.chatRoom.deleteMany({}); await prisma.event.deleteMany({}); await prisma.user.deleteMany({}); await prisma.$disconnect(); }); describe('Event Check-in API Tests', () => { describe('GET /api/events/:slug/details', () => { it('should get event details with generated check-in token', async () => { const response = await request(app) .get(`/api/events/${testEvent.slug}/details`) .set('Authorization', `Bearer ${authToken}`) .expect('Content-Type', /json/) .expect(200); expect(response.body).toHaveProperty('success', true); expect(response.body.data).toHaveProperty('event'); expect(response.body.data).toHaveProperty('checkin'); expect(response.body.data).toHaveProperty('participants'); expect(response.body.data).toHaveProperty('stats'); // Event data expect(response.body.data.event).toHaveProperty('slug', testEvent.slug); expect(response.body.data.event).toHaveProperty('name', testEvent.name); // Check-in token expect(response.body.data.checkin).toHaveProperty('token'); expect(response.body.data.checkin).toHaveProperty('url'); expect(response.body.data.checkin).toHaveProperty('validFrom'); expect(response.body.data.checkin).toHaveProperty('validUntil'); expect(response.body.data.checkin.url).toContain(response.body.data.checkin.token); // Stats expect(response.body.data.stats).toHaveProperty('totalParticipants', 0); expect(response.body.data.participants).toEqual([]); }); it('should return 404 for non-existent event', async () => { const response = await request(app) .get('/api/events/non-existent-slug/details') .set('Authorization', `Bearer ${authToken}`) .expect('Content-Type', /json/) .expect(404); expect(response.body).toHaveProperty('success', false); expect(response.body).toHaveProperty('error', 'Event not found'); }); it('should require authentication', async () => { await request(app) .get(`/api/events/${testEvent.slug}/details`) .expect(401); }); }); describe('POST /api/events/checkin/:token', () => { let checkinToken; beforeAll(async () => { // Get check-in token const detailsResponse = await request(app) .get(`/api/events/${testEvent.slug}/details`) .set('Authorization', `Bearer ${authToken}`); checkinToken = detailsResponse.body.data.checkin.token; }); it('should check-in user with valid token', async () => { const response = await request(app) .post(`/api/events/checkin/${checkinToken}`) .set('Authorization', `Bearer ${authToken}`) .expect('Content-Type', /json/) .expect(200); expect(response.body).toHaveProperty('success', true); expect(response.body).toHaveProperty('alreadyCheckedIn', false); expect(response.body.data).toHaveProperty('event'); expect(response.body.data).toHaveProperty('joinedAt'); expect(response.body.data.event).toHaveProperty('slug', testEvent.slug); }); it('should detect already checked-in user', async () => { const response = await request(app) .post(`/api/events/checkin/${checkinToken}`) .set('Authorization', `Bearer ${authToken}`) .expect('Content-Type', /json/) .expect(200); expect(response.body).toHaveProperty('success', true); expect(response.body).toHaveProperty('alreadyCheckedIn', true); }); it('should return 404 for invalid token', async () => { const response = await request(app) .post('/api/events/checkin/invalid-token-123') .set('Authorization', `Bearer ${authToken}`) .expect('Content-Type', /json/) .expect(404); expect(response.body).toHaveProperty('success', false); expect(response.body).toHaveProperty('error', 'Invalid check-in token'); }); it('should require authentication', async () => { await request(app) .post(`/api/events/checkin/${checkinToken}`) .expect(401); }); it('should update participants count', async () => { const detailsResponse = await request(app) .get(`/api/events/${testEvent.slug}/details`) .set('Authorization', `Bearer ${authToken}`); expect(detailsResponse.body.data.stats.totalParticipants).toBeGreaterThan(0); expect(detailsResponse.body.data.participants.length).toBeGreaterThan(0); expect(detailsResponse.body.data.participants[0]).toHaveProperty('userId', testUser.id); expect(detailsResponse.body.data.participants[0]).toHaveProperty('username', testUser.username); }); }); describe('DELETE /api/events/:slug/leave', () => { it('should allow user to leave event', async () => { const response = await request(app) .delete(`/api/events/${testEvent.slug}/leave`) .set('Authorization', `Bearer ${authToken}`) .expect('Content-Type', /json/) .expect(200); expect(response.body).toHaveProperty('success', true); expect(response.body).toHaveProperty('message', 'Successfully left the event'); }); it('should return error if user is not participant', async () => { const response = await request(app) .delete(`/api/events/${testEvent.slug}/leave`) .set('Authorization', `Bearer ${authToken}`) .expect('Content-Type', /json/) .expect(400); expect(response.body).toHaveProperty('success', false); expect(response.body).toHaveProperty('error', 'You are not a participant of this event'); }); it('should return 404 for non-existent event', async () => { const response = await request(app) .delete('/api/events/non-existent-slug/leave') .set('Authorization', `Bearer ${authToken}`) .expect('Content-Type', /json/) .expect(404); expect(response.body).toHaveProperty('success', false); expect(response.body).toHaveProperty('error', 'Event not found'); }); it('should require authentication', async () => { await request(app) .delete(`/api/events/${testEvent.slug}/leave`) .expect(401); }); it('should update participants count after leaving', async () => { // First check-in const detailsResponse1 = await request(app) .get(`/api/events/${testEvent.slug}/details`) .set('Authorization', `Bearer ${authToken}`); const checkinToken = detailsResponse1.body.data.checkin.token; await request(app) .post(`/api/events/checkin/${checkinToken}`) .set('Authorization', `Bearer ${authToken}`); // Then leave await request(app) .delete(`/api/events/${testEvent.slug}/leave`) .set('Authorization', `Bearer ${authToken}`) .expect(200); // Check participants count const detailsResponse2 = await request(app) .get(`/api/events/${testEvent.slug}/details`) .set('Authorization', `Bearer ${authToken}`); expect(detailsResponse2.body.data.stats.totalParticipants).toBe(0); expect(detailsResponse2.body.data.participants).toEqual([]); }); }); describe('Check-in token generation', () => { it('should reuse existing token for same event', async () => { const response1 = await request(app) .get(`/api/events/${testEvent.slug}/details`) .set('Authorization', `Bearer ${authToken}`); const token1 = response1.body.data.checkin.token; const response2 = await request(app) .get(`/api/events/${testEvent.slug}/details`) .set('Authorization', `Bearer ${authToken}`); const token2 = response2.body.data.checkin.token; expect(token1).toBe(token2); }); }); });