From 3bf69814cac29040d63c3e6745eff5549fe62b30 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 16 Jan 2023 16:47:33 +0100 Subject: [PATCH] test error cases for semaphore --- .../resolver/TransactionLinkResolver.test.ts | 926 ++++++++++-------- .../resolver/TransactionLinkResolver.ts | 9 +- 2 files changed, 538 insertions(+), 397 deletions(-) diff --git a/backend/src/graphql/resolver/TransactionLinkResolver.test.ts b/backend/src/graphql/resolver/TransactionLinkResolver.test.ts index 9f7d30244..e841e8fad 100644 --- a/backend/src/graphql/resolver/TransactionLinkResolver.test.ts +++ b/backend/src/graphql/resolver/TransactionLinkResolver.test.ts @@ -4,7 +4,7 @@ import { transactionLinkCode } from './TransactionLinkResolver' import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' import { peterLustig } from '@/seeds/users/peter-lustig' -import { cleanDB, testEnvironment, resetToken } from '@test/helpers' +import { cleanDB, testEnvironment, resetToken, resetEntity } from '@test/helpers' import { creationFactory } from '@/seeds/factory/creation' import { creations } from '@/seeds/creation/index' import { userFactory } from '@/seeds/factory/user' @@ -50,238 +50,344 @@ afterAll(async () => { }) describe('TransactionLinkResolver', () => { - // TODO: have this test separated into a transactionLink and a contributionLink part (if possible) - describe('redeem daily Contribution Link', () => { - const now = new Date() - let contributionLink: DbContributionLink | undefined - let contribution: UnconfirmedContribution | undefined - - beforeAll(async () => { - await mutate({ - mutation: login, - variables: { email: 'peter@lustig.de', password: 'Aa12345_' }, - }) - await mutate({ - mutation: createContributionLink, - variables: { - amount: new Decimal(5), - name: 'Daily Contribution Link', - memo: 'Thank you for contribute daily to the community', - cycle: 'DAILY', - validFrom: new Date(now.getFullYear(), 0, 1).toISOString(), - validTo: new Date(now.getFullYear(), 11, 31, 23, 59, 59, 999).toISOString(), - maxAmountPerMonth: new Decimal(200), - maxPerCycle: 1, - }, - }) - }) - - it('has a daily contribution link in the database', async () => { - const cls = await DbContributionLink.find() - expect(cls).toHaveLength(1) - contributionLink = cls[0] - expect(contributionLink).toEqual( - expect.objectContaining({ - id: expect.any(Number), - name: 'Daily Contribution Link', - memo: 'Thank you for contribute daily to the community', - validFrom: new Date(now.getFullYear(), 0, 1), - validTo: new Date(now.getFullYear(), 11, 31, 23, 59, 59, 0), - cycle: 'DAILY', - maxPerCycle: 1, - totalMaxCountOfContribution: null, - maxAccountBalance: null, - minGapHours: null, - createdAt: expect.any(Date), - deletedAt: null, - code: expect.stringMatching(/^[0-9a-f]{24,24}$/), - linkEnabled: true, - amount: expect.decimalEqual(5), - maxAmountPerMonth: expect.decimalEqual(200), - }), - ) - }) - - describe('user has pending contribution of 1000 GDD', () => { - beforeAll(async () => { - await mutate({ - mutation: login, - variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, - }) - const result = await mutate({ - mutation: createContribution, - variables: { - amount: new Decimal(1000), - memo: 'I was brewing potions for the community the whole month', - creationDate: now.toISOString(), - }, - }) - contribution = result.data.createContribution - }) - - it('does not allow the user to redeem the contribution link', async () => { - await expect( - mutate({ - mutation: redeemTransactionLink, - variables: { - code: 'CL-' + (contributionLink ? contributionLink.code : ''), - }, - }), - ).resolves.toMatchObject({ - errors: [ - new GraphQLError( - 'Creation from contribution link was not successful. Error: The amount (5 GDD) to be created exceeds the amount (0 GDD) still available for this month.', - ), - ], - }) - }) - }) - - describe('user has no pending contributions that would not allow to redeem the link', () => { - beforeAll(async () => { - await mutate({ - mutation: login, - variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, - }) - await mutate({ - mutation: updateContribution, - variables: { - contributionId: contribution ? contribution.id : -1, - amount: new Decimal(800), - memo: 'I was brewing potions for the community the whole month', - creationDate: now.toISOString(), - }, - }) - }) - - it('allows the user to redeem the contribution link', async () => { - await expect( - mutate({ - mutation: redeemTransactionLink, - variables: { - code: 'CL-' + (contributionLink ? contributionLink.code : ''), - }, - }), - ).resolves.toMatchObject({ - data: { - redeemTransactionLink: true, - }, - errors: undefined, - }) - }) - - it('does not allow the user to redeem the contribution link a second time on the same day', async () => { - await expect( - mutate({ - mutation: redeemTransactionLink, - variables: { - code: 'CL-' + (contributionLink ? contributionLink.code : ''), - }, - }), - ).resolves.toMatchObject({ - errors: [ - new GraphQLError( - 'Creation from contribution link was not successful. Error: Contribution link already redeemed today', - ), - ], - }) - }) - - describe('after one day', () => { + describe('redeemTransactionLink', () => { + describe('contributionLink', () => { + describe('input not valid', () => { beforeAll(async () => { - jest.useFakeTimers() - setTimeout(jest.fn(), 1000 * 60 * 60 * 24) - jest.runAllTimers() await mutate({ mutation: login, - variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, + variables: { email: 'peter@lustig.de', password: 'Aa12345_' }, }) }) - afterAll(() => { - jest.useRealTimers() - }) - - it('allows the user to redeem the contribution link again', async () => { + it('throws error when link does not exists', async () => { await expect( mutate({ mutation: redeemTransactionLink, variables: { - code: 'CL-' + (contributionLink ? contributionLink.code : ''), - }, - }), - ).resolves.toMatchObject({ - data: { - redeemTransactionLink: true, - }, - errors: undefined, - }) - }) - - it('does not allow the user to redeem the contribution link a second time on the same day', async () => { - await expect( - mutate({ - mutation: redeemTransactionLink, - variables: { - code: 'CL-' + (contributionLink ? contributionLink.code : ''), + code: 'CL-123456', }, }), ).resolves.toMatchObject({ errors: [ new GraphQLError( - 'Creation from contribution link was not successful. Error: Contribution link already redeemed today', + 'Creation from contribution link was not successful. Error: No contribution link found to given code: CL-123456', ), ], }) }) + + it('throws error when link is not valid yet', async () => { + const now = new Date() + await mutate({ + mutation: createContributionLink, + variables: { + amount: new Decimal(5), + name: 'Daily Contribution Link', + memo: 'Thank you for contribute daily to the community', + cycle: 'DAILY', + validFrom: new Date(now.getFullYear() + 1, 0, 1).toISOString(), + validTo: new Date(now.getFullYear() + 1, 11, 31, 23, 59, 59, 999).toISOString(), + maxAmountPerMonth: new Decimal(200), + maxPerCycle: 1, + }, + }) + const cls = await DbContributionLink.find() + expect(cls).toHaveLength(1) + const contributionLink = cls[0] + await expect( + mutate({ + mutation: redeemTransactionLink, + variables: { + code: 'CL-' + contributionLink.code, + }, + }), + ).resolves.toMatchObject({ + errors: [ + new GraphQLError( + 'Creation from contribution link was not successful. Error: Contribution link not valid yet', + ), + ], + }) + await resetEntity(DbContributionLink) + }) + + // TODO: why the fuck is this possible? ¯\_(ツ)_/¯ + it('throws error when contributionLink cycle is invalid', async () => { + const now = new Date() + await mutate({ + mutation: createContributionLink, + variables: { + amount: new Decimal(5), + name: 'Daily Contribution Link', + memo: 'Thank you for contribute daily to the community', + cycle: 'INVALID', + validFrom: new Date(now.getFullYear(), 0, 1).toISOString(), + validTo: new Date(now.getFullYear(), 11, 31, 23, 59, 59, 999).toISOString(), + maxAmountPerMonth: new Decimal(200), + maxPerCycle: 1, + }, + }) + const cls = await DbContributionLink.find() + expect(cls).toHaveLength(1) + const contributionLink = cls[0] + await expect( + mutate({ + mutation: redeemTransactionLink, + variables: { + code: 'CL-' + contributionLink.code, + }, + }), + ).resolves.toMatchObject({ + errors: [ + new GraphQLError( + 'Creation from contribution link was not successful. Error: Contribution link has unknown cycle', + ), + ], + }) + await resetEntity(DbContributionLink) + }) + + it('throws error when link is no longer valid', async () => { + const now = new Date() + await mutate({ + mutation: createContributionLink, + variables: { + amount: new Decimal(5), + name: 'Daily Contribution Link', + memo: 'Thank you for contribute daily to the community', + cycle: 'DAILY', + validFrom: new Date(now.getFullYear() - 1, 0, 1).toISOString(), + validTo: new Date(now.getFullYear() - 1, 11, 31, 23, 59, 59, 999).toISOString(), + maxAmountPerMonth: new Decimal(200), + maxPerCycle: 1, + }, + }) + const cls = await DbContributionLink.find() + expect(cls).toHaveLength(1) + const contributionLink = cls[0] + await expect( + mutate({ + mutation: redeemTransactionLink, + variables: { + code: 'CL-' + contributionLink.code, + }, + }), + ).resolves.toMatchObject({ + errors: [ + new GraphQLError( + 'Creation from contribution link was not successful. Error: Contribution link is no longer valid', + ), + ], + }) + await resetEntity(DbContributionLink) + }) }) - }) - }) - describe('transaction links list', () => { - const variables = { - userId: 1, // dummy, may be replaced - filters: null, - currentPage: 1, - pageSize: 5, - } + // TODO: have this test separated into a transactionLink and a contributionLink part (if possible) + describe('redeem daily Contribution Link', () => { + const now = new Date() + let contributionLink: DbContributionLink | undefined + let contribution: UnconfirmedContribution | undefined - // TODO: there is a test not cleaning up after itself! Fix it! - beforeAll(async () => { - await cleanDB() - resetToken() - }) - - describe('unauthenticated', () => { - it('returns an error', async () => { - await expect( - query({ - query: listTransactionLinksAdmin, - variables, - }), - ).resolves.toEqual( - expect.objectContaining({ - errors: [new GraphQLError('401 Unauthorized')], - }), - ) - }) - }) - - describe('authenticated', () => { - describe('without admin rights', () => { beforeAll(async () => { - user = await userFactory(testEnv, bibiBloxberg) await mutate({ mutation: login, - variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, + variables: { email: 'peter@lustig.de', password: 'Aa12345_' }, + }) + await mutate({ + mutation: createContributionLink, + variables: { + amount: new Decimal(5), + name: 'Daily Contribution Link', + memo: 'Thank you for contribute daily to the community', + cycle: 'DAILY', + validFrom: new Date(now.getFullYear(), 0, 1).toISOString(), + validTo: new Date(now.getFullYear(), 11, 31, 23, 59, 59, 999).toISOString(), + maxAmountPerMonth: new Decimal(200), + maxPerCycle: 1, + }, }) }) - afterAll(async () => { - await cleanDB() - resetToken() + it('has a daily contribution link in the database', async () => { + const cls = await DbContributionLink.find() + expect(cls).toHaveLength(1) + contributionLink = cls[0] + expect(contributionLink).toEqual( + expect.objectContaining({ + id: expect.any(Number), + name: 'Daily Contribution Link', + memo: 'Thank you for contribute daily to the community', + validFrom: new Date(now.getFullYear(), 0, 1), + validTo: new Date(now.getFullYear(), 11, 31, 23, 59, 59, 0), + cycle: 'DAILY', + maxPerCycle: 1, + totalMaxCountOfContribution: null, + maxAccountBalance: null, + minGapHours: null, + createdAt: expect.any(Date), + deletedAt: null, + code: expect.stringMatching(/^[0-9a-f]{24,24}$/), + linkEnabled: true, + amount: expect.decimalEqual(5), + maxAmountPerMonth: expect.decimalEqual(200), + }), + ) }) + describe('user has pending contribution of 1000 GDD', () => { + beforeAll(async () => { + await mutate({ + mutation: login, + variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, + }) + const result = await mutate({ + mutation: createContribution, + variables: { + amount: new Decimal(1000), + memo: 'I was brewing potions for the community the whole month', + creationDate: now.toISOString(), + }, + }) + contribution = result.data.createContribution + }) + + it('does not allow the user to redeem the contribution link', async () => { + await expect( + mutate({ + mutation: redeemTransactionLink, + variables: { + code: 'CL-' + (contributionLink ? contributionLink.code : ''), + }, + }), + ).resolves.toMatchObject({ + errors: [ + new GraphQLError( + 'Creation from contribution link was not successful. Error: The amount (5 GDD) to be created exceeds the amount (0 GDD) still available for this month.', + ), + ], + }) + }) + }) + + describe('user has no pending contributions that would not allow to redeem the link', () => { + beforeAll(async () => { + await mutate({ + mutation: login, + variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, + }) + await mutate({ + mutation: updateContribution, + variables: { + contributionId: contribution ? contribution.id : -1, + amount: new Decimal(800), + memo: 'I was brewing potions for the community the whole month', + creationDate: now.toISOString(), + }, + }) + }) + + it('allows the user to redeem the contribution link', async () => { + await expect( + mutate({ + mutation: redeemTransactionLink, + variables: { + code: 'CL-' + (contributionLink ? contributionLink.code : ''), + }, + }), + ).resolves.toMatchObject({ + data: { + redeemTransactionLink: true, + }, + errors: undefined, + }) + }) + + it('does not allow the user to redeem the contribution link a second time on the same day', async () => { + await expect( + mutate({ + mutation: redeemTransactionLink, + variables: { + code: 'CL-' + (contributionLink ? contributionLink.code : ''), + }, + }), + ).resolves.toMatchObject({ + errors: [ + new GraphQLError( + 'Creation from contribution link was not successful. Error: Contribution link already redeemed today', + ), + ], + }) + }) + + describe('after one day', () => { + beforeAll(async () => { + jest.useFakeTimers() + setTimeout(jest.fn(), 1000 * 60 * 60 * 24) + jest.runAllTimers() + await mutate({ + mutation: login, + variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, + }) + }) + + afterAll(() => { + jest.useRealTimers() + }) + + it('allows the user to redeem the contribution link again', async () => { + await expect( + mutate({ + mutation: redeemTransactionLink, + variables: { + code: 'CL-' + (contributionLink ? contributionLink.code : ''), + }, + }), + ).resolves.toMatchObject({ + data: { + redeemTransactionLink: true, + }, + errors: undefined, + }) + }) + + it('does not allow the user to redeem the contribution link a second time on the same day', async () => { + await expect( + mutate({ + mutation: redeemTransactionLink, + variables: { + code: 'CL-' + (contributionLink ? contributionLink.code : ''), + }, + }), + ).resolves.toMatchObject({ + errors: [ + new GraphQLError( + 'Creation from contribution link was not successful. Error: Contribution link already redeemed today', + ), + ], + }) + }) + }) + }) + }) + }) + + describe('transaction links list', () => { + const variables = { + userId: 1, // dummy, may be replaced + filters: null, + currentPage: 1, + pageSize: 5, + } + + // TODO: there is a test not cleaning up after itself! Fix it! + beforeAll(async () => { + await cleanDB() + resetToken() + }) + + describe('unauthenticated', () => { it('returns an error', async () => { await expect( query({ @@ -296,40 +402,22 @@ describe('TransactionLinkResolver', () => { }) }) - describe('with admin rights', () => { - beforeAll(async () => { - // admin 'peter@lustig.de' has to exists for 'creationFactory' - await userFactory(testEnv, peterLustig) - - user = await userFactory(testEnv, bibiBloxberg) - variables.userId = user.id - variables.pageSize = 25 - // bibi needs GDDs - const bibisCreation = creations.find((creation) => creation.email === 'bibi@bloxberg.de') - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await creationFactory(testEnv, bibisCreation!) - // bibis transaktion links - const bibisTransaktionLinks = transactionLinks.filter( - (transactionLink) => transactionLink.email === 'bibi@bloxberg.de', - ) - for (let i = 0; i < bibisTransaktionLinks.length; i++) { - await transactionLinkFactory(testEnv, bibisTransaktionLinks[i]) - } - - // admin: only now log in - await mutate({ - mutation: login, - variables: { email: 'peter@lustig.de', password: 'Aa12345_' }, + describe('authenticated', () => { + describe('without admin rights', () => { + beforeAll(async () => { + user = await userFactory(testEnv, bibiBloxberg) + await mutate({ + mutation: login, + variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, + }) }) - }) - afterAll(async () => { - await cleanDB() - resetToken() - }) + afterAll(async () => { + await cleanDB() + resetToken() + }) - describe('without any filters', () => { - it('finds 6 open transaction links and no deleted or redeemed', async () => { + it('returns an error', async () => { await expect( query({ query: listTransactionLinksAdmin, @@ -337,185 +425,235 @@ describe('TransactionLinkResolver', () => { }), ).resolves.toEqual( expect.objectContaining({ - data: { - listTransactionLinksAdmin: { - linkCount: 6, - linkList: expect.not.arrayContaining([ - expect.objectContaining({ - memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(', - createdAt: expect.any(String), - }), - expect.objectContaining({ - memo: 'Da habe ich mich wohl etwas übernommen.', - deletedAt: expect.any(String), - }), - ]), - }, - }, + errors: [new GraphQLError('401 Unauthorized')], }), ) }) }) - describe('all filters are null', () => { - it('finds 6 open transaction links and no deleted or redeemed', async () => { - await expect( - query({ - query: listTransactionLinksAdmin, - variables: { - ...variables, - filters: { - withDeleted: null, - withExpired: null, - withRedeemed: null, - }, - }, - }), - ).resolves.toEqual( - expect.objectContaining({ - data: { - listTransactionLinksAdmin: { - linkCount: 6, - linkList: expect.not.arrayContaining([ - expect.objectContaining({ - memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(', - createdAt: expect.any(String), - }), - expect.objectContaining({ - memo: 'Da habe ich mich wohl etwas übernommen.', - deletedAt: expect.any(String), - }), - ]), - }, - }, - }), - ) - }) - }) + describe('with admin rights', () => { + beforeAll(async () => { + // admin 'peter@lustig.de' has to exists for 'creationFactory' + await userFactory(testEnv, peterLustig) - describe('filter with deleted', () => { - it('finds 6 open transaction links, 1 deleted, and no redeemed', async () => { - await expect( - query({ - query: listTransactionLinksAdmin, - variables: { - ...variables, - filters: { - withDeleted: true, - }, - }, - }), - ).resolves.toEqual( - expect.objectContaining({ - data: { - listTransactionLinksAdmin: { - linkCount: 7, - linkList: expect.arrayContaining([ - expect.not.objectContaining({ - memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(', - createdAt: expect.any(String), - }), - expect.objectContaining({ - memo: 'Da habe ich mich wohl etwas übernommen.', - deletedAt: expect.any(String), - }), - ]), - }, - }, - }), + user = await userFactory(testEnv, bibiBloxberg) + variables.userId = user.id + variables.pageSize = 25 + // bibi needs GDDs + const bibisCreation = creations.find( + (creation) => creation.email === 'bibi@bloxberg.de', ) - }) - }) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await creationFactory(testEnv, bibisCreation!) + // bibis transaktion links + const bibisTransaktionLinks = transactionLinks.filter( + (transactionLink) => transactionLink.email === 'bibi@bloxberg.de', + ) + for (let i = 0; i < bibisTransaktionLinks.length; i++) { + await transactionLinkFactory(testEnv, bibisTransaktionLinks[i]) + } - describe('filter by expired', () => { - it('finds 5 open transaction links, 1 expired, and no redeemed', async () => { - await expect( - query({ - query: listTransactionLinksAdmin, - variables: { - ...variables, - filters: { - withExpired: true, - }, - }, - }), - ).resolves.toEqual( - expect.objectContaining({ - data: { - listTransactionLinksAdmin: { - linkCount: 7, - linkList: expect.arrayContaining([ - expect.objectContaining({ - memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(', - createdAt: expect.any(String), - }), - expect.not.objectContaining({ - memo: 'Da habe ich mich wohl etwas übernommen.', - deletedAt: expect.any(String), - }), - ]), - }, - }, - }), - ) + // admin: only now log in + await mutate({ + mutation: login, + variables: { email: 'peter@lustig.de', password: 'Aa12345_' }, + }) }) - }) - // TODO: works not as expected, because 'redeemedAt' and 'redeemedBy' have to be added to the transaktion link factory - describe.skip('filter by redeemed', () => { - it('finds 6 open transaction links, 1 deleted, and no redeemed', async () => { - await expect( - query({ - query: listTransactionLinksAdmin, - variables: { - ...variables, - filters: { - withDeleted: null, - withExpired: null, - withRedeemed: true, + afterAll(async () => { + await cleanDB() + resetToken() + }) + + describe('without any filters', () => { + it('finds 6 open transaction links and no deleted or redeemed', async () => { + await expect( + query({ + query: listTransactionLinksAdmin, + variables, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + listTransactionLinksAdmin: { + linkCount: 6, + linkList: expect.not.arrayContaining([ + expect.objectContaining({ + memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(', + createdAt: expect.any(String), + }), + expect.objectContaining({ + memo: 'Da habe ich mich wohl etwas übernommen.', + deletedAt: expect.any(String), + }), + ]), + }, }, - }, - }), - ).resolves.toEqual( - expect.objectContaining({ - data: { - listTransactionLinksAdmin: { - linkCount: 6, - linkList: expect.arrayContaining([ - expect.not.objectContaining({ - memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(', - createdAt: expect.any(String), - }), - expect.objectContaining({ - memo: 'Yeah, eingelöst!', - redeemedAt: expect.any(String), - redeemedBy: expect.any(Number), - }), - expect.not.objectContaining({ - memo: 'Da habe ich mich wohl etwas übernommen.', - deletedAt: expect.any(String), - }), - ]), + }), + ) + }) + }) + + describe('all filters are null', () => { + it('finds 6 open transaction links and no deleted or redeemed', async () => { + await expect( + query({ + query: listTransactionLinksAdmin, + variables: { + ...variables, + filters: { + withDeleted: null, + withExpired: null, + withRedeemed: null, + }, }, - }, - }), - ) + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + listTransactionLinksAdmin: { + linkCount: 6, + linkList: expect.not.arrayContaining([ + expect.objectContaining({ + memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(', + createdAt: expect.any(String), + }), + expect.objectContaining({ + memo: 'Da habe ich mich wohl etwas übernommen.', + deletedAt: expect.any(String), + }), + ]), + }, + }, + }), + ) + }) + }) + + describe('filter with deleted', () => { + it('finds 6 open transaction links, 1 deleted, and no redeemed', async () => { + await expect( + query({ + query: listTransactionLinksAdmin, + variables: { + ...variables, + filters: { + withDeleted: true, + }, + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + listTransactionLinksAdmin: { + linkCount: 7, + linkList: expect.arrayContaining([ + expect.not.objectContaining({ + memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(', + createdAt: expect.any(String), + }), + expect.objectContaining({ + memo: 'Da habe ich mich wohl etwas übernommen.', + deletedAt: expect.any(String), + }), + ]), + }, + }, + }), + ) + }) + }) + + describe('filter by expired', () => { + it('finds 5 open transaction links, 1 expired, and no redeemed', async () => { + await expect( + query({ + query: listTransactionLinksAdmin, + variables: { + ...variables, + filters: { + withExpired: true, + }, + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + listTransactionLinksAdmin: { + linkCount: 7, + linkList: expect.arrayContaining([ + expect.objectContaining({ + memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(', + createdAt: expect.any(String), + }), + expect.not.objectContaining({ + memo: 'Da habe ich mich wohl etwas übernommen.', + deletedAt: expect.any(String), + }), + ]), + }, + }, + }), + ) + }) + }) + + // TODO: works not as expected, because 'redeemedAt' and 'redeemedBy' have to be added to the transaktion link factory + describe.skip('filter by redeemed', () => { + it('finds 6 open transaction links, 1 deleted, and no redeemed', async () => { + await expect( + query({ + query: listTransactionLinksAdmin, + variables: { + ...variables, + filters: { + withDeleted: null, + withExpired: null, + withRedeemed: true, + }, + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + listTransactionLinksAdmin: { + linkCount: 6, + linkList: expect.arrayContaining([ + expect.not.objectContaining({ + memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(', + createdAt: expect.any(String), + }), + expect.objectContaining({ + memo: 'Yeah, eingelöst!', + redeemedAt: expect.any(String), + redeemedBy: expect.any(Number), + }), + expect.not.objectContaining({ + memo: 'Da habe ich mich wohl etwas übernommen.', + deletedAt: expect.any(String), + }), + ]), + }, + }, + }), + ) + }) }) }) }) }) }) -}) -describe('transactionLinkCode', () => { - const date = new Date() + describe('transactionLinkCode', () => { + const date = new Date() - it('returns a string of length 24', () => { - expect(transactionLinkCode(date)).toHaveLength(24) - }) + it('returns a string of length 24', () => { + expect(transactionLinkCode(date)).toHaveLength(24) + }) - it('returns a string that ends with the hex value of date', () => { - const regexp = new RegExp(date.getTime().toString(16) + '$') - expect(transactionLinkCode(date)).toEqual(expect.stringMatching(regexp)) + it('returns a string that ends with the hex value of date', () => { + const regexp = new RegExp(date.getTime().toString(16) + '$') + expect(transactionLinkCode(date)).toEqual(expect.stringMatching(regexp)) + }) }) }) diff --git a/backend/src/graphql/resolver/TransactionLinkResolver.ts b/backend/src/graphql/resolver/TransactionLinkResolver.ts index ac57db131..df70b4bc9 100644 --- a/backend/src/graphql/resolver/TransactionLinkResolver.ts +++ b/backend/src/graphql/resolver/TransactionLinkResolver.ts @@ -185,7 +185,7 @@ export class TransactionLinkResolver { .getOne() if (!contributionLink) { logger.error('no contribution link found to given code:', code) - throw new Error('No contribution link found') + throw new Error(`No contribution link found to given code: ${code}`) } logger.info('...contribution link found with id', contributionLink.id) if (new Date(contributionLink.validFrom).getTime() > now.getTime()) { @@ -197,8 +197,11 @@ export class TransactionLinkResolver { } if (contributionLink.validTo) { if (new Date(contributionLink.validTo).setHours(23, 59, 59) < now.getTime()) { - logger.error('contribution link is depricated. Valid to: ', contributionLink.validTo) - throw new Error('Contribution link is depricated') + logger.error( + 'contribution link is no longer valid. Valid to: ', + contributionLink.validTo, + ) + throw new Error('Contribution link is no longer valid') } } let alreadyRedeemed: DbContribution | undefined