Skip to content

Commit

Permalink
Fix up jest and mock prisma
Browse files Browse the repository at this point in the history
  • Loading branch information
d1str0 committed Nov 29, 2024
1 parent be9478c commit a178d55
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 4 deletions.
Empty file added api/test/__mocks__/prisma.ts
Empty file.
47 changes: 43 additions & 4 deletions api/test/routes/api/user.test.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,38 @@
// test/api/user.test.ts
jest.mock('../../../src/lib/prisma', () => {
const { mockDeep } = jest.requireActual('jest-mock-extended');
return {
prisma: mockDeep(),
};
});
jest.mock('bcrypt');

import Fastify, { FastifyInstance } from 'fastify';
import sensible from '@fastify/sensible';
import rootRoutes from '../../../src/routes';
import errorHandler from '../../../src/plugins/errorHandler';
import { DeepMockProxy } from 'jest-mock-extended';
import { PrismaClient } from '@prisma/client';
import { prisma } from '../../../src/lib/prisma';

describe('User API Routes', () => {
let app: FastifyInstance;
const prismaMock = prisma as DeepMockProxy<PrismaClient>;

beforeEach(() => {
jest.clearAllMocks();
});

beforeAll(async () => {
app = Fastify();
await app.register(sensible);

// Register routes with API prefix and error handler
await app.register(
async (fastify) => {
await fastify.register(errorHandler);
await fastify.register(rootRoutes);
},
{ prefix: '/api' },
);

await app.ready();
});

Expand All @@ -34,9 +47,34 @@ describe('User API Routes', () => {
password: 'password123',
};

// TODO: Add test for valid user creation
it('should create a new user successfully', async () => {
prismaMock.user.findFirst.mockResolvedValue(null);
prismaMock.user.create.mockResolvedValue({
id: 1,
...validUser,
});

const response = await app.inject({
method: 'POST',
url: '/api/user',
payload: validUser,
});

expect(response.statusCode).toBe(201);
const responseBody = JSON.parse(response.payload);
expect(responseBody).toMatchObject({
id: 1,
name: validUser.name,
email: validUser.email,
});
expect(responseBody).not.toHaveProperty('password');
});

test('should reject user with conflicting name', async () => {
prismaMock.user.findFirst.mockResolvedValue({
id: 1,
...validUser,
});
// Try to create another user with the same name
const response = await app.inject({
method: 'POST',
Expand Down Expand Up @@ -91,6 +129,7 @@ describe('User API Routes', () => {
});

describe('GET /api/user', () => {
prismaMock.user.findMany.mockResolvedValue([]);
test('should return list of user names', async () => {
const response = await app.inject({
method: 'GET',
Expand Down
115 changes: 115 additions & 0 deletions api/test/services/user.service.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// src/__tests__/user.service.test.ts
jest.mock('../../src/lib/prisma', () => ({
prisma: jest.requireActual('jest-mock-extended').mockDeep(),
}));
jest.mock('bcrypt');

import {
getAllUserNames,
createUser,
UserExistsError,
} from '../../src/services/user.service';
import bcrypt from 'bcrypt';
import { PrismaClient } from '@prisma/client';
import { DeepMockProxy } from 'jest-mock-extended';
import { prisma } from '../../src/lib/prisma';

describe('User Service', () => {
let prismaMock: DeepMockProxy<PrismaClient>;

beforeEach(() => {
prismaMock = prisma as DeepMockProxy<PrismaClient>;
jest.clearAllMocks();
});

describe('getAllUserNames', () => {
it('should return all user names', async () => {
const mockUsers = [
{
id: 1,
name: 'user1',
email: '[email protected]',
password: 'password1',
},
{
id: 2,
name: 'user2',
email: '[email protected]',
password: 'password2',
},
];

prismaMock.user.findMany.mockResolvedValue(mockUsers);

const result = await getAllUserNames();
expect(result).toEqual(['user1', 'user2']);
expect(prismaMock.user.findMany).toHaveBeenCalledWith({
select: { name: true },
});
});
});

describe('createUser', () => {
const mockUser = {
id: 1,
name: 'testuser',
email: '[email protected]',
password: 'hashedPassword123',
};

it('should create a new user successfully', async () => {
prismaMock.user.findFirst.mockResolvedValue(null);
(bcrypt.hash as jest.Mock).mockResolvedValue('hashedPassword123');

prismaMock.user.create.mockResolvedValue({
...mockUser,
});

const result = await createUser(
mockUser.name,
mockUser.email,
'password123',
);

expect(result).toEqual({
id: 1,
name: mockUser.name,
email: mockUser.email,
});
expect(result).not.toHaveProperty('password');
});

it('should throw UserExistsError if name exists', async () => {
prismaMock.user.findFirst.mockResolvedValue({ ...mockUser });

await expect(
createUser(mockUser.name, '[email protected]', 'password123'),
).rejects.toThrow(
new UserExistsError('User with this name already exists'),
);
});

it('should throw UserExistsError if email exists', async () => {
prismaMock.user.findFirst.mockResolvedValue({
...mockUser,
});

await expect(
createUser('newuser', mockUser.email, 'password123'),
).rejects.toThrow(
new UserExistsError('User with this email already exists'),
);
});

it('should hash password before saving', async () => {
prismaMock.user.findFirst.mockResolvedValue(null);
prismaMock.user.create.mockResolvedValue({
...mockUser,
});

await createUser(mockUser.name, mockUser.email, 'password123');

expect(bcrypt.hash).toHaveBeenCalledWith('password123', 10);
});
});
});

0 comments on commit a178d55

Please sign in to comment.