From d1ba7c7f418fbc6af57cd44c9039de6bee0fd000 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 5 Jul 2023 09:05:20 +0200 Subject: [PATCH 1/9] do not change contribution status on moderator message --- .../resolver/ContributionMessageResolver.ts | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.ts b/backend/src/graphql/resolver/ContributionMessageResolver.ts index 0cbc2446c..5910befa1 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.ts @@ -158,16 +158,18 @@ export class ContributionMessageResolver { contributionMessage.isModerator = true await queryRunner.manager.insert(DbContributionMessage, contributionMessage) - if ( - contribution.contributionStatus === ContributionStatus.DELETED || - contribution.contributionStatus === ContributionStatus.DENIED || - contribution.contributionStatus === ContributionStatus.PENDING - ) { - contribution.contributionStatus = ContributionStatus.IN_PROGRESS - await queryRunner.manager.update(DbContribution, { id: contributionId }, contribution) - } - if (messageType !== ContributionMessageType.MODERATOR) { + // change status (does not apply to moderator messages) + if ( + contribution.contributionStatus === ContributionStatus.DELETED || + contribution.contributionStatus === ContributionStatus.DENIED || + contribution.contributionStatus === ContributionStatus.PENDING + ) { + contribution.contributionStatus = ContributionStatus.IN_PROGRESS + await queryRunner.manager.update(DbContribution, { id: contributionId }, contribution) + } + + // send email (never for moderator messages) void sendAddedContributionMessageEmail({ firstName: contribution.user.firstName, lastName: contribution.user.lastName, From 69ae2e91f0089bf69a1b445d6b79e41d81000caf Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 5 Jul 2023 09:37:35 +0200 Subject: [PATCH 2/9] fix message count on moderator message contributions (filter out MODERATOR messages) --- backend/src/graphql/resolver/ContributionResolver.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 21bc416e5..8739e3ebf 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -142,9 +142,16 @@ export class ContributionResolver { userId: user.id, statusFilter, }) + return new ContributionListResult( count, - dbContributions.map((contribution) => new Contribution(contribution, user)), + dbContributions.map((contribution) => { + // filter out moderator messages for this call8user) + contribution.messages = contribution.messages?.filter( + (m) => m.type !== ContributionMessageType.MODERATOR, + ) + return new Contribution(contribution, user) + }), ) } From ff03b05e5e0621d7002a9d7c6aa3e9e535049712 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 5 Jul 2023 10:40:36 +0200 Subject: [PATCH 3/9] test case for Moderation Contribution message does not change contribution status, normal message does --- .../ContributionMessageResolver.test.ts | 90 +++++++++++-------- 1 file changed, 55 insertions(+), 35 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts index e4929483f..a48e005d6 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts @@ -5,6 +5,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { Connection } from '@dbTools/typeorm' import { Event as DbEvent } from '@entity/Event' +import { Contribution as DbContribution } from '@entity/Contribution' import { ApolloServerTestClient } from 'apollo-server-testing' import { GraphQLError } from 'graphql' @@ -23,6 +24,7 @@ import { import { listContributionMessages, adminListContributionMessages } from '@/seeds/graphql/queries' import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' import { peterLustig } from '@/seeds/users/peter-lustig' +import { ContributionStatus } from '../enum/ContributionStatus' jest.mock('@/emails/sendEmailVariants', () => { const originalModule = jest.requireActual('@/emails/sendEmailVariants') @@ -168,6 +170,50 @@ describe('ContributionMessageResolver', () => { }) }) + describe('contribution message type MODERATOR', () => { + beforeAll(() => { + jest.clearAllMocks() + }) + + it('creates ContributionMessage', async () => { + await expect( + mutate({ + mutation: adminCreateContributionMessage, + variables: { + contributionId: result.data.createContribution.id, + message: 'Internal moderator communication', + messageType: 'MODERATOR', + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + adminCreateContributionMessage: expect.objectContaining({ + id: expect.any(Number), + message: 'Internal moderator communication', + type: 'MODERATOR', + userFirstName: 'Peter', + userLastName: 'Lustig', + }), + }, + }), + ) + }) + + it("don't call sendAddedContributionMessageEmail", () => { + expect(sendAddedContributionMessageEmail).not.toBeCalled() + }) + + it("don't change contribution status", async () => { + await expect(DbContribution.find()).resolves.toContainEqual( + expect.objectContaining({ + id: result.data.createContribution.id, + contributionStatus: ContributionStatus.PENDING, + }), + ) + }) + }) + describe('valid input', () => { it('creates ContributionMessage', async () => { await expect( @@ -205,6 +251,15 @@ describe('ContributionMessageResolver', () => { }) }) + it('change contribution status', async () => { + await expect(DbContribution.find()).resolves.toContainEqual( + expect.objectContaining({ + id: result.data.createContribution.id, + contributionStatus: ContributionStatus.IN_PROGRESS, + }), + ) + }) + it('stores the ADMIN_CONTRIBUTION_MESSAGE_CREATE event in the database', async () => { await expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ @@ -217,41 +272,6 @@ describe('ContributionMessageResolver', () => { ) }) }) - - describe('contribution message type MODERATOR', () => { - beforeAll(() => { - jest.clearAllMocks() - }) - - it('creates ContributionMessage', async () => { - await expect( - mutate({ - mutation: adminCreateContributionMessage, - variables: { - contributionId: result.data.createContribution.id, - message: 'Internal moderator communication', - messageType: 'MODERATOR', - }, - }), - ).resolves.toEqual( - expect.objectContaining({ - data: { - adminCreateContributionMessage: expect.objectContaining({ - id: expect.any(Number), - message: 'Internal moderator communication', - type: 'MODERATOR', - userFirstName: 'Peter', - userLastName: 'Lustig', - }), - }, - }), - ) - }) - - it("don't call sendAddedContributionMessageEmail", () => { - expect(sendAddedContributionMessageEmail).not.toBeCalled() - }) - }) }) }) From e6f18f718bbfa8ae19bc246c39a9397bf85295d0 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 5 Jul 2023 10:41:55 +0200 Subject: [PATCH 4/9] fix linting --- .../src/graphql/resolver/ContributionMessageResolver.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts index a48e005d6..d13d959eb 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts @@ -4,11 +4,12 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-explicit-any */ import { Connection } from '@dbTools/typeorm' -import { Event as DbEvent } from '@entity/Event' import { Contribution as DbContribution } from '@entity/Contribution' +import { Event as DbEvent } from '@entity/Event' import { ApolloServerTestClient } from 'apollo-server-testing' import { GraphQLError } from 'graphql' +import { ContributionStatus } from '@enum/ContributionStatus' import { cleanDB, resetToken, testEnvironment } from '@test/helpers' import { logger, i18n as localization } from '@test/testSetup' @@ -24,7 +25,6 @@ import { import { listContributionMessages, adminListContributionMessages } from '@/seeds/graphql/queries' import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' import { peterLustig } from '@/seeds/users/peter-lustig' -import { ContributionStatus } from '../enum/ContributionStatus' jest.mock('@/emails/sendEmailVariants', () => { const originalModule = jest.requireActual('@/emails/sendEmailVariants') From f5a761c769b26d28cd126658e3157dcfcd5df75e Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 5 Jul 2023 11:07:45 +0200 Subject: [PATCH 5/9] implicitly test for messagescounts to be correct when moderator message is present(not shown on listContributions) --- .../resolver/ContributionResolver.test.ts | 52 ++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 735b6ee69..68ff97b05 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -55,6 +55,7 @@ import { garrickOllivander } from '@/seeds/users/garrick-ollivander' import { peterLustig } from '@/seeds/users/peter-lustig' import { raeuberHotzenplotz } from '@/seeds/users/raeuber-hotzenplotz' import { stephenHawking } from '@/seeds/users/stephen-hawking' +import { ContributionMessageType } from '../enum/ContributionMessageType' jest.mock('@/emails/sendEmailVariants') @@ -153,6 +154,14 @@ describe('ContributionResolver', () => { message: 'Test message to IN_PROGRESS contribution', }, }) + await mutate({ + mutation: adminCreateContributionMessage, + variables: { + contributionId: pendingContribution.data.createContribution.id, + message: 'Test moderator message', + messageType: ContributionMessageType.MODERATOR, + }, + }) await mutate({ mutation: logout, }) @@ -1043,31 +1052,37 @@ describe('ContributionResolver', () => { amount: '100', id: contributionToConfirm.data.createContribution.id, memo: 'Test contribution to confirm', + messagesCount: 0, }), expect.objectContaining({ id: pendingContribution.data.createContribution.id, memo: 'Test PENDING contribution update', amount: '10', + messagesCount: 1, }), expect.objectContaining({ id: contributionToDeny.data.createContribution.id, memo: 'Test contribution to deny', amount: '100', + messagesCount: 0, }), expect.objectContaining({ id: contributionToDelete.data.createContribution.id, memo: 'Test contribution to delete', amount: '100', + messagesCount: 0, }), expect.objectContaining({ id: inProgressContribution.data.createContribution.id, memo: 'Test IN_PROGRESS contribution', amount: '100', + messagesCount: 1, }), expect.objectContaining({ id: bibiCreatedContribution.id, memo: 'Herzlich Willkommen bei Gradido!', amount: '1000', + messagesCount: 0, }), ]), }) @@ -1099,24 +1114,28 @@ describe('ContributionResolver', () => { status: 'PENDING', memo: 'Test PENDING contribution update', amount: '10', + messagesCount: 1, }), expect.objectContaining({ id: contributionToDeny.data.createContribution.id, status: 'DENIED', memo: 'Test contribution to deny', amount: '100', + messagesCount: 0, }), expect.objectContaining({ id: contributionToDelete.data.createContribution.id, status: 'DELETED', memo: 'Test contribution to delete', amount: '100', + messagesCount: 0, }), expect.objectContaining({ id: inProgressContribution.data.createContribution.id, status: 'IN_PROGRESS', memo: 'Test IN_PROGRESS contribution', amount: '100', + messagesCount: 1, }), ]), }) @@ -1230,42 +1249,49 @@ describe('ContributionResolver', () => { status: 'CONFIRMED', id: contributionToConfirm.data.createContribution.id, memo: 'Test contribution to confirm', + messagesCount: 0, }), expect.objectContaining({ id: pendingContribution.data.createContribution.id, status: 'PENDING', memo: 'Test PENDING contribution update', amount: '10', + messagesCount: 0, }), expect.objectContaining({ id: contributionToDeny.data.createContribution.id, status: 'DENIED', memo: 'Test contribution to deny', amount: '100', + messagesCount: 0, }), expect.objectContaining({ id: inProgressContribution.data.createContribution.id, status: 'IN_PROGRESS', memo: 'Test IN_PROGRESS contribution', amount: '100', + messagesCount: 0, }), expect.objectContaining({ id: bibiCreatedContribution.id, status: 'CONFIRMED', memo: 'Herzlich Willkommen bei Gradido!', amount: '1000', + messagesCount: 0, }), expect.objectContaining({ id: expect.any(Number), status: 'CONFIRMED', memo: 'Whatever contribution', amount: '166', + messagesCount: 0, }), expect.objectContaining({ id: expect.any(Number), status: 'DENIED', memo: 'Whatever contribution', amount: '166', + messagesCount: 0, }), ]), }) @@ -1295,42 +1321,49 @@ describe('ContributionResolver', () => { status: 'CONFIRMED', id: contributionToConfirm.data.createContribution.id, memo: 'Test contribution to confirm', + messagesCount: 0, }), expect.objectContaining({ id: pendingContribution.data.createContribution.id, status: 'PENDING', memo: 'Test PENDING contribution update', amount: '10', + messagesCount: 0, }), expect.objectContaining({ id: contributionToDeny.data.createContribution.id, status: 'DENIED', memo: 'Test contribution to deny', amount: '100', + messagesCount: 0, }), expect.objectContaining({ id: inProgressContribution.data.createContribution.id, status: 'IN_PROGRESS', memo: 'Test IN_PROGRESS contribution', amount: '100', + messagesCount: 0, }), expect.objectContaining({ id: bibiCreatedContribution.id, status: 'CONFIRMED', memo: 'Herzlich Willkommen bei Gradido!', amount: '1000', + messagesCount: 0, }), expect.objectContaining({ id: expect.any(Number), status: 'CONFIRMED', memo: 'Whatever contribution', amount: '166', + messagesCount: 0, }), expect.objectContaining({ id: expect.any(Number), status: 'DENIED', memo: 'Whatever contribution', amount: '166', + messagesCount: 0, }), ]), }) @@ -1360,42 +1393,49 @@ describe('ContributionResolver', () => { status: 'CONFIRMED', id: contributionToConfirm.data.createContribution.id, memo: 'Test contribution to confirm', + messagesCount: 0, }), expect.objectContaining({ id: pendingContribution.data.createContribution.id, status: 'PENDING', memo: 'Test PENDING contribution update', amount: '10', + messagesCount: 0, }), expect.objectContaining({ id: contributionToDeny.data.createContribution.id, status: 'DENIED', memo: 'Test contribution to deny', amount: '100', + messagesCount: 0, }), expect.objectContaining({ id: inProgressContribution.data.createContribution.id, status: 'IN_PROGRESS', memo: 'Test IN_PROGRESS contribution', amount: '100', + messagesCount: 0, }), expect.objectContaining({ id: bibiCreatedContribution.id, status: 'CONFIRMED', memo: 'Herzlich Willkommen bei Gradido!', amount: '1000', + messagesCount: 0, }), expect.objectContaining({ id: expect.any(Number), status: 'CONFIRMED', memo: 'Whatever contribution', amount: '166', + messagesCount: 0, }), expect.objectContaining({ id: expect.any(Number), status: 'DENIED', memo: 'Whatever contribution', amount: '166', + messagesCount: 0, }), ]), }) @@ -1422,18 +1462,21 @@ describe('ContributionResolver', () => { status: 'CONFIRMED', id: contributionToConfirm.data.createContribution.id, memo: 'Test contribution to confirm', + messagesCount: 0, }), expect.objectContaining({ id: bibiCreatedContribution.id, status: 'CONFIRMED', memo: 'Herzlich Willkommen bei Gradido!', amount: '1000', + messagesCount: 0, }), expect.objectContaining({ id: expect.any(Number), status: 'CONFIRMED', memo: 'Whatever contribution', amount: '166', + messagesCount: 0, }), expect.not.objectContaining({ status: 'PENDING', @@ -1484,6 +1527,7 @@ describe('ContributionResolver', () => { status: 'PENDING', memo: 'Test PENDING contribution update', amount: '10', + messagesCount: 0, }), ]), }) @@ -1522,6 +1566,7 @@ describe('ContributionResolver', () => { status: 'IN_PROGRESS', memo: 'Test IN_PROGRESS contribution', amount: '100', + messagesCount: 0, }), ]), }) @@ -1554,6 +1599,7 @@ describe('ContributionResolver', () => { status: 'DENIED', memo: 'Whatever contribution', amount: '166', + messagesCount: 0, }), expect.not.objectContaining({ status: 'CONFIRMED', @@ -1611,24 +1657,28 @@ describe('ContributionResolver', () => { status: 'CONFIRMED', id: contributionToConfirm.data.createContribution.id, memo: 'Test contribution to confirm', + messagesCount: 0, }), expect.objectContaining({ id: pendingContribution.data.createContribution.id, status: 'PENDING', memo: 'Test PENDING contribution update', amount: '10', + messagesCount: 0, }), expect.objectContaining({ id: bibiCreatedContribution.id, status: 'CONFIRMED', memo: 'Herzlich Willkommen bei Gradido!', amount: '1000', + messagesCount: 0, }), expect.objectContaining({ id: expect.any(Number), status: 'CONFIRMED', memo: 'Whatever contribution', amount: '166', + messagesCount: 0, }), expect.not.objectContaining({ status: 'DENIED', @@ -2825,7 +2875,7 @@ describe('ContributionResolver', () => { id: expect.any(Number), lastName: 'Bloxberg', memo: 'Test PENDING contribution update', - messagesCount: 1, + messagesCount: 2, status: 'PENDING', }), expect.objectContaining({ From 604d67dd63cb428d29027927b5955fd90d9ac32f Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 5 Jul 2023 11:08:23 +0200 Subject: [PATCH 6/9] lint --- backend/src/graphql/resolver/ContributionResolver.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 68ff97b05..cbb92eff8 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -11,6 +11,7 @@ import { ApolloServerTestClient } from 'apollo-server-testing' import { Decimal } from 'decimal.js-light' import { GraphQLError } from 'graphql' +import { ContributionMessageType } from '@enum/ContributionMessageType' import { ContributionStatus } from '@enum/ContributionStatus' import { Order } from '@enum/Order' import { @@ -55,7 +56,6 @@ import { garrickOllivander } from '@/seeds/users/garrick-ollivander' import { peterLustig } from '@/seeds/users/peter-lustig' import { raeuberHotzenplotz } from '@/seeds/users/raeuber-hotzenplotz' import { stephenHawking } from '@/seeds/users/stephen-hawking' -import { ContributionMessageType } from '../enum/ContributionMessageType' jest.mock('@/emails/sendEmailVariants') From e228d8b3e7a562a0cdd32ab74dd04ccddae2943f Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 5 Jul 2023 12:56:29 +0200 Subject: [PATCH 7/9] Correct the test message. --- .../src/graphql/resolver/ContributionMessageResolver.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts index d13d959eb..efb024de3 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts @@ -200,11 +200,11 @@ describe('ContributionMessageResolver', () => { ) }) - it("don't call sendAddedContributionMessageEmail", () => { + it('does not call sendAddedContributionMessageEmail', () => { expect(sendAddedContributionMessageEmail).not.toBeCalled() }) - it("don't change contribution status", async () => { + it('does not change contribution status', async () => { await expect(DbContribution.find()).resolves.toContainEqual( expect.objectContaining({ id: result.data.createContribution.id, From 960252e9fc3b5a540cf9a9d7dc6559431bd58912 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 6 Jul 2023 02:05:48 +0200 Subject: [PATCH 8/9] fix comment, typo --- backend/src/graphql/resolver/ContributionResolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 8739e3ebf..80ea3e783 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -146,7 +146,7 @@ export class ContributionResolver { return new ContributionListResult( count, dbContributions.map((contribution) => { - // filter out moderator messages for this call8user) + // filter out moderator messages for this call contribution.messages = contribution.messages?.filter( (m) => m.type !== ContributionMessageType.MODERATOR, ) From 89c2c1c778daa49af0f2a5b8e164112569c78e17 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 6 Jul 2023 02:08:17 +0200 Subject: [PATCH 9/9] english typo --- .../src/graphql/resolver/ContributionMessageResolver.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts index efb024de3..a6e7d8867 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts @@ -251,7 +251,7 @@ describe('ContributionMessageResolver', () => { }) }) - it('change contribution status', async () => { + it('changes contribution status', async () => { await expect(DbContribution.find()).resolves.toContainEqual( expect.objectContaining({ id: result.data.createContribution.id,