Initial attendee management
This commit is contained in:
155
apps/backend/test/invitations.test.ts
Normal file
155
apps/backend/test/invitations.test.ts
Normal file
@@ -0,0 +1,155 @@
|
||||
import { beforeEach, describe, expect, it, mock, vi } from 'bun:test';
|
||||
import { Hono } from 'hono';
|
||||
|
||||
const db = {
|
||||
group: {
|
||||
findUnique: mock(),
|
||||
},
|
||||
attendee: {
|
||||
findFirst: mock(),
|
||||
create: mock(),
|
||||
},
|
||||
};
|
||||
|
||||
mock.module('@/lib/prisma', () => ({
|
||||
default: db,
|
||||
getPrismaClient: mock(),
|
||||
UnitOfWork: class {},
|
||||
}));
|
||||
|
||||
import prisma from '@/lib/prisma';
|
||||
import { invitationsRoutes } from '@/modules/invitations';
|
||||
|
||||
const app = new Hono();
|
||||
app.route('/invitations', invitationsRoutes);
|
||||
|
||||
const mockGroup = {
|
||||
id: 'group-1',
|
||||
name: 'Taller de Cerámica',
|
||||
description: 'Clases de cerámica los sábados',
|
||||
days: ['SATURDAY'],
|
||||
time: '10:00',
|
||||
capacity: 15,
|
||||
price: 500,
|
||||
billingType: 'MONTHLY',
|
||||
createdById: 'teacher-1',
|
||||
owner: { name: 'Prof. Mario Rossi' },
|
||||
};
|
||||
|
||||
describe('public invitations routes', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('GET /invitations/:token', () => {
|
||||
it('returns group details for a valid invite token', async () => {
|
||||
prisma.group.findUnique.mockResolvedValue(mockGroup as never);
|
||||
|
||||
const res = await app.request('/invitations/valid-token-123');
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
const data = await res.json();
|
||||
expect(data).toEqual({
|
||||
id: 'group-1',
|
||||
name: 'Taller de Cerámica',
|
||||
description: 'Clases de cerámica los sábados',
|
||||
teacherName: 'Prof. Mario Rossi',
|
||||
days: ['SATURDAY'],
|
||||
time: '10:00',
|
||||
capacity: 15,
|
||||
price: 500,
|
||||
billingType: 'MONTHLY',
|
||||
});
|
||||
expect(prisma.group.findUnique).toHaveBeenCalledWith({
|
||||
where: { inviteToken: 'valid-token-123' },
|
||||
include: { owner: { select: { name: true } } },
|
||||
});
|
||||
});
|
||||
|
||||
it('returns 404 when the invite token is invalid', async () => {
|
||||
prisma.group.findUnique.mockResolvedValue(null);
|
||||
|
||||
const res = await app.request('/invitations/invalid-token');
|
||||
|
||||
expect(res.status).toBe(404);
|
||||
const body = await res.json();
|
||||
expect(body.title).toBe('Not Found');
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST /invitations/:token/join', () => {
|
||||
it('registers a new attendee successfully', async () => {
|
||||
prisma.group.findUnique.mockResolvedValue(mockGroup as never);
|
||||
prisma.attendee.findFirst.mockResolvedValue(null);
|
||||
prisma.attendee.create.mockResolvedValue({
|
||||
id: 'att-1',
|
||||
groupId: 'group-1',
|
||||
fullName: 'Lucía Méndez',
|
||||
email: 'lucia@test.com',
|
||||
phone: '+5491122334455',
|
||||
guardianName: null,
|
||||
guardianPhone: null,
|
||||
notes: null,
|
||||
createdAt: new Date('2026-09-18T10:00:00.000Z'),
|
||||
updatedAt: new Date('2026-09-18T10:00:00.000Z'),
|
||||
} as never);
|
||||
|
||||
const res = await app.request('/invitations/valid-token-123/join', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
firstName: 'Lucía',
|
||||
lastName: 'Méndez',
|
||||
phone: '+54 9 11 2233-4455',
|
||||
email: 'lucia@test.com',
|
||||
}),
|
||||
});
|
||||
|
||||
expect(res.status).toBe(201);
|
||||
const body = await res.json();
|
||||
expect(body.message).toBe('Inscripción realizada con éxito');
|
||||
expect(body.attendee.fullName).toBe('Lucía Méndez');
|
||||
expect(prisma.attendee.create).toHaveBeenCalledWith({
|
||||
data: {
|
||||
groupId: 'group-1',
|
||||
fullName: 'Lucía Méndez',
|
||||
phone: '+5491122334455',
|
||||
email: 'lucia@test.com',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects registration when phone already exists in the group', async () => {
|
||||
prisma.group.findUnique.mockResolvedValue(mockGroup as never);
|
||||
prisma.attendee.findFirst.mockResolvedValue({ id: 'existing-att' } as never);
|
||||
|
||||
const res = await app.request('/invitations/valid-token-123/join', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
firstName: 'Lucía',
|
||||
lastName: 'Méndez',
|
||||
phone: '1122334455',
|
||||
}),
|
||||
});
|
||||
|
||||
expect(res.status).toBe(409);
|
||||
const body = await res.json();
|
||||
expect(body.code).toBe('attendee_already_registered');
|
||||
expect(prisma.attendee.create).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('rejects invalid inputs (missing required name/phone)', async () => {
|
||||
const res = await app.request('/invitations/valid-token-123/join', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
firstName: '',
|
||||
phone: '12',
|
||||
}),
|
||||
});
|
||||
|
||||
expect(res.status).toBe(400);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user