diff --git a/backend/jest-config.js b/backend/jest-config.js index d9aea66e6..8aff03657 100644 --- a/backend/jest-config.js +++ b/backend/jest-config.js @@ -1,17 +1,6 @@ module.exports = { - testPathIgnorePatterns: [ - "/build", - "/node_modules", - ], - collectCoverageFrom: [ - "./src/**", - "!./src/api-tests/**" - ], - roots: [ - "./src" - ], - coverageReporters: [ - "text", - "lcov" - ] + testPathIgnorePatterns: ["/build", "/node_modules", "/../src/api-tests"], + collectCoverageFrom: ["./src/**", "!./src/api-tests/**"], + roots: ["./src"], + coverageReporters: ["text", "lcov"], } diff --git a/backend/package.json b/backend/package.json index f62f08b67..31990060f 100644 --- a/backend/package.json +++ b/backend/package.json @@ -6,8 +6,8 @@ "scripts": { "dev": "npx tsx watch --clear-screen=false src/index.ts", "build": "tsc", - "test:api": "jest src/api-tests --runInBand --coverage --config jest-config.js --detectOpenHandles --forceExit --silent", - "test:unit": "jest src/unit-tests --coverage --config jest-config.js --detectOpenHandles --forceExit --silent", + "test:api": "jest ./src/api-tests --runInBand --coverage --config jest-config.js --detectOpenHandles --forceExit --silent", + "test:unit": "DOTENV_CONFIG_PATH=../.test.env jest src/unit-tests --coverage --config jest-config.js --detectOpenHandles --forceExit --silent --setupFiles dotenv/config", "test:api:local": "DOTENV_CONFIG_PATH=../.test.env npm run test:api -- --setupFiles dotenv/config", "start": "node build/index.js", "lint": "eslint --report-unused-disable-directives --max-warnings 0", diff --git a/backend/src/api-tests/locality/update.test.ts b/backend/src/api-tests/locality/update.test.ts index 894753103..0a8fda828 100644 --- a/backend/src/api-tests/locality/update.test.ts +++ b/backend/src/api-tests/locality/update.test.ts @@ -21,9 +21,11 @@ describe('Locality update works', () => { it('Edits name, synonyms and locality species correctly', async () => { const writeResult = await send<{ id: number }>('locality', 'PUT', { locality: editedLocality }) + expect(writeResult.status).toEqual(200) expect(writeResult.body.id).toEqual(editedLocality.lid) // `Invalid result returned on write: ${writeResult.body.id}` - const { body } = await send(`locality/${editedLocality.lid}`, 'GET') + const { body, status } = await send(`locality/${editedLocality.lid}`, 'GET') + expect(status).toEqual(200) resultLocality = body }) diff --git a/backend/src/api-tests/utils.ts b/backend/src/api-tests/utils.ts index 310c67522..eb2b22a8c 100644 --- a/backend/src/api-tests/utils.ts +++ b/backend/src/api-tests/utils.ts @@ -60,6 +60,9 @@ export const setToken = (newToken: string) => (token = newToken) export const login = async (username: string = 'testSu', password: string = 'test') => { // Login and set token const result = await send<{ token: string }>('user/login', 'POST', { username, password }) + if (!result.body.token || result.status !== 200) { + throw new Error(`Login failed for ${username}: ${result.status}`) + } token = result.body.token } @@ -95,6 +98,7 @@ export const testLogRows = (logRows: UpdateLog[], expectedLogRows: Partial { await send('test/reset-test-database', 'GET') + await send('test/create-test-users', 'GET') } export const resetDatabaseTimeout: number = 1200000 // 120 seconds diff --git a/backend/src/controllers/projectsController.ts b/backend/src/controllers/projectsController.ts index e6070677a..4ee44b3ae 100644 --- a/backend/src/controllers/projectsController.ts +++ b/backend/src/controllers/projectsController.ts @@ -1,14 +1,27 @@ import { Request, Response } from 'express' import { createProject, CreateProjectInput, updateProject, UpdateProjectInput } from '../services/projectsService' +import { NotFoundError, ValidationError } from '../validators/projectsValidator' export const createProjectHandler = async (req: Request, res: Response) => { - const createdProject = await createProject(req.body) - - return res.status(201).json(createdProject) + try { + const createdProject = await createProject(req.body) + return res.status(201).json(createdProject) + } catch (error) { + if (error instanceof ValidationError) { + return res.status(error.status).json({ message: error.message }) + } + throw error + } } export const updateProjectHandler = async (req: Request<{ id: string }, object, UpdateProjectInput>, res: Response) => { - const updatedProject = await updateProject(Number(req.params.id), req.body) - - return res.status(200).json(updatedProject) + try { + const updatedProject = await updateProject(Number(req.params.id), req.body) + return res.status(200).json(updatedProject) + } catch (error) { + if (error instanceof ValidationError || error instanceof NotFoundError) { + return res.status(error.status).json({ message: error.message }) + } + throw error + } } diff --git a/backend/src/services/user.ts b/backend/src/services/user.ts index 51db73135..90502cfc5 100644 --- a/backend/src/services/user.ts +++ b/backend/src/services/user.ts @@ -73,6 +73,13 @@ export const createTestUsers = async () => { userId = createdUser.user_id } else { userId = existingUser.user_id + await nowDb.com_users.update({ + where: { user_id: userId }, + data: { + newpassword: passwordHash, + now_user_group: testUser.now_user_group, + }, + }) } const initials = `TEST-${testUser.now_user_group.toUpperCase()}` const existingPerson = await nowDb.com_people.findFirst({ diff --git a/backend/src/utils/config.ts b/backend/src/utils/config.ts index 63bd6435b..dce4d020f 100644 --- a/backend/src/utils/config.ts +++ b/backend/src/utils/config.ts @@ -52,5 +52,7 @@ const requiredEnvs = { } const missingEnvs = Object.entries(requiredEnvs).filter(([, value]) => value === undefined) -if (missingEnvs.length > 0) +const isJestEnvironment = process.env.JEST_WORKER_ID !== undefined +if (missingEnvs.length > 0 && !isJestEnvironment) { throw new Error(`Missing environment variables: ${missingEnvs.map(env => env[0]).join(', ')}`) +} diff --git a/src/api-tests/species/create.test.ts b/src/api-tests/species/create.test.ts deleted file mode 100644 index d571d5e0a..000000000 --- a/src/api-tests/species/create.test.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { beforeEach, beforeAll, afterAll, describe, it, expect } from '@jest/globals' -import { LocalityDetailsType, SpeciesDetailsType } from '../../../../frontend/src/shared/types' -import { LogRow } from '../../services/write/writeOperations/types' -import { newSpeciesBasis, newSpeciesWithoutRequiredFields } from './data' -import { login, logout, resetDatabase, send, testLogRows, resetDatabaseTimeout, noPermError } from '../utils' -import { pool } from '../../utils/db' - -let createdSpecies: SpeciesDetailsType | null = null - -describe('Creating new species works', () => { - beforeAll(async () => { - await resetDatabase() - }, resetDatabaseTimeout) - beforeEach(async () => { - await login() - }) - afterAll(async () => { - await pool.end() - }) - - it('Request succeeds and returns valid number id', async () => { - const { body: resultBody } = await send<{ species_id: number }>('species', 'PUT', { - species: { ...newSpeciesBasis, comment: 'species test' }, - }) - const { species_id: createdId } = resultBody - - expect(typeof createdId).toEqual('number') // `Invalid result returned on write: ${createdId}` - - const { body } = await send(`species/${createdId}`, 'GET') - createdSpecies = body - }) - - it('Contains correct data', () => { - const { species_name, now_ls } = createdSpecies! - expect(species_name).toEqual(newSpeciesBasis.species_name) // 'Name is different' - const locality = now_ls.find(ls => ls.now_loc.lid === 24750) - expect(!!locality).toEqual(true) // 'Locality in locality-species not found' - }) - - it('Locality-species change was updated also to locality', async () => { - const localityFound = createdSpecies!.now_ls.find(ls => ls.now_loc.loc_name.startsWith('Romany')) - if (!localityFound) throw new Error('Locality was not found in now_ls') - const speciesResult = await send(`locality/24750`, 'GET') - const update = speciesResult.body.now_lau.find(lau => lau.lid === 24750 && lau.lau_comment === 'species test') - if (!update) throw new Error('Update not found') - const logRows = update.updates - const expectedLogRows: Partial[] = [ - { - oldValue: null, - value: '24750', - type: 'add', - column: 'lid', - table: 'now_ls', - }, - ] - testLogRows(logRows, expectedLogRows, 2) - }) - - it('Species without required fields fails', async () => { - const res = await send('species', 'PUT', { - species: { ...newSpeciesWithoutRequiredFields, comment: 'species test' }, - }) - expect(res.status).toEqual(403) - }) - - it('Creation fails without reference', async () => { - const resultNoRef = await send('species', 'PUT', { - species: { ...newSpeciesBasis, references: [] }, - }) - expect(resultNoRef.status).toEqual(403) // can't create one without a reference - - const resultWithRef = await send('species', 'PUT', { - species: { ...newSpeciesBasis }, - }) - expect(resultWithRef.status).toEqual(200) - }) - - it('Creation fails without permissions for non-authenticated and non-privileged users', async () => { - logout() - const result1 = await send('species', 'PUT', { - species: { ...newSpeciesBasis, comment: 'species test' }, - }) - expect(result1.body).toEqual(noPermError) - expect(result1.status).toEqual(403) - - logout() - await login('testEr', 'test') - const result2 = await send('species', 'PUT', { - species: { ...newSpeciesBasis, comment: 'species test' }, - }) - expect(result2.body).toEqual(noPermError) - expect(result2.status).toEqual(403) - }) -}) \ No newline at end of file diff --git a/test_data/sqlfiles/now_test.sql b/test_data/sqlfiles/now_test.sql index f4833a7e5..51a0f1bd9 100755 --- a/test_data/sqlfiles/now_test.sql +++ b/test_data/sqlfiles/now_test.sql @@ -376,11 +376,11 @@ INSERT INTO `com_users` VALUES (160,'project',NULL,'0cc175b9c0f1b6a831c399e269772661','2024-05-27','pl',NULL,NULL), (161,'read',NULL,'0cc175b9c0f1b6a831c399e269772661','2024-05-23','ro',NULL,NULL), (162,'coord',NULL,'0cc175b9c0f1b6a831c399e269772661','2024-05-27','su',NULL,NULL), -(163,'testSu',NULL,'$2b$10$hPVP4Zl9WmKle5S42WIeBu3otM2eV0BWB21eXxn/YEcMm7TNJiQ02',NULL,'su',NULL,NULL), -(164,'testEu',NULL,'$2b$10$hPVP4Zl9WmKle5S42WIeBu3otM2eV0BWB21eXxn/YEcMm7TNJiQ02',NULL,'eu',NULL,NULL), -(165,'testEr',NULL,'$2b$10$hPVP4Zl9WmKle5S42WIeBu3otM2eV0BWB21eXxn/YEcMm7TNJiQ02',NULL,'er',NULL,NULL), -(166,'testNo',NULL,'$2b$10$hPVP4Zl9WmKle5S42WIeBu3otM2eV0BWB21eXxn/YEcMm7TNJiQ02',NULL,'no',NULL,NULL), -(167,'testPl',NULL,'$2b$10$hPVP4Zl9WmKle5S42WIeBu3otM2eV0BWB21eXxn/YEcMm7TNJiQ02',NULL,'pl',NULL,NULL); +(163,'testSu',NULL,'$2b$10$JGpnCPk9ONMuE3MNLjO8ce7kRElCyq1hdVgteYZlCCczq5kkKiVVG',NULL,'su',NULL,NULL), +(164,'testEu',NULL,'$2b$10$JGpnCPk9ONMuE3MNLjO8ce7kRElCyq1hdVgteYZlCCczq5kkKiVVG',NULL,'eu',NULL,NULL), +(165,'testEr',NULL,'$2b$10$JGpnCPk9ONMuE3MNLjO8ce7kRElCyq1hdVgteYZlCCczq5kkKiVVG',NULL,'er',NULL,NULL), +(166,'testNo',NULL,'$2b$10$JGpnCPk9ONMuE3MNLjO8ce7kRElCyq1hdVgteYZlCCczq5kkKiVVG',NULL,'no',NULL,NULL), +(167,'testPl',NULL,'$2b$10$JGpnCPk9ONMuE3MNLjO8ce7kRElCyq1hdVgteYZlCCczq5kkKiVVG',NULL,'pl',NULL,NULL); /*!40000 ALTER TABLE `com_users` ENABLE KEYS */; UNLOCK TABLES; @@ -2241,4 +2241,3 @@ INSERT INTO `ref_ref_type` VALUES (14,'Undefined'); /*!40000 ALTER TABLE `ref_ref_type` ENABLE KEYS */; UNLOCK TABLES; -