diff --git a/backend/src/graphql/resolver/TransactionLinkResolver.test.ts b/backend/src/graphql/resolver/TransactionLinkResolver.test.ts index fc5ca170a..14c5b350b 100644 --- a/backend/src/graphql/resolver/TransactionLinkResolver.test.ts +++ b/backend/src/graphql/resolver/TransactionLinkResolver.test.ts @@ -22,10 +22,12 @@ import { createContribution, updateContribution, createTransactionLink, + confirmContribution, } from '@/seeds/graphql/mutations' import { listTransactionLinksAdmin } from '@/seeds/graphql/queries' import { ContributionLink as DbContributionLink } from '@entity/ContributionLink' import { User } from '@entity/User' +import { Transaction } from '@entity/Transaction' import { UnconfirmedContribution } from '@model/UnconfirmedContribution' import Decimal from 'decimal.js-light' import { GraphQLError } from 'graphql' @@ -142,6 +144,8 @@ describe('TransactionLinkResolver', () => { resetToken() }) + let contributionId: number + describe('unauthenticated', () => { it('throws an error', async () => { jest.clearAllMocks() @@ -311,7 +315,6 @@ describe('TransactionLinkResolver', () => { }) }) - // TODO: have this test separated into a transactionLink and a contributionLink part describe('redeem daily Contribution Link', () => { const now = new Date() let contributionLink: DbContributionLink | undefined @@ -337,6 +340,10 @@ describe('TransactionLinkResolver', () => { }) }) + afterAll(async () => { + await resetEntity(Transaction) + }) + it('has a daily contribution link in the database', async () => { const cls = await DbContributionLink.find() expect(cls).toHaveLength(1) @@ -378,6 +385,7 @@ describe('TransactionLinkResolver', () => { }, }) contribution = result.data.createContribution + contributionId = result.data.createContribution.id }) it('does not allow the user to redeem the contribution link', async () => { @@ -513,6 +521,92 @@ describe('TransactionLinkResolver', () => { }) }) }) + + describe('transaction link', () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + describe('link does not exits', () => { + beforeAll(async () => { + await mutate({ + mutation: login, + variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, + }) + }) + + it('throws and logs the error', async () => { + await expect( + mutate({ + mutation: redeemTransactionLink, + variables: { + code: 'not-valid', + }, + }), + ).resolves.toMatchObject({ + errors: [new GraphQLError('Transaction link not found')], + }) + expect(logger.error).toBeCalledWith('Transaction link not found', 'not-valid') + }) + }) + + describe('link exists', () => { + let myCode: string + + beforeAll(async () => { + await mutate({ + mutation: login, + variables: { email: 'peter@lustig.de', password: 'Aa12345_' }, + }) + await mutate({ + mutation: confirmContribution, + variables: { id: contributionId }, + }) + await mutate({ + mutation: login, + variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, + }) + const { + data: { + createTransactionLink: { code }, + }, + } = await mutate({ + mutation: createTransactionLink, + variables: { + amount: 200, + memo: 'This is a transaction link from bibi', + }, + }) + myCode = code + }) + + describe('own link', () => { + beforeAll(async () => { + await mutate({ + mutation: login, + variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, + }) + }) + + it('throws and logs an error', async () => { + await expect( + mutate({ + mutation: redeemTransactionLink, + variables: { + code: myCode, + }, + }), + ).resolves.toMatchObject({ + errors: [new GraphQLError('Cannot redeem own transaction link')], + }) + expect(logger.error).toBeCalledWith( + 'Cannot redeem own transaction link', + expect.any(Number), + ) + }) + }) + }) + }) }) }) diff --git a/backend/src/graphql/resolver/TransactionLinkResolver.ts b/backend/src/graphql/resolver/TransactionLinkResolver.ts index d6e8e09ae..9e365ab51 100644 --- a/backend/src/graphql/resolver/TransactionLinkResolver.ts +++ b/backend/src/graphql/resolver/TransactionLinkResolver.ts @@ -285,12 +285,20 @@ export class TransactionLinkResolver { return true } else { const now = new Date() - const transactionLink = await DbTransactionLink.findOneOrFail({ code }) - const linkedUser = await DbUser.findOneOrFail( + const transactionLink = await DbTransactionLink.findOne({ code }) + if (!transactionLink) { + throw new LogError('Transaction link not found', code) + } + + const linkedUser = await DbUser.findOne( { id: transactionLink.userId }, { relations: ['emailContact'] }, ) + if (!linkedUser) { + throw new LogError('Linked user not found for given link', transactionLink.userId) + } + if (user.id === linkedUser.id) { throw new LogError('Cannot redeem own transaction link', user.id) } diff --git a/backend/src/graphql/resolver/semaphore.test.ts b/backend/src/graphql/resolver/semaphore.test.ts index 70e3e5f96..b3c99ba7d 100644 --- a/backend/src/graphql/resolver/semaphore.test.ts +++ b/backend/src/graphql/resolver/semaphore.test.ts @@ -191,4 +191,50 @@ describe('semaphore', () => { await expect(confirmBibisContribution).resolves.toMatchObject({ errors: undefined }) await expect(confirmBobsContribution).resolves.toMatchObject({ errors: undefined }) }) + + describe('redeem transaction link twice', () => { + let myCode: string + + beforeAll(async () => { + await mutate({ + mutation: login, + variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, + }) + const { + data: { createTransactionLink: bibisLink }, + } = await mutate({ + mutation: createTransactionLink, + variables: { + amount: 20, + memo: 'Bibis Link', + }, + }) + myCode = bibisLink.code + await mutate({ + mutation: login, + variables: { email: 'bob@baumeister.de', password: 'Aa12345_' }, + }) + }) + + it('does not throw, but should', async () => { + const redeem1 = mutate({ + mutation: redeemTransactionLink, + variables: { + code: myCode, + }, + }) + const redeem2 = mutate({ + mutation: redeemTransactionLink, + variables: { + code: myCode, + }, + }) + await expect(redeem1).resolves.toMatchObject({ + errors: undefined, + }) + await expect(redeem2).resolves.toMatchObject({ + errors: undefined, + }) + }) + }) }) diff --git a/backend/src/seeds/graphql/mutations.ts b/backend/src/seeds/graphql/mutations.ts index 69d6d16d8..1aa12a32f 100644 --- a/backend/src/seeds/graphql/mutations.ts +++ b/backend/src/seeds/graphql/mutations.ts @@ -89,6 +89,12 @@ export const createTransactionLink = gql` } ` +export const deleteTransactionLink = gql` + mutation ($id: Int!) { + deleteTransactionLink(id: $id) + } +` + // from admin interface export const adminCreateContribution = gql`