Merge branch 'master' into model-transaction-link

This commit is contained in:
Moriz Wahl 2022-03-09 15:51:52 +01:00 committed by GitHub
commit 0bc070b784
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 218 additions and 77 deletions

View File

@ -11,6 +11,7 @@ module.exports = {
'@arg/(.*)': '<rootDir>/src/graphql/arg/$1',
'@enum/(.*)': '<rootDir>/src/graphql/enum/$1',
'@repository/(.*)': '<rootDir>/src/typeorm/repository/$1',
'@test/(.*)': '<rootDir>/test/$1',
'@entity/(.*)':
process.env.NODE_ENV === 'development'
? '<rootDir>/../database/entity/$1'

View File

@ -1,11 +1,11 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { createTestClient } from 'apollo-server-testing'
import { testEnvironment, resetEntities, createUser } from '@test/helpers'
import { createUserMutation, setPasswordMutation } from '@test/graphql'
import gql from 'graphql-tag'
import { GraphQLError } from 'graphql'
import createServer from '@/server/createServer'
import { resetDB, initialize } from '@dbTools/helpers'
import { resetDB } from '@dbTools/helpers'
import { LoginEmailOptIn } from '@entity/LoginEmailOptIn'
import { User } from '@entity/User'
import CONFIG from '@/config'
@ -30,15 +30,25 @@ jest.mock('@/apis/KlicktippController', () => {
})
*/
let mutate: any
let con: any
let token: string
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const headerPushMock = jest.fn((t) => (token = t.value))
const context = {
setHeaders: {
push: headerPushMock,
forEach: jest.fn(),
},
}
let mutate: any, query: any, con: any
beforeAll(async () => {
const server = await createServer({})
con = server.con
mutate = createTestClient(server.apollo).mutate
await initialize()
await resetDB()
const testEnv = await testEnvironment(context)
mutate = testEnv.mutate
query = testEnv.query
con = testEnv.con
})
afterAll(async () => {
@ -56,33 +66,16 @@ describe('UserResolver', () => {
publisherId: 1234,
}
const mutation = gql`
mutation (
$email: String!
$firstName: String!
$lastName: String!
$language: String!
$publisherId: Int
) {
createUser(
email: $email
firstName: $firstName
lastName: $lastName
language: $language
publisherId: $publisherId
)
}
`
let result: any
let emailOptIn: string
beforeAll(async () => {
result = await mutate({ mutation, variables })
jest.clearAllMocks()
result = await mutate({ mutation: createUserMutation, variables })
})
afterAll(async () => {
await resetDB()
await resetEntities([User, LoginEmailOptIn])
})
it('returns success', () => {
@ -150,7 +143,7 @@ describe('UserResolver', () => {
describe('email already exists', () => {
it('throws an error', async () => {
await expect(mutate({ mutation, variables })).resolves.toEqual(
await expect(mutate({ mutation: createUserMutation, variables })).resolves.toEqual(
expect.objectContaining({
errors: [new GraphQLError('User already exists.')],
}),
@ -161,7 +154,7 @@ describe('UserResolver', () => {
describe('unknown language', () => {
it('sets "de" as default language', async () => {
await mutate({
mutation,
mutation: createUserMutation,
variables: { ...variables, email: 'bibi@bloxberg.de', language: 'es' },
})
await expect(User.find()).resolves.toEqual(
@ -178,7 +171,7 @@ describe('UserResolver', () => {
describe('no publisher id', () => {
it('sets publisher id to null', async () => {
await mutate({
mutation,
mutation: createUserMutation,
variables: { ...variables, email: 'raeuber@hotzenplotz.de', publisherId: undefined },
})
await expect(User.find()).resolves.toEqual(
@ -194,24 +187,6 @@ describe('UserResolver', () => {
})
describe('setPassword', () => {
const createUserMutation = gql`
mutation (
$email: String!
$firstName: String!
$lastName: String!
$language: String!
$publisherId: Int
) {
createUser(
email: $email
firstName: $firstName
lastName: $lastName
language: $language
publisherId: $publisherId
)
}
`
const createUserVariables = {
email: 'peter@lustig.de',
firstName: 'Peter',
@ -220,11 +195,6 @@ describe('UserResolver', () => {
publisherId: 1234,
}
const setPasswordMutation = gql`
mutation ($code: String!, $password: String!) {
setPassword(code: $code, password: $password)
}
`
let result: any
let emailOptIn: string
@ -243,7 +213,7 @@ describe('UserResolver', () => {
})
afterAll(async () => {
await resetDB()
await resetEntities([User, LoginEmailOptIn])
})
it('sets email checked to true', () => {
@ -286,7 +256,7 @@ describe('UserResolver', () => {
})
afterAll(async () => {
await resetDB()
await resetEntities([User, LoginEmailOptIn])
})
it('throws an error', () => {
@ -312,7 +282,7 @@ describe('UserResolver', () => {
})
afterAll(async () => {
await resetDB()
await resetEntities([User, LoginEmailOptIn])
})
it('throws an error', () => {
@ -324,4 +294,93 @@ describe('UserResolver', () => {
})
})
})
describe('login', () => {
const loginQuery = gql`
query ($email: String!, $password: String!, $publisherId: Int) {
login(email: $email, password: $password, publisherId: $publisherId) {
email
firstName
lastName
language
coinanimation
klickTipp {
newsletterState
}
hasElopage
publisherId
isAdmin
}
}
`
const variables = {
email: 'peter@lustig.de',
password: 'Aa12345_',
publisherId: 1234,
}
let result: User
afterAll(async () => {
await resetEntities([User, LoginEmailOptIn])
})
describe('no users in database', () => {
beforeAll(async () => {
result = await query({ query: loginQuery, variables })
})
it('throws an error', () => {
expect(result).toEqual(
expect.objectContaining({
errors: [new GraphQLError('No user with this credentials')],
}),
)
})
})
describe('user is in database', () => {
beforeAll(async () => {
await createUser(mutate, {
email: 'peter@lustig.de',
firstName: 'Peter',
lastName: 'Lustig',
language: 'de',
publisherId: 1234,
})
result = await query({ query: loginQuery, variables })
})
afterAll(async () => {
await resetEntities([User, LoginEmailOptIn])
})
it('returns the user object', () => {
expect(result).toEqual(
expect.objectContaining({
data: {
login: {
coinanimation: true,
email: 'peter@lustig.de',
firstName: 'Peter',
hasElopage: false,
isAdmin: false,
klickTipp: {
newsletterState: false,
},
language: 'de',
lastName: 'Lustig',
publisherId: 1234,
},
},
}),
)
})
it('sets the token in the header', () => {
expect(headerPushMock).toBeCalledWith({ key: 'token', value: expect.any(String) })
})
})
})
})

25
backend/test/graphql.ts Normal file
View File

@ -0,0 +1,25 @@
import gql from 'graphql-tag'
export const createUserMutation = gql`
mutation (
$email: String!
$firstName: String!
$lastName: String!
$language: String!
$publisherId: Int
) {
createUser(
email: $email
firstName: $firstName
lastName: $lastName
language: $language
publisherId: $publisherId
)
}
`
export const setPasswordMutation = gql`
mutation ($code: String!, $password: String!) {
setPassword(code: $code, password: $password)
}
`

46
backend/test/helpers.ts Normal file
View File

@ -0,0 +1,46 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { createTestClient } from 'apollo-server-testing'
import createServer from '../src/server/createServer'
import { resetDB, initialize } from '@dbTools/helpers'
import { createUserMutation, setPasswordMutation } from './graphql'
import { LoginEmailOptIn } from '@entity/LoginEmailOptIn'
import { User } from '@entity/User'
export const testEnvironment = async (context: any) => {
const server = await createServer(context)
const con = server.con
const testClient = createTestClient(server.apollo)
const mutate = testClient.mutate
const query = testClient.query
await initialize()
await resetDB()
return { mutate, query, con }
}
export const resetEntity = async (entity: any) => {
const items = await entity.find()
if (items.length > 0) {
const ids = items.map((i: any) => i.id)
await entity.delete(ids)
}
}
export const resetEntities = async (entities: any[]) => {
for (let i = 0; i < entities.length; i++) {
await resetEntity(entities[i])
}
}
export const createUser = async (mutate: any, user: any) => {
await mutate({ mutation: createUserMutation, variables: user })
const dbUser = await User.findOne({ where: { email: user.email } })
if (!dbUser) throw new Error('Ups, no user found')
const optin = await LoginEmailOptIn.findOne(dbUser.id)
if (!optin) throw new Error('Ups, no optin found')
await mutate({
mutation: setPasswordMutation,
variables: { password: 'Aa12345_', code: optin.verificationCode },
})
}

View File

@ -53,7 +53,8 @@
"@entity/*": ["../database/entity/*"],
"@enum/*": ["./src/graphql/enum/*"],
"@model/*": ["./src/graphql/model/*"],
"@repository/*": ["./src/typeorm/repository/*"]
"@repository/*": ["./src/typeorm/repository/*"],
"@test/*": ["./test/*"]
},
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */

View File

@ -1,5 +1,6 @@
import CONFIG from './config'
import { createPool, PoolConfig } from 'mysql'
import { useSeeding, runSeeder } from 'typeorm-seeding'
import { Migration } from 'ts-mysql-migrate'
import path from 'path'
@ -31,4 +32,17 @@ const resetDB = async (closePool = false): Promise<void> => {
if (closePool) pool.end()
}
export { resetDB, pool, migration, initialize }
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
const runSeeds = async (seeds: any[]): Promise<void> => {
if (seeds.length > 0) {
await useSeeding({
root: process.cwd(),
configName: 'ormconfig.js',
})
for (let i = 0; i < seeds.length; i++) {
await runSeeder(seeds[i])
}
}
}
export { resetDB, pool, migration, initialize, runSeeds }

View File

@ -1,7 +1,6 @@
import 'reflect-metadata'
import prepare from './prepare'
import connection from './typeorm/connection'
import { useSeeding, runSeeder } from 'typeorm-seeding'
import { CreatePeterLustigSeed } from './seeds/users/peter-lustig.admin.seed'
import { CreateBibiBloxbergSeed } from './seeds/users/bibi-bloxberg.seed'
import { CreateRaeuberHotzenplotzSeed } from './seeds/users/raeuber-hotzenplotz.seed'
@ -9,7 +8,7 @@ import { CreateBobBaumeisterSeed } from './seeds/users/bob-baumeister.seed'
import { CreateStephenHawkingSeed } from './seeds/users/stephen-hawking.seed'
import { CreateGarrickOllivanderSeed } from './seeds/users/garrick-ollivander.seed'
import { CreateUserSeed } from './seeds/create-user.seed'
import { resetDB, pool, migration } from './helpers'
import { resetDB, pool, migration, runSeeds } from './helpers'
const run = async (command: string) => {
// Database actions not supported by our migration library
@ -37,20 +36,16 @@ const run = async (command: string) => {
break
case 'seed':
// TODO protect from production
await useSeeding({
root: process.cwd(),
configName: 'ormconfig.js',
})
await runSeeder(CreatePeterLustigSeed)
await runSeeder(CreateBibiBloxbergSeed)
await runSeeder(CreateRaeuberHotzenplotzSeed)
await runSeeder(CreateBobBaumeisterSeed)
await runSeeder(CreateStephenHawkingSeed)
// eslint-disable-next-line prefer-spread
Array.apply(null, Array(96)).forEach(async () => {
await runSeeder(CreateUserSeed)
})
await runSeeder(CreateGarrickOllivanderSeed)
// await runSeeder(CreatePeterLustigSeed)
await runSeeds([
CreatePeterLustigSeed,
CreateBibiBloxbergSeed,
CreateRaeuberHotzenplotzSeed,
CreateBobBaumeisterSeed,
CreateStephenHawkingSeed,
CreateGarrickOllivanderSeed,
...Array(96).fill(CreateUserSeed),
])
break
default:
throw new Error(`Unsupported command ${command}`)