From f80bdec6419f8e7632cc12d633b037da26d4c18b Mon Sep 17 00:00:00 2001 From: koekiebox Date: Wed, 20 Nov 2024 14:57:37 +0100 Subject: [PATCH] feat(2981): Test case for cache on wallet service and asset. --- .../backend/src/asset/service.cache.test.ts | 174 ++++++++++++++++++ .../wallet_address/service.cache.test.ts | 54 ++---- 2 files changed, 190 insertions(+), 38 deletions(-) create mode 100644 packages/backend/src/asset/service.cache.test.ts diff --git a/packages/backend/src/asset/service.cache.test.ts b/packages/backend/src/asset/service.cache.test.ts new file mode 100644 index 0000000000..3c1c153a70 --- /dev/null +++ b/packages/backend/src/asset/service.cache.test.ts @@ -0,0 +1,174 @@ +import assert from 'assert' +import { v4 as uuid } from 'uuid' + +import { AssetError, isAssetError } from './errors' +import { AssetService } from './service' +import { Asset } from './model' +import { Pagination, SortOrder } from '../shared/baseModel' +import { getPageTests } from '../shared/baseModel.test' +import { createTestApp, TestContainer } from '../tests/app' +import { createAsset, randomAsset } from '../tests/asset' +import { truncateTables } from '../tests/tableManager' +import { Config } from '../config/app' +import { IocContract } from '@adonisjs/fold' +import { initIocContainer } from '../' +import { AppServices } from '../app' + +describe('Asset Service using Cache', (): void => { + let deps: IocContract + let appContainer: TestContainer + let assetService: AssetService + + beforeAll(async (): Promise => { + deps = initIocContainer({ + ...Config, + localCacheDuration: 5_000 // 5-second default. + }) + appContainer = await createTestApp(deps) + assetService = await deps.use('assetService') + }) + + afterEach(async (): Promise => { + await truncateTables(appContainer.knex) + }) + + afterAll(async (): Promise => { + await appContainer.shutdown() + }) + + describe('create', (): void => { + test.each` + withdrawalThreshold | liquidityThreshold + ${undefined} | ${undefined} + ${BigInt(5)} | ${undefined} + ${undefined} | ${BigInt(5)} + ${BigInt(5)} | ${BigInt(5)} + `( + 'Asset can be created and fetched', + async ({ withdrawalThreshold, liquidityThreshold }): Promise => { + const options = { + ...randomAsset(), + withdrawalThreshold, + liquidityThreshold + } + const asset = await assetService.create(options) + assert.ok(!isAssetError(asset)) + expect(asset).toMatchObject({ + ...options, + id: asset.id, + ledger: asset.ledger, + withdrawalThreshold: withdrawalThreshold || null, + liquidityThreshold: liquidityThreshold || null + }) + await expect(assetService.get(asset.id)).resolves.toEqual(asset) + } + ) + }) + + describe('get', (): void => { + test('Can get asset by id', async (): Promise => { + const asset = await assetService.create(randomAsset()) + assert.ok(!isAssetError(asset)) + await expect(assetService.get(asset.id)).resolves.toEqual(asset) + }) + + test('Cannot get unknown asset', async (): Promise => { + await expect(assetService.get(uuid())).resolves.toBeUndefined() + }) + }) + + describe('update', (): void => { + describe.each` + withdrawalThreshold | liquidityThreshold + ${null} | ${null} + ${BigInt(0)} | ${null} + ${BigInt(5)} | ${null} + ${null} | ${BigInt(0)} + ${null} | ${BigInt(5)} + ${BigInt(0)} | ${BigInt(0)} + ${BigInt(5)} | ${BigInt(5)} + `( + 'Asset threshold can be updated from withdrawalThreshold: $withdrawalThreshold, liquidityThreshold: $liquidityThreshold', + ({ withdrawalThreshold, liquidityThreshold }): void => { + let assetId: string + + beforeEach(async (): Promise => { + const asset = await assetService.create({ + ...randomAsset(), + withdrawalThreshold, + liquidityThreshold + }) + assert.ok(!isAssetError(asset)) + expect(asset.withdrawalThreshold).toEqual(withdrawalThreshold) + assetId = asset.id + }) + + test.each` + withdrawalThreshold | liquidityThreshold + ${null} | ${null} + ${BigInt(0)} | ${null} + ${BigInt(5)} | ${null} + ${null} | ${BigInt(0)} + ${null} | ${BigInt(5)} + ${BigInt(0)} | ${BigInt(0)} + ${BigInt(5)} | ${BigInt(5)} + `( + 'to withdrawalThreshold: $withdrawalThreshold, liquidityThreshold: $liquidityThreshold', + async ({ + withdrawalThreshold, + liquidityThreshold + }): Promise => { + const asset = await assetService.update({ + id: assetId, + withdrawalThreshold, + liquidityThreshold + }) + assert.ok(!isAssetError(asset)) + expect(asset.withdrawalThreshold).toEqual(withdrawalThreshold) + expect(asset.liquidityThreshold).toEqual(liquidityThreshold) + await expect(assetService.get(assetId)).resolves.toEqual(asset) + } + ) + } + ) + }) + + describe('getPage', (): void => { + getPageTests({ + createModel: () => createAsset(deps), + getPage: (pagination?: Pagination, sortOrder?: SortOrder) => + assetService.getPage(pagination, sortOrder) + }) + }) + + describe('getAll', (): void => { + test('returns all assets', async (): Promise => { + const assets: (Asset | AssetError)[] = [] + for (let i = 0; i < 3; i++) { + const asset = await assetService.create(randomAsset()) + assets.push(asset) + } + + await expect(assetService.getAll()).resolves.toEqual(assets) + }) + + test('returns empty array if no assets', async (): Promise => { + await expect(assetService.getAll()).resolves.toEqual([]) + }) + }) + + describe('delete', (): void => { + test('Can delete asset', async (): Promise => { + const newAsset = await assetService.create(randomAsset()) + assert.ok(!isAssetError(newAsset)) + const newAssetId = newAsset.id + + const deletedAsset = await assetService.delete({ + id: newAssetId, + deletedAt: new Date() + }) + assert.ok(!isAssetError(deletedAsset)) + expect(deletedAsset.deletedAt).not.toBeNull() + }) + }) +}) diff --git a/packages/backend/src/open_payments/wallet_address/service.cache.test.ts b/packages/backend/src/open_payments/wallet_address/service.cache.test.ts index a78a15dd15..00eac3e4e1 100644 --- a/packages/backend/src/open_payments/wallet_address/service.cache.test.ts +++ b/packages/backend/src/open_payments/wallet_address/service.cache.test.ts @@ -3,13 +3,8 @@ import { Knex } from 'knex' import { v4 as uuid } from 'uuid' import { isWalletAddressError, WalletAddressError } from './errors' -import { - WalletAddress, - WalletAddressEvent, - WalletAddressEventType -} from './model' -import { CreateOptions, FORBIDDEN_PATHS, WalletAddressService } from './service' -import { AccountingService } from '../../accounting/service' +import { WalletAddress } from './model' +import { CreateOptions, WalletAddressService } from './service' import { createTestApp, TestContainer } from '../../tests/app' import { createAsset } from '../../tests/asset' import { createWalletAddress } from '../../tests/walletAddress' @@ -22,9 +17,7 @@ import { faker } from '@faker-js/faker' import { createIncomingPayment } from '../../tests/incomingPayment' import { getPageTests } from '../../shared/baseModel.test' import { Pagination, SortOrder } from '../../shared/baseModel' -import { sleep } from '../../shared/utils' import { withConfigOverride } from '../../tests/helpers' -import { WalletAddressAdditionalProperty } from './additional_property/model' describe('Open Payments Wallet Address Service using Cache', (): void => { let deps: IocContract @@ -36,7 +29,7 @@ describe('Open Payments Wallet Address Service using Cache', (): void => { beforeAll(async (): Promise => { deps = initIocContainer({ ...Config, - localCacheDuration: 5_000// 5-second default. + localCacheDuration: 5_000 // 5-second default. }) config = await deps.use('config') appContainer = await createTestApp(deps) @@ -97,8 +90,20 @@ describe('Open Payments Wallet Address Service using Cache', (): void => { const walletAddress = await createWalletAddress(deps) if (!initialIsActive) { + // Only update the database: + await walletAddress.$query(knex).patch({ deactivatedAt: new Date() }) + const fromCacheActive = await walletAddressService.get( + walletAddress.id + ) + + // We don't expect a match here, since the cache and database is out-of-sync: + expect(fromCacheActive!.isActive).toEqual(false) + // Update through the service, will also update the wallet-address cache: - await walletAddressService.update({ id: walletAddress.id, status: 'INACTIVE' }) + await walletAddressService.update({ + id: walletAddress.id, + status: 'INACTIVE' + }) } const updatedWalletAddress = await walletAddressService.update({ @@ -239,33 +244,6 @@ describe('Open Payments Wallet Address Service using Cache', (): void => { }) }) - describe('onCredit with cache', (): void => { - let walletAddress: WalletAddress - - beforeEach(async (): Promise => { - walletAddress = await createWalletAddress(deps) - }) - - describe.each` - withdrawalThrottleDelay - ${undefined} - ${0} - ${60_000} - `( - 'withdrawalThrottleDelay: $withdrawalThrottleDelay', - ({ withdrawalThrottleDelay }): void => { - let delayProcessAt: Date | null = null - - beforeEach((): void => { - jest.useFakeTimers({ now: Date.now() }) - if (withdrawalThrottleDelay !== undefined) { - delayProcessAt = new Date(Date.now() + withdrawalThrottleDelay) - } - }) - } - ) - }) - describe('processNext', (): void => { let walletAddress: WalletAddress