feat(tests): Jest backend (38 tests), Jest RTL frontend (28 tests), Playwright E2E spec

This commit is contained in:
Experiments DB Dev
2026-04-15 13:23:48 -04:00
parent c359cb4bb9
commit 5874ed8374
25 changed files with 9620 additions and 3652 deletions
+120
View File
@@ -0,0 +1,120 @@
const request = require('supertest');
jest.mock('../src/lib/prisma', () => require('./__mocks__/prisma'));
const prisma = require('../src/lib/prisma');
const app = require('../src/app');
const EXP_ID = 'bbbbbbbb-0000-0000-0000-000000000001';
const ANIMAL = {
id: 'cccccccc-0000-0000-0000-000000000001',
experiment_id: EXP_ID,
animal_id_string: 'M-001',
animal_name: 'Whiskers',
_count: { daily_statuses: 0 },
};
beforeEach(() => jest.clearAllMocks());
describe('GET /api/animals', () => {
it('returns animals filtered by experimentId', async () => {
prisma.animal.findMany.mockResolvedValue([ANIMAL]);
const res = await request(app).get(`/api/animals?experimentId=${EXP_ID}`);
expect(res.status).toBe(200);
expect(res.body).toHaveLength(1);
expect(res.body[0].animal_id_string).toBe('M-001');
});
it('returns 422 for invalid UUID experimentId', async () => {
const res = await request(app).get('/api/animals?experimentId=not-a-uuid');
expect(res.status).toBe(422);
});
});
describe('GET /api/animals/:id', () => {
it('returns animal with daily_statuses', async () => {
prisma.animal.findUnique.mockResolvedValue({ ...ANIMAL, daily_statuses: [] });
const res = await request(app).get(`/api/animals/${ANIMAL.id}`);
expect(res.status).toBe(200);
expect(res.body.animal_name).toBe('Whiskers');
});
it('returns 404 for unknown animal', async () => {
prisma.animal.findUnique.mockResolvedValue(null);
const res = await request(app).get('/api/animals/00000000-0000-0000-0000-000000000000');
expect(res.status).toBe(404);
});
});
describe('POST /api/animals', () => {
it('creates animal with UUID and writes CREATE audit log', async () => {
prisma.animal.create.mockResolvedValue(ANIMAL);
prisma.auditLog.create.mockResolvedValue({});
const res = await request(app)
.post('/api/animals')
.send({ experiment_id: EXP_ID, animal_id_string: 'M-001', animal_name: 'Whiskers' });
expect(res.status).toBe(201);
expect(res.body.id).toMatch(/^[0-9a-f-]{36}$/i);
expect(prisma.auditLog.create).toHaveBeenCalled();
});
it('returns 422 when experiment_id is missing', async () => {
const res = await request(app)
.post('/api/animals')
.send({ animal_id_string: 'M-001', animal_name: 'Whiskers' });
expect(res.status).toBe(422);
expect(res.body.details).toEqual(
expect.arrayContaining([expect.objectContaining({ field: 'experiment_id' })])
);
});
it('returns 422 when experiment_id is not a UUID', async () => {
const res = await request(app)
.post('/api/animals')
.send({ experiment_id: 'bad-id', animal_id_string: 'M-001', animal_name: 'Whiskers' });
expect(res.status).toBe(422);
});
it('returns 422 when animal_name is missing', async () => {
const res = await request(app)
.post('/api/animals')
.send({ experiment_id: EXP_ID, animal_id_string: 'M-001' });
expect(res.status).toBe(422);
});
});
describe('PUT /api/animals/:id', () => {
it('updates animal and writes UPDATE audit log', async () => {
const updated = { ...ANIMAL, animal_name: 'Mr Whiskers' };
prisma.animal.findUnique
.mockResolvedValueOnce(ANIMAL)
.mockResolvedValueOnce(updated);
prisma.animal.update.mockResolvedValue(updated);
prisma.auditLog.create.mockResolvedValue({});
const res = await request(app)
.put(`/api/animals/${ANIMAL.id}`)
.send({ animal_name: 'Mr Whiskers' });
expect(res.status).toBe(200);
expect(res.body.animal_name).toBe('Mr Whiskers');
expect(prisma.auditLog.create).toHaveBeenCalledWith(
expect.objectContaining({ data: expect.objectContaining({ action: 'UPDATE' }) })
);
});
});
describe('DELETE /api/animals/:id', () => {
it('deletes animal and writes DELETE audit log', async () => {
prisma.animal.findUnique.mockResolvedValue(ANIMAL);
prisma.animal.delete.mockResolvedValue(ANIMAL);
prisma.auditLog.create.mockResolvedValue({});
const res = await request(app).delete(`/api/animals/${ANIMAL.id}`);
expect(res.status).toBe(204);
expect(prisma.auditLog.create).toHaveBeenCalledWith(
expect.objectContaining({ data: expect.objectContaining({ action: 'DELETE' }) })
);
});
});