Skip to content

Commit de4b0e3

Browse files
authored
chore: update test memory leaks (#2394)
* chore: cleaning up main files * chore: update all nock imports * chore: fix timer mocks * chore: update redis spin down * chore: update faker method * chore: set log level to silent for tests * chore: use tigerbeetle environment to spin down container * chore: remove ts-jest * chore: correct timer faking * chore: log heap usage * chore: remove pino pretty in tests * chore: dont log heap usage * chore: revert test timeout to default * ci: try --runInBand * Revert "ci: try --runInBand" This reverts commit 9b4b53e. * chore: remove ts-jest from jest.config.js
1 parent ffa8b03 commit de4b0e3

File tree

27 files changed

+244
-295
lines changed

27 files changed

+244
-295
lines changed

jest.config.js

-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
'use strict'
22

33
module.exports = {
4-
preset: 'ts-jest',
54
testEnvironment: 'node',
65
projects: ['<rootDir>/packages/*/jest.config.js']
76
}

package.json

-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@
4343
"jest": "^29.7.0",
4444
"npm-run-all2": "^5.0.2",
4545
"prettier": "^3.2.5",
46-
"ts-jest": "^29.1.2",
4746
"tunnelmole": "^2.2.14",
4847
"typescript": "^5.3.3",
4948
"uuid": "^9.0.1"

packages/backend/jest.config.js

+3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ const baseConfig = require('../../jest.config.base.js')
44
// eslint-disable-next-line @typescript-eslint/no-var-requires
55
const packageName = require('./package.json').name
66

7+
process.env.LOG_LEVEL = 'silent'
8+
79
module.exports = {
810
...baseConfig,
911
clearMocks: true,
@@ -12,6 +14,7 @@ module.exports = {
1214
globalSetup: `<rootDir>/packages/${packageName}/jest.setup.ts`,
1315
globalTeardown: `<rootDir>/packages/${packageName}/jest.teardown.js`,
1416
testRegex: `(packages/${packageName}/.*/__tests__/.*|\\.(test|spec))\\.tsx?$`,
17+
testEnvironment: `<rootDir>/packages/${packageName}/jest.custom-environment.ts`,
1518
moduleDirectories: [
1619
`node_modules`,
1720
`packages/${packageName}/node_modules`,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { TestEnvironment } from 'jest-environment-node'
2+
import nock from 'nock'
3+
4+
export default class CustomEnvironment extends TestEnvironment {
5+
constructor(config, context) {
6+
super(config, context)
7+
this.global.nock = nock
8+
}
9+
}

packages/backend/jest.setup.ts

-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
require('ts-node/register')
2-
31
import { knex } from 'knex'
42
import { GenericContainer, Wait } from 'testcontainers'
53

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { startTigerbeetleContainer } from './src/tests/tigerbeetle'
2+
import { StartedTestContainer } from 'testcontainers'
3+
4+
import CustomTestEnvironment from './jest.custom-environment'
5+
6+
export default class TigerbeetleEnvironment extends CustomTestEnvironment {
7+
private tbContainer: StartedTestContainer | undefined
8+
9+
public async setup(): Promise<void> {
10+
await super.setup()
11+
const tbContainer = await startTigerbeetleContainer()
12+
13+
this.tbContainer = tbContainer.container
14+
this.global.tigerbeetlePort = tbContainer.port
15+
}
16+
17+
public async teardown(): Promise<void> {
18+
await super.teardown()
19+
await this.tbContainer?.stop()
20+
}
21+
}

packages/backend/package.json

+2-3
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,15 @@
3232
"@types/uuid": "^9.0.8",
3333
"cross-fetch": "^4.0.0",
3434
"ilp-protocol-stream": "^2.7.2-alpha.2",
35+
"jest-environment-node": "^29.7.0",
3536
"jest-openapi": "^0.14.2",
3637
"nock": "^13.5.3",
3738
"node-mocks-http": "^1.14.1",
3839
"openapi-types": "^12.1.3",
39-
"pino-pretty": "^10.3.1",
4040
"react": "~18.2.0",
4141
"rosie": "^2.1.1",
4242
"testcontainers": "^10.7.1",
43-
"tmp": "^0.2.1",
44-
"ts-node": "^10.9.2"
43+
"tmp": "^0.2.1"
4544
},
4645
"dependencies": {
4746
"@adonisjs/fold": "^8.2.0",

packages/backend/src/accounting/tigerbeetle/service.test.ts

+15-9
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
/**
2+
* @jest-environment ./packages/backend/jest.tigerbeetle-environment.ts
3+
*/
4+
15
import assert from 'assert'
26
import { CreateAccountError as CreateTbAccountError } from 'tigerbeetle-node'
37
import { v4 as uuid } from 'uuid'
@@ -9,7 +13,6 @@ import { IocContract } from '@adonisjs/fold'
913
import { initIocContainer } from '../../'
1014
import { AppServices } from '../../app'
1115
import { truncateTables } from '../../tests/tableManager'
12-
import { startTigerbeetleContainer } from '../../tests/tigerbeetle'
1316
import { AccountFactory, FactoryAccount } from '../../tests/accountFactory'
1417
import { isTransferError, TransferError } from '../errors'
1518
import {
@@ -20,7 +23,7 @@ import {
2023
Withdrawal
2124
} from '../service'
2225

23-
describe('Accounting Service', (): void => {
26+
describe('Tigerbeetle Accounting Service', (): void => {
2427
let deps: IocContract<AppServices>
2528
let appContainer: TestContainer
2629
let accountingService: AccountingService
@@ -33,10 +36,14 @@ describe('Accounting Service', (): void => {
3336
}
3437

3538
beforeAll(async (): Promise<void> => {
36-
const { port } = await startTigerbeetleContainer()
37-
Config.tigerbeetleReplicaAddresses = [port.toString()]
39+
const tigerbeetlePort = (global as unknown as { tigerbeetlePort: number })
40+
.tigerbeetlePort
3841

39-
deps = await initIocContainer({ ...Config, useTigerbeetle: true })
42+
deps = initIocContainer({
43+
...Config,
44+
tigerbeetleReplicaAddresses: [tigerbeetlePort.toString()],
45+
useTigerbeetle: true
46+
})
4047
appContainer = await createTestApp(deps)
4148
accountingService = await deps.use('accountingService')
4249
accountFactory = new AccountFactory(accountingService, newLedger)
@@ -48,7 +55,6 @@ describe('Accounting Service', (): void => {
4855

4956
afterAll(async (): Promise<void> => {
5057
await appContainer.shutdown()
51-
// TODO: find a way to gracefully stop TB container without running into a thread panic
5258
})
5359

5460
describe('Create Liquidity Account', (): void => {
@@ -83,11 +89,11 @@ describe('Accounting Service', (): void => {
8389
},
8490
LiquidityAccountType.ASSET
8591
)
86-
).rejects.toThrowError('unable to create account, invalid id')
92+
).rejects.toThrow('unable to create account, invalid id')
8793
})
8894

8995
test('Create throws on error', async (): Promise<void> => {
90-
const tigerbeetle = await deps.use('tigerbeetle')
96+
const tigerbeetle = await deps.use('tigerbeetle')!
9197
jest.spyOn(tigerbeetle, 'createAccounts').mockResolvedValueOnce([
9298
{
9399
index: 0,
@@ -106,7 +112,7 @@ describe('Accounting Service', (): void => {
106112
},
107113
LiquidityAccountType.ASSET
108114
)
109-
).rejects.toThrowError(
115+
).rejects.toThrow(
110116
new TigerbeetleCreateAccountError(
111117
CreateTbAccountError.exists_with_different_ledger
112118
)

packages/backend/src/app.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ export interface AppServices {
239239
autoPeeringService: Promise<AutoPeeringService>
240240
autoPeeringRoutes: Promise<AutoPeeringRoutes>
241241
connectorApp: Promise<ConnectorApp>
242-
tigerbeetle: Promise<TigerbeetleClient>
242+
tigerbeetle?: Promise<TigerbeetleClient>
243243
paymentMethodHandlerService: Promise<PaymentMethodHandlerService>
244244
ilpPaymentService: Promise<IlpPaymentService>
245245
}
@@ -615,8 +615,8 @@ export class App {
615615
if (this.openPaymentsServer) {
616616
await this.stopServer(this.openPaymentsServer)
617617
}
618-
if (this.adminServer) {
619-
await this.stopServer(this.adminServer)
618+
if (this.apolloServer) {
619+
await this.apolloServer.stop()
620620
}
621621
if (this.ilpConnectorService) {
622622
await this.stopServer(this.ilpConnectorService)

packages/backend/src/graphql/resolvers/auto-peering.test.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { faker } from '@faker-js/faker'
22
import { gql } from '@apollo/client'
33
import assert from 'assert'
4-
import nock from 'nock'
54

65
import { createTestApp, TestContainer } from '../../tests/app'
76
import { IocContract } from '@adonisjs/fold'
@@ -20,6 +19,8 @@ import { CreateOrUpdatePeerByUrlInput } from '../generated/graphql'
2019
import { AutoPeeringService } from '../../payment-method/ilp/auto-peering/service'
2120
import { v4 as uuid } from 'uuid'
2221

22+
const nock = (global as unknown as { nock: typeof import('nock') }).nock
23+
2324
describe('Auto Peering Resolvers', (): void => {
2425
let deps: IocContract<AppServices>
2526
let appContainer: TestContainer

packages/backend/src/index.ts

+21-14
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,6 @@ BigInt.prototype.toJSON = function () {
5454
return this.toString()
5555
}
5656

57-
const container = initIocContainer(Config)
58-
const app = new App(container)
59-
6057
export function initIocContainer(
6158
config: typeof Config
6259
): IocContract<AppServices> {
@@ -121,13 +118,6 @@ export function initIocContainer(
121118
serverAddress: config.ilpAddress
122119
})
123120
})
124-
container.singleton('tigerbeetle', async (deps) => {
125-
const config = await deps.use('config')
126-
return createClient({
127-
cluster_id: BigInt(config.tigerbeetleClusterId),
128-
replica_addresses: config.tigerbeetleReplicaAddresses
129-
})
130-
})
131121

132122
container.singleton('ratesService', async (deps) => {
133123
const config = await deps.use('config')
@@ -225,7 +215,15 @@ export function initIocContainer(
225215
}
226216

227217
if (config.useTigerbeetle) {
228-
const tigerbeetle = await deps.use('tigerbeetle')
218+
container.singleton('tigerbeetle', async (deps) => {
219+
const config = await deps.use('config')
220+
return createClient({
221+
cluster_id: BigInt(config.tigerbeetleClusterId),
222+
replica_addresses: config.tigerbeetleReplicaAddresses
223+
})
224+
})
225+
226+
const tigerbeetle = await deps.use('tigerbeetle')!
229227

230228
return createTigerbeetleAccountingService({
231229
logger,
@@ -504,9 +502,15 @@ export const gracefulShutdown = async (
504502
await app.shutdown()
505503
const knex = await container.use('knex')
506504
await knex.destroy()
507-
const tigerbeetle = await container.use('tigerbeetle')
508-
tigerbeetle.destroy()
505+
506+
const config = await container.use('config')
507+
if (config.useTigerbeetle) {
508+
const tigerbeetle = await container.use('tigerbeetle')
509+
tigerbeetle?.destroy()
510+
}
511+
509512
const redis = await container.use('redis')
513+
await redis.quit()
510514
redis.disconnect()
511515

512516
const telemetry = await container.use('telemetry')
@@ -600,7 +604,10 @@ export const start = async (
600604
}
601605

602606
// If this script is run directly, start the server
603-
if (!module.parent) {
607+
if (require.main === module) {
608+
const container = initIocContainer(Config)
609+
const app = new App(container)
610+
604611
start(container, app).catch(async (e): Promise<void> => {
605612
const errInfo = e && typeof e === 'object' && e.stack ? e.stack : e
606613
const logger = await container.use('logger')

packages/backend/src/middleware/cache/data-stores/redis.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ describe('Redis Data Store', (): void => {
1313
const dataStore = createRedisDataStore(redis, ttlMs)
1414

1515
afterEach(async () => {
16+
jest.useRealTimers()
1617
await redis.flushall()
1718
})
1819

1920
afterAll(async () => {
20-
redis.disconnect()
21-
jest.useRealTimers()
21+
await redis.quit()
2222
})
2323

2424
describe('set', (): void => {

packages/backend/src/middleware/lock/redis.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ describe('Redis Lock', (): void => {
1515
})
1616

1717
afterAll(async () => {
18-
redis.disconnect()
18+
await redis.quit()
1919
})
2020

2121
describe('acquire', () => {

packages/backend/src/open_payments/auth/middleware.test.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { generateKeyPairSync } from 'crypto'
22
import { faker } from '@faker-js/faker'
3-
import nock from 'nock'
43
import { Client, ActiveTokenInfo } from 'token-introspection'
54
import { v4 as uuid } from 'uuid'
65
import {
@@ -30,6 +29,8 @@ import { setup } from '../wallet_address/model.test'
3029
import { parseLimits } from '../payment/outgoing/limits'
3130
import { AccessAction, AccessType } from '@interledger/open-payments'
3231

32+
const nock = (global as unknown as { nock: typeof import('nock') }).nock
33+
3334
type AppMiddleware = (
3435
ctx: WalletAddressContext,
3536
next: () => Promise<void>

packages/backend/src/open_payments/grant/service.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ describe('Grant Service', (): void => {
3232
})
3333

3434
afterEach(async (): Promise<void> => {
35-
await truncateTables(knex)
3635
jest.useRealTimers()
36+
await truncateTables(knex)
3737
})
3838

3939
afterAll(async (): Promise<void> => {

packages/backend/src/open_payments/wallet_address/key/service.test.ts

+1-6
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,14 @@ describe('Wallet Address Key Service', (): void => {
1616
let deps: IocContract<AppServices>
1717
let appContainer: TestContainer
1818
let walletAddressKeyService: WalletAddressKeyService
19-
const mockMessageProducer = {
20-
send: jest.fn()
21-
}
2219

2320
beforeAll(async (): Promise<void> => {
24-
deps = await initIocContainer(Config)
25-
deps.bind('messageProducer', async () => mockMessageProducer)
21+
deps = initIocContainer(Config)
2622
appContainer = await createTestApp(deps)
2723
walletAddressKeyService = await deps.use('walletAddressKeyService')
2824
})
2925

3026
afterEach(async (): Promise<void> => {
31-
jest.useRealTimers()
3227
await truncateTables(appContainer.knex)
3328
})
3429

packages/backend/src/open_payments/wallet_address/service.test.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -413,8 +413,7 @@ describe('Open Payments Wallet Address Service', (): void => {
413413
let delayProcessAt: Date | null = null
414414

415415
beforeEach((): void => {
416-
jest.useFakeTimers()
417-
jest.setSystemTime(new Date())
416+
jest.useFakeTimers({ now: Date.now() })
418417
if (withdrawalThrottleDelay !== undefined) {
419418
delayProcessAt = new Date(Date.now() + withdrawalThrottleDelay)
420419
}

packages/backend/src/payment-method/ilp/auto-peering/service.test.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import assert from 'assert'
22
import { IocContract } from '@adonisjs/fold'
3-
import nock from 'nock'
43
import { initIocContainer } from '../../..'
54
import { AppServices } from '../../../app'
65
import { Config, IAppConfig } from '../../../config/app'
@@ -19,6 +18,8 @@ import { PeerError } from '../peer/errors'
1918
import { v4 as uuid } from 'uuid'
2019
import { AccountingService } from '../../../accounting/service'
2120

21+
const nock = (global as unknown as { nock: typeof import('nock') }).nock
22+
2223
describe('Auto Peering Service', (): void => {
2324
let deps: IocContract<AppServices>
2425
let appContainer: TestContainer

packages/backend/src/payment-method/ilp/connector/core/factories/ilp-packet.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { faker } from '@faker-js/faker'
55

66
export const IlpPrepareFactory = Factory.define<IlpPrepare>('IlpPrepare').attrs(
77
{
8-
amount: faker.finance.amount(1, 100, 0),
8+
amount: faker.finance.amount({ min: 1, max: 100, dec: 0 }),
99
data: Buffer.alloc(0),
1010
destination: 'test.rafiki.' + faker.person.firstName(),
1111
expiresAt: new Date(Date.now() + 10 * 1000),

packages/backend/src/payment-method/ilp/connector/core/test/controllers/stream-controller.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ describe('Stream Controller', function () {
3030

3131
afterAll(async () => {
3232
await services.redis.flushdb()
33-
await services.redis.disconnect()
33+
await services.redis.quit()
3434
})
3535

3636
test('constructs a reply for a receive account', async () => {

0 commit comments

Comments
 (0)