diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts index e4929483f..a6e7d8867 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts @@ -4,10 +4,12 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-explicit-any */ import { Connection } from '@dbTools/typeorm' +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' @@ -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('does not call sendAddedContributionMessageEmail', () => { + expect(sendAddedContributionMessageEmail).not.toBeCalled() + }) + + it('does not 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('changes 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() - }) - }) }) }) 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, diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 735b6ee69..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 { @@ -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({ diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 21bc416e5..80ea3e783 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 call + contribution.messages = contribution.messages?.filter( + (m) => m.type !== ContributionMessageType.MODERATOR, + ) + return new Contribution(contribution, user) + }), ) }