Merge pull request #1513 from gradido/backend-tests-running-again

fix: Backend Unit Tests Running Again
This commit is contained in:
Moriz Wahl 2022-02-21 16:02:28 +01:00 committed by GitHub
commit 5efb7115de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 95 additions and 157 deletions

View File

@ -431,7 +431,7 @@ jobs:
unit_test_backend:
name: Unit tests - Backend
runs-on: ubuntu-latest
needs: [build_test_backend,build_test_mariadb]
needs: [build_test_mariadb]
steps:
##########################################################################
# CHECKOUT CODE ##########################################################
@ -448,13 +448,6 @@ jobs:
path: /tmp
- name: Load Docker Image
run: docker load < /tmp/mariadb.tar
- name: Download Docker Image (Backend)
uses: actions/download-artifact@v2
with:
name: docker-backend-test
path: /tmp
- name: Load Docker Image
run: docker load < /tmp/backend.tar
##########################################################################
# UNIT TESTS BACKEND #####################################################
##########################################################################
@ -469,7 +462,7 @@ jobs:
run: sleep 30s
shell: bash
- name: backend Unit tests | test
run: cd database && yarn && yarn build && cd ../backend && yarn && yarn CI_workflow_test
run: cd database && yarn && yarn build && cd ../backend && yarn && yarn test
# run: docker-compose -f docker-compose.yml -f docker-compose.test.yml exec -T backend yarn test
##########################################################################
# COVERAGE CHECK BACKEND #################################################
@ -480,7 +473,7 @@ jobs:
report_name: Coverage Backend
type: lcov
result_path: ./backend/coverage/lcov.info
min_coverage: 38
min_coverage: 48
token: ${{ github.token }}
##########################################################################

View File

@ -1,21 +1,18 @@
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
module.exports = async () => {
process.env.TZ = 'UTC'
return {
verbose: true,
preset: 'ts-jest',
collectCoverage: true,
collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**'],
moduleNameMapper: {
'@entity/(.*)': '<rootDir>/../database/build/entity/$1',
// This is hack to fix a problem with the library `ts-mysql-migrate` which does differentiate between its ts/js state
'@dbTools/(.*)': '<rootDir>/../database/src/$1',
/*
'@dbTools/(.*)':
process.env.NODE_ENV === 'development'
? '<rootDir>/../database/src/$1'
: '<rootDir>/../database/build/src/$1',
*/
},
}
module.exports = {
verbose: true,
preset: 'ts-jest',
collectCoverage: true,
collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**'],
setupFiles: ['<rootDir>/test/testSetup.ts'],
moduleNameMapper: {
'@entity/(.*)':
process.env.NODE_ENV === 'development'
? '<rootDir>/../database/entity/$1'
: '<rootDir>/../database/build/entity/$1',
'@dbTools/(.*)':
process.env.NODE_ENV === 'development'
? '<rootDir>/../database/src/$1'
: '<rootDir>/../database/build/src/$1',
},
}

View File

@ -13,8 +13,7 @@
"start": "node build/index.js",
"dev": "nodemon -w src --ext ts --exec ts-node src/index.ts",
"lint": "eslint . --ext .js,.ts",
"CI_workflow_test": "jest --runInBand --coverage ",
"test": "NODE_ENV=development jest --runInBand --coverage "
"test": "TZ=UTC NODE_ENV=development jest --runInBand --coverage --forceExit --detectOpenHandles"
},
"dependencies": {
"@types/jest": "^27.0.2",

View File

@ -6,14 +6,13 @@ import gql from 'graphql-tag'
import { GraphQLError } from 'graphql'
import createServer from '../../server/createServer'
import { resetDB, initialize } from '@dbTools/helpers'
import { getRepository } from 'typeorm'
import { LoginUser } from '@entity/LoginUser'
import { LoginUserBackup } from '@entity/LoginUserBackup'
import { LoginEmailOptIn } from '@entity/LoginEmailOptIn'
import { User } from '@entity/User'
import CONFIG from '../../config'
import { sendAccountActivationEmail } from '../../mailer/sendAccountActivationEmail'
import { klicktippSignIn } from '../../apis/KlicktippController'
// import { klicktippSignIn } from '../../apis/KlicktippController'
jest.setTimeout(10000)
jest.mock('../../mailer/sendAccountActivationEmail', () => {
return {
@ -22,12 +21,14 @@ jest.mock('../../mailer/sendAccountActivationEmail', () => {
}
})
/*
jest.mock('../../apis/KlicktippController', () => {
return {
__esModule: true,
klicktippSignIn: jest.fn(),
}
})
*/
let mutate: any
let con: any
@ -40,6 +41,11 @@ beforeAll(async () => {
await resetDB()
})
afterAll(async () => {
await resetDB(true)
await con.close()
})
describe('UserResolver', () => {
describe('createUser', () => {
const variables = {
@ -84,70 +90,32 @@ describe('UserResolver', () => {
})
describe('valid input data', () => {
let loginUser: LoginUser[]
let user: User[]
let loginUserBackup: LoginUserBackup[]
let loginEmailOptIn: LoginEmailOptIn[]
beforeAll(async () => {
loginUser = await getRepository(LoginUser).createQueryBuilder('login_user').getMany()
user = await getRepository(User).createQueryBuilder('state_user').getMany()
loginUserBackup = await getRepository(LoginUserBackup)
.createQueryBuilder('login_user_backup')
.getMany()
loginEmailOptIn = await getRepository(LoginEmailOptIn)
.createQueryBuilder('login_email_optin')
.getMany()
user = await User.find()
loginEmailOptIn = await LoginEmailOptIn.find()
emailOptIn = loginEmailOptIn[0].verificationCode.toString()
})
describe('filling all tables', () => {
it('saves the user in login_user table', () => {
expect(loginUser).toEqual([
expect(user).toEqual([
{
id: expect.any(Number),
email: 'peter@lustig.de',
firstName: 'Peter',
lastName: 'Lustig',
username: '',
description: '',
password: '0',
pubKey: null,
privKey: null,
emailHash: expect.any(Buffer),
createdAt: expect.any(Date),
emailChecked: false,
passphraseShown: false,
language: 'de',
disabled: false,
groupId: 1,
publisherId: 1234,
},
])
})
it('saves the user in state_user table', () => {
expect(user).toEqual([
{
id: expect.any(Number),
indexId: 0,
groupId: 0,
pubkey: expect.any(Buffer),
email: 'peter@lustig.de',
firstName: 'Peter',
lastName: 'Lustig',
username: '',
disabled: false,
},
])
})
it('saves the user in login_user_backup table', () => {
expect(loginUserBackup).toEqual([
{
id: expect.any(Number),
passphrase: expect.any(String),
userId: loginUser[0].id,
mnemonicType: 2,
language: 'de',
deletedAt: null,
publisherId: 1234,
},
])
})
@ -156,7 +124,7 @@ describe('UserResolver', () => {
expect(loginEmailOptIn).toEqual([
{
id: expect.any(Number),
userId: loginUser[0].id,
userId: user[0].id,
verificationCode: expect.any(String),
emailOptInTypeId: 1,
createdAt: expect.any(Date),
@ -196,9 +164,7 @@ describe('UserResolver', () => {
mutation,
variables: { ...variables, email: 'bibi@bloxberg.de', language: 'es' },
})
await expect(
getRepository(LoginUser).createQueryBuilder('login_user').getMany(),
).resolves.toEqual(
await expect(User.find()).resolves.toEqual(
expect.arrayContaining([
expect.objectContaining({
email: 'bibi@bloxberg.de',
@ -215,9 +181,7 @@ describe('UserResolver', () => {
mutation,
variables: { ...variables, email: 'raeuber@hotzenplotz.de', publisherId: undefined },
})
await expect(
getRepository(LoginUser).createQueryBuilder('login_user').getMany(),
).resolves.toEqual(
await expect(User.find()).resolves.toEqual(
expect.arrayContaining([
expect.objectContaining({
email: 'raeuber@hotzenplotz.de',
@ -265,23 +229,17 @@ describe('UserResolver', () => {
let emailOptIn: string
describe('valid optin code and valid password', () => {
let loginUser: any
let newLoginUser: any
let newUser: any
beforeAll(async () => {
await mutate({ mutation: createUserMutation, variables: createUserVariables })
const loginEmailOptIn = await getRepository(LoginEmailOptIn)
.createQueryBuilder('login_email_optin')
.getMany()
loginUser = await getRepository(LoginUser).createQueryBuilder('login_user').getMany()
const loginEmailOptIn = await LoginEmailOptIn.find()
emailOptIn = loginEmailOptIn[0].verificationCode.toString()
result = await mutate({
mutation: setPasswordMutation,
variables: { code: emailOptIn, password: 'Aa12345_' },
})
newLoginUser = await getRepository(LoginUser).createQueryBuilder('login_user').getMany()
newUser = await getRepository(User).createQueryBuilder('state_user').getMany()
newUser = await User.find()
})
afterAll(async () => {
@ -289,38 +247,27 @@ describe('UserResolver', () => {
})
it('sets email checked to true', () => {
expect(newLoginUser[0].emailChecked).toBeTruthy()
expect(newUser[0].emailChecked).toBeTruthy()
})
it('updates the password', () => {
expect(newLoginUser[0].password).toEqual('3917921995996627700')
})
it('updates the public Key on both user tables', () => {
expect(newLoginUser[0].pubKey).toEqual(expect.any(Buffer))
expect(newLoginUser[0].pubKey).not.toEqual(loginUser[0].pubKey)
expect(newLoginUser[0].pubKey).toEqual(newUser[0].pubkey)
})
it('updates the private Key', () => {
expect(newLoginUser[0].privKey).toEqual(expect.any(Buffer))
expect(newLoginUser[0].privKey).not.toEqual(loginUser[0].privKey)
expect(newUser[0].password).toEqual('3917921995996627700')
})
it('removes the optin', async () => {
await expect(
getRepository(LoginEmailOptIn).createQueryBuilder('login_email_optin').getMany(),
).resolves.toHaveLength(0)
await expect(LoginEmailOptIn.find()).resolves.toHaveLength(0)
})
/*
it('calls the klicktipp API', () => {
expect(klicktippSignIn).toBeCalledWith(
loginUser[0].email,
loginUser[0].language,
loginUser[0].firstName,
loginUser[0].lastName,
user[0].email,
user[0].language,
user[0].firstName,
user[0].lastName,
)
})
*/
it('returns true', () => {
expect(result).toBeTruthy()
@ -330,9 +277,7 @@ describe('UserResolver', () => {
describe('no valid password', () => {
beforeAll(async () => {
await mutate({ mutation: createUserMutation, variables: createUserVariables })
const loginEmailOptIn = await getRepository(LoginEmailOptIn)
.createQueryBuilder('login_email_optin')
.getMany()
const loginEmailOptIn = await LoginEmailOptIn.find()
emailOptIn = loginEmailOptIn[0].verificationCode.toString()
result = await mutate({
mutation: setPasswordMutation,
@ -380,8 +325,3 @@ describe('UserResolver', () => {
})
})
})
afterAll(async () => {
await resetDB(true)
await con.close()
})

View File

@ -4,40 +4,42 @@
import { ApolloLogPlugin, LogMutateData } from 'apollo-log'
import cloneDeep from 'lodash.clonedeep'
const plugins = [
{
requestDidStart() {
return {
willSendResponse(requestContext: any) {
const { setHeaders = [] } = requestContext.context
setHeaders.forEach(({ key, value }: { [key: string]: string }) => {
if (requestContext.response.http.headers.get(key)) {
requestContext.response.http.headers.set(key, value)
} else {
requestContext.response.http.headers.append(key, value)
}
})
return requestContext
},
}
},
const setHeadersPlugin = {
requestDidStart() {
return {
willSendResponse(requestContext: any) {
const { setHeaders = [] } = requestContext.context
setHeaders.forEach(({ key, value }: { [key: string]: string }) => {
if (requestContext.response.http.headers.get(key)) {
requestContext.response.http.headers.set(key, value)
} else {
requestContext.response.http.headers.append(key, value)
}
})
return requestContext
},
}
},
ApolloLogPlugin({
mutate: (data: LogMutateData) => {
// We need to deep clone the object in order to not modify the actual request
const dataCopy = cloneDeep(data)
}
// mask password if part of the query
if (dataCopy.context.request.variables && dataCopy.context.request.variables.password) {
dataCopy.context.request.variables.password = '***'
}
const apolloLogPlugin = ApolloLogPlugin({
mutate: (data: LogMutateData) => {
// We need to deep clone the object in order to not modify the actual request
const dataCopy = cloneDeep(data)
// mask token at all times
dataCopy.context.context.token = '***'
// mask password if part of the query
if (dataCopy.context.request.variables && dataCopy.context.request.variables.password) {
dataCopy.context.request.variables.password = '***'
}
return dataCopy
},
}),
]
// mask token at all times
dataCopy.context.context.token = '***'
return dataCopy
},
})
const plugins =
process.env.NODE_ENV === 'development' ? [setHeadersPlugin] : [setHeadersPlugin, apolloLogPlugin]
export default plugins

View File

@ -0,0 +1,6 @@
/* eslint-disable no-console */
// disable console.info for apollo log
// eslint-disable-next-line @typescript-eslint/no-empty-function
console.info = () => {}

View File

@ -5,19 +5,20 @@
* This also removes the trailing space
*/
import fs from 'fs'
import path from 'path'
const TARGET_MNEMONIC_TYPE = 2
const PHRASE_WORD_COUNT = 24
const WORDS_MNEMONIC_0 = fs
.readFileSync('src/config/mnemonic.uncompressed_buffer18112.txt')
.readFileSync(path.resolve(__dirname, '../src/config/mnemonic.uncompressed_buffer18112.txt'))
.toString()
.split(',')
const WORDS_MNEMONIC_1 = fs
.readFileSync('src/config/mnemonic.uncompressed_buffer18113.txt')
.readFileSync(path.resolve(__dirname, '../src/config/mnemonic.uncompressed_buffer18113.txt'))
.toString()
.split(',')
const WORDS_MNEMONIC_2 = fs
.readFileSync('src/config/mnemonic.uncompressed_buffer13116.txt')
.readFileSync(path.resolve(__dirname, '../src/config/mnemonic.uncompressed_buffer13116.txt'))
.toString()
.split(',')
const WORDS_MNEMONIC = [WORDS_MNEMONIC_0, WORDS_MNEMONIC_1, WORDS_MNEMONIC_2]

View File

@ -8,7 +8,7 @@
"license": "MIT",
"private": false,
"scripts": {
"build": "tsc --build",
"build": "mkdir -p build/src/config/ && cp src/config/*.txt build/src/config/ && tsc --build",
"clean": "tsc --build --clean",
"up": "node build/src/index.js up",
"down": "node build/src/index.js down",