From bea2ceda4c87429b8255f6b58b94a1271334d225 Mon Sep 17 00:00:00 2001 From: Claus-Peter Huebner Date: Thu, 31 Aug 2023 23:36:07 +0200 Subject: [PATCH] federation module now with Decimal and decay calculations as in backend --- .../federation/client/1_0/SendCoinsClient.ts | 2 +- .../resolver/util/processXComSendCoins.ts | 1 + .../util/settlePendingSenderTransaction.ts | 4 +- federation/src/config/index.ts | 9 +- .../1_0/resolver/SendCoinsResolver.test.ts | 107 ++++++++++++++++++ federation/src/index.ts | 4 +- federation/src/server/createServer.ts | 4 +- federation/test/extensions.ts | 37 ++++++ federation/test/helpers.test.ts | 7 ++ federation/test/helpers.ts | 66 +++++++++++ federation/tsconfig.json | 6 +- 11 files changed, 233 insertions(+), 14 deletions(-) create mode 100644 federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts create mode 100644 federation/test/extensions.ts create mode 100644 federation/test/helpers.test.ts create mode 100644 federation/test/helpers.ts diff --git a/backend/src/federation/client/1_0/SendCoinsClient.ts b/backend/src/federation/client/1_0/SendCoinsClient.ts index 90c3e2e20..7f1fad186 100644 --- a/backend/src/federation/client/1_0/SendCoinsClient.ts +++ b/backend/src/federation/client/1_0/SendCoinsClient.ts @@ -6,9 +6,9 @@ import { backendLogger as logger } from '@/server/logger' import { SendCoinsArgs } from './model/SendCoinsArgs' import { revertSendCoins } from './query/revertSendCoins' +import { revertSettledSendCoins } from './query/revertSettledSendCoins' import { settleSendCoins } from './query/settleSendCoins' import { voteForSendCoins } from './query/voteForSendCoins' -import { revertSettledSendCoins } from './query/revertSettledSendCoins' // eslint-disable-next-line camelcase export class SendCoinsClient { diff --git a/backend/src/graphql/resolver/util/processXComSendCoins.ts b/backend/src/graphql/resolver/util/processXComSendCoins.ts index 40b7dd775..1fd526251 100644 --- a/backend/src/graphql/resolver/util/processXComSendCoins.ts +++ b/backend/src/graphql/resolver/util/processXComSendCoins.ts @@ -15,6 +15,7 @@ import { LogError } from '@/server/LogError' import { backendLogger as logger } from '@/server/logger' import { calculateSenderBalance } from '@/util/calculateSenderBalance' import { fullName } from '@/util/utilities' + import { settlePendingSenderTransaction } from './settlePendingSenderTransaction' export async function processXComPendingSendCoins( diff --git a/backend/src/graphql/resolver/util/settlePendingSenderTransaction.ts b/backend/src/graphql/resolver/util/settlePendingSenderTransaction.ts index 8db8ba695..e22276823 100644 --- a/backend/src/graphql/resolver/util/settlePendingSenderTransaction.ts +++ b/backend/src/graphql/resolver/util/settlePendingSenderTransaction.ts @@ -8,13 +8,13 @@ import { PendingTransaction as DbPendingTransaction } from '@entity/PendingTrans import { Transaction as dbTransaction } from '@entity/Transaction' import { User as DbUser } from '@entity/User' +import { PendingTransactionState } from '@/graphql/enum/PendingTransactionState' import { LogError } from '@/server/LogError' import { backendLogger as logger } from '@/server/logger' +import { calculateSenderBalance } from '@/util/calculateSenderBalance' import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK' import { getLastTransaction } from './getLastTransaction' -import { calculateSenderBalance } from '@/util/calculateSenderBalance' -import { PendingTransactionState } from '@/graphql/enum/PendingTransactionState' export async function settlePendingSenderTransaction( homeCom: DbCommunity, diff --git a/federation/src/config/index.ts b/federation/src/config/index.ts index 1200bc1b8..f56f9decc 100644 --- a/federation/src/config/index.ts +++ b/federation/src/config/index.ts @@ -1,14 +1,15 @@ // ATTENTION: DO NOT PUT ANY SECRETS IN HERE (or the .env) +/* eslint-disable n/no-process-env */ + +import { Decimal } from 'decimal.js-light' import dotenv from 'dotenv' + dotenv.config() -/* -import Decimal from 'decimal.js-light' Decimal.set({ precision: 25, rounding: Decimal.ROUND_HALF_UP, }) -*/ const constants = { DB_VERSION: '0071-add-pending_transactions-table', @@ -62,7 +63,7 @@ const federation = { }, } -const CONFIG = { +export const CONFIG = { ...constants, ...server, ...database, diff --git a/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts new file mode 100644 index 000000000..1400cfbeb --- /dev/null +++ b/federation/src/graphql/api/1_0/resolver/SendCoinsResolver.test.ts @@ -0,0 +1,107 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ + +/* +import { createTestClient } from 'apollo-server-testing' +import { Community as DbCommunity } from '@entity/Community' +import CONFIG from '@/config' +import { cleanDB, testEnvironment } from '@test/helpers' +import { createServer } from '@/server/createServer' + +let query: any + +// to do: We need a setup for the tests that closes the connection +let con: any + +CONFIG.FEDERATION_API = '1_0' + +beforeAll(async () => { + const server = await createServer() + con = server.con + query = createTestClient(server.apollo).query + await cleanDB() + // DbCommunity.clear() +}) + +afterAll(async () => { + await con.close() +}) + +describe('SendCoinsResolver', () => { + const voteForSendCoinsMutation = ` + mutation ( + $communityReceiverIdentifier: String! + $userReceiverIdentifier: String! + $creationDate: Date! + $amount: Decimal! + $memo: String! + $communitySenderIdentifier: String! + $userSenderIdentifier: String! + $userSenderName: String! + ) { + voteForSendCoins( + communityReceiverIdentifier: $communityReceiverIdentifier + userReceiverIdentifier: $userReceiverIdentifier + creationDate: $creationDate + amount: $amount + memo: $memo + communitySenderIdentifier: $communitySenderIdentifier + userSenderIdentifier: $userSenderIdentifier + userSenderName: $userSenderName + ) + } +` + + describe('get 1st TX at all', () => { + let homeCom: DbCommunity + let foreignCom: DbCommunity + const varsPeterLustig = { + email: 'peter@lustig.de', + firstName: 'Peter', + lastName: 'Lustig', + language: 'de', + publisherId: 1234, + } + const varsBibiBloxberg = { + email: 'bibi@bloxberg.de', + firstName: 'Bibi', + lastName: 'Bloxberg', + language: 'de', + publisherId: 1234, + } + beforeEach(async () => { + homeCom = DbCommunity.create() + homeCom.foreign = false + homeCom.url = 'homeCommunity-url' + homeCom.name = 'Community-Name' + homeCom.description = 'Community-Description' + homeCom.creationDate = new Date() + homeCom.publicKey = Buffer.from('homeCommunity-publicKey') + await DbCommunity.insert(homeCom) + + foreignCom = DbCommunity.create() + foreignCom.foreign = true + foreignCom.url = 'foreignCommunity-url' + foreignCom.name = 'foreignCommunity-Name' + foreignCom.description = 'foreign Community-Description' + foreignCom.creationDate = new Date() + foreignCom.publicKey = Buffer.from('foreignCommunity-publicKey') + await DbCommunity.insert(foreignCom) + + }) + + it('returns public CommunityInfo', async () => { + await expect(query({ query: getPublicCommunityInfoQuery })).resolves.toMatchObject({ + data: { + getPublicCommunityInfo: { + name: 'Community-Name', + description: 'Community-Description', + creationDate: homeCom.creationDate?.toISOString(), + publicKey: expect.stringMatching('homeCommunity-publicKey'), + }, + }, + }) + }) + }) +}) +*/ diff --git a/federation/src/index.ts b/federation/src/index.ts index 997edb7aa..bdc66c87a 100644 --- a/federation/src/index.ts +++ b/federation/src/index.ts @@ -1,9 +1,9 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import createServer from './server/createServer' +import { createServer } from './server/createServer' // config -import CONFIG from './config' +import { CONFIG } from './config' async function main() { // eslint-disable-next-line no-console diff --git a/federation/src/server/createServer.ts b/federation/src/server/createServer.ts index a47b6da7f..b79847254 100644 --- a/federation/src/server/createServer.ts +++ b/federation/src/server/createServer.ts @@ -13,7 +13,7 @@ import cors from './cors' import plugins from './plugins' // config -import CONFIG from '@/config' +import { CONFIG } from '@/config' // graphql import schema from '@/graphql/schema' @@ -33,7 +33,7 @@ import { Logger } from 'log4js' type ServerDef = { apollo: ApolloServer; app: Express; con: Connection } -const createServer = async ( +export const createServer = async ( // eslint-disable-next-line @typescript-eslint/no-explicit-any // context: any = serverContext, logger: Logger = apolloLogger, diff --git a/federation/test/extensions.ts b/federation/test/extensions.ts new file mode 100644 index 000000000..262a9bcdb --- /dev/null +++ b/federation/test/extensions.ts @@ -0,0 +1,37 @@ +/* eslint-disable @typescript-eslint/no-unsafe-call */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable @typescript-eslint/restrict-template-expressions */ +/* eslint-disable @typescript-eslint/no-empty-interface */ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ + +import { Decimal } from 'decimal.js-light' + +expect.extend({ + decimalEqual(received, value) { + const pass = new Decimal(value).equals(received.toString()) + if (pass) { + return { + message: () => `expected ${received} to not equal ${value}`, + pass: true, + } + } else { + return { + message: () => `expected ${received} to equal ${value}`, + pass: false, + } + } + }, +}) + +interface CustomMatchers { + decimalEqual(value: number): R +} + +declare global { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace jest { + interface Expect extends CustomMatchers {} + interface Matchers extends CustomMatchers {} + interface InverseAsymmetricMatchers extends CustomMatchers {} + } +} diff --git a/federation/test/helpers.test.ts b/federation/test/helpers.test.ts new file mode 100644 index 000000000..69d8f3fa4 --- /dev/null +++ b/federation/test/helpers.test.ts @@ -0,0 +1,7 @@ +import { contributionDateFormatter } from '@test/helpers' + +describe('contributionDateFormatter', () => { + it('formats the date correctly', () => { + expect(contributionDateFormatter(new Date('Thu Feb 29 2024 13:12:11'))).toEqual('2/29/2024') + }) +}) diff --git a/federation/test/helpers.ts b/federation/test/helpers.ts new file mode 100644 index 000000000..b5352e508 --- /dev/null +++ b/federation/test/helpers.ts @@ -0,0 +1,66 @@ +/* eslint-disable @typescript-eslint/unbound-method */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/no-unsafe-call */ +/* eslint-disable @typescript-eslint/no-unsafe-return */ + +import { createServer } from '@/server/createServer' +import { entities } from '@entity/index' +import { createTestClient } from 'apollo-server-testing' +import { logger } from './testSetup' + +// import { createServer } from '@/server/createServer' + +// import { i18n, logger } from './testSetup' + +export const headerPushMock = jest.fn((t) => { + context.token = t.value +}) + +const context = { + token: '', + setHeaders: { + push: headerPushMock, + forEach: jest.fn(), + }, + clientTimezoneOffset: 0, +} + +export const cleanDB = async () => { + // this only works as long we do not have foreign key constraints + for (const entity of entities) { + await resetEntity(entity) + } +} + +export const testEnvironment = async (testLogger = logger) => { + // , testI18n = i18n) => { + const server = await createServer(testLogger) // context, testLogger, testI18n) + const con = server.con + const testClient = createTestClient(server.apollo) + const mutate = testClient.mutate + const query = testClient.query + return { mutate, query, con } +} + +export const resetEntity = async (entity: any) => { + const items = await entity.find({ withDeleted: true }) + if (items.length > 0) { + const ids = items.map((e: any) => e.id) + await entity.delete(ids) + } +} + +export const resetToken = () => { + context.token = '' +} + +// format date string as it comes from the frontend for the contribution date +export const contributionDateFormatter = (date: Date): string => { + return `${date.getMonth() + 1}/${date.getDate()}/${date.getFullYear()}` +} + +export const setClientTimezoneOffset = (offset: number): void => { + context.clientTimezoneOffset = offset +} diff --git a/federation/tsconfig.json b/federation/tsconfig.json index ba0d6fef3..50ce9d0c7 100644 --- a/federation/tsconfig.json +++ b/federation/tsconfig.json @@ -54,9 +54,9 @@ "@repository/*": ["src/typeorm/repository/*"], "@test/*": ["test/*"], /* common */ - "@common/*": ["../common/src/*"], - "@email/*": ["../common/scr/email/*"], - "@event/*": ["../common/src/event/*"], + // "@common/*": ["../common/src/*"], + // "@email/*": ["../common/scr/email/*"], + // "@event/*": ["../common/src/event/*"], /* external */ "@typeorm/*": ["../backend/src/typeorm/*", "../../backend/src/typeorm/*"], "@dbTools/*": ["../database/src/*", "../../database/build/src/*"],