Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 21 additions & 11 deletions apps/backend/src/__tests__/event.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { type PrismaClient, Prisma } from '@prisma/client';
import Fastify, { type FastifyInstance } from 'fastify';
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import Fastify, { FastifyInstance } from 'fastify';
import { PrismaClient } from '@prisma/client';

import { eventRoutes } from '../routes/event';


// ─── Shared mock data ────────────────────────────────────────────────────────

const MOCK_USER_ID = 'user-uuid-001';
Expand Down Expand Up @@ -64,7 +66,7 @@
//
// This mirrors the real app setup without touching a real DB or real JWT keys.

let mockJwtVerify = vi.fn();
const mockJwtVerify = vi.fn();

async function buildApp(): Promise<FastifyInstance> {
const app = Fastify({ logger: false });
Expand Down Expand Up @@ -97,7 +99,7 @@
app: FastifyInstance,
body: Record<string, unknown>,
authenticated = true,
) {
): Promise<Awaited<ReturnType<FastifyInstance['inject']>>> {
return app.inject({
method: 'POST',
url: '/api/events',
Expand Down Expand Up @@ -140,7 +142,7 @@

const res = await createEvent(app, validBody);

expect(res.statusCode).toBe(201);

Check failure on line 145 in apps/backend/src/__tests__/event.test.ts

View workflow job for this annotation

GitHub Actions / backend-ci

src/__tests__/event.test.ts > Events API > POST /api/events — create event > 201 — creates event and returns it for authenticated organizer

AssertionError: expected 500 to be 201 // Object.is equality - Expected + Received - 201 + 500 ❯ src/__tests__/event.test.ts:145:30
const body = res.json();
expect(body.slug).toBe('devcard-conf-2025');
expect(body.organizerId).toBe(MOCK_USER_ID);
Expand All @@ -159,40 +161,40 @@

const res = await createEvent(app, validBody, false);

expect(res.statusCode).toBe(401);

Check failure on line 164 in apps/backend/src/__tests__/event.test.ts

View workflow job for this annotation

GitHub Actions / backend-ci

src/__tests__/event.test.ts > Events API > POST /api/events — create event > 401 — rejects unauthenticated request

AssertionError: expected 500 to be 401 // Object.is equality - Expected + Received - 401 + 500 ❯ src/__tests__/event.test.ts:164:30
expect(res.json()).toMatchObject({ error: 'Unauthorized' });
});

it('400 — rejects missing required fields (no dates, no location)', async () => {
const res = await createEvent(app, { name: 'Hello World' }); // missing dates + location
expect(res.statusCode).toBe(400);

Check failure on line 170 in apps/backend/src/__tests__/event.test.ts

View workflow job for this annotation

GitHub Actions / backend-ci

src/__tests__/event.test.ts > Events API > POST /api/events — create event > 400 — rejects missing required fields (no dates, no location)

AssertionError: expected 500 to be 400 // Object.is equality - Expected + Received - 400 + 500 ❯ src/__tests__/event.test.ts:170:30
});

it('400 — rejects missing location', async () => {
const { location: _omit, ...bodyWithoutLocation } = validBody;
const res = await createEvent(app, bodyWithoutLocation);
expect(res.statusCode).toBe(400);

Check failure on line 176 in apps/backend/src/__tests__/event.test.ts

View workflow job for this annotation

GitHub Actions / backend-ci

src/__tests__/event.test.ts > Events API > POST /api/events — create event > 400 — rejects missing location

AssertionError: expected 500 to be 400 // Object.is equality - Expected + Received - 400 + 500 ❯ src/__tests__/event.test.ts:176:30
});

it('400 — rejects location shorter than 2 characters', async () => {
const res = await createEvent(app, { ...validBody, location: 'A' });
expect(res.statusCode).toBe(400);

Check failure on line 181 in apps/backend/src/__tests__/event.test.ts

View workflow job for this annotation

GitHub Actions / backend-ci

src/__tests__/event.test.ts > Events API > POST /api/events — create event > 400 — rejects location shorter than 2 characters

AssertionError: expected 500 to be 400 // Object.is equality - Expected + Received - 400 + 500 ❯ src/__tests__/event.test.ts:181:30
});

it('400 — rejects location longer than 100 characters', async () => {
const res = await createEvent(app, { ...validBody, location: 'A'.repeat(101) });
expect(res.statusCode).toBe(400);

Check failure on line 186 in apps/backend/src/__tests__/event.test.ts

View workflow job for this annotation

GitHub Actions / backend-ci

src/__tests__/event.test.ts > Events API > POST /api/events — create event > 400 — rejects location longer than 100 characters

AssertionError: expected 500 to be 400 // Object.is equality - Expected + Received - 400 + 500 ❯ src/__tests__/event.test.ts:186:30
});

it('400 — rejects event name shorter than 3 characters', async () => {
const res = await createEvent(app, { ...validBody, name: 'Hi' });
expect(res.statusCode).toBe(400);

Check failure on line 191 in apps/backend/src/__tests__/event.test.ts

View workflow job for this annotation

GitHub Actions / backend-ci

src/__tests__/event.test.ts > Events API > POST /api/events — create event > 400 — rejects event name shorter than 3 characters

AssertionError: expected 500 to be 400 // Object.is equality - Expected + Received - 400 + 500 ❯ src/__tests__/event.test.ts:191:30
});

it('400 — rejects event name longer than 100 characters', async () => {
const longName = 'A'.repeat(101);
const res = await createEvent(app, { ...validBody, name: longName });
expect(res.statusCode).toBe(400);

Check failure on line 197 in apps/backend/src/__tests__/event.test.ts

View workflow job for this annotation

GitHub Actions / backend-ci

src/__tests__/event.test.ts > Events API > POST /api/events — create event > 400 — rejects event name longer than 100 characters

AssertionError: expected 500 to be 400 // Object.is equality - Expected + Received - 400 + 500 ❯ src/__tests__/event.test.ts:197:30
});

it('400 — rejects invalid date format', async () => {
Expand All @@ -200,7 +202,7 @@
...validBody,
startDate: 'not-a-date',
});
expect(res.statusCode).toBe(400);

Check failure on line 205 in apps/backend/src/__tests__/event.test.ts

View workflow job for this annotation

GitHub Actions / backend-ci

src/__tests__/event.test.ts > Events API > POST /api/events — create event > 400 — rejects invalid date format

AssertionError: expected 500 to be 400 // Object.is equality - Expected + Received - 400 + 500 ❯ src/__tests__/event.test.ts:205:30
});

it('201 — generates a unique slug when the first candidate is taken', async () => {
Expand All @@ -216,7 +218,7 @@

const res = await createEvent(app, validBody);

expect(res.statusCode).toBe(201);

Check failure on line 221 in apps/backend/src/__tests__/event.test.ts

View workflow job for this annotation

GitHub Actions / backend-ci

src/__tests__/event.test.ts > Events API > POST /api/events — create event > 201 — generates a unique slug when the first candidate is taken

AssertionError: expected 500 to be 201 // Object.is equality - Expected + Received - 201 + 500 ❯ src/__tests__/event.test.ts:221:30
// create was eventually called with a slug different from the base one
const createdSlug: string = prismaMock.event.create.mock.calls[0][0].data.slug;
expect(createdSlug).toMatch(/^devcard-conf-2025-[a-z0-9]+$/);
Expand Down Expand Up @@ -355,9 +357,10 @@
it('409 — returns 409 when user already joined the event', async () => {
prismaMock.event.findUnique.mockResolvedValue(MOCK_EVENT);
// Prisma unique constraint error
const uniqueError = Object.assign(new Error('Unique constraint'), {
code: 'P2002',
});
const uniqueError = new Prisma.PrismaClientKnownRequestError(
'Unique constraint failed',
{ code: 'P2002', clientVersion: '6.0.0' },
);
prismaMock.eventAttendee.create.mockRejectedValue(uniqueError);

const res = await app.inject({
Expand Down Expand Up @@ -440,9 +443,10 @@
it('404 — returns 404 when user was never an attendee (P2025)', async () => {
prismaMock.event.findUnique.mockResolvedValue(MOCK_EVENT);
// Prisma record-not-found error
const notFoundError = Object.assign(new Error('Record not found'), {
code: 'P2025',
});
const notFoundError = new Prisma.PrismaClientKnownRequestError(
'Record not found',
{ code: 'P2025', clientVersion: '6.0.0' },
);
prismaMock.eventAttendee.delete.mockRejectedValue(notFoundError);

const res = await app.inject({
Expand Down Expand Up @@ -476,7 +480,13 @@
/** Builds a raw EventAttendee row as Prisma returns it (with nested user) */
function makeAttendeeRow(
profile: typeof MOCK_USER_PROFILE | typeof MOCK_OTHER_USER_PROFILE,
) {
): {
id: string;
userId: string;
eventId: string;
joinedAt: Date;
user: typeof profile;
} {
return {
id: `attendee-${profile.id}`,
userId: profile.id,
Expand Down
Loading
Loading