264 lines
9.0 KiB
JavaScript
264 lines
9.0 KiB
JavaScript
|
|
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);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|