From ac3969d409162c3ace5b46251e8dcbe75dd29cde Mon Sep 17 00:00:00 2001 From: Tereshchenko Aleksandr Date: Wed, 15 May 2024 15:29:41 +0300 Subject: [PATCH] Add integration tests for /theses route --- setupIntegrationTests.ts | 17 +- src/server/db/models/User.ts | 2 + src/server/routes/thesis.integration-test.js | 228 +++++++++++++++++++ src/server/routes/thesis.integration-test.ts | 17 -- src/server/routes/thesis.ts | 4 +- 5 files changed, 248 insertions(+), 20 deletions(-) create mode 100644 src/server/routes/thesis.integration-test.js delete mode 100644 src/server/routes/thesis.integration-test.ts diff --git a/setupIntegrationTests.ts b/setupIntegrationTests.ts index e14efb0..9ae782b 100644 --- a/setupIntegrationTests.ts +++ b/setupIntegrationTests.ts @@ -2,6 +2,13 @@ import supertest from 'supertest' import { jest } from '@jest/globals' import * as db from './src/server/db/connection' import app from './src/server/index' +import { + Attachment, + Author, + Supervision, + Thesis, + User, +} from './src/server/db/models' // eslint-disable-next-line no-promise-executor-return const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) @@ -11,7 +18,7 @@ global.jest = jest // eslint-disable-next-line import/no-mutable-exports let request: any = null -global.beforeEach(async () => { +global.beforeAll(async () => { // retry to connect to the DB in case the test runner starts before the DB is ready await db.connectToDatabase(3) request = supertest(app) @@ -33,6 +40,14 @@ global.beforeEach(async () => { }, 20000) global.afterEach(async () => { + await Attachment.destroy({ where: {} }) + await Supervision.destroy({ where: {} }) + await Author.destroy({ where: {} }) + await Thesis.destroy({ where: {} }) + await User.destroy({ where: {} }) +}) + +global.afterAll(async () => { await db.resetDatabase() }) diff --git a/src/server/db/models/User.ts b/src/server/db/models/User.ts index da2e6fa..07221c2 100644 --- a/src/server/db/models/User.ts +++ b/src/server/db/models/User.ts @@ -3,6 +3,7 @@ import { InferAttributes, InferCreationAttributes, DataTypes, + UUIDV4, } from 'sequelize' import { sequelize } from '../connection' @@ -28,6 +29,7 @@ User.init( id: { type: DataTypes.STRING, allowNull: false, + defaultValue: UUIDV4, primaryKey: true, }, username: { diff --git a/src/server/routes/thesis.integration-test.js b/src/server/routes/thesis.integration-test.js new file mode 100644 index 0000000..da998f8 --- /dev/null +++ b/src/server/routes/thesis.integration-test.js @@ -0,0 +1,228 @@ +import supertest from 'supertest' +import fs from 'fs' +import path from 'path' +import { dirname } from 'node:path' +import { fileURLToPath } from 'node:url' + +import app from '../index' +import { Attachment, Author, Supervision, Thesis, User } from '../db/models' + +const request = supertest(app) + +describe('thesis router', () => { + let mockUnlinkSync + beforeEach(() => { + mockUnlinkSync = jest.fn() + fs.unlinkSync = mockUnlinkSync + }) + + describe('when there are no theses', () => { + describe('GET /api/theses', () => { + it('should return 200 and an empty array', async () => { + const response = await request.get('/api/theses') + expect(response.status).toEqual(200) + expect(response.body).toEqual([]) + }) + }) + }) + + describe('when there are theses saved in the DB', () => { + let user1 + let user2 + let thesis1 + + beforeEach(async () => { + user1 = await User.create({ + username: 'test1', + firstName: 'test1', + lastName: 'test1', + email: 'test@test.test1', + language: 'fi', + }) + user2 = await User.create({ + username: 'test2', + firstName: 'test2', + lastName: 'test2', + email: 'test@test.test2', + language: 'fi', + }) + thesis1 = await Thesis.create({ + programId: 'Testing program', + topic: 'test topic', + status: 'PLANNING', + startDate: '1970-01-01', + targetDate: '2070-01-01', + }) + await Supervision.create({ + userId: user1.id, + thesisId: thesis1.id, + percentage: 100, + }) + await Author.create({ + userId: user2.id, + thesisId: thesis1.id, + }) + await Attachment.create({ + thesisId: thesis1.id, + label: 'researchPlan', + filename: 'testfile.pdf1', + mimetype: 'application/pdf1', + originalname: 'testfile.pdf1', + }) + await Attachment.create({ + thesisId: thesis1.id, + label: 'waysOfWorking', + filename: 'testfile.pdf2', + mimetype: 'application/pdf2', + originalname: 'testfile.pdf2', + }) + }) + + describe('GET /api/theses', () => { + it('should return 200 and the theses correctly associated with users and attachments', async () => { + const response = await request.get('/api/theses') + expect(response.status).toEqual(200) + expect(response.body).toMatchObject([ + { + programId: 'Testing program', + topic: 'test topic', + status: 'PLANNING', + startDate: '1970-01-01T00:00:00.000Z', + targetDate: '2070-01-01T00:00:00.000Z', + supervisions: [ + { + userId: user1.id, + percentage: 100, + }, + ], + authors: [ + { + userId: user2.id, + }, + ], + researchPlan: { + filename: 'testfile.pdf1', + name: 'testfile.pdf1', + mimetype: 'application/pdf1', + }, + waysOfWorking: { + filename: 'testfile.pdf2', + name: 'testfile.pdf2', + mimetype: 'application/pdf2', + }, + }, + ]) + }) + }) + + describe('DELETE /api/theses/:id', () => { + it('should return 204 and delete the thesis', async () => { + const response = await request.delete(`/api/theses/${thesis1.id}`) + expect(response.status).toEqual(204) + const thesis = await Thesis.findByPk(thesis1.id) + expect(thesis).toBeNull() + + expect(fs.unlinkSync).toHaveBeenCalledTimes(2) + expect(fs.unlinkSync).toHaveBeenCalledWith( + '/opt/app-root/src/uploads/testfile.pdf1' + ) + expect(fs.unlinkSync).toHaveBeenCalledWith( + '/opt/app-root/src/uploads/testfile.pdf2' + ) + }) + }) + + describe('POST /api/theses', () => { + it('should return 201 and create a new thesis', async () => { + const newThesis = { + programId: 'New program', + topic: 'New topic', + status: 'PLANNING', + startDate: '1970-01-01T00:00:00.000Z', + targetDate: '2070-01-01T00:00:00.000Z', + supervisions: [ + { + userId: user1.id, + percentage: 100, + }, + ], + authors: [ + { + userId: user2.id, + }, + ], + } + const response = await request + .post('/api/theses') + .attach( + 'waysOfWorking', + path.resolve(dirname(fileURLToPath(import.meta.url)), './index.ts') + ) + .attach( + 'researchPlan', + path.resolve(dirname(fileURLToPath(import.meta.url)), './index.ts') + ) + .field('json', JSON.stringify(newThesis)) + expect(response.status).toEqual(201) + + delete newThesis.supervisions + delete newThesis.authors + expect(response.body).toMatchObject(newThesis) + + const thesis = await Thesis.findByPk(response.body.id) + expect(thesis).not.toBeNull() + }) + }) + + describe('PUT /api/theses/:id', () => { + it('should return 200 and update the thesis', async () => { + const updatedThesis = { + programId: 'Updated program', + topic: 'Updated topic', + status: 'PLANNING', + startDate: '1970-01-01T00:00:00.000Z', + targetDate: '2070-01-01T00:00:00.000Z', + supervisions: [ + { + userId: user1.id, + percentage: 100, + }, + ], + authors: [ + { + userId: user2.id, + }, + ], + } + const response = await request + .put(`/api/theses/${thesis1.id}`) + .attach( + 'waysOfWorking', + path.resolve(dirname(fileURLToPath(import.meta.url)), './index.ts') + ) + .attach( + 'researchPlan', + path.resolve(dirname(fileURLToPath(import.meta.url)), './index.ts') + ) + .field('json', JSON.stringify(updatedThesis)) + + expect(fs.unlinkSync).toHaveBeenCalledTimes(2) + expect(fs.unlinkSync).toHaveBeenCalledWith( + '/opt/app-root/src/uploads/testfile.pdf1' + ) + expect(fs.unlinkSync).toHaveBeenCalledWith( + '/opt/app-root/src/uploads/testfile.pdf2' + ) + + expect(response.status).toEqual(200) + delete updatedThesis.supervisions + delete updatedThesis.authors + expect(response.body).toMatchObject(updatedThesis) + + const thesis = await Thesis.findByPk(thesis1.id) + expect(thesis.programId).toEqual('Updated program') + expect(thesis.topic).toEqual('Updated topic') + }) + }) + }) +}) diff --git a/src/server/routes/thesis.integration-test.ts b/src/server/routes/thesis.integration-test.ts deleted file mode 100644 index 50c6720..0000000 --- a/src/server/routes/thesis.integration-test.ts +++ /dev/null @@ -1,17 +0,0 @@ -import supertest from 'supertest' - -import app from '../index' -import { User } from '../db/models' - -const request = supertest(app) - -describe('testing the test', () => { - it('should pass', async () => { - const users = await User.findAll() - expect(users).toEqual([]) - - const response = await request.get('/api/theses') - expect(response.status).toEqual(200) - expect(response.body).toEqual([]) - }) -}) diff --git a/src/server/routes/thesis.ts b/src/server/routes/thesis.ts index 552a8f0..5a05ea0 100644 --- a/src/server/routes/thesis.ts +++ b/src/server/routes/thesis.ts @@ -200,7 +200,7 @@ thesisRouter.post( return newThesis.toJSON() }) - res.send(createdThesis) + res.status(201).send(createdThesis) } ) @@ -234,7 +234,7 @@ thesisRouter.delete('/:id', async (req, res) => { await deleteThesis(id, t) }) - res.send(`Deleted thesis with id ${id}`) + res.status(204).send(`Deleted thesis with id ${id}`) }) export default thesisRouter