From 5713b9ad3d787120ef931de57865157c709ffb94 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 27 Apr 2022 09:57:11 +0200 Subject: [PATCH 01/26] user factory returns created user entity, add delete user mutation --- backend/src/seeds/factory/user.ts | 10 ++++++---- backend/src/seeds/graphql/mutations.ts | 6 ++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/backend/src/seeds/factory/user.ts b/backend/src/seeds/factory/user.ts index 4b5913d48..d94f94b3c 100644 --- a/backend/src/seeds/factory/user.ts +++ b/backend/src/seeds/factory/user.ts @@ -7,7 +7,7 @@ import { ApolloServerTestClient } from 'apollo-server-testing' export const userFactory = async ( client: ApolloServerTestClient, user: UserInterface, -): Promise => { +): Promise => { const { mutate } = client const { @@ -24,13 +24,15 @@ export const userFactory = async ( }) } - if (user.createdAt || user.deletedAt || user.isAdmin) { - // get user from database - const dbUser = await User.findOneOrFail({ id }) + // get user from database + const dbUser = await User.findOneOrFail({ id }) + if (user.createdAt || user.deletedAt || user.isAdmin) { if (user.createdAt) dbUser.createdAt = user.createdAt if (user.deletedAt) dbUser.deletedAt = user.deletedAt if (user.isAdmin) dbUser.isAdmin = new Date() await dbUser.save() } + + return dbUser } diff --git a/backend/src/seeds/graphql/mutations.ts b/backend/src/seeds/graphql/mutations.ts index 601b1fbbf..30c453eea 100644 --- a/backend/src/seeds/graphql/mutations.ts +++ b/backend/src/seeds/graphql/mutations.ts @@ -106,3 +106,9 @@ export const confirmPendingCreation = gql` confirmPendingCreation(id: $id) } ` + +export const deleteUser = gql` + mutation ($userId: Int!) { + deleteUser(userId: $userId) + } +` From 06639d53d9a9623ec801655e5a584dd59f71b691 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 27 Apr 2022 09:57:39 +0200 Subject: [PATCH 02/26] test delete user mutation --- .../graphql/resolver/AdminResolver.test.ts | 150 ++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 backend/src/graphql/resolver/AdminResolver.test.ts diff --git a/backend/src/graphql/resolver/AdminResolver.test.ts b/backend/src/graphql/resolver/AdminResolver.test.ts new file mode 100644 index 000000000..4460030fe --- /dev/null +++ b/backend/src/graphql/resolver/AdminResolver.test.ts @@ -0,0 +1,150 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ + +import { testEnvironment, resetToken, cleanDB } from '@test/helpers' +import { userFactory } from '@/seeds/factory/user' +import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' +import { peterLustig } from '@/seeds/users/peter-lustig' +import { deleteUser } from '@/seeds/graphql/mutations' +import { GraphQLError } from 'graphql' +import { User } from '@entity/User' +/* eslint-disable-next-line @typescript-eslint/no-unused-vars */ +import { sendAccountActivationEmail } from '@/mailer/sendAccountActivationEmail' +import { login } from '@/seeds/graphql/queries' + +// mock account activation email to avoid console spam +jest.mock('@/mailer/sendAccountActivationEmail', () => { + return { + __esModule: true, + sendAccountActivationEmail: jest.fn(), + } +}) + +let mutate: any, query: any, con: any +let testEnv: any + +beforeAll(async () => { + testEnv = await testEnvironment() + mutate = testEnv.mutate + query = testEnv.query + con = testEnv.con + await cleanDB() +}) + +afterAll(async () => { + await cleanDB() + await con.close() +}) + +let admin: User +let user: User + +describe('AdminResolver', () => { + describe('delete user', () => { + describe('unauthenticated', () => { + it('returns an error', async () => { + await expect(mutate({ mutation: deleteUser, variables: { userId: 1 } })).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) + + describe('authenticated', () => { + describe('with user rights', () => { + beforeAll(async () => { + user = await userFactory(testEnv, bibiBloxberg) + await query({ + query: login, + variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, + }) + }) + + afterAll(async () => { + await cleanDB() + resetToken() + }) + + it('returns an error', async () => { + await expect( + mutate({ mutation: deleteUser, variables: { userId: user.id + 1 } }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) + + describe('with admin rights', () => { + beforeAll(async () => { + admin = await userFactory(testEnv, peterLustig) + await query({ + query: login, + variables: { email: 'peter@lustig.de', password: 'Aa12345_' }, + }) + }) + + afterAll(async () => { + await cleanDB() + resetToken() + }) + + describe('user to delete user does not exist', () => { + it('throws an error', async () => { + await expect( + mutate({ mutation: deleteUser, variables: { userId: admin.id + 1 } }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError(`Could not find user with userId: ${admin.id + 1}`)], + }), + ) + }) + }) + + describe('delete self', () => { + it('throws an error', async () => { + await expect( + mutate({ mutation: deleteUser, variables: { userId: admin.id } }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('Moderator can not delete his own account!')], + }), + ) + }) + }) + + describe('delete with success', () => { + beforeAll(async () => { + user = await userFactory(testEnv, bibiBloxberg) + }) + + it('returns date string', async () => { + const result = await mutate({ mutation: deleteUser, variables: { userId: user.id } }) + expect(result).toEqual( + expect.objectContaining({ + data: { + deleteUser: expect.any(String), + }, + }), + ) + expect(new Date(result.data.deleteUser)).toEqual(expect.any(Date)) + }) + + describe('delete deleted user', () => { + it('throws an error', async () => { + await expect( + mutate({ mutation: deleteUser, variables: { userId: user.id } }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError(`Could not find user with userId: ${user.id}`)], + }), + ) + }) + }) + }) + }) + }) + }) +}) From 2836d63f2f8536fa206e21b5ca236f5e5221b15b Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 27 Apr 2022 09:59:21 +0200 Subject: [PATCH 03/26] add unDelete User mutation --- backend/src/seeds/graphql/mutations.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/backend/src/seeds/graphql/mutations.ts b/backend/src/seeds/graphql/mutations.ts index 30c453eea..6ee1bafd0 100644 --- a/backend/src/seeds/graphql/mutations.ts +++ b/backend/src/seeds/graphql/mutations.ts @@ -112,3 +112,9 @@ export const deleteUser = gql` deleteUser(userId: $userId) } ` + +export const unDeleteUser = gql` + mutation ($userId: Int!) { + unDeleteUser(userId: $userId) + } +` From ad8de559aa66698bb6ac4b1d0631780480c20086 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 27 Apr 2022 10:11:14 +0200 Subject: [PATCH 04/26] test unDelete User mutation --- .../graphql/resolver/AdminResolver.test.ts | 100 +++++++++++++++++- backend/src/graphql/resolver/AdminResolver.ts | 5 +- 2 files changed, 102 insertions(+), 3 deletions(-) diff --git a/backend/src/graphql/resolver/AdminResolver.test.ts b/backend/src/graphql/resolver/AdminResolver.test.ts index 4460030fe..91a9db770 100644 --- a/backend/src/graphql/resolver/AdminResolver.test.ts +++ b/backend/src/graphql/resolver/AdminResolver.test.ts @@ -5,7 +5,7 @@ import { testEnvironment, resetToken, cleanDB } from '@test/helpers' import { userFactory } from '@/seeds/factory/user' import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' import { peterLustig } from '@/seeds/users/peter-lustig' -import { deleteUser } from '@/seeds/graphql/mutations' +import { deleteUser, unDeleteUser } from '@/seeds/graphql/mutations' import { GraphQLError } from 'graphql' import { User } from '@entity/User' /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ @@ -147,4 +147,102 @@ describe('AdminResolver', () => { }) }) }) + + describe('unDelete user', () => { + describe('unauthenticated', () => { + it('returns an error', async () => { + await expect(mutate({ mutation: unDeleteUser, variables: { userId: 1 } })).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) + + describe('authenticated', () => { + describe('with user rights', () => { + beforeAll(async () => { + user = await userFactory(testEnv, bibiBloxberg) + await query({ + query: login, + variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, + }) + }) + + afterAll(async () => { + await cleanDB() + resetToken() + }) + + it('returns an error', async () => { + await expect( + mutate({ mutation: unDeleteUser, variables: { userId: user.id + 1 } }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) + + describe('with admin rights', () => { + beforeAll(async () => { + admin = await userFactory(testEnv, peterLustig) + await query({ + query: login, + variables: { email: 'peter@lustig.de', password: 'Aa12345_' }, + }) + }) + + afterAll(async () => { + await cleanDB() + resetToken() + }) + + describe('user to undelete user does not exist', () => { + it('throws an error', async () => { + await expect( + mutate({ mutation: unDeleteUser, variables: { userId: admin.id + 1 } }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError(`Could not find user with userId: ${admin.id + 1}`)], + }), + ) + }) + }) + + describe('user to undelete is not deleted', () => { + beforeAll(async () => { + user = await userFactory(testEnv, bibiBloxberg) + }) + + it('throws an error', async () => { + await expect( + mutate({ mutation: unDeleteUser, variables: { userId: user.id } }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('User already deleted')], + }), + ) + }) + + describe('undelete deleted user', () => { + beforeAll(async () => { + await mutate({ mutation: deleteUser, variables: { userId: user.id } }) + }) + + it('returns null', async () => { + await expect( + mutate({ mutation: unDeleteUser, variables: { userId: user.id } }), + ).resolves.toEqual( + expect.objectContaining({ + data: { unDeleteUser: null }, + }), + ) + }) + }) + }) + }) + }) + }) }) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 2009af3b0..757b6d6dd 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -157,11 +157,12 @@ export class AdminResolver { @Mutation(() => Date, { nullable: true }) async unDeleteUser(@Arg('userId', () => Int) userId: number): Promise { const user = await dbUser.findOne({ id: userId }, { withDeleted: true }) - // user exists ? if (!user) { throw new Error(`Could not find user with userId: ${userId}`) } - // recover user account + if (!user.deletedAt) { + throw new Error('User already deleted') + } await user.recover() return null } From 2aa7772d20844431207f7f03e61961b91aa6bbcd Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 27 Apr 2022 11:03:34 +0200 Subject: [PATCH 05/26] remove moderator ID form create pending creation mutation args --- admin/src/components/CreationFormular.spec.js | 9 --------- admin/src/components/CreationFormular.vue | 2 -- admin/src/graphql/createPendingCreation.js | 16 ++-------------- 3 files changed, 2 insertions(+), 25 deletions(-) diff --git a/admin/src/components/CreationFormular.spec.js b/admin/src/components/CreationFormular.spec.js index 083b7ca67..08ec71bdc 100644 --- a/admin/src/components/CreationFormular.spec.js +++ b/admin/src/components/CreationFormular.spec.js @@ -24,12 +24,6 @@ const mocks = { }, $store: { commit: stateCommitMock, - state: { - moderator: { - id: 0, - name: 'test moderator', - }, - }, }, } @@ -122,7 +116,6 @@ describe('CreationFormular', () => { creationDate: getCreationDate(2), amount: 90, memo: 'Test create coins', - moderator: 0, }, }), ) @@ -370,14 +363,12 @@ describe('CreationFormular', () => { creationDate: getCreationDate(1), amount: 200, memo: 'Test mass create coins', - moderator: 0, }, { email: 'bibi@bloxberg.de', creationDate: getCreationDate(1), amount: 200, memo: 'Test mass create coins', - moderator: 0, }, ], }, diff --git a/admin/src/components/CreationFormular.vue b/admin/src/components/CreationFormular.vue index cd4de5fd6..cdcd6ef1d 100644 --- a/admin/src/components/CreationFormular.vue +++ b/admin/src/components/CreationFormular.vue @@ -154,7 +154,6 @@ export default { creationDate: this.selected.date, amount: Number(this.value), memo: this.text, - moderator: Number(this.$store.state.moderator.id), }) }) this.$apollo @@ -188,7 +187,6 @@ export default { creationDate: this.selected.date, amount: Number(this.value), memo: this.text, - moderator: Number(this.$store.state.moderator.id), } this.$apollo .mutate({ diff --git a/admin/src/graphql/createPendingCreation.js b/admin/src/graphql/createPendingCreation.js index 05402ed9f..9301ea489 100644 --- a/admin/src/graphql/createPendingCreation.js +++ b/admin/src/graphql/createPendingCreation.js @@ -1,19 +1,7 @@ import gql from 'graphql-tag' export const createPendingCreation = gql` - mutation ( - $email: String! - $amount: Decimal! - $memo: String! - $creationDate: String! - $moderator: Int! - ) { - createPendingCreation( - email: $email - amount: $amount - memo: $memo - creationDate: $creationDate - moderator: $moderator - ) + mutation ($email: String!, $amount: Decimal!, $memo: String!, $creationDate: String!) { + createPendingCreation(email: $email, amount: $amount, memo: $memo, creationDate: $creationDate) } ` From e9eaa756b010169ac805d277cceecde531993b61 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 27 Apr 2022 11:10:21 +0200 Subject: [PATCH 06/26] remove moderator from create pending creation mutation args --- backend/src/seeds/factory/creation.ts | 9 ++------- backend/src/seeds/graphql/mutations.ts | 16 ++-------------- 2 files changed, 4 insertions(+), 21 deletions(-) diff --git a/backend/src/seeds/factory/creation.ts b/backend/src/seeds/factory/creation.ts index 64f693360..88a681ef7 100644 --- a/backend/src/seeds/factory/creation.ts +++ b/backend/src/seeds/factory/creation.ts @@ -20,14 +20,9 @@ export const creationFactory = async ( ): Promise => { const { mutate, query } = client - // login as Peter Lustig (admin) and get his user ID - const { - data: { - login: { id }, - }, - } = await query({ query: login, variables: { email: 'peter@lustig.de', password: 'Aa12345_' } }) + await query({ query: login, variables: { email: 'peter@lustig.de', password: 'Aa12345_' } }) - await mutate({ mutation: createPendingCreation, variables: { ...creation, moderator: id } }) + await mutate({ mutation: createPendingCreation, variables: { ...creation } }) // get User const user = await User.findOneOrFail({ where: { email: creation.email } }) diff --git a/backend/src/seeds/graphql/mutations.ts b/backend/src/seeds/graphql/mutations.ts index 6ee1bafd0..f6022998e 100644 --- a/backend/src/seeds/graphql/mutations.ts +++ b/backend/src/seeds/graphql/mutations.ts @@ -84,20 +84,8 @@ export const createTransactionLink = gql` // from admin interface export const createPendingCreation = gql` - mutation ( - $email: String! - $amount: Decimal! - $memo: String! - $creationDate: String! - $moderator: Int! - ) { - createPendingCreation( - email: $email - amount: $amount - memo: $memo - creationDate: $creationDate - moderator: $moderator - ) + mutation ($email: String!, $amount: Decimal!, $memo: String!, $creationDate: String!) { + createPendingCreation(email: $email, amount: $amount, memo: $memo, creationDate: $creationDate) } ` From 64454b6fa2069bda298d340f77887e95a8030b64 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 27 Apr 2022 11:11:30 +0200 Subject: [PATCH 07/26] remove moderator from create pending creation mutation args, use context instead to get moderator ID --- backend/src/graphql/arg/CreatePendingCreationArgs.ts | 5 +---- backend/src/graphql/resolver/AdminResolver.ts | 9 ++++++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/backend/src/graphql/arg/CreatePendingCreationArgs.ts b/backend/src/graphql/arg/CreatePendingCreationArgs.ts index 0cadf5e62..11c345465 100644 --- a/backend/src/graphql/arg/CreatePendingCreationArgs.ts +++ b/backend/src/graphql/arg/CreatePendingCreationArgs.ts @@ -1,4 +1,4 @@ -import { ArgsType, Field, InputType, Int } from 'type-graphql' +import { ArgsType, Field, InputType } from 'type-graphql' import Decimal from 'decimal.js-light' @InputType() @@ -15,7 +15,4 @@ export default class CreatePendingCreationArgs { @Field(() => String) creationDate: string - - @Field(() => Int) - moderator: number } diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 757b6d6dd..779d81c99 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -170,7 +170,8 @@ export class AdminResolver { @Authorized([RIGHTS.CREATE_PENDING_CREATION]) @Mutation(() => [Number]) async createPendingCreation( - @Args() { email, amount, memo, creationDate, moderator }: CreatePendingCreationArgs, + @Args() { email, amount, memo, creationDate }: CreatePendingCreationArgs, + @Ctx() context: Context, ): Promise { const user = await dbUser.findOne({ email }, { withDeleted: true }) if (!user) { @@ -182,6 +183,7 @@ export class AdminResolver { if (!user.emailChecked) { throw new Error('Creation could not be saved, Email is not activated') } + const moderator = getUser(context) const creations = await getUserCreation(user.id) const creationDateObj = new Date(creationDate) if (isCreationValid(creations, amount, creationDateObj)) { @@ -191,7 +193,7 @@ export class AdminResolver { adminPendingCreation.created = new Date() adminPendingCreation.date = creationDateObj adminPendingCreation.memo = memo - adminPendingCreation.moderator = moderator + adminPendingCreation.moderator = moderator.id await AdminPendingCreation.save(adminPendingCreation) } @@ -203,12 +205,13 @@ export class AdminResolver { async createPendingCreations( @Arg('pendingCreations', () => [CreatePendingCreationArgs]) pendingCreations: CreatePendingCreationArgs[], + @Ctx() context: Context, ): Promise { let success = false const successfulCreation: string[] = [] const failedCreation: string[] = [] for (const pendingCreation of pendingCreations) { - await this.createPendingCreation(pendingCreation) + await this.createPendingCreation(pendingCreation, context) .then(() => { successfulCreation.push(pendingCreation.email) success = true From 2343279e7be9c73e6c2d2cd40abf26c6f37a344c Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 27 Apr 2022 11:35:18 +0200 Subject: [PATCH 08/26] add createPendingCreations mutation --- backend/src/seeds/graphql/mutations.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/backend/src/seeds/graphql/mutations.ts b/backend/src/seeds/graphql/mutations.ts index f6022998e..33b863768 100644 --- a/backend/src/seeds/graphql/mutations.ts +++ b/backend/src/seeds/graphql/mutations.ts @@ -106,3 +106,13 @@ export const unDeleteUser = gql` unDeleteUser(userId: $userId) } ` + +export const createPendingCreations = gql` + mutation ($pendingCreations: [CreatePendingCreationArgs!]!) { + createPendingCreations(pendingCreations: $pendingCreations) { + success + successfulCreation + failedCreation + } + } +` From 2e82f007e65d93cceccf7cc4e1ae89e1078256b2 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 27 Apr 2022 11:53:50 +0200 Subject: [PATCH 09/26] test createPendingCreations --- .../graphql/resolver/AdminResolver.test.ts | 296 +++++++++++++++++- 1 file changed, 295 insertions(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/AdminResolver.test.ts b/backend/src/graphql/resolver/AdminResolver.test.ts index 91a9db770..d43941e45 100644 --- a/backend/src/graphql/resolver/AdminResolver.test.ts +++ b/backend/src/graphql/resolver/AdminResolver.test.ts @@ -5,12 +5,20 @@ import { testEnvironment, resetToken, cleanDB } from '@test/helpers' import { userFactory } from '@/seeds/factory/user' import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' import { peterLustig } from '@/seeds/users/peter-lustig' -import { deleteUser, unDeleteUser } from '@/seeds/graphql/mutations' +import { stephenHawking } from '@/seeds/users/stephen-hawking' +import { garrickOllivander } from '@/seeds/users/garrick-ollivander' +import { + deleteUser, + unDeleteUser, + createPendingCreation, + createPendingCreations, +} from '@/seeds/graphql/mutations' import { GraphQLError } from 'graphql' import { User } from '@entity/User' /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ import { sendAccountActivationEmail } from '@/mailer/sendAccountActivationEmail' import { login } from '@/seeds/graphql/queries' +import Decimal from 'decimal.js-light' // mock account activation email to avoid console spam jest.mock('@/mailer/sendAccountActivationEmail', () => { @@ -245,4 +253,290 @@ describe('AdminResolver', () => { }) }) }) + + describe('creations', () => { + const variables = { + email: 'bibi@bloxberg.de', + amount: new Decimal(2000), + memo: 'Vielen Dank für den Zaubertrank!', + creationDate: 'not-valid', + } + + describe('unauthenticated', () => { + describe('createPendingCreation', () => { + it('returns an error', async () => { + await expect(mutate({ mutation: createPendingCreation, variables })).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) + + describe('createPendingCreations', () => { + it('returns an error', async () => { + await expect( + mutate({ + mutation: createPendingCreations, + variables: { pendingCreations: [variables] }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) + }) + + describe('authenticated', () => { + describe('with user rights', () => { + beforeAll(async () => { + user = await userFactory(testEnv, bibiBloxberg) + await query({ + query: login, + variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, + }) + }) + + afterAll(async () => { + await cleanDB() + resetToken() + }) + + describe('createPendingCreation', () => { + it('returns an error', async () => { + await expect(mutate({ mutation: createPendingCreation, variables })).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) + + describe('createPendingCreations', () => { + it('returns an error', async () => { + await expect( + mutate({ + mutation: createPendingCreations, + variables: { pendingCreations: [variables] }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) + }) + + describe('with admin rights', () => { + beforeAll(async () => { + admin = await userFactory(testEnv, peterLustig) + await query({ + query: login, + variables: { email: 'peter@lustig.de', password: 'Aa12345_' }, + }) + }) + + afterAll(async () => { + await cleanDB() + resetToken() + }) + + describe('createPendingCreation', () => { + describe('user to create for does not exist', () => { + it('throws an error', async () => { + await expect(mutate({ mutation: createPendingCreation, variables })).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('Could not find user with email: bibi@bloxberg.de')], + }), + ) + }) + }) + + describe('user to create for is deleted', () => { + beforeAll(async () => { + user = await userFactory(testEnv, stephenHawking) + variables.email = 'stephen@hawking.uk' + }) + + it('throws an error', async () => { + await expect(mutate({ mutation: createPendingCreation, variables })).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('This user was deleted. Cannot make a creation.')], + }), + ) + }) + }) + + describe('user to create for has email not confirmed', () => { + beforeAll(async () => { + user = await userFactory(testEnv, garrickOllivander) + variables.email = 'garrick@ollivander.com' + }) + + it('throws an error', async () => { + await expect(mutate({ mutation: createPendingCreation, variables })).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('Creation could not be saved, Email is not activated')], + }), + ) + }) + }) + + describe('valid user to create for', () => { + beforeAll(async () => { + user = await userFactory(testEnv, bibiBloxberg) + variables.email = 'bibi@bloxberg.de' + }) + + describe('date of creation is not a date string', () => { + it('throws an error', async () => { + await expect( + mutate({ mutation: createPendingCreation, variables }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('No Creation found!')], + }), + ) + }) + }) + + describe('date of creation is four months ago', () => { + it('throws an error', async () => { + const now = new Date() + variables.creationDate = new Date( + now.getFullYear(), + now.getMonth() - 4, + 1, + ).toString() + await expect( + mutate({ mutation: createPendingCreation, variables }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('No Creation found!')], + }), + ) + }) + }) + + describe('date of creation is in the future', () => { + it('throws an error', async () => { + const now = new Date() + variables.creationDate = new Date( + now.getFullYear(), + now.getMonth() + 4, + 1, + ).toString() + await expect( + mutate({ mutation: createPendingCreation, variables }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('No Creation found!')], + }), + ) + }) + }) + + describe('amount of creation is too high', () => { + it('throws an error', async () => { + variables.creationDate = new Date().toString() + await expect( + mutate({ mutation: createPendingCreation, variables }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [ + new GraphQLError( + 'The amount (2000 GDD) to be created exceeds the available amount (1000 GDD) for this month.', + ), + ], + }), + ) + }) + }) + + describe('creation is valid', () => { + it('returns an array of the open creations for the last three months', async () => { + variables.amount = new Decimal(200) + await expect( + mutate({ mutation: createPendingCreation, variables }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + createPendingCreation: [1000, 1000, 800], + }, + }), + ) + }) + }) + + describe('second creation surpasses the available amount ', () => { + it('returns an array of the open creations for the last three months', async () => { + variables.amount = new Decimal(1000) + await expect( + mutate({ mutation: createPendingCreation, variables }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [ + new GraphQLError( + 'The amount (1000 GDD) to be created exceeds the available amount (800 GDD) for this month.', + ), + ], + }), + ) + }) + }) + }) + + describe('createPendingCreations', () => { + // at this point we have this data in DB: + // bibi@bloxberg.de: [1000, 1000, 800] + // peter@lustig.de: [1000, 1000, 1000] + // stephen@hawking.uk: [1000, 1000, 1000] - deleted + // garrick@ollivander.com: [1000, 1000, 1000] - not activated + + const massCreationVariables = [ + 'bibi@bloxberg.de', + 'peter@lustig.de', + 'stephen@hawking.uk', + 'garrick@ollivander.com', + 'bob@baumeister.de', + ].map((email) => { + return { + email, + amount: new Decimal(1000), + memo: 'Grundeinkommen', + creationDate: new Date().toString(), + } + }) + + it('returns success, one successful creation and four failed creations', async () => { + await expect( + mutate({ + mutation: createPendingCreations, + variables: { pendingCreations: massCreationVariables }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + createPendingCreations: { + success: true, + successfulCreation: ['peter@lustig.de'], + failedCreation: [ + 'bibi@bloxberg.de', + 'stephen@hawking.uk', + 'garrick@ollivander.com', + 'bob@baumeister.de', + ], + }, + }, + }), + ) + }) + }) + }) + }) + }) + }) }) From d14d4c56400351b27a23cc5009a441533d26708b Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 27 Apr 2022 11:57:41 +0200 Subject: [PATCH 10/26] remove moderator from update pending creation mutation args --- admin/src/components/EditCreationFormular.spec.js | 9 --------- admin/src/components/EditCreationFormular.vue | 2 -- admin/src/graphql/updatePendingCreation.js | 11 +---------- 3 files changed, 1 insertion(+), 21 deletions(-) diff --git a/admin/src/components/EditCreationFormular.spec.js b/admin/src/components/EditCreationFormular.spec.js index f5c7fb0fe..f39edad52 100644 --- a/admin/src/components/EditCreationFormular.spec.js +++ b/admin/src/components/EditCreationFormular.spec.js @@ -11,7 +11,6 @@ const apolloMutateMock = jest.fn().mockResolvedValue({ amount: 500, date: new Date(), memo: 'Test Schöpfung 2', - moderator: 0, }, }, }) @@ -28,12 +27,6 @@ const mocks = { mutate: apolloMutateMock, }, $store: { - state: { - moderator: { - id: 0, - name: 'test moderator', - }, - }, commit: stateCommitMock, }, } @@ -104,7 +97,6 @@ describe('EditCreationFormular', () => { creationDate: getCreationDate(0), amount: 500, memo: 'Test Schöpfung 2', - moderator: 0, }, }), ) @@ -129,7 +121,6 @@ describe('EditCreationFormular', () => { amount: 500, date: expect.any(Date), memo: 'Test Schöpfung 2', - moderator: 0, row: expect.any(Object), }, ], diff --git a/admin/src/components/EditCreationFormular.vue b/admin/src/components/EditCreationFormular.vue index 82b444154..fb30f2b77 100644 --- a/admin/src/components/EditCreationFormular.vue +++ b/admin/src/components/EditCreationFormular.vue @@ -120,7 +120,6 @@ export default { creationDate: this.selected.date, amount: Number(this.value), memo: this.text, - moderator: Number(this.$store.state.moderator.id), }, }) .then((result) => { @@ -129,7 +128,6 @@ export default { amount: Number(result.data.updatePendingCreation.amount), date: result.data.updatePendingCreation.date, memo: result.data.updatePendingCreation.memo, - moderator: Number(result.data.updatePendingCreation.moderator), row: this.row, }) this.toastSuccess( diff --git a/admin/src/graphql/updatePendingCreation.js b/admin/src/graphql/updatePendingCreation.js index cd0ae6c8e..f0775e68b 100644 --- a/admin/src/graphql/updatePendingCreation.js +++ b/admin/src/graphql/updatePendingCreation.js @@ -1,27 +1,18 @@ import gql from 'graphql-tag' export const updatePendingCreation = gql` - mutation ( - $id: Int! - $email: String! - $amount: Decimal! - $memo: String! - $creationDate: String! - $moderator: Int! - ) { + mutation ($id: Int!, $email: String!, $amount: Decimal!, $memo: String!, $creationDate: String!) { updatePendingCreation( id: $id email: $email amount: $amount memo: $memo creationDate: $creationDate - moderator: $moderator ) { amount date memo creation - moderator } } ` From a0e29e2524de3c464b4a60042f8eb108c5dbb18e Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 27 Apr 2022 12:06:01 +0200 Subject: [PATCH 11/26] remove moderator from update pending creation mutation args --- .../graphql/arg/UpdatePendingCreationArgs.ts | 3 --- .../src/graphql/model/UpdatePendingCreation.ts | 3 --- backend/src/graphql/resolver/AdminResolver.ts | 8 +++++--- backend/src/seeds/graphql/mutations.ts | 17 +++++++++++++++++ 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/backend/src/graphql/arg/UpdatePendingCreationArgs.ts b/backend/src/graphql/arg/UpdatePendingCreationArgs.ts index 3cd85e84b..691d73154 100644 --- a/backend/src/graphql/arg/UpdatePendingCreationArgs.ts +++ b/backend/src/graphql/arg/UpdatePendingCreationArgs.ts @@ -17,7 +17,4 @@ export default class UpdatePendingCreationArgs { @Field(() => String) creationDate: string - - @Field(() => Int) - moderator: number } diff --git a/backend/src/graphql/model/UpdatePendingCreation.ts b/backend/src/graphql/model/UpdatePendingCreation.ts index 85d3af2cc..e19e1e064 100644 --- a/backend/src/graphql/model/UpdatePendingCreation.ts +++ b/backend/src/graphql/model/UpdatePendingCreation.ts @@ -12,9 +12,6 @@ export class UpdatePendingCreation { @Field(() => Decimal) amount: Decimal - @Field(() => Number) - moderator: number - @Field(() => [Decimal]) creation: Decimal[] } diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 779d81c99..fe3861cfa 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -230,7 +230,8 @@ export class AdminResolver { @Authorized([RIGHTS.UPDATE_PENDING_CREATION]) @Mutation(() => UpdatePendingCreation) async updatePendingCreation( - @Args() { id, email, amount, memo, creationDate, moderator }: UpdatePendingCreationArgs, + @Args() { id, email, amount, memo, creationDate }: UpdatePendingCreationArgs, + @Ctx() context: Context, ): Promise { const user = await dbUser.findOne({ email }, { withDeleted: true }) if (!user) { @@ -240,6 +241,8 @@ export class AdminResolver { throw new Error(`User was deleted (${email})`) } + const moderator = getUser(context) + const pendingCreationToUpdate = await AdminPendingCreation.findOneOrFail({ id }) if (pendingCreationToUpdate.userId !== user.id) { @@ -258,14 +261,13 @@ export class AdminResolver { pendingCreationToUpdate.amount = amount pendingCreationToUpdate.memo = memo pendingCreationToUpdate.date = new Date(creationDate) - pendingCreationToUpdate.moderator = moderator + pendingCreationToUpdate.moderator = moderator.id await AdminPendingCreation.save(pendingCreationToUpdate) const result = new UpdatePendingCreation() result.amount = amount result.memo = pendingCreationToUpdate.memo result.date = pendingCreationToUpdate.date - result.moderator = pendingCreationToUpdate.moderator result.creation = await getUserCreation(user.id) diff --git a/backend/src/seeds/graphql/mutations.ts b/backend/src/seeds/graphql/mutations.ts index 33b863768..e68ba13d0 100644 --- a/backend/src/seeds/graphql/mutations.ts +++ b/backend/src/seeds/graphql/mutations.ts @@ -116,3 +116,20 @@ export const createPendingCreations = gql` } } ` + +export const updatePendingCreation = gql` + mutation ($id: Int!, $email: String!, $amount: Decimal!, $memo: String!, $creationDate: String!) { + updatePendingCreation( + id: $id + email: $email + amount: $amount + memo: $memo + creationDate: $creationDate + ) { + amount + date + memo + creation + } + } +` From 61f3da38fa3d1f0610fae7f424ab5bc64f47e20f Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 27 Apr 2022 12:13:57 +0200 Subject: [PATCH 12/26] creation factory returns unconfirmed creation object --- backend/src/seeds/factory/creation.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/backend/src/seeds/factory/creation.ts b/backend/src/seeds/factory/creation.ts index 88a681ef7..3c319fb9f 100644 --- a/backend/src/seeds/factory/creation.ts +++ b/backend/src/seeds/factory/creation.ts @@ -17,7 +17,7 @@ export const nMonthsBefore = (date: Date, months = 1): string => { export const creationFactory = async ( client: ApolloServerTestClient, creation: CreationInterface, -): Promise => { +): Promise => { const { mutate, query } = client await query({ query: login, variables: { email: 'peter@lustig.de', password: 'Aa12345_' } }) @@ -27,12 +27,12 @@ export const creationFactory = async ( // get User const user = await User.findOneOrFail({ where: { email: creation.email } }) - if (creation.confirmed) { - const pendingCreation = await AdminPendingCreation.findOneOrFail({ - where: { userId: user.id }, - order: { created: 'DESC' }, - }) + const pendingCreation = await AdminPendingCreation.findOneOrFail({ + where: { userId: user.id }, + order: { created: 'DESC' }, + }) + if (creation.confirmed) { await mutate({ mutation: confirmPendingCreation, variables: { id: pendingCreation.id } }) if (creation.moveCreationDate) { @@ -50,5 +50,7 @@ export const creationFactory = async ( await transaction.save() } } + } else { + return pendingCreation } } From 4bf2871ba164744a79d135f9ca8cf18fe09b3409 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 27 Apr 2022 13:38:40 +0200 Subject: [PATCH 13/26] test update pending creation mutation --- .../graphql/resolver/AdminResolver.test.ts | 300 ++++++++++++++++-- backend/src/graphql/resolver/AdminResolver.ts | 13 +- 2 files changed, 274 insertions(+), 39 deletions(-) diff --git a/backend/src/graphql/resolver/AdminResolver.test.ts b/backend/src/graphql/resolver/AdminResolver.test.ts index d43941e45..0f606429c 100644 --- a/backend/src/graphql/resolver/AdminResolver.test.ts +++ b/backend/src/graphql/resolver/AdminResolver.test.ts @@ -3,6 +3,7 @@ import { testEnvironment, resetToken, cleanDB } from '@test/helpers' import { userFactory } from '@/seeds/factory/user' +import { creationFactory } from '@/seeds/factory/creation' import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' import { peterLustig } from '@/seeds/users/peter-lustig' import { stephenHawking } from '@/seeds/users/stephen-hawking' @@ -12,6 +13,7 @@ import { unDeleteUser, createPendingCreation, createPendingCreations, + updatePendingCreation, } from '@/seeds/graphql/mutations' import { GraphQLError } from 'graphql' import { User } from '@entity/User' @@ -19,6 +21,7 @@ import { User } from '@entity/User' import { sendAccountActivationEmail } from '@/mailer/sendAccountActivationEmail' import { login } from '@/seeds/graphql/queries' import Decimal from 'decimal.js-light' +import { AdminPendingCreation } from '@entity/AdminPendingCreation' // mock account activation email to avoid console spam jest.mock('@/mailer/sendAccountActivationEmail', () => { @@ -40,12 +43,13 @@ beforeAll(async () => { }) afterAll(async () => { - await cleanDB() + // await cleanDB() await con.close() }) let admin: User let user: User +let creation: AdminPendingCreation | void describe('AdminResolver', () => { describe('delete user', () => { @@ -287,6 +291,27 @@ describe('AdminResolver', () => { ) }) }) + + describe('updatePendingCreation', () => { + it('returns an error', async () => { + await expect( + mutate({ + mutation: updatePendingCreation, + variables: { + id: 1, + email: 'bibi@bloxberg.de', + amount: new Decimal(300), + memo: 'Danke Bibi!', + creationDate: new Date().toString(), + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) }) describe('authenticated', () => { @@ -328,6 +353,27 @@ describe('AdminResolver', () => { ) }) }) + + describe('updatePendingCreation', () => { + it('returns an error', async () => { + await expect( + mutate({ + mutation: updatePendingCreation, + variables: { + id: 1, + email: 'bibi@bloxberg.de', + amount: new Decimal(300), + memo: 'Danke Bibi!', + creationDate: new Date().toString(), + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) }) describe('with admin rights', () => { @@ -340,11 +386,21 @@ describe('AdminResolver', () => { }) afterAll(async () => { - await cleanDB() + // await cleanDB() resetToken() }) describe('createPendingCreation', () => { + beforeAll(async () => { + const now = new Date() + creation = await creationFactory(testEnv, { + email: 'peter@lustig.de', + amount: 400, + memo: 'Herzlich Willkommen bei Gradido!', + creationDate: new Date(now.getFullYear(), now.getMonth() - 1, 1).toISOString(), + }) + }) + describe('user to create for does not exist', () => { it('throws an error', async () => { await expect(mutate({ mutation: createPendingCreation, variables })).resolves.toEqual( @@ -488,47 +544,223 @@ describe('AdminResolver', () => { }) }) }) + }) - describe('createPendingCreations', () => { - // at this point we have this data in DB: - // bibi@bloxberg.de: [1000, 1000, 800] - // peter@lustig.de: [1000, 1000, 1000] - // stephen@hawking.uk: [1000, 1000, 1000] - deleted - // garrick@ollivander.com: [1000, 1000, 1000] - not activated + describe('createPendingCreations', () => { + // at this point we have this data in DB: + // bibi@bloxberg.de: [1000, 1000, 800] + // peter@lustig.de: [1000, 600, 1000] + // stephen@hawking.uk: [1000, 1000, 1000] - deleted + // garrick@ollivander.com: [1000, 1000, 1000] - not activated - const massCreationVariables = [ - 'bibi@bloxberg.de', - 'peter@lustig.de', - 'stephen@hawking.uk', - 'garrick@ollivander.com', - 'bob@baumeister.de', - ].map((email) => { - return { - email, - amount: new Decimal(1000), - memo: 'Grundeinkommen', - creationDate: new Date().toString(), - } - }) + const massCreationVariables = [ + 'bibi@bloxberg.de', + 'peter@lustig.de', + 'stephen@hawking.uk', + 'garrick@ollivander.com', + 'bob@baumeister.de', + ].map((email) => { + return { + email, + amount: new Decimal(500), + memo: 'Grundeinkommen', + creationDate: new Date().toString(), + } + }) - it('returns success, one successful creation and four failed creations', async () => { + it('returns success, two successful creation and three failed creations', async () => { + await expect( + mutate({ + mutation: createPendingCreations, + variables: { pendingCreations: massCreationVariables }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + createPendingCreations: { + success: true, + successfulCreation: ['bibi@bloxberg.de', 'peter@lustig.de'], + failedCreation: [ + 'stephen@hawking.uk', + 'garrick@ollivander.com', + 'bob@baumeister.de', + ], + }, + }, + }), + ) + }) + }) + + describe('updatePendingCreation', () => { + // at this I expect to have this data in DB: + // bibi@bloxberg.de: [1000, 1000, 300] + // peter@lustig.de: [1000, 600, 500] + // stephen@hawking.uk: [1000, 1000, 1000] - deleted + // garrick@ollivander.com: [1000, 1000, 1000] - not activated + + describe('user for creation to update does not exist', () => { + it('throws an error', async () => { await expect( mutate({ - mutation: createPendingCreations, - variables: { pendingCreations: massCreationVariables }, + mutation: updatePendingCreation, + variables: { + id: 1, + email: 'bob@baumeister.de', + amount: new Decimal(300), + memo: 'Danke Bibi!', + creationDate: new Date().toString(), + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('Could not find user with email: bob@baumeister.de')], + }), + ) + }) + }) + + describe('user for creation to update is deleted', () => { + it('throws an error', async () => { + await expect( + mutate({ + mutation: updatePendingCreation, + variables: { + id: 1, + email: 'stephen@hawking.uk', + amount: new Decimal(300), + memo: 'Danke Bibi!', + creationDate: new Date().toString(), + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('User was deleted (stephen@hawking.uk)')], + }), + ) + }) + }) + + describe('creation does not exist', () => { + it('throws an error', async () => { + await expect( + mutate({ + mutation: updatePendingCreation, + variables: { + id: -1, + email: 'bibi@bloxberg.de', + amount: new Decimal(300), + memo: 'Danke Bibi!', + creationDate: new Date().toString(), + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('No creation found to given id.')], + }), + ) + }) + }) + + describe('user email does not match creation user', () => { + it('throws an error', async () => { + await expect( + mutate({ + mutation: updatePendingCreation, + variables: { + id: creation ? creation.id : -1, + email: 'bibi@bloxberg.de', + amount: new Decimal(300), + memo: 'Danke Bibi!', + creationDate: new Date().toString(), + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [ + new GraphQLError( + 'user of the pending creation and send user does not correspond', + ), + ], + }), + ) + }) + }) + + describe('creation update is not valid', () => { + it('throws an error', async () => { + await expect( + mutate({ + mutation: updatePendingCreation, + variables: { + id: creation ? creation.id : -1, + email: 'peter@lustig.de', + amount: new Decimal(1900), + memo: 'Danke Peter!', + creationDate: new Date().toString(), + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [ + new GraphQLError( + 'The amount (1900 GDD) to be created exceeds the available amount (500 GDD) for this month.', + ), + ], + }), + ) + }) + }) + + describe('creation update is successful changing month', () => { + it('returns update creation object', async () => { + await expect( + mutate({ + mutation: updatePendingCreation, + variables: { + id: creation ? creation.id : -1, + email: 'peter@lustig.de', + amount: new Decimal(300), + memo: 'Danke Peter!', + creationDate: new Date().toString(), + }, }), ).resolves.toEqual( expect.objectContaining({ data: { - createPendingCreations: { - success: true, - successfulCreation: ['peter@lustig.de'], - failedCreation: [ - 'bibi@bloxberg.de', - 'stephen@hawking.uk', - 'garrick@ollivander.com', - 'bob@baumeister.de', - ], + updatePendingCreation: { + date: expect.any(String), + memo: 'Danke Peter!', + amount: '300', + creation: ['1000', '1000', '200'], + }, + }, + }), + ) + }) + }) + + describe('creation update is successful without changing month', () => { + it('returns update creation object', async () => { + await expect( + mutate({ + mutation: updatePendingCreation, + variables: { + id: creation ? creation.id : -1, + email: 'peter@lustig.de', + amount: new Decimal(200), + memo: 'Das war leider zu Viel!', + creationDate: new Date().toString(), + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + updatePendingCreation: { + date: expect.any(String), + memo: 'Das war leider zu Viel!', + amount: '200', + creation: ['1000', '1000', '300'], }, }, }), diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index fe3861cfa..96ad44ccc 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -243,7 +243,11 @@ export class AdminResolver { const moderator = getUser(context) - const pendingCreationToUpdate = await AdminPendingCreation.findOneOrFail({ id }) + const pendingCreationToUpdate = await AdminPendingCreation.findOne({ id }) + + if (!pendingCreationToUpdate) { + throw new Error('No creation found to given id.') + } if (pendingCreationToUpdate.userId !== user.id) { throw new Error('user of the pending creation and send user does not correspond') @@ -255,9 +259,8 @@ export class AdminResolver { creations = updateCreations(creations, pendingCreationToUpdate) } - if (!isCreationValid(creations, amount, creationDateObj)) { - throw new Error('Creation is not valid') - } + // all possible cases not to be true are thrown in this function + isCreationValid(creations, amount, creationDateObj) pendingCreationToUpdate.amount = amount pendingCreationToUpdate.memo = memo pendingCreationToUpdate.date = new Date(creationDate) @@ -510,7 +513,7 @@ function updateCreations(creations: Decimal[], pendingCreation: AdminPendingCrea if (index < 0) { throw new Error('You cannot create GDD for a month older than the last three months.') } - creations[index] = creations[index].plus(pendingCreation.amount) + creations[index] = creations[index].plus(pendingCreation.amount.toString()) return creations } From e4776fcfd5f08762a204bed7e47934a805b21ebe Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 27 Apr 2022 14:08:56 +0200 Subject: [PATCH 14/26] test get pending creations query --- .../graphql/resolver/AdminResolver.test.ts | 95 ++++++++++++++++++- backend/src/seeds/graphql/queries.ts | 18 ++++ 2 files changed, 110 insertions(+), 3 deletions(-) diff --git a/backend/src/graphql/resolver/AdminResolver.test.ts b/backend/src/graphql/resolver/AdminResolver.test.ts index 0f606429c..c0962cbe8 100644 --- a/backend/src/graphql/resolver/AdminResolver.test.ts +++ b/backend/src/graphql/resolver/AdminResolver.test.ts @@ -15,11 +15,11 @@ import { createPendingCreations, updatePendingCreation, } from '@/seeds/graphql/mutations' +import { getPendingCreations, login } from '@/seeds/graphql/queries' import { GraphQLError } from 'graphql' import { User } from '@entity/User' /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ import { sendAccountActivationEmail } from '@/mailer/sendAccountActivationEmail' -import { login } from '@/seeds/graphql/queries' import Decimal from 'decimal.js-light' import { AdminPendingCreation } from '@entity/AdminPendingCreation' @@ -43,7 +43,7 @@ beforeAll(async () => { }) afterAll(async () => { - // await cleanDB() + await cleanDB() await con.close() }) @@ -312,6 +312,20 @@ describe('AdminResolver', () => { ) }) }) + + describe('getPendingCreations', () => { + it('returns an error', async () => { + await expect( + query({ + query: getPendingCreations, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) }) describe('authenticated', () => { @@ -374,6 +388,20 @@ describe('AdminResolver', () => { ) }) }) + + describe('getPendingCreations', () => { + it('returns an error', async () => { + await expect( + query({ + query: getPendingCreations, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) }) describe('with admin rights', () => { @@ -386,7 +414,7 @@ describe('AdminResolver', () => { }) afterAll(async () => { - // await cleanDB() + await cleanDB() resetToken() }) @@ -768,6 +796,67 @@ describe('AdminResolver', () => { }) }) }) + + describe('getPendingCreations', () => { + it('returns four pending creations', async () => { + await expect( + query({ + query: getPendingCreations, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + getPendingCreations: expect.arrayContaining([ + { + id: expect.any(Number), + firstName: 'Peter', + lastName: 'Lustig', + email: 'peter@lustig.de', + date: expect.any(String), + memo: 'Das war leider zu Viel!', + amount: '200', + moderator: admin.id, + creation: ['1000', '1000', '300'], + }, + { + id: expect.any(Number), + firstName: 'Peter', + lastName: 'Lustig', + email: 'peter@lustig.de', + date: expect.any(String), + memo: 'Grundeinkommen', + amount: '500', + moderator: admin.id, + creation: ['1000', '1000', '300'], + }, + { + id: expect.any(Number), + firstName: 'Bibi', + lastName: 'Bloxberg', + email: 'bibi@bloxberg.de', + date: expect.any(String), + memo: 'Grundeinkommen', + amount: '500', + moderator: admin.id, + creation: ['1000', '1000', '300'], + }, + { + id: expect.any(Number), + firstName: 'Bibi', + lastName: 'Bloxberg', + email: 'bibi@bloxberg.de', + date: expect.any(String), + memo: 'Vielen Dank für den Zaubertrank!', + amount: '200', + moderator: admin.id, + creation: ['1000', '1000', '300'], + }, + ]), + }, + }), + ) + }) + }) }) }) }) diff --git a/backend/src/seeds/graphql/queries.ts b/backend/src/seeds/graphql/queries.ts index 76a386953..82067c968 100644 --- a/backend/src/seeds/graphql/queries.ts +++ b/backend/src/seeds/graphql/queries.ts @@ -148,3 +148,21 @@ export const queryTransactionLink = gql` } } ` + +// from admin interface + +export const getPendingCreations = gql` + query { + getPendingCreations { + id + firstName + lastName + email + amount + memo + date + moderator + creation + } + } +` From 6f0c3c11a5aa6b0094173cfe6b5fdd3bcd9ac75e Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 27 Apr 2022 17:51:26 +0200 Subject: [PATCH 15/26] improve matching of created pending creation, see TODO --- backend/src/seeds/factory/creation.ts | 4 ++-- backend/src/seeds/graphql/mutations.ts | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/backend/src/seeds/factory/creation.ts b/backend/src/seeds/factory/creation.ts index 3c319fb9f..e49be3758 100644 --- a/backend/src/seeds/factory/creation.ts +++ b/backend/src/seeds/factory/creation.ts @@ -22,13 +22,13 @@ export const creationFactory = async ( await query({ query: login, variables: { email: 'peter@lustig.de', password: 'Aa12345_' } }) + // TODO it would be nice to have this mutation return the id await mutate({ mutation: createPendingCreation, variables: { ...creation } }) - // get User const user = await User.findOneOrFail({ where: { email: creation.email } }) const pendingCreation = await AdminPendingCreation.findOneOrFail({ - where: { userId: user.id }, + where: { userId: user.id, amount: creation.amount }, order: { created: 'DESC' }, }) diff --git a/backend/src/seeds/graphql/mutations.ts b/backend/src/seeds/graphql/mutations.ts index e68ba13d0..d3026dbdd 100644 --- a/backend/src/seeds/graphql/mutations.ts +++ b/backend/src/seeds/graphql/mutations.ts @@ -133,3 +133,9 @@ export const updatePendingCreation = gql` } } ` + +export const deletePendingCreation = gql` + mutation ($id: Int!) { + deletePendingCreation(id: $id) + } +` From 76b1763bc04ce208a044efddcf6959a8a428e6e5 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 27 Apr 2022 17:52:00 +0200 Subject: [PATCH 16/26] test deletePendingCreation and confirmPendingCreation mutations --- .../graphql/resolver/AdminResolver.test.ts | 191 ++++++++++++++++++ backend/src/graphql/resolver/AdminResolver.ts | 15 +- 2 files changed, 201 insertions(+), 5 deletions(-) diff --git a/backend/src/graphql/resolver/AdminResolver.test.ts b/backend/src/graphql/resolver/AdminResolver.test.ts index c0962cbe8..4102cb382 100644 --- a/backend/src/graphql/resolver/AdminResolver.test.ts +++ b/backend/src/graphql/resolver/AdminResolver.test.ts @@ -14,6 +14,8 @@ import { createPendingCreation, createPendingCreations, updatePendingCreation, + deletePendingCreation, + confirmPendingCreation, } from '@/seeds/graphql/mutations' import { getPendingCreations, login } from '@/seeds/graphql/queries' import { GraphQLError } from 'graphql' @@ -22,6 +24,7 @@ import { User } from '@entity/User' import { sendAccountActivationEmail } from '@/mailer/sendAccountActivationEmail' import Decimal from 'decimal.js-light' import { AdminPendingCreation } from '@entity/AdminPendingCreation' +import { Transaction as DbTransaction } from '@entity/Transaction' // mock account activation email to avoid console spam jest.mock('@/mailer/sendAccountActivationEmail', () => { @@ -326,6 +329,40 @@ describe('AdminResolver', () => { ) }) }) + + describe('deletePendingCreation', () => { + it('returns an error', async () => { + await expect( + mutate({ + mutation: deletePendingCreation, + variables: { + id: 1, + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) + + describe('confirmPendingCreation', () => { + it('returns an error', async () => { + await expect( + mutate({ + mutation: confirmPendingCreation, + variables: { + id: 1, + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) }) describe('authenticated', () => { @@ -402,6 +439,40 @@ describe('AdminResolver', () => { ) }) }) + + describe('deletePendingCreation', () => { + it('returns an error', async () => { + await expect( + mutate({ + mutation: deletePendingCreation, + variables: { + id: 1, + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) + + describe('confirmPendingCreation', () => { + it('returns an error', async () => { + await expect( + mutate({ + mutation: confirmPendingCreation, + variables: { + id: 1, + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) }) describe('with admin rights', () => { @@ -857,6 +928,126 @@ describe('AdminResolver', () => { ) }) }) + + describe('deletePendingCreation', () => { + describe('creation id does not exist', () => { + it('throws an error', async () => { + await expect( + mutate({ + mutation: deletePendingCreation, + variables: { + id: -1, + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('Creation not found to given id.')], + }), + ) + }) + }) + + describe('creation id does exist', () => { + it('returns true', async () => { + await expect( + mutate({ + mutation: deletePendingCreation, + variables: { + id: creation ? creation.id : -1, + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { deletePendingCreation: true }, + }), + ) + }) + }) + }) + + describe('confirmPendingCreation', () => { + describe('creation does not exits', () => { + it('throws an error', async () => { + await expect( + mutate({ + mutation: confirmPendingCreation, + variables: { + id: -1, + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('Creation not found to given id.')], + }), + ) + }) + }) + + describe('confirm own creation', () => { + beforeAll(async () => { + const now = new Date() + creation = await creationFactory(testEnv, { + email: 'peter@lustig.de', + amount: 400, + memo: 'Herzlich Willkommen bei Gradido!', + creationDate: new Date(now.getFullYear(), now.getMonth() - 1, 1).toISOString(), + }) + }) + + it('thows an error', async () => { + await expect( + mutate({ + mutation: confirmPendingCreation, + variables: { + id: creation ? creation.id : -1, + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('Moderator can not confirm own pending creation')], + }), + ) + }) + }) + + describe('creation of other user', () => { + beforeAll(async () => { + const now = new Date() + creation = await creationFactory(testEnv, { + email: 'bibi@bloxberg.de', + amount: 450, + memo: 'Herzlich Willkommen bei Gradido liebe Bibi!', + creationDate: new Date(now.getFullYear(), now.getMonth() - 2, 1).toISOString(), + }) + }) + + it('returns true', async () => { + await expect( + mutate({ + mutation: confirmPendingCreation, + variables: { + id: creation ? creation.id : -1, + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { confirmPendingCreation: true }, + }), + ) + }) + + it('creates a transaction', async () => { + const transaction = await DbTransaction.find() + expect(transaction[0].amount.toString()).toBe('450') + expect(transaction[0].memo).toBe('Herzlich Willkommen bei Gradido liebe Bibi!') + expect(transaction[0].linkedTransactionId).toEqual(null) + expect(transaction[0].transactionLinkId).toEqual(null) + expect(transaction[0].previous).toEqual(null) + expect(transaction[0].linkedUserId).toEqual(null) + expect(transaction[0].typeId).toEqual(1) + }) + }) + }) }) }) }) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 96ad44ccc..ba703a554 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -307,8 +307,11 @@ export class AdminResolver { @Authorized([RIGHTS.DELETE_PENDING_CREATION]) @Mutation(() => Boolean) async deletePendingCreation(@Arg('id', () => Int) id: number): Promise { - const entity = await AdminPendingCreation.findOneOrFail(id) - const res = await AdminPendingCreation.delete(entity) + const pendingCreation = await AdminPendingCreation.findOne(id) + if (!pendingCreation) { + throw new Error('Creation not found to given id.') + } + const res = await AdminPendingCreation.delete(pendingCreation) return !!res } @@ -318,7 +321,10 @@ export class AdminResolver { @Arg('id', () => Int) id: number, @Ctx() context: Context, ): Promise { - const pendingCreation = await AdminPendingCreation.findOneOrFail(id) + const pendingCreation = await AdminPendingCreation.findOne(id) + if (!pendingCreation) { + throw new Error('Creation not found to given id.') + } const moderatorUser = getUser(context) if (moderatorUser.id === pendingCreation.userId) throw new Error('Moderator can not confirm own pending creation') @@ -349,8 +355,7 @@ export class AdminResolver { transaction.memo = pendingCreation.memo transaction.userId = pendingCreation.userId transaction.previous = lastTransaction ? lastTransaction.id : null - // TODO pending creations decimal - transaction.amount = new Decimal(Number(pendingCreation.amount)) + transaction.amount = pendingCreation.amount transaction.creationDate = pendingCreation.date transaction.balance = newBalance transaction.balanceDate = receivedCallDate From 372b9d16d04f41b5eb4f0d6e2b62e0ffce585821 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 27 Apr 2022 17:52:52 +0200 Subject: [PATCH 17/26] test coverage backend to 65% --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3d046fcda..999863dd9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -528,7 +528,7 @@ jobs: report_name: Coverage Backend type: lcov result_path: ./backend/coverage/lcov.info - min_coverage: 58 + min_coverage: 65 token: ${{ github.token }} ########################################################################## From 549aa1c3aab7aec7077a5c995f5499321818e766 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Thu, 28 Apr 2022 11:18:50 +0200 Subject: [PATCH 18/26] Update backend/src/graphql/resolver/AdminResolver.test.ts Co-authored-by: Ulf Gebhardt --- backend/src/graphql/resolver/AdminResolver.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/AdminResolver.test.ts b/backend/src/graphql/resolver/AdminResolver.test.ts index 4102cb382..25c026ea7 100644 --- a/backend/src/graphql/resolver/AdminResolver.test.ts +++ b/backend/src/graphql/resolver/AdminResolver.test.ts @@ -106,7 +106,7 @@ describe('AdminResolver', () => { resetToken() }) - describe('user to delete user does not exist', () => { + describe('user to be delete does not exist', () => { it('throws an error', async () => { await expect( mutate({ mutation: deleteUser, variables: { userId: admin.id + 1 } }), From f448daf83473a2e731153892ee899525a4cc00d2 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Thu, 28 Apr 2022 11:18:59 +0200 Subject: [PATCH 19/26] Update backend/src/graphql/resolver/AdminResolver.test.ts Co-authored-by: Ulf Gebhardt --- backend/src/graphql/resolver/AdminResolver.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/AdminResolver.test.ts b/backend/src/graphql/resolver/AdminResolver.test.ts index 25c026ea7..b36ae5b4c 100644 --- a/backend/src/graphql/resolver/AdminResolver.test.ts +++ b/backend/src/graphql/resolver/AdminResolver.test.ts @@ -67,7 +67,7 @@ describe('AdminResolver', () => { }) describe('authenticated', () => { - describe('with user rights', () => { + describe('without admin rights', () => { beforeAll(async () => { user = await userFactory(testEnv, bibiBloxberg) await query({ From 4c6660a7a57942f958d62e0ee71012499a0ab819 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Thu, 28 Apr 2022 11:19:34 +0200 Subject: [PATCH 20/26] Update backend/src/graphql/resolver/AdminResolver.test.ts Co-authored-by: Ulf Gebhardt --- backend/src/graphql/resolver/AdminResolver.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/AdminResolver.test.ts b/backend/src/graphql/resolver/AdminResolver.test.ts index b36ae5b4c..2895cac88 100644 --- a/backend/src/graphql/resolver/AdminResolver.test.ts +++ b/backend/src/graphql/resolver/AdminResolver.test.ts @@ -175,7 +175,7 @@ describe('AdminResolver', () => { }) describe('authenticated', () => { - describe('with user rights', () => { + describe('without admin rights', () => { beforeAll(async () => { user = await userFactory(testEnv, bibiBloxberg) await query({ From 51f8dfb89f4ff7b2a1747a8d981be47d68685e86 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Thu, 28 Apr 2022 11:19:51 +0200 Subject: [PATCH 21/26] Update backend/src/graphql/resolver/AdminResolver.test.ts Co-authored-by: Ulf Gebhardt --- backend/src/graphql/resolver/AdminResolver.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/AdminResolver.test.ts b/backend/src/graphql/resolver/AdminResolver.test.ts index 2895cac88..bc650ed57 100644 --- a/backend/src/graphql/resolver/AdminResolver.test.ts +++ b/backend/src/graphql/resolver/AdminResolver.test.ts @@ -214,7 +214,7 @@ describe('AdminResolver', () => { resetToken() }) - describe('user to undelete user does not exist', () => { + describe('user to be undelete does not exist', () => { it('throws an error', async () => { await expect( mutate({ mutation: unDeleteUser, variables: { userId: admin.id + 1 } }), From 77ae24d54d96468cdcb8b2f3d201a1b1ee1a43b0 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Thu, 28 Apr 2022 11:20:44 +0200 Subject: [PATCH 22/26] Update backend/src/graphql/resolver/AdminResolver.test.ts Co-authored-by: Ulf Gebhardt --- backend/src/graphql/resolver/AdminResolver.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/AdminResolver.test.ts b/backend/src/graphql/resolver/AdminResolver.test.ts index bc650ed57..e0dc6b54c 100644 --- a/backend/src/graphql/resolver/AdminResolver.test.ts +++ b/backend/src/graphql/resolver/AdminResolver.test.ts @@ -366,7 +366,7 @@ describe('AdminResolver', () => { }) describe('authenticated', () => { - describe('with user rights', () => { + describe('without admin rights', () => { beforeAll(async () => { user = await userFactory(testEnv, bibiBloxberg) await query({ From 6251ad54d2f5a7147d8184622995d1859d3b232d Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Thu, 28 Apr 2022 11:22:11 +0200 Subject: [PATCH 23/26] Update backend/src/graphql/resolver/AdminResolver.test.ts Co-authored-by: Ulf Gebhardt --- backend/src/graphql/resolver/AdminResolver.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/AdminResolver.test.ts b/backend/src/graphql/resolver/AdminResolver.test.ts index e0dc6b54c..5872a5092 100644 --- a/backend/src/graphql/resolver/AdminResolver.test.ts +++ b/backend/src/graphql/resolver/AdminResolver.test.ts @@ -1010,7 +1010,7 @@ describe('AdminResolver', () => { }) }) - describe('creation of other user', () => { + describe('confirm creation for other user', () => { beforeAll(async () => { const now = new Date() creation = await creationFactory(testEnv, { From 48826feaa64fa2c35d5bdc9bd49b0bf3a965253f Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Thu, 28 Apr 2022 11:53:24 +0200 Subject: [PATCH 24/26] improve error messages --- .../graphql/resolver/AdminResolver.test.ts | 20 +++++++++---------- backend/src/graphql/resolver/AdminResolver.ts | 8 ++++---- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/backend/src/graphql/resolver/AdminResolver.test.ts b/backend/src/graphql/resolver/AdminResolver.test.ts index 5872a5092..c2a9256fd 100644 --- a/backend/src/graphql/resolver/AdminResolver.test.ts +++ b/backend/src/graphql/resolver/AdminResolver.test.ts @@ -236,7 +236,7 @@ describe('AdminResolver', () => { mutate({ mutation: unDeleteUser, variables: { userId: user.id } }), ).resolves.toEqual( expect.objectContaining({ - errors: [new GraphQLError('User already deleted')], + errors: [new GraphQLError('User is not deleted')], }), ) }) @@ -265,7 +265,7 @@ describe('AdminResolver', () => { const variables = { email: 'bibi@bloxberg.de', amount: new Decimal(2000), - memo: 'Vielen Dank für den Zaubertrank!', + memo: 'Aktives Grundeinkommen', creationDate: 'not-valid', } @@ -552,7 +552,7 @@ describe('AdminResolver', () => { mutate({ mutation: createPendingCreation, variables }), ).resolves.toEqual( expect.objectContaining({ - errors: [new GraphQLError('No Creation found!')], + errors: [new GraphQLError('No information for available creations for the given date')], }), ) }) @@ -570,7 +570,7 @@ describe('AdminResolver', () => { mutate({ mutation: createPendingCreation, variables }), ).resolves.toEqual( expect.objectContaining({ - errors: [new GraphQLError('No Creation found!')], + errors: [new GraphQLError('No information for available creations for the given date')], }), ) }) @@ -588,7 +588,7 @@ describe('AdminResolver', () => { mutate({ mutation: createPendingCreation, variables }), ).resolves.toEqual( expect.objectContaining({ - errors: [new GraphQLError('No Creation found!')], + errors: [new GraphQLError('No information for available creations for the given date')], }), ) }) @@ -603,7 +603,7 @@ describe('AdminResolver', () => { expect.objectContaining({ errors: [ new GraphQLError( - 'The amount (2000 GDD) to be created exceeds the available amount (1000 GDD) for this month.', + 'The amount (2000 GDD) to be created exceeds the amount (1000 GDD) still available for this month.', ), ], }), @@ -635,7 +635,7 @@ describe('AdminResolver', () => { expect.objectContaining({ errors: [ new GraphQLError( - 'The amount (1000 GDD) to be created exceeds the available amount (800 GDD) for this month.', + 'The amount (1000 GDD) to be created exceeds the amount (800 GDD) still available for this month.', ), ], }), @@ -803,7 +803,7 @@ describe('AdminResolver', () => { expect.objectContaining({ errors: [ new GraphQLError( - 'The amount (1900 GDD) to be created exceeds the available amount (500 GDD) for this month.', + 'The amount (1900 GDD) to be created exceeds the amount (500 GDD) still available for this month.', ), ], }), @@ -917,7 +917,7 @@ describe('AdminResolver', () => { lastName: 'Bloxberg', email: 'bibi@bloxberg.de', date: expect.any(String), - memo: 'Vielen Dank für den Zaubertrank!', + memo: 'Aktives Grundeinkommen', amount: '200', moderator: admin.id, creation: ['1000', '1000', '300'], @@ -941,7 +941,7 @@ describe('AdminResolver', () => { }), ).resolves.toEqual( expect.objectContaining({ - errors: [new GraphQLError('Creation not found to given id.')], + errors: [new GraphQLError('Creation not found for given id.')], }), ) }) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index ba703a554..94734d239 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -161,7 +161,7 @@ export class AdminResolver { throw new Error(`Could not find user with userId: ${userId}`) } if (!user.deletedAt) { - throw new Error('User already deleted') + throw new Error('User is not deleted') } await user.recover() return null @@ -309,7 +309,7 @@ export class AdminResolver { async deletePendingCreation(@Arg('id', () => Int) id: number): Promise { const pendingCreation = await AdminPendingCreation.findOne(id) if (!pendingCreation) { - throw new Error('Creation not found to given id.') + throw new Error('Creation not found for given id.') } const res = await AdminPendingCreation.delete(pendingCreation) return !!res @@ -526,12 +526,12 @@ function isCreationValid(creations: Decimal[], amount: Decimal, creationDate: Da const index = getCreationIndex(creationDate.getMonth()) if (index < 0) { - throw new Error(`No Creation found!`) + throw new Error('No information for available creations for the given date') } if (amount.greaterThan(creations[index].toString())) { throw new Error( - `The amount (${amount} GDD) to be created exceeds the available amount (${creations[index]} GDD) for this month.`, + `The amount (${amount} GDD) to be created exceeds the amount (${creations[index]} GDD) still available for this month.`, ) } From eec79000c9003e415daf6bed901a968a4b976140 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Thu, 28 Apr 2022 11:59:52 +0200 Subject: [PATCH 25/26] linting --- backend/src/graphql/resolver/AdminResolver.test.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/backend/src/graphql/resolver/AdminResolver.test.ts b/backend/src/graphql/resolver/AdminResolver.test.ts index c2a9256fd..cf51b9e25 100644 --- a/backend/src/graphql/resolver/AdminResolver.test.ts +++ b/backend/src/graphql/resolver/AdminResolver.test.ts @@ -552,7 +552,9 @@ describe('AdminResolver', () => { mutate({ mutation: createPendingCreation, variables }), ).resolves.toEqual( expect.objectContaining({ - errors: [new GraphQLError('No information for available creations for the given date')], + errors: [ + new GraphQLError('No information for available creations for the given date'), + ], }), ) }) @@ -570,7 +572,9 @@ describe('AdminResolver', () => { mutate({ mutation: createPendingCreation, variables }), ).resolves.toEqual( expect.objectContaining({ - errors: [new GraphQLError('No information for available creations for the given date')], + errors: [ + new GraphQLError('No information for available creations for the given date'), + ], }), ) }) @@ -588,7 +592,9 @@ describe('AdminResolver', () => { mutate({ mutation: createPendingCreation, variables }), ).resolves.toEqual( expect.objectContaining({ - errors: [new GraphQLError('No information for available creations for the given date')], + errors: [ + new GraphQLError('No information for available creations for the given date'), + ], }), ) }) From 82044da043aaced5de252fb0580d1683ac815d84 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Thu, 28 Apr 2022 16:19:56 +0200 Subject: [PATCH 26/26] Update backend/src/graphql/resolver/AdminResolver.test.ts --- backend/src/graphql/resolver/AdminResolver.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/AdminResolver.test.ts b/backend/src/graphql/resolver/AdminResolver.test.ts index cf51b9e25..6842f09ca 100644 --- a/backend/src/graphql/resolver/AdminResolver.test.ts +++ b/backend/src/graphql/resolver/AdminResolver.test.ts @@ -106,7 +106,7 @@ describe('AdminResolver', () => { resetToken() }) - describe('user to be delete does not exist', () => { + describe('user to be deleted does not exist', () => { it('throws an error', async () => { await expect( mutate({ mutation: deleteUser, variables: { userId: admin.id + 1 } }),