From 8392bdb300a6bf95cb18c12e2bf40854561bdd31 Mon Sep 17 00:00:00 2001 From: Aaron Cook Date: Tue, 4 Feb 2025 12:35:24 +0100 Subject: [PATCH 01/12] fix: add integration tests for user domain --- .../users.repository.integration.spec.ts | 627 ++++++++++++++++++ src/domain/users/users.repository.ts | 8 + .../wallets.repository.integration.spec.ts | 73 ++ src/routes/users/users.controller.ts | 4 +- 4 files changed, 710 insertions(+), 2 deletions(-) create mode 100644 src/domain/users/users.repository.integration.spec.ts create mode 100644 src/domain/wallets/wallets.repository.integration.spec.ts diff --git a/src/domain/users/users.repository.integration.spec.ts b/src/domain/users/users.repository.integration.spec.ts new file mode 100644 index 0000000000..b7102d2d86 --- /dev/null +++ b/src/domain/users/users.repository.integration.spec.ts @@ -0,0 +1,627 @@ +import { faker } from '@faker-js/faker'; +import { DataSource } from 'typeorm'; +import configuration from '@/config/entities/__tests__/configuration'; +import { postgresConfig } from '@/config/entities/postgres.config'; +import { PostgresDatabaseService } from '@/datasources/db/v2/postgres-database.service'; +import { DatabaseMigrator } from '@/datasources/db/v2/database-migrator.service'; +import { User } from '@/datasources/users/entities/users.entity.db'; +import { UsersRepository } from '@/domain/users/users.repository'; +import { WalletsRepository } from '@/domain/wallets/wallets.repository'; +import { authPayloadDtoBuilder } from '@/domain/auth/entities/__tests__/auth-payload-dto.entity.builder'; +import { AuthPayload } from '@/domain/auth/entities/auth-payload.entity'; +import { UserStatus } from '@/domain/users/entities/user.entity'; +import { Wallet } from '@/datasources/wallets/entities/wallets.entity.db'; +import type { ConfigService } from '@nestjs/config'; +import type { ILoggingService } from '@/logging/logging.interface'; +import { getAddress } from 'viem'; + +const mockLoggingService = { + debug: jest.fn(), + error: jest.fn(), + info: jest.fn(), + warn: jest.fn(), +} as jest.MockedObjectDeep; + +describe('UsersRepository', () => { + let postgresDatabaseService: PostgresDatabaseService; + let usersRepository: UsersRepository; + + const testDatabaseName = faker.string.alpha({ + length: 10, + casing: 'lower', + }); + const testConfiguration = configuration(); + + const dataSource = new DataSource({ + ...postgresConfig({ + ...testConfiguration.db.connection.postgres, + type: 'postgres', + database: testDatabaseName, + }), + migrationsTableName: testConfiguration.db.orm.migrationsTableName, + entities: [User, Wallet], + }); + + beforeAll(async () => { + // Create database + const testDataSource = new DataSource({ + ...postgresConfig({ + ...testConfiguration.db.connection.postgres, + type: 'postgres', + database: 'postgres', + }), + }); + const testPostgresDatabaseService = new PostgresDatabaseService( + mockLoggingService, + testDataSource, + ); + await testPostgresDatabaseService.initializeDatabaseConnection(); + await testPostgresDatabaseService + .getDataSource() + .query(`CREATE DATABASE ${testDatabaseName}`); + await testPostgresDatabaseService.destroyDatabaseConnection(); + + // Create database connection + postgresDatabaseService = new PostgresDatabaseService( + mockLoggingService, + dataSource, + ); + await postgresDatabaseService.initializeDatabaseConnection(); + + // Migrate database + const mockConfigService = { + getOrThrow: jest.fn().mockImplementation((key: string) => { + if (key === 'db.migrator.numberOfRetries') { + return testConfiguration.db.migrator.numberOfRetries; + } + if (key === 'db.migrator.retryAfterMs') { + return testConfiguration.db.migrator.retryAfterMs; + } + }), + } as jest.MockedObjectDeep; + const migrator = new DatabaseMigrator( + mockLoggingService, + postgresDatabaseService, + mockConfigService, + ); + await migrator.migrate(); + + usersRepository = new UsersRepository( + postgresDatabaseService, + new WalletsRepository(postgresDatabaseService), + ); + }); + + afterEach(async () => { + jest.resetAllMocks(); + + // Truncate tables + const walletRepository = dataSource.getRepository(Wallet); + const userRepository = dataSource.getRepository(User); + + await walletRepository.createQueryBuilder().delete().where('1=1').execute(); + await userRepository.createQueryBuilder().delete().where('1=1').execute(); + }); + + afterAll(async () => { + await postgresDatabaseService.getDataSource().dropDatabase(); + await postgresDatabaseService.destroyDatabaseConnection(); + }); + + describe('createWithWallet', () => { + it('should insert a new user and a linked wallet', async () => { + const authPayloadDto = authPayloadDtoBuilder().build(); + const authPayload = new AuthPayload(authPayloadDto); + const status = faker.helpers.enumValue(UserStatus); + + await usersRepository.createWithWallet({ status, authPayload }); + + const walletRepository = dataSource.getRepository(Wallet); + const userRepository = dataSource.getRepository(User); + const wallet = await walletRepository.findOneOrFail({ + where: { address: authPayload.signer_address }, + relations: { user: true }, + }); + const user = await userRepository.findOneOrFail({ + where: { id: wallet.user.id }, + }); + + expect(wallet).toStrictEqual( + expect.objectContaining({ + address: authPayload.signer_address, + created_at: expect.any(Date), + id: wallet.id, + updated_at: expect.any(Date), + user: expect.objectContaining({ + created_at: expect.any(Date), + id: user.id, + status, + updated_at: expect.any(Date), + }), + }), + ); + expect(user).toStrictEqual( + expect.objectContaining({ + created_at: expect.any(Date), + id: user.id, + status, + updated_at: expect.any(Date), + }), + ); + }); + + it('should throw an error if the wallet already exists', async () => { + const authPayloadDto = authPayloadDtoBuilder().build(); + const authPayload = new AuthPayload(authPayloadDto); + const status = faker.helpers.enumValue(UserStatus); + + await usersRepository.createWithWallet({ status, authPayload }); + + await expect( + usersRepository.createWithWallet({ status, authPayload }), + ).rejects.toThrow( + `A wallet with the same address already exists. Wallet=${authPayload.signer_address}`, + ); + }); + + it('should throw if an incorrect UserStatus is provided', async () => { + const authPayloadDto = authPayloadDtoBuilder().build(); + const authPayload = new AuthPayload(authPayloadDto); + const status = faker.string.alpha() as unknown as UserStatus; + + await expect( + usersRepository.createWithWallet({ status, authPayload }), + ).rejects.toThrow(`invalid input syntax for type integer: "${status}"`); + }); + + it('should throw if an invalid wallet address is provided', async () => { + const signerAddress = faker.string.hexadecimal({ + length: { min: 41, max: 41 }, + }); + const authPayloadDto = authPayloadDtoBuilder() + .with('signer_address', signerAddress as `0x${string}`) + .build(); + const authPayload = new AuthPayload(authPayloadDto); + const status = faker.helpers.enumValue(UserStatus); + + await expect( + usersRepository.createWithWallet({ status, authPayload }), + ).rejects.toThrow(new RegExp(`^Address "${signerAddress}" is invalid.`)); + }); + + it('should checksum the inserted wallet address', async () => { + const nonChecksummedAddress = faker.finance + .ethereumAddress() + .toLowerCase(); + const authPayloadDto = authPayloadDtoBuilder() + .with('signer_address', nonChecksummedAddress as `0x${string}`) + .build(); + const authPayload = new AuthPayload(authPayloadDto); + const status = faker.helpers.enumValue(UserStatus); + + await usersRepository.createWithWallet({ status, authPayload }); + + const walletRepository = dataSource.getRepository(Wallet); + const wallet = await walletRepository.findOneOrFail({ + where: { address: authPayload.signer_address }, + }); + + expect(wallet).toStrictEqual( + expect.objectContaining({ + address: getAddress(nonChecksummedAddress), + }), + ); + }); + }); + + describe('create', () => { + it('should insert a new user', async () => { + const status = faker.helpers.enumValue(UserStatus); + + await postgresDatabaseService.transaction(async (entityManager) => { + await usersRepository.create(status, entityManager); + }); + + const userRepository = dataSource.getRepository(User); + const users = await userRepository.find(); + + expect(users).toStrictEqual([ + expect.objectContaining({ + created_at: expect.any(Date), + id: users[0].id, + status, + updated_at: expect.any(Date), + }), + ]); + }); + + it('should throw if an incorrect UserStatus is provided', async () => { + const status = faker.string.alpha() as unknown as UserStatus; + + await expect( + postgresDatabaseService.transaction(async (entityManager) => { + await usersRepository.create(status, entityManager); + }), + ).rejects.toThrow(`invalid input syntax for type integer: "${status}"`); + }); + }); + + describe('getWithWallets', () => { + it('should return a user with their wallets', async () => { + const authPayloadDto = authPayloadDtoBuilder().build(); + const authPayload = new AuthPayload(authPayloadDto); + const status = faker.helpers.enumValue(UserStatus); + await postgresDatabaseService.transaction(async (entityManager) => { + const userInsertResult = await entityManager.insert(User, { + status, + }); + + await entityManager.insert(Wallet, { + user: { + id: userInsertResult.identifiers[0].id, + }, + address: authPayloadDto.signer_address, + }); + }); + const walletRepository = dataSource.getRepository(Wallet); + const userRepository = dataSource.getRepository(User); + const wallet = await walletRepository.findOneOrFail({ + where: { address: authPayload.signer_address }, + relations: { user: true }, + }); + const user = await userRepository.findOneOrFail({ + where: { id: wallet.user.id }, + }); + + await expect( + usersRepository.getWithWallets(authPayload), + ).resolves.toEqual({ + id: user.id, + status, + wallets: [ + { + id: wallet.id, + address: authPayload.signer_address, + }, + ], + }); + }); + + it('should throw if no user wallet is found', async () => { + const authPayloadDto = authPayloadDtoBuilder().build(); + const authPayload = new AuthPayload(authPayloadDto); + const status = faker.helpers.enumValue(UserStatus); + const userRepository = dataSource.getRepository(User); + await userRepository.insert({ status }); + + await expect(usersRepository.getWithWallets(authPayload)).rejects.toThrow( + `Wallet not found. Address=${authPayload.signer_address}`, + ); + }); + + it('should throw if no user is found', async () => { + const authPayloadDto = authPayloadDtoBuilder().build(); + const authPayload = new AuthPayload(authPayloadDto); + const walletRepository = dataSource.getRepository(Wallet); + await walletRepository.insert({ address: authPayload.signer_address }); + + await expect(usersRepository.getWithWallets(authPayload)).rejects.toThrow( + 'User not found.', + ); + }); + + it('should find by non-checksummed address', async () => { + const nonChecksummedAddress = faker.finance + .ethereumAddress() + .toLowerCase(); + const authPayloadDto = authPayloadDtoBuilder() + .with('signer_address', nonChecksummedAddress as `0x${string}`) + .build(); + const authPayload = new AuthPayload(authPayloadDto); + const status = faker.helpers.enumValue(UserStatus); + await postgresDatabaseService.transaction(async (entityManager) => { + const userInsertResult = await entityManager.insert(User, { + status, + }); + + await entityManager.insert(Wallet, { + user: { + id: userInsertResult.identifiers[0].id, + }, + address: authPayloadDto.signer_address, + }); + }); + const walletRepository = dataSource.getRepository(Wallet); + const userRepository = dataSource.getRepository(User); + const wallet = await walletRepository.findOneOrFail({ + where: { address: getAddress(nonChecksummedAddress) }, + relations: { user: true }, + }); + const user = await userRepository.findOneOrFail({ + where: { id: wallet.user.id }, + }); + + await expect( + usersRepository.getWithWallets(authPayload), + ).resolves.toEqual({ + id: user.id, + status, + wallets: [ + { + id: wallet.id, + address: getAddress(nonChecksummedAddress), + }, + ], + }); + }); + }); + + describe('addWalletToUser', () => { + it('should add a wallet to a user', async () => { + const walletAddress = getAddress(faker.finance.ethereumAddress()); + const authPayloadDto = authPayloadDtoBuilder().build(); + const authPayload = new AuthPayload(authPayloadDto); + const status = faker.helpers.enumValue(UserStatus); + await postgresDatabaseService.transaction(async (entityManager) => { + const userInsertResult = await entityManager.insert(User, { + status, + }); + + await entityManager.insert(Wallet, { + user: { + id: userInsertResult.identifiers[0].id, + }, + address: authPayloadDto.signer_address, + }); + }); + + await usersRepository.addWalletToUser({ + authPayload, + walletAddress, + }); + + const walletRepository = dataSource.getRepository(Wallet); + const wallet = await walletRepository.findOneOrFail({ + where: { address: walletAddress }, + relations: { user: true }, + }); + expect(wallet).toStrictEqual( + expect.objectContaining({ + address: walletAddress, + created_at: expect.any(Date), + id: wallet.id, + updated_at: expect.any(Date), + user: expect.objectContaining({ + created_at: expect.any(Date), + id: wallet.user.id, + status, + updated_at: expect.any(Date), + }), + }), + ); + }); + + it('should throw if the user wallet already exists', async () => { + const authPayloadDto = authPayloadDtoBuilder().build(); + const authPayload = new AuthPayload(authPayloadDto); + const walletRepository = dataSource.getRepository(Wallet); + await walletRepository.insert({ address: authPayloadDto.signer_address }); + + await expect( + usersRepository.addWalletToUser({ + authPayload, + walletAddress: authPayloadDto.signer_address, + }), + ).rejects.toThrow( + `A wallet with the same address already exists. Wallet=${authPayloadDto.signer_address}`, + ); + }); + + it('should throw if an invalid wallet address is provided', async () => { + const walletAddress = faker.string.hexadecimal({ + length: { min: 41, max: 41 }, + }); + const authPayloadDto = authPayloadDtoBuilder().build(); + const authPayload = new AuthPayload(authPayloadDto); + const status = faker.helpers.enumValue(UserStatus); + const userRepository = dataSource.getRepository(User); + await userRepository.insert({ status }); + + await expect( + usersRepository.addWalletToUser({ + authPayload, + walletAddress: walletAddress as `0x${string}`, + }), + ).rejects.toThrow(new RegExp(`^Address "${walletAddress}" is invalid.`)); + }); + + it('should checksum the inserted wallet address', async () => { + const nonChecksummedAddress = faker.finance + .ethereumAddress() + .toLowerCase(); + const authPayloadDto = authPayloadDtoBuilder().build(); + const authPayload = new AuthPayload(authPayloadDto); + const status = faker.helpers.enumValue(UserStatus); + await postgresDatabaseService.transaction(async (entityManager) => { + const userInsertResult = await entityManager.insert(User, { + status, + }); + + await entityManager.insert(Wallet, { + user: { + id: userInsertResult.identifiers[0].id, + }, + address: authPayloadDto.signer_address, + }); + }); + + await usersRepository.addWalletToUser({ + authPayload, + walletAddress: nonChecksummedAddress as `0x${string}`, + }); + + const walletRepository = dataSource.getRepository(Wallet); + const wallet = await walletRepository.findOneOrFail({ + where: { address: getAddress(nonChecksummedAddress) }, + relations: { user: true }, + }); + expect(wallet).toStrictEqual( + expect.objectContaining({ + address: getAddress(nonChecksummedAddress), + }), + ); + }); + }); + + describe('delete', () => { + it('should delete a user and their wallets', async () => { + const walletAddress = getAddress(faker.finance.ethereumAddress()); + const authPayloadDto = authPayloadDtoBuilder().build(); + const authPayload = new AuthPayload(authPayloadDto); + const status = faker.helpers.enumValue(UserStatus); + await postgresDatabaseService.transaction(async (entityManager) => { + const userInsertResult = await entityManager.insert(User, { + status, + }); + const userId = userInsertResult.identifiers[0].id; + + await entityManager.insert(Wallet, { + user: { + id: userId, + }, + address: authPayloadDto.signer_address, + }); + await entityManager.insert(Wallet, { + user: { + id: userId, + }, + address: walletAddress, + }); + }); + + await usersRepository.delete(authPayload); + + await expect(dataSource.getRepository(User).find()).resolves.toEqual([]); + // By cascade + await expect(dataSource.getRepository(Wallet).find()).resolves.toEqual( + [], + ); + }); + + it('should throw if no user wallet is found', async () => { + const authPayloadDto = authPayloadDtoBuilder().build(); + const authPayload = new AuthPayload(authPayloadDto); + const status = faker.helpers.enumValue(UserStatus); + const userRepository = dataSource.getRepository(User); + await userRepository.insert({ status }); + + await expect(usersRepository.delete(authPayload)).rejects.toThrow( + `Wallet not found. Address=${authPayload.signer_address}`, + ); + }); + + it('should throw if no user is found', async () => { + const authPayloadDto = authPayloadDtoBuilder().build(); + const authPayload = new AuthPayload(authPayloadDto); + const walletRepository = dataSource.getRepository(Wallet); + await walletRepository.insert({ address: authPayloadDto.signer_address }); + + await expect(usersRepository.delete(authPayload)).rejects.toThrow( + 'User not found.', + ); + }); + }); + + describe('deleteWalletFromUser', () => { + it('should delete a wallet from a user', async () => { + const walletAddress = getAddress(faker.finance.ethereumAddress()); + const authPayloadDto = authPayloadDtoBuilder().build(); + const authPayload = new AuthPayload(authPayloadDto); + const status = faker.helpers.enumValue(UserStatus); + await postgresDatabaseService.transaction(async (entityManager) => { + const userInsertResult = await entityManager.insert(User, { + status, + }); + const userId = userInsertResult.identifiers[0].id; + + await entityManager.insert(Wallet, { + user: { + id: userId, + }, + address: authPayloadDto.signer_address, + }); + await entityManager.insert(Wallet, { + user: { + id: userId, + }, + address: walletAddress, + }); + }); + + await usersRepository.deleteWalletFromUser({ + walletAddress, + authPayload, + }); + + const walletRepository = dataSource.getRepository(Wallet); + const wallets = await walletRepository.find({ + relations: { user: true }, + }); + expect(wallets).toEqual([ + { + address: authPayload.signer_address, + created_at: expect.any(Date), + id: wallets[0].id, + updated_at: expect.any(Date), + user: expect.objectContaining({ + created_at: expect.any(Date), + id: wallets[0].user.id, + status, + updated_at: expect.any(Date), + }), + }, + ]); + }); + + it('should throw if no user is found', async () => { + const walletAddress = getAddress(faker.finance.ethereumAddress()); + const authPayloadDto = authPayloadDtoBuilder().build(); + const authPayload = new AuthPayload(authPayloadDto); + const walletRepository = dataSource.getRepository(Wallet); + await walletRepository.insert({ address: walletAddress }); + + await expect( + usersRepository.deleteWalletFromUser({ + walletAddress, + authPayload, + }), + ).rejects.toThrow('User not found.'); + }); + + it('should throw if no wallet is found', async () => { + const walletAddress = getAddress(faker.finance.ethereumAddress()); + const authPayloadDto = authPayloadDtoBuilder().build(); + const authPayload = new AuthPayload(authPayloadDto); + const status = faker.helpers.enumValue(UserStatus); + await postgresDatabaseService.transaction(async (entityManager) => { + const userInsertResult = await entityManager.insert(User, { + status, + }); + + await entityManager.insert(Wallet, { + user: { + id: userInsertResult.identifiers[0].id, + }, + address: authPayloadDto.signer_address, + }); + }); + + await expect( + usersRepository.deleteWalletFromUser({ + walletAddress, + authPayload, + }), + ).rejects.toThrow('Wallet not found.'); + }); + }); +}); diff --git a/src/domain/users/users.repository.ts b/src/domain/users/users.repository.ts index 2c8572dc3b..ee47e2e4fe 100644 --- a/src/domain/users/users.repository.ts +++ b/src/domain/users/users.repository.ts @@ -71,6 +71,10 @@ export class UsersRepository implements IUsersRepository { { user: true }, ); + if (!wallet.user) { + throw new NotFoundException('User not found.'); + } + const wallets = await this.walletsRepository.findByUser(wallet.user.id, { address: true, id: true, @@ -120,6 +124,10 @@ export class UsersRepository implements IUsersRepository { { user: true }, ); + if (!wallet.user) { + throw new NotFoundException('User not found.'); + } + await userRepository.delete({ id: wallet.user.id, }); diff --git a/src/domain/wallets/wallets.repository.integration.spec.ts b/src/domain/wallets/wallets.repository.integration.spec.ts new file mode 100644 index 0000000000..d6e860e671 --- /dev/null +++ b/src/domain/wallets/wallets.repository.integration.spec.ts @@ -0,0 +1,73 @@ +describe('WalletsRepository', () => { + describe('findOneOrFail', () => { + it.todo('should find a wallet'); + + it.todo('should throw an error if wallet is not found'); + }); + + describe('findOne', () => { + it.todo('should find a wallet'); + + it.todo('should return null if wallet is not found'); + }); + + describe('findOrFail', () => { + it.todo('should find wallets'); + + it.todo('should throw an error if no wallets are found'); + }); + + describe('find', () => { + it.todo('should find wallets'); + + it.todo('should return an empty array if no wallets are found'); + }); + + describe('findOneByAddressOrFail', () => { + it.todo('should find a wallet by address'); + + it.todo('should find a wallet by non-checksummed address'); + + it.todo('should throw an error if wallet is not found'); + }); + + describe('findOneByAddress', () => { + it.todo('should find a wallet by address'); + + it.todo('should find a wallet by non-checksummed address'); + + it.todo('should return null if wallet is not found'); + }); + + describe('findByUser', () => { + it.todo('should find wallets by user'); + + it.todo('should throw an error if invalid user ID is provided'); + + it.todo('should return an empty array if no wallets are found'); + }); + + describe('create', () => { + it.todo('should create a wallet'); + + it.todo('should checksum the address before saving'); + + it.todo( + 'should throw an error if wallet with the same address already exists', + ); + + it.todo('should throw an error if non-existent user ID is provided'); + + it.todo('should throw if invalid wallet address is provided'); + }); + + describe('deleteByAddress', () => { + it.todo('should delete a wallet by address'); + + it.todo('should delete by non-checksummed address'); + + it.todo('should throw if providing invalid wallet address'); + + it.todo('should throw an error if wallet is not found'); + }); +}); diff --git a/src/routes/users/users.controller.ts b/src/routes/users/users.controller.ts index 4f90055e9a..33ac7020df 100644 --- a/src/routes/users/users.controller.ts +++ b/src/routes/users/users.controller.ts @@ -30,7 +30,7 @@ export class UsersController { @ApiOkResponse({ type: UserWithWallets }) @ApiUnauthorizedResponse({ description: 'Signer address not provided' }) - @ApiNotFoundResponse({ description: 'Wallet not found' }) + @ApiNotFoundResponse({ description: 'User (wallet) not found' }) @Get() @UseGuards(AuthGuard) public async getWithWallets( @@ -41,7 +41,7 @@ export class UsersController { @ApiOkResponse({ description: 'User deleted' }) @ApiUnauthorizedResponse({ description: 'Signer address not provided' }) - @ApiNotFoundResponse({ description: 'Wallet not found' }) + @ApiNotFoundResponse({ description: 'User (wallet) not found' }) @Delete() @UseGuards(AuthGuard) public async delete(@Auth() authPayload: AuthPayload): Promise { From 2bc51fef9857996d2712532012916a0d12997a92 Mon Sep 17 00:00:00 2001 From: Aaron Cook Date: Tue, 4 Feb 2025 13:03:23 +0100 Subject: [PATCH 02/12] fix: add integration tests for wallet --- .../wallets.repository.integration.spec.ts | 546 +++++++++++++++++- 1 file changed, 518 insertions(+), 28 deletions(-) diff --git a/src/domain/wallets/wallets.repository.integration.spec.ts b/src/domain/wallets/wallets.repository.integration.spec.ts index d6e860e671..117850a5ce 100644 --- a/src/domain/wallets/wallets.repository.integration.spec.ts +++ b/src/domain/wallets/wallets.repository.integration.spec.ts @@ -1,73 +1,563 @@ +import { faker } from '@faker-js/faker'; +import { DataSource } from 'typeorm'; +import configuration from '@/config/entities/__tests__/configuration'; +import { postgresConfig } from '@/config/entities/postgres.config'; +import { PostgresDatabaseService } from '@/datasources/db/v2/postgres-database.service'; +import { DatabaseMigrator } from '@/datasources/db/v2/database-migrator.service'; +import { User } from '@/datasources/users/entities/users.entity.db'; +import { WalletsRepository } from '@/domain/wallets/wallets.repository'; +import { Wallet } from '@/datasources/wallets/entities/wallets.entity.db'; +import type { ConfigService } from '@nestjs/config'; +import type { ILoggingService } from '@/logging/logging.interface'; +import { getAddress } from 'viem'; +import { UserStatus } from '@/domain/users/entities/user.entity'; + +const mockLoggingService = { + debug: jest.fn(), + error: jest.fn(), + info: jest.fn(), + warn: jest.fn(), +} as jest.MockedObjectDeep; + describe('WalletsRepository', () => { + let postgresDatabaseService: PostgresDatabaseService; + let walletsRepository: WalletsRepository; + + const testDatabaseName = faker.string.alpha({ + length: 10, + casing: 'lower', + }); + const testConfiguration = configuration(); + + const dataSource = new DataSource({ + ...postgresConfig({ + ...testConfiguration.db.connection.postgres, + type: 'postgres', + database: testDatabaseName, + }), + migrationsTableName: testConfiguration.db.orm.migrationsTableName, + entities: [User, Wallet], + }); + + beforeAll(async () => { + // Create database + const testDataSource = new DataSource({ + ...postgresConfig({ + ...testConfiguration.db.connection.postgres, + type: 'postgres', + database: 'postgres', + }), + }); + const testPostgresDatabaseService = new PostgresDatabaseService( + mockLoggingService, + testDataSource, + ); + await testPostgresDatabaseService.initializeDatabaseConnection(); + await testPostgresDatabaseService + .getDataSource() + .query(`CREATE DATABASE ${testDatabaseName}`); + await testPostgresDatabaseService.destroyDatabaseConnection(); + + // Create database connection + postgresDatabaseService = new PostgresDatabaseService( + mockLoggingService, + dataSource, + ); + await postgresDatabaseService.initializeDatabaseConnection(); + + // Migrate database + const mockConfigService = { + getOrThrow: jest.fn().mockImplementation((key: string) => { + if (key === 'db.migrator.numberOfRetries') { + return testConfiguration.db.migrator.numberOfRetries; + } + if (key === 'db.migrator.retryAfterMs') { + return testConfiguration.db.migrator.retryAfterMs; + } + }), + } as jest.MockedObjectDeep; + const migrator = new DatabaseMigrator( + mockLoggingService, + postgresDatabaseService, + mockConfigService, + ); + await migrator.migrate(); + + walletsRepository = new WalletsRepository(postgresDatabaseService); + }); + + afterEach(async () => { + jest.resetAllMocks(); + + // Truncate tables + const walletRepository = dataSource.getRepository(Wallet); + const userRepository = dataSource.getRepository(User); + + await walletRepository.createQueryBuilder().delete().where('1=1').execute(); + await userRepository.createQueryBuilder().delete().where('1=1').execute(); + }); + + afterAll(async () => { + await postgresDatabaseService.getDataSource().dropDatabase(); + await postgresDatabaseService.destroyDatabaseConnection(); + }); + describe('findOneOrFail', () => { - it.todo('should find a wallet'); + it('should find a wallet', async () => { + const address = getAddress(faker.finance.ethereumAddress()); + const walletRepository = dataSource.getRepository(Wallet); + await walletRepository.insert({ address }); + + const wallet = await walletsRepository.findOneOrFail({}); + + expect(wallet).toEqual( + expect.objectContaining({ + address, + created_at: expect.any(Date), + id: wallet.id, + updated_at: expect.any(Date), + }), + ); + }); + + it('should throw an error if wallet is not found', async () => { + const address = getAddress(faker.finance.ethereumAddress()); - it.todo('should throw an error if wallet is not found'); + await expect( + walletsRepository.findOneOrFail({ address }), + ).rejects.toThrow('Wallet not found.'); + }); }); describe('findOne', () => { - it.todo('should find a wallet'); + it('should find a wallet', async () => { + const address = getAddress(faker.finance.ethereumAddress()); + const walletRepository = dataSource.getRepository(Wallet); + await walletRepository.insert({ address }); - it.todo('should return null if wallet is not found'); + const wallet = await walletsRepository.findOne({ address }); + + // Appease TypeScript + if (!wallet) { + throw new Error('Wallet not found.'); + } + + expect(wallet).toEqual( + expect.objectContaining({ + address, + created_at: expect.any(Date), + id: wallet.id, + updated_at: expect.any(Date), + }), + ); + }); + + it('should return null if wallet is not found', async () => { + const wallet = await walletsRepository.findOne({}); + + expect(wallet).toBeNull(); + }); }); describe('findOrFail', () => { - it.todo('should find wallets'); + it('should find wallets', async () => { + const address1 = getAddress(faker.finance.ethereumAddress()); + const address2 = getAddress(faker.finance.ethereumAddress()); + const walletRepository = dataSource.getRepository(Wallet); + await walletRepository.insert({ address: address1 }); + await walletRepository.insert({ address: address2 }); - it.todo('should throw an error if no wallets are found'); + const wallets = await walletsRepository.findOrFail({ where: {} }); + + expect(wallets).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + address: address1, + created_at: expect.any(Date), + id: expect.any(Number), + updated_at: expect.any(Date), + }), + expect.objectContaining({ + address: address2, + created_at: expect.any(Date), + id: expect.any(Number), + updated_at: expect.any(Date), + }), + ]), + ); + }); + + it('should throw an error if no wallets are found', async () => { + await expect(walletsRepository.findOrFail({ where: {} })).rejects.toThrow( + 'Wallets not found.', + ); + }); }); describe('find', () => { - it.todo('should find wallets'); + it('should find wallets', async () => { + const address1 = getAddress(faker.finance.ethereumAddress()); + const address2 = getAddress(faker.finance.ethereumAddress()); + const walletRepository = dataSource.getRepository(Wallet); + await walletRepository.insert({ address: address1 }); + await walletRepository.insert({ address: address2 }); + + const wallets = await walletsRepository.find({ where: {} }); + + expect(wallets).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + address: address1, + created_at: expect.any(Date), + id: expect.any(Number), + updated_at: expect.any(Date), + }), + expect.objectContaining({ + address: address2, + created_at: expect.any(Date), + id: expect.any(Number), + updated_at: expect.any(Date), + }), + ]), + ); + }); - it.todo('should return an empty array if no wallets are found'); + it('should return an empty array if no wallets are found', async () => { + await expect(walletsRepository.find({ where: {} })).resolves.toEqual([]); + }); }); describe('findOneByAddressOrFail', () => { - it.todo('should find a wallet by address'); + it('should find a wallet by address', async () => { + const address = getAddress(faker.finance.ethereumAddress()); + const walletRepository = dataSource.getRepository(Wallet); + await walletRepository.insert({ address }); - it.todo('should find a wallet by non-checksummed address'); + const wallet = await walletsRepository.findOneByAddressOrFail(address); - it.todo('should throw an error if wallet is not found'); + expect(wallet).toEqual( + expect.objectContaining({ + address, + created_at: expect.any(Date), + id: expect.any(Number), + updated_at: expect.any(Date), + }), + ); + }); + + it('should find a wallet by non-checksummed address', async () => { + const nonChecksummedAddress = faker.finance + .ethereumAddress() + .toLowerCase(); + const walletRepository = dataSource.getRepository(Wallet); + await walletRepository.insert({ + address: getAddress(nonChecksummedAddress), + }); + + const wallet = await walletsRepository.findOneByAddressOrFail( + getAddress(nonChecksummedAddress), + ); + + expect(wallet).toEqual( + expect.objectContaining({ + address: getAddress(nonChecksummedAddress), + created_at: expect.any(Date), + id: expect.any(Number), + updated_at: expect.any(Date), + }), + ); + }); + + it('should throw an error if wallet is not found', async () => { + const address = getAddress(faker.finance.ethereumAddress()); + + await expect( + walletsRepository.findOneByAddressOrFail(address), + ).rejects.toThrow(`Wallet not found. Address=${address}`); + }); }); describe('findOneByAddress', () => { - it.todo('should find a wallet by address'); + it('should find a wallet by address', async () => { + const address = getAddress(faker.finance.ethereumAddress()); + const walletRepository = dataSource.getRepository(Wallet); + await walletRepository.insert({ address }); + + const wallet = await walletsRepository.findOneByAddress(address); + + expect(wallet).toEqual( + expect.objectContaining({ + address, + created_at: expect.any(Date), + id: expect.any(Number), + updated_at: expect.any(Date), + }), + ); + }); + + it('should find a wallet by non-checksummed address', async () => { + const nonChecksummedAddress = faker.finance + .ethereumAddress() + .toLowerCase(); + const walletRepository = dataSource.getRepository(Wallet); + await walletRepository.insert({ + address: getAddress(nonChecksummedAddress), + }); - it.todo('should find a wallet by non-checksummed address'); + const wallet = await walletsRepository.findOneByAddress( + getAddress(nonChecksummedAddress), + ); - it.todo('should return null if wallet is not found'); + expect(wallet).toEqual( + expect.objectContaining({ + address: getAddress(nonChecksummedAddress), + created_at: expect.any(Date), + id: expect.any(Number), + updated_at: expect.any(Date), + }), + ); + }); + + it('should return null if wallet is not found', async () => { + const address = getAddress(faker.finance.ethereumAddress()); + + const wallet = await walletsRepository.findOneByAddress(address); + + expect(wallet).toBeNull(); + }); }); describe('findByUser', () => { - it.todo('should find wallets by user'); + it('should find wallets by user', async () => { + const status = faker.helpers.enumValue(UserStatus); + + const address1 = getAddress(faker.finance.ethereumAddress()); + const address2 = getAddress(faker.finance.ethereumAddress()); + const userRepository = dataSource.getRepository(User); + const walletRepository = dataSource.getRepository(Wallet); + const user = await userRepository.insert({ status }); + const userId = user.identifiers[0].id as User['id']; + await walletRepository.insert({ + address: address1, + user: { id: userId }, + }); + await walletRepository.insert({ + address: address2, + user: { id: userId }, + }); + + const wallets = await walletsRepository.findByUser(userId); + + expect(wallets).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + address: address1, + created_at: expect.any(Date), + id: expect.any(Number), + updated_at: expect.any(Date), + }), + expect.objectContaining({ + address: address2, + created_at: expect.any(Date), + id: expect.any(Number), + updated_at: expect.any(Date), + }), + ]), + ); + }); + + it('should throw an error if invalid user ID is provided', async () => { + const userId = faker.string.alpha() as unknown as User['id']; + + await expect(walletsRepository.findByUser(userId)).rejects.toThrow( + `invalid input syntax for type integer: "${userId}"`, + ); + }); - it.todo('should throw an error if invalid user ID is provided'); + it('should return an empty array if no wallets are found', async () => { + const userRepository = dataSource.getRepository(User); + const user = await userRepository.insert({ + status: faker.helpers.enumValue(UserStatus), + }); - it.todo('should return an empty array if no wallets are found'); + const wallets = await walletsRepository.findByUser( + user.identifiers[0].id as User['id'], + ); + + expect(wallets).toEqual([]); + }); }); describe('create', () => { - it.todo('should create a wallet'); + it('should create a wallet', async () => { + const walletAddress = getAddress(faker.finance.ethereumAddress()); - it.todo('should checksum the address before saving'); + await postgresDatabaseService.transaction(async (entityManager) => { + const user = await entityManager.getRepository(User).insert({ + status: faker.helpers.enumValue(UserStatus), + }); + await walletsRepository.create( + { + userId: user.identifiers[0].id as User['id'], + walletAddress, + }, + entityManager, + ); + }); - it.todo( - 'should throw an error if wallet with the same address already exists', - ); + const walletRepository = dataSource.getRepository(Wallet); + await expect(walletRepository.find()).resolves.toEqual([ + expect.objectContaining({ + address: walletAddress, + created_at: expect.any(Date), + id: expect.any(Number), + updated_at: expect.any(Date), + }), + ]); + }); + + it('should checksum the address before saving', async () => { + const nonChecksummedAddress = faker.finance + .ethereumAddress() + .toLowerCase(); + + await postgresDatabaseService.transaction(async (entityManager) => { + const user = await entityManager.getRepository(User).insert({ + status: faker.helpers.enumValue(UserStatus), + }); + await walletsRepository.create( + { + userId: user.identifiers[0].id as User['id'], + walletAddress: nonChecksummedAddress as `0x${string}`, + }, + entityManager, + ); + }); + + const walletRepository = dataSource.getRepository(Wallet); + await expect(walletRepository.find()).resolves.toEqual([ + expect.objectContaining({ + address: getAddress(nonChecksummedAddress), + created_at: expect.any(Date), + id: expect.any(Number), + updated_at: expect.any(Date), + }), + ]); + }); + + it('should throw an error if wallet with the same address already exists', async () => { + const walletAddress = getAddress(faker.finance.ethereumAddress()); + const walletRepository = dataSource.getRepository(Wallet); + await walletRepository.insert({ address: walletAddress }); + + await expect( + postgresDatabaseService.transaction(async (entityManager) => { + const user = await entityManager.getRepository(User).insert({ + status: faker.helpers.enumValue(UserStatus), + }); + await walletsRepository.create( + { + userId: user.identifiers[0].id as User['id'], + walletAddress, + }, + entityManager, + ); + }), + ).rejects.toThrow( + 'duplicate key value violates unique constraint "UQ_wallet_address"', + ); + }); + + it('should throw an error if non-existent user ID is provided', async () => { + // Ensure not out of range for integer type + const userId = faker.number.int({ max: 100 }); + const walletAddress = getAddress(faker.finance.ethereumAddress()); - it.todo('should throw an error if non-existent user ID is provided'); + await expect( + postgresDatabaseService.transaction(async (entityManager) => { + await walletsRepository.create( + { + userId, + walletAddress, + }, + entityManager, + ); + }), + ).rejects.toThrow( + 'insert or update on table "wallets" violates foreign key constraint', + ); + }); - it.todo('should throw if invalid wallet address is provided'); + it('should throw if invalid wallet address is provided', async () => { + const walletAddress = faker.string.hexadecimal({ + length: { min: 41, max: 41 }, + }); + + await expect( + postgresDatabaseService.transaction(async (entityManager) => { + const user = await entityManager.getRepository(User).insert({ + status: faker.helpers.enumValue(UserStatus), + }); + await walletsRepository.create( + { + userId: user.identifiers[0].id as User['id'], + walletAddress: walletAddress as `0x${string}`, + }, + entityManager, + ); + }), + ).rejects.toThrow(new RegExp(`^Address "${walletAddress}" is invalid.`)); + }); }); describe('deleteByAddress', () => { - it.todo('should delete a wallet by address'); + it('should delete a wallet by address', async () => { + const address = getAddress(faker.finance.ethereumAddress()); + const walletRepository = dataSource.getRepository(Wallet); + await walletRepository.insert({ address }); + + await walletsRepository.deleteByAddress(address); + + await expect(walletRepository.find()).resolves.toEqual([]); + }); + + it('should delete by non-checksummed address', async () => { + const nonChecksummedAddress = faker.finance + .ethereumAddress() + .toLowerCase(); + const walletRepository = dataSource.getRepository(Wallet); + await walletRepository.insert({ + address: getAddress(nonChecksummedAddress), + }); + + await walletsRepository.deleteByAddress( + nonChecksummedAddress as `0x${string}`, + ); + + await expect(walletRepository.find()).resolves.toEqual([]); + }); + + it('should throw if providing invalid wallet address', async () => { + const walletAddress = faker.string.hexadecimal({ + length: { min: 41, max: 41 }, + }); + + await expect( + walletsRepository.deleteByAddress(walletAddress as `0x${string}`), + ).rejects.toThrow(new RegExp(`^Address "${walletAddress}" is invalid.`)); + }); - it.todo('should delete by non-checksummed address'); + it('should throw an error if wallet is not found', async () => { + const address = getAddress(faker.finance.ethereumAddress()); - it.todo('should throw if providing invalid wallet address'); + await expect(walletsRepository.deleteByAddress(address)).resolves.toEqual( + { + affected: 0, + raw: [], + }, + ); + }); - it.todo('should throw an error if wallet is not found'); + it.todo('should delete orphaned users'); }); }); From 03d013e816891a3806fbeb4f97027b06d0d97705 Mon Sep 17 00:00:00 2001 From: Aaron Cook Date: Tue, 4 Feb 2025 14:03:05 +0100 Subject: [PATCH 03/12] fix: add tests for triggers --- .../users.repository.integration.spec.ts | 45 +++++++++++++++++++ .../wallets.repository.integration.spec.ts | 45 +++++++++++++++++++ 2 files changed, 90 insertions(+) diff --git a/src/domain/users/users.repository.integration.spec.ts b/src/domain/users/users.repository.integration.spec.ts index b7102d2d86..269745bff6 100644 --- a/src/domain/users/users.repository.integration.spec.ts +++ b/src/domain/users/users.repository.integration.spec.ts @@ -108,6 +108,51 @@ describe('UsersRepository', () => { await postgresDatabaseService.destroyDatabaseConnection(); }); + // As the triggers are set on the database level, Jest's fake timers are not accurate + describe('created_at/updated_at', () => { + it('should set created_at and updated_at when creating a User', async () => { + const userRepository = dataSource.getRepository(User); + + const before = new Date().getTime(); + const user = await userRepository.insert({ + status: faker.helpers.enumValue(UserStatus), + }); + + const after = new Date().getTime(); + + const createdAt = (user.generatedMaps[0].created_at as Date).getTime(); + const updatedAt = (user.generatedMaps[0].updated_at as Date).getTime(); + + expect(createdAt).toEqual(updatedAt); + + expect(createdAt).toBeGreaterThanOrEqual(before); + expect(createdAt).toBeLessThanOrEqual(after); + + expect(updatedAt).toBeGreaterThanOrEqual(before); + expect(updatedAt).toBeLessThanOrEqual(after); + }); + + it('should update updated_at when updating a User', async () => { + const userRepository = dataSource.getRepository(User); + + const user = await userRepository.insert({ + status: UserStatus.PENDING, + }); + const userId = user.identifiers[0].id as User['id']; + await userRepository.update(userId, { + status: UserStatus.ACTIVE, + }); + const updatedUser = await userRepository.findOneOrFail({ + where: { id: userId }, + }); + + const createdAt = updatedUser.created_at.getTime(); + const updatedAt = updatedUser.updated_at.getTime(); + + expect(createdAt).toBeLessThan(updatedAt); + }); + }); + describe('createWithWallet', () => { it('should insert a new user and a linked wallet', async () => { const authPayloadDto = authPayloadDtoBuilder().build(); diff --git a/src/domain/wallets/wallets.repository.integration.spec.ts b/src/domain/wallets/wallets.repository.integration.spec.ts index 117850a5ce..fd4b9e019e 100644 --- a/src/domain/wallets/wallets.repository.integration.spec.ts +++ b/src/domain/wallets/wallets.repository.integration.spec.ts @@ -102,6 +102,51 @@ describe('WalletsRepository', () => { await postgresDatabaseService.destroyDatabaseConnection(); }); + // As the triggers are set on the database level, Jest's fake timers are not accurate + describe('created_at/updated_at', () => { + it('should set created_at and updated_at when creating a Wallet', async () => { + const walletRepository = dataSource.getRepository(Wallet); + + const before = new Date().getTime(); + const wallet = await walletRepository.insert({ + address: getAddress(faker.finance.ethereumAddress()), + }); + + const after = new Date().getTime(); + + const createdAt = (wallet.generatedMaps[0].created_at as Date).getTime(); + const updatedAt = (wallet.generatedMaps[0].updated_at as Date).getTime(); + + expect(createdAt).toEqual(updatedAt); + + expect(createdAt).toBeGreaterThanOrEqual(before); + expect(createdAt).toBeLessThanOrEqual(after); + + expect(updatedAt).toBeGreaterThanOrEqual(before); + expect(updatedAt).toBeLessThanOrEqual(after); + }); + + it('should update updated_at when updating a Wallet', async () => { + const walletRepository = dataSource.getRepository(Wallet); + + const wallet = await walletRepository.insert({ + address: getAddress(faker.finance.ethereumAddress()), + }); + const walletId = wallet.identifiers[0].id as Wallet['id']; + await walletRepository.update(walletId, { + address: getAddress(faker.finance.ethereumAddress()), + }); + const updatedWallet = await walletRepository.findOneOrFail({ + where: { id: walletId }, + }); + + const createdAt = updatedWallet.created_at.getTime(); + const updatedAt = updatedWallet.updated_at.getTime(); + + expect(createdAt).toBeLessThan(updatedAt); + }); + }); + describe('findOneOrFail', () => { it('should find a wallet', async () => { const address = getAddress(faker.finance.ethereumAddress()); From c1962838b3f3c7046afb0e40600d84183a4fe6c7 Mon Sep 17 00:00:00 2001 From: Aaron Cook Date: Tue, 4 Feb 2025 17:09:10 +0100 Subject: [PATCH 04/12] fix: make `user_id` of `Wallet` non-nullable --- .../1738684563598-wallet_user_id_nullable.ts | 29 +++++++++++ .../wallets/entities/wallets.entity.db.ts | 1 + .../users.repository.integration.spec.ts | 52 +++++-------------- src/domain/users/users.repository.ts | 8 --- 4 files changed, 43 insertions(+), 47 deletions(-) create mode 100644 migrations/1738684563598-wallet_user_id_nullable.ts diff --git a/migrations/1738684563598-wallet_user_id_nullable.ts b/migrations/1738684563598-wallet_user_id_nullable.ts new file mode 100644 index 0000000000..8ba00074cb --- /dev/null +++ b/migrations/1738684563598-wallet_user_id_nullable.ts @@ -0,0 +1,29 @@ +import type { MigrationInterface, QueryRunner } from 'typeorm'; + +export class WalletUserIdNullable1738684563598 implements MigrationInterface { + name = 'WalletUserIdNullable1738684563598'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "wallets" DROP CONSTRAINT "FK_wallets_user_id"`, + ); + await queryRunner.query( + `ALTER TABLE "wallets" ALTER COLUMN "user_id" SET NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "wallets" ADD CONSTRAINT "FK_wallets_user_id" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "wallets" DROP CONSTRAINT "FK_wallets_user_id"`, + ); + await queryRunner.query( + `ALTER TABLE "wallets" ALTER COLUMN "user_id" DROP NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "wallets" ADD CONSTRAINT "FK_wallets_user_id" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + } +} diff --git a/src/datasources/wallets/entities/wallets.entity.db.ts b/src/datasources/wallets/entities/wallets.entity.db.ts index 53bed7e7ae..a241404fa0 100644 --- a/src/datasources/wallets/entities/wallets.entity.db.ts +++ b/src/datasources/wallets/entities/wallets.entity.db.ts @@ -19,6 +19,7 @@ export class Wallet implements z.infer { @ManyToOne(() => User, (user: User) => user.id, { onDelete: 'CASCADE', + nullable: false, }) @JoinColumn({ name: 'user_id', diff --git a/src/domain/users/users.repository.integration.spec.ts b/src/domain/users/users.repository.integration.spec.ts index 269745bff6..725726cbcb 100644 --- a/src/domain/users/users.repository.integration.spec.ts +++ b/src/domain/users/users.repository.integration.spec.ts @@ -344,17 +344,6 @@ describe('UsersRepository', () => { ); }); - it('should throw if no user is found', async () => { - const authPayloadDto = authPayloadDtoBuilder().build(); - const authPayload = new AuthPayload(authPayloadDto); - const walletRepository = dataSource.getRepository(Wallet); - await walletRepository.insert({ address: authPayload.signer_address }); - - await expect(usersRepository.getWithWallets(authPayload)).rejects.toThrow( - 'User not found.', - ); - }); - it('should find by non-checksummed address', async () => { const nonChecksummedAddress = faker.finance .ethereumAddress() @@ -449,8 +438,19 @@ describe('UsersRepository', () => { it('should throw if the user wallet already exists', async () => { const authPayloadDto = authPayloadDtoBuilder().build(); const authPayload = new AuthPayload(authPayloadDto); - const walletRepository = dataSource.getRepository(Wallet); - await walletRepository.insert({ address: authPayloadDto.signer_address }); + const status = faker.helpers.enumValue(UserStatus); + await postgresDatabaseService.transaction(async (entityManager) => { + const userInsertResult = await entityManager.insert(User, { + status, + }); + + await entityManager.insert(Wallet, { + user: { + id: userInsertResult.identifiers[0].id, + }, + address: authPayloadDto.signer_address, + }); + }); await expect( usersRepository.addWalletToUser({ @@ -564,17 +564,6 @@ describe('UsersRepository', () => { `Wallet not found. Address=${authPayload.signer_address}`, ); }); - - it('should throw if no user is found', async () => { - const authPayloadDto = authPayloadDtoBuilder().build(); - const authPayload = new AuthPayload(authPayloadDto); - const walletRepository = dataSource.getRepository(Wallet); - await walletRepository.insert({ address: authPayloadDto.signer_address }); - - await expect(usersRepository.delete(authPayload)).rejects.toThrow( - 'User not found.', - ); - }); }); describe('deleteWalletFromUser', () => { @@ -628,21 +617,6 @@ describe('UsersRepository', () => { ]); }); - it('should throw if no user is found', async () => { - const walletAddress = getAddress(faker.finance.ethereumAddress()); - const authPayloadDto = authPayloadDtoBuilder().build(); - const authPayload = new AuthPayload(authPayloadDto); - const walletRepository = dataSource.getRepository(Wallet); - await walletRepository.insert({ address: walletAddress }); - - await expect( - usersRepository.deleteWalletFromUser({ - walletAddress, - authPayload, - }), - ).rejects.toThrow('User not found.'); - }); - it('should throw if no wallet is found', async () => { const walletAddress = getAddress(faker.finance.ethereumAddress()); const authPayloadDto = authPayloadDtoBuilder().build(); diff --git a/src/domain/users/users.repository.ts b/src/domain/users/users.repository.ts index ee47e2e4fe..2c8572dc3b 100644 --- a/src/domain/users/users.repository.ts +++ b/src/domain/users/users.repository.ts @@ -71,10 +71,6 @@ export class UsersRepository implements IUsersRepository { { user: true }, ); - if (!wallet.user) { - throw new NotFoundException('User not found.'); - } - const wallets = await this.walletsRepository.findByUser(wallet.user.id, { address: true, id: true, @@ -124,10 +120,6 @@ export class UsersRepository implements IUsersRepository { { user: true }, ); - if (!wallet.user) { - throw new NotFoundException('User not found.'); - } - await userRepository.delete({ id: wallet.user.id, }); From 5ce39a0f3ef28c51a972c5f5d44109f6dae0557d Mon Sep 17 00:00:00 2001 From: Aaron Cook Date: Tue, 4 Feb 2025 17:27:56 +0100 Subject: [PATCH 05/12] fix: wallet tests --- .../wallets.repository.integration.spec.ts | 148 ++++++++++++++---- 1 file changed, 114 insertions(+), 34 deletions(-) diff --git a/src/domain/wallets/wallets.repository.integration.spec.ts b/src/domain/wallets/wallets.repository.integration.spec.ts index fd4b9e019e..ef1d0d1ccc 100644 --- a/src/domain/wallets/wallets.repository.integration.spec.ts +++ b/src/domain/wallets/wallets.repository.integration.spec.ts @@ -38,6 +38,8 @@ describe('WalletsRepository', () => { migrationsTableName: testConfiguration.db.orm.migrationsTableName, entities: [User, Wallet], }); + const walletRepository = dataSource.getRepository(Wallet); + const userRepository = dataSource.getRepository(User); beforeAll(async () => { // Create database @@ -90,9 +92,6 @@ describe('WalletsRepository', () => { jest.resetAllMocks(); // Truncate tables - const walletRepository = dataSource.getRepository(Wallet); - const userRepository = dataSource.getRepository(User); - await walletRepository.createQueryBuilder().delete().where('1=1').execute(); await userRepository.createQueryBuilder().delete().where('1=1').execute(); }); @@ -105,11 +104,15 @@ describe('WalletsRepository', () => { // As the triggers are set on the database level, Jest's fake timers are not accurate describe('created_at/updated_at', () => { it('should set created_at and updated_at when creating a Wallet', async () => { - const walletRepository = dataSource.getRepository(Wallet); - const before = new Date().getTime(); + const user = await userRepository.insert({ + status: faker.helpers.enumValue(UserStatus), + }); const wallet = await walletRepository.insert({ address: getAddress(faker.finance.ethereumAddress()), + user: { + id: user.identifiers[0].id as User['id'], + }, }); const after = new Date().getTime(); @@ -127,10 +130,14 @@ describe('WalletsRepository', () => { }); it('should update updated_at when updating a Wallet', async () => { - const walletRepository = dataSource.getRepository(Wallet); - + const user = await userRepository.insert({ + status: faker.helpers.enumValue(UserStatus), + }); const wallet = await walletRepository.insert({ address: getAddress(faker.finance.ethereumAddress()), + user: { + id: user.identifiers[0].id as User['id'], + }, }); const walletId = wallet.identifiers[0].id as Wallet['id']; await walletRepository.update(walletId, { @@ -150,8 +157,15 @@ describe('WalletsRepository', () => { describe('findOneOrFail', () => { it('should find a wallet', async () => { const address = getAddress(faker.finance.ethereumAddress()); - const walletRepository = dataSource.getRepository(Wallet); - await walletRepository.insert({ address }); + const user = await userRepository.insert({ + status: faker.helpers.enumValue(UserStatus), + }); + await walletRepository.insert({ + address, + user: { + id: user.identifiers[0].id as User['id'], + }, + }); const wallet = await walletsRepository.findOneOrFail({}); @@ -177,8 +191,15 @@ describe('WalletsRepository', () => { describe('findOne', () => { it('should find a wallet', async () => { const address = getAddress(faker.finance.ethereumAddress()); - const walletRepository = dataSource.getRepository(Wallet); - await walletRepository.insert({ address }); + const user = await userRepository.insert({ + status: faker.helpers.enumValue(UserStatus), + }); + await walletRepository.insert({ + address, + user: { + id: user.identifiers[0].id as User['id'], + }, + }); const wallet = await walletsRepository.findOne({ address }); @@ -208,9 +229,22 @@ describe('WalletsRepository', () => { it('should find wallets', async () => { const address1 = getAddress(faker.finance.ethereumAddress()); const address2 = getAddress(faker.finance.ethereumAddress()); - const walletRepository = dataSource.getRepository(Wallet); - await walletRepository.insert({ address: address1 }); - await walletRepository.insert({ address: address2 }); + const user = await userRepository.insert({ + status: faker.helpers.enumValue(UserStatus), + }); + const userId = user.identifiers[0].id as User['id']; + await walletRepository.insert({ + address: address1, + user: { + id: userId, + }, + }); + await walletRepository.insert({ + address: address2, + user: { + id: userId, + }, + }); const wallets = await walletsRepository.findOrFail({ where: {} }); @@ -243,9 +277,18 @@ describe('WalletsRepository', () => { it('should find wallets', async () => { const address1 = getAddress(faker.finance.ethereumAddress()); const address2 = getAddress(faker.finance.ethereumAddress()); - const walletRepository = dataSource.getRepository(Wallet); - await walletRepository.insert({ address: address1 }); - await walletRepository.insert({ address: address2 }); + const user = await userRepository.insert({ + status: faker.helpers.enumValue(UserStatus), + }); + const userId = user.identifiers[0].id as User['id']; + await walletRepository.insert({ + address: address1, + user: { id: userId }, + }); + await walletRepository.insert({ + address: address2, + user: { id: userId }, + }); const wallets = await walletsRepository.find({ where: {} }); @@ -275,8 +318,15 @@ describe('WalletsRepository', () => { describe('findOneByAddressOrFail', () => { it('should find a wallet by address', async () => { const address = getAddress(faker.finance.ethereumAddress()); - const walletRepository = dataSource.getRepository(Wallet); - await walletRepository.insert({ address }); + const user = await userRepository.insert({ + status: faker.helpers.enumValue(UserStatus), + }); + await walletRepository.insert({ + address, + user: { + id: user.identifiers[0].id as User['id'], + }, + }); const wallet = await walletsRepository.findOneByAddressOrFail(address); @@ -294,9 +344,14 @@ describe('WalletsRepository', () => { const nonChecksummedAddress = faker.finance .ethereumAddress() .toLowerCase(); - const walletRepository = dataSource.getRepository(Wallet); + const user = await userRepository.insert({ + status: faker.helpers.enumValue(UserStatus), + }); await walletRepository.insert({ address: getAddress(nonChecksummedAddress), + user: { + id: user.identifiers[0].id as User['id'], + }, }); const wallet = await walletsRepository.findOneByAddressOrFail( @@ -325,8 +380,15 @@ describe('WalletsRepository', () => { describe('findOneByAddress', () => { it('should find a wallet by address', async () => { const address = getAddress(faker.finance.ethereumAddress()); - const walletRepository = dataSource.getRepository(Wallet); - await walletRepository.insert({ address }); + const user = await userRepository.insert({ + status: faker.helpers.enumValue(UserStatus), + }); + await walletRepository.insert({ + address, + user: { + id: user.identifiers[0].id as User['id'], + }, + }); const wallet = await walletsRepository.findOneByAddress(address); @@ -344,9 +406,14 @@ describe('WalletsRepository', () => { const nonChecksummedAddress = faker.finance .ethereumAddress() .toLowerCase(); - const walletRepository = dataSource.getRepository(Wallet); + const user = await userRepository.insert({ + status: faker.helpers.enumValue(UserStatus), + }); await walletRepository.insert({ address: getAddress(nonChecksummedAddress), + user: { + id: user.identifiers[0].id as User['id'], + }, }); const wallet = await walletsRepository.findOneByAddress( @@ -378,8 +445,6 @@ describe('WalletsRepository', () => { const address1 = getAddress(faker.finance.ethereumAddress()); const address2 = getAddress(faker.finance.ethereumAddress()); - const userRepository = dataSource.getRepository(User); - const walletRepository = dataSource.getRepository(Wallet); const user = await userRepository.insert({ status }); const userId = user.identifiers[0].id as User['id']; await walletRepository.insert({ @@ -450,7 +515,6 @@ describe('WalletsRepository', () => { ); }); - const walletRepository = dataSource.getRepository(Wallet); await expect(walletRepository.find()).resolves.toEqual([ expect.objectContaining({ address: walletAddress, @@ -479,7 +543,6 @@ describe('WalletsRepository', () => { ); }); - const walletRepository = dataSource.getRepository(Wallet); await expect(walletRepository.find()).resolves.toEqual([ expect.objectContaining({ address: getAddress(nonChecksummedAddress), @@ -492,8 +555,15 @@ describe('WalletsRepository', () => { it('should throw an error if wallet with the same address already exists', async () => { const walletAddress = getAddress(faker.finance.ethereumAddress()); - const walletRepository = dataSource.getRepository(Wallet); - await walletRepository.insert({ address: walletAddress }); + const user = await userRepository.insert({ + status: faker.helpers.enumValue(UserStatus), + }); + await walletRepository.insert({ + address: walletAddress, + user: { + id: user.identifiers[0].id as User['id'], + }, + }); await expect( postgresDatabaseService.transaction(async (entityManager) => { @@ -558,8 +628,15 @@ describe('WalletsRepository', () => { describe('deleteByAddress', () => { it('should delete a wallet by address', async () => { const address = getAddress(faker.finance.ethereumAddress()); - const walletRepository = dataSource.getRepository(Wallet); - await walletRepository.insert({ address }); + const user = await userRepository.insert({ + status: faker.helpers.enumValue(UserStatus), + }); + await walletRepository.insert({ + address, + user: { + id: user.identifiers[0].id as User['id'], + }, + }); await walletsRepository.deleteByAddress(address); @@ -570,9 +647,14 @@ describe('WalletsRepository', () => { const nonChecksummedAddress = faker.finance .ethereumAddress() .toLowerCase(); - const walletRepository = dataSource.getRepository(Wallet); + const user = await userRepository.insert({ + status: faker.helpers.enumValue(UserStatus), + }); await walletRepository.insert({ address: getAddress(nonChecksummedAddress), + user: { + id: user.identifiers[0].id as User['id'], + }, }); await walletsRepository.deleteByAddress( @@ -602,7 +684,5 @@ describe('WalletsRepository', () => { }, ); }); - - it.todo('should delete orphaned users'); }); }); From 0448687293411d775da92a61cd58c8495d87ad10 Mon Sep 17 00:00:00 2001 From: Aaron Cook Date: Wed, 5 Feb 2025 11:49:05 +0100 Subject: [PATCH 06/12] fix: update tests --- .../users.repository.integration.spec.ts | 242 +++++++----------- .../wallets.repository.integration.spec.ts | 9 +- 2 files changed, 100 insertions(+), 151 deletions(-) diff --git a/src/domain/users/users.repository.integration.spec.ts b/src/domain/users/users.repository.integration.spec.ts index 725726cbcb..8ed85695b4 100644 --- a/src/domain/users/users.repository.integration.spec.ts +++ b/src/domain/users/users.repository.integration.spec.ts @@ -41,6 +41,8 @@ describe('UsersRepository', () => { migrationsTableName: testConfiguration.db.orm.migrationsTableName, entities: [User, Wallet], }); + const walletRepository = dataSource.getRepository(Wallet); + const userRepository = dataSource.getRepository(User); beforeAll(async () => { // Create database @@ -95,10 +97,6 @@ describe('UsersRepository', () => { afterEach(async () => { jest.resetAllMocks(); - // Truncate tables - const walletRepository = dataSource.getRepository(Wallet); - const userRepository = dataSource.getRepository(User); - await walletRepository.createQueryBuilder().delete().where('1=1').execute(); await userRepository.createQueryBuilder().delete().where('1=1').execute(); }); @@ -111,8 +109,6 @@ describe('UsersRepository', () => { // As the triggers are set on the database level, Jest's fake timers are not accurate describe('created_at/updated_at', () => { it('should set created_at and updated_at when creating a User', async () => { - const userRepository = dataSource.getRepository(User); - const before = new Date().getTime(); const user = await userRepository.insert({ status: faker.helpers.enumValue(UserStatus), @@ -133,12 +129,10 @@ describe('UsersRepository', () => { }); it('should update updated_at when updating a User', async () => { - const userRepository = dataSource.getRepository(User); - - const user = await userRepository.insert({ + const prevUser = await userRepository.insert({ status: UserStatus.PENDING, }); - const userId = user.identifiers[0].id as User['id']; + const userId = prevUser.identifiers[0].id as User['id']; await userRepository.update(userId, { status: UserStatus.ACTIVE, }); @@ -146,10 +140,14 @@ describe('UsersRepository', () => { where: { id: userId }, }); + const prevUpdatedAt = ( + prevUser.generatedMaps[0].updated_at as Date + ).getTime(); const createdAt = updatedUser.created_at.getTime(); const updatedAt = updatedUser.updated_at.getTime(); expect(createdAt).toBeLessThan(updatedAt); + expect(prevUpdatedAt).toBeLessThanOrEqual(updatedAt); }); }); @@ -161,15 +159,10 @@ describe('UsersRepository', () => { await usersRepository.createWithWallet({ status, authPayload }); - const walletRepository = dataSource.getRepository(Wallet); - const userRepository = dataSource.getRepository(User); const wallet = await walletRepository.findOneOrFail({ where: { address: authPayload.signer_address }, relations: { user: true }, }); - const user = await userRepository.findOneOrFail({ - where: { id: wallet.user.id }, - }); expect(wallet).toStrictEqual( expect.objectContaining({ @@ -179,20 +172,12 @@ describe('UsersRepository', () => { updated_at: expect.any(Date), user: expect.objectContaining({ created_at: expect.any(Date), - id: user.id, + id: wallet.user.id, status, updated_at: expect.any(Date), }), }), ); - expect(user).toStrictEqual( - expect.objectContaining({ - created_at: expect.any(Date), - id: user.id, - status, - updated_at: expect.any(Date), - }), - ); }); it('should throw an error if the wallet already exists', async () => { @@ -246,7 +231,6 @@ describe('UsersRepository', () => { await usersRepository.createWithWallet({ status, authPayload }); - const walletRepository = dataSource.getRepository(Wallet); const wallet = await walletRepository.findOneOrFail({ where: { address: authPayload.signer_address }, }); @@ -267,7 +251,6 @@ describe('UsersRepository', () => { await usersRepository.create(status, entityManager); }); - const userRepository = dataSource.getRepository(User); const users = await userRepository.find(); expect(users).toStrictEqual([ @@ -296,32 +279,24 @@ describe('UsersRepository', () => { const authPayloadDto = authPayloadDtoBuilder().build(); const authPayload = new AuthPayload(authPayloadDto); const status = faker.helpers.enumValue(UserStatus); - await postgresDatabaseService.transaction(async (entityManager) => { - const userInsertResult = await entityManager.insert(User, { - status, - }); - - await entityManager.insert(Wallet, { - user: { - id: userInsertResult.identifiers[0].id, - }, - address: authPayloadDto.signer_address, - }); + const userInsertResult = await userRepository.insert({ + status, + }); + await walletRepository.insert({ + user: { + id: userInsertResult.identifiers[0].id, + }, + address: authPayloadDto.signer_address, }); - const walletRepository = dataSource.getRepository(Wallet); - const userRepository = dataSource.getRepository(User); const wallet = await walletRepository.findOneOrFail({ where: { address: authPayload.signer_address }, relations: { user: true }, }); - const user = await userRepository.findOneOrFail({ - where: { id: wallet.user.id }, - }); await expect( usersRepository.getWithWallets(authPayload), ).resolves.toEqual({ - id: user.id, + id: wallet.user.id, status, wallets: [ { @@ -336,7 +311,6 @@ describe('UsersRepository', () => { const authPayloadDto = authPayloadDtoBuilder().build(); const authPayload = new AuthPayload(authPayloadDto); const status = faker.helpers.enumValue(UserStatus); - const userRepository = dataSource.getRepository(User); await userRepository.insert({ status }); await expect(usersRepository.getWithWallets(authPayload)).rejects.toThrow( @@ -353,32 +327,24 @@ describe('UsersRepository', () => { .build(); const authPayload = new AuthPayload(authPayloadDto); const status = faker.helpers.enumValue(UserStatus); - await postgresDatabaseService.transaction(async (entityManager) => { - const userInsertResult = await entityManager.insert(User, { - status, - }); - - await entityManager.insert(Wallet, { - user: { - id: userInsertResult.identifiers[0].id, - }, - address: authPayloadDto.signer_address, - }); + const userInsertResult = await userRepository.insert({ + status, + }); + await walletRepository.insert({ + user: { + id: userInsertResult.identifiers[0].id, + }, + address: authPayloadDto.signer_address, }); - const walletRepository = dataSource.getRepository(Wallet); - const userRepository = dataSource.getRepository(User); const wallet = await walletRepository.findOneOrFail({ where: { address: getAddress(nonChecksummedAddress) }, relations: { user: true }, }); - const user = await userRepository.findOneOrFail({ - where: { id: wallet.user.id }, - }); await expect( usersRepository.getWithWallets(authPayload), ).resolves.toEqual({ - id: user.id, + id: wallet.user.id, status, wallets: [ { @@ -396,17 +362,14 @@ describe('UsersRepository', () => { const authPayloadDto = authPayloadDtoBuilder().build(); const authPayload = new AuthPayload(authPayloadDto); const status = faker.helpers.enumValue(UserStatus); - await postgresDatabaseService.transaction(async (entityManager) => { - const userInsertResult = await entityManager.insert(User, { - status, - }); - - await entityManager.insert(Wallet, { - user: { - id: userInsertResult.identifiers[0].id, - }, - address: authPayloadDto.signer_address, - }); + const userInsertResult = await userRepository.insert({ + status, + }); + await walletRepository.insert({ + user: { + id: userInsertResult.identifiers[0].id, + }, + address: authPayloadDto.signer_address, }); await usersRepository.addWalletToUser({ @@ -414,7 +377,6 @@ describe('UsersRepository', () => { walletAddress, }); - const walletRepository = dataSource.getRepository(Wallet); const wallet = await walletRepository.findOneOrFail({ where: { address: walletAddress }, relations: { user: true }, @@ -439,17 +401,15 @@ describe('UsersRepository', () => { const authPayloadDto = authPayloadDtoBuilder().build(); const authPayload = new AuthPayload(authPayloadDto); const status = faker.helpers.enumValue(UserStatus); - await postgresDatabaseService.transaction(async (entityManager) => { - const userInsertResult = await entityManager.insert(User, { - status, - }); + const userInsertResult = await userRepository.insert({ + status, + }); - await entityManager.insert(Wallet, { - user: { - id: userInsertResult.identifiers[0].id, - }, - address: authPayloadDto.signer_address, - }); + await walletRepository.insert({ + user: { + id: userInsertResult.identifiers[0].id, + }, + address: authPayloadDto.signer_address, }); await expect( @@ -469,7 +429,6 @@ describe('UsersRepository', () => { const authPayloadDto = authPayloadDtoBuilder().build(); const authPayload = new AuthPayload(authPayloadDto); const status = faker.helpers.enumValue(UserStatus); - const userRepository = dataSource.getRepository(User); await userRepository.insert({ status }); await expect( @@ -487,17 +446,14 @@ describe('UsersRepository', () => { const authPayloadDto = authPayloadDtoBuilder().build(); const authPayload = new AuthPayload(authPayloadDto); const status = faker.helpers.enumValue(UserStatus); - await postgresDatabaseService.transaction(async (entityManager) => { - const userInsertResult = await entityManager.insert(User, { - status, - }); - - await entityManager.insert(Wallet, { - user: { - id: userInsertResult.identifiers[0].id, - }, - address: authPayloadDto.signer_address, - }); + const userInsertResult = await userRepository.insert({ + status, + }); + await walletRepository.insert({ + user: { + id: userInsertResult.identifiers[0].id, + }, + address: authPayloadDto.signer_address, }); await usersRepository.addWalletToUser({ @@ -505,7 +461,6 @@ describe('UsersRepository', () => { walletAddress: nonChecksummedAddress as `0x${string}`, }); - const walletRepository = dataSource.getRepository(Wallet); const wallet = await walletRepository.findOneOrFail({ where: { address: getAddress(nonChecksummedAddress) }, relations: { user: true }, @@ -524,40 +479,35 @@ describe('UsersRepository', () => { const authPayloadDto = authPayloadDtoBuilder().build(); const authPayload = new AuthPayload(authPayloadDto); const status = faker.helpers.enumValue(UserStatus); - await postgresDatabaseService.transaction(async (entityManager) => { - const userInsertResult = await entityManager.insert(User, { - status, - }); - const userId = userInsertResult.identifiers[0].id; - - await entityManager.insert(Wallet, { - user: { - id: userId, - }, - address: authPayloadDto.signer_address, - }); - await entityManager.insert(Wallet, { - user: { - id: userId, - }, - address: walletAddress, - }); + const userInsertResult = await userRepository.insert({ + status, + }); + const userId = userInsertResult.identifiers[0].id; + await walletRepository.insert({ + user: { + id: userId, + }, + address: authPayloadDto.signer_address, + }); + await walletRepository.insert({ + user: { + id: userId, + }, + address: walletAddress, }); + await expect(walletRepository.find()).resolves.toHaveLength(2); await usersRepository.delete(authPayload); - await expect(dataSource.getRepository(User).find()).resolves.toEqual([]); + await expect(userRepository.find()).resolves.toEqual([]); // By cascade - await expect(dataSource.getRepository(Wallet).find()).resolves.toEqual( - [], - ); + await expect(walletRepository.find()).resolves.toEqual([]); }); it('should throw if no user wallet is found', async () => { const authPayloadDto = authPayloadDtoBuilder().build(); const authPayload = new AuthPayload(authPayloadDto); const status = faker.helpers.enumValue(UserStatus); - const userRepository = dataSource.getRepository(User); await userRepository.insert({ status }); await expect(usersRepository.delete(authPayload)).rejects.toThrow( @@ -572,32 +522,29 @@ describe('UsersRepository', () => { const authPayloadDto = authPayloadDtoBuilder().build(); const authPayload = new AuthPayload(authPayloadDto); const status = faker.helpers.enumValue(UserStatus); - await postgresDatabaseService.transaction(async (entityManager) => { - const userInsertResult = await entityManager.insert(User, { - status, - }); - const userId = userInsertResult.identifiers[0].id; - - await entityManager.insert(Wallet, { - user: { - id: userId, - }, - address: authPayloadDto.signer_address, - }); - await entityManager.insert(Wallet, { - user: { - id: userId, - }, - address: walletAddress, - }); + const userInsertResult = await userRepository.insert({ + status, + }); + const userId = userInsertResult.identifiers[0].id; + await walletRepository.insert({ + user: { + id: userId, + }, + address: authPayloadDto.signer_address, }); + await walletRepository.insert({ + user: { + id: userId, + }, + address: walletAddress, + }); + await expect(walletRepository.find()).resolves.toHaveLength(2); await usersRepository.deleteWalletFromUser({ walletAddress, authPayload, }); - const walletRepository = dataSource.getRepository(Wallet); const wallets = await walletRepository.find({ relations: { user: true }, }); @@ -622,17 +569,14 @@ describe('UsersRepository', () => { const authPayloadDto = authPayloadDtoBuilder().build(); const authPayload = new AuthPayload(authPayloadDto); const status = faker.helpers.enumValue(UserStatus); - await postgresDatabaseService.transaction(async (entityManager) => { - const userInsertResult = await entityManager.insert(User, { - status, - }); - - await entityManager.insert(Wallet, { - user: { - id: userInsertResult.identifiers[0].id, - }, - address: authPayloadDto.signer_address, - }); + const userInsertResult = await userRepository.insert({ + status, + }); + await walletRepository.insert({ + user: { + id: userInsertResult.identifiers[0].id, + }, + address: authPayloadDto.signer_address, }); await expect( diff --git a/src/domain/wallets/wallets.repository.integration.spec.ts b/src/domain/wallets/wallets.repository.integration.spec.ts index ef1d0d1ccc..a7b826d6a4 100644 --- a/src/domain/wallets/wallets.repository.integration.spec.ts +++ b/src/domain/wallets/wallets.repository.integration.spec.ts @@ -133,13 +133,13 @@ describe('WalletsRepository', () => { const user = await userRepository.insert({ status: faker.helpers.enumValue(UserStatus), }); - const wallet = await walletRepository.insert({ + const prevWallet = await walletRepository.insert({ address: getAddress(faker.finance.ethereumAddress()), user: { id: user.identifiers[0].id as User['id'], }, }); - const walletId = wallet.identifiers[0].id as Wallet['id']; + const walletId = prevWallet.identifiers[0].id as Wallet['id']; await walletRepository.update(walletId, { address: getAddress(faker.finance.ethereumAddress()), }); @@ -147,10 +147,14 @@ describe('WalletsRepository', () => { where: { id: walletId }, }); + const prevUpdatedAt = ( + prevWallet.generatedMaps[0].updated_at as Date + ).getTime(); const createdAt = updatedWallet.created_at.getTime(); const updatedAt = updatedWallet.updated_at.getTime(); expect(createdAt).toBeLessThan(updatedAt); + expect(prevUpdatedAt).toBeLessThanOrEqual(updatedAt); }); }); @@ -637,6 +641,7 @@ describe('WalletsRepository', () => { id: user.identifiers[0].id as User['id'], }, }); + await expect(walletRepository.find()).resolves.toHaveLength(1); await walletsRepository.deleteByAddress(address); From d8fb541c13f125cc67ceb25cac7b062c8ec656fb Mon Sep 17 00:00:00 2001 From: Aaron Cook Date: Thu, 6 Feb 2025 09:04:49 +0100 Subject: [PATCH 07/12] fix: rename ORM repositories and remove comment --- .../users.repository.integration.spec.ts | 91 ++++++++++--------- .../wallets.repository.integration.spec.ts | 88 +++++++++--------- 2 files changed, 93 insertions(+), 86 deletions(-) diff --git a/src/domain/users/users.repository.integration.spec.ts b/src/domain/users/users.repository.integration.spec.ts index 8ed85695b4..712c6a2ad3 100644 --- a/src/domain/users/users.repository.integration.spec.ts +++ b/src/domain/users/users.repository.integration.spec.ts @@ -41,8 +41,8 @@ describe('UsersRepository', () => { migrationsTableName: testConfiguration.db.orm.migrationsTableName, entities: [User, Wallet], }); - const walletRepository = dataSource.getRepository(Wallet); - const userRepository = dataSource.getRepository(User); + const dbWalletRepository = dataSource.getRepository(Wallet); + const dbUserRepository = dataSource.getRepository(User); beforeAll(async () => { // Create database @@ -97,8 +97,12 @@ describe('UsersRepository', () => { afterEach(async () => { jest.resetAllMocks(); - await walletRepository.createQueryBuilder().delete().where('1=1').execute(); - await userRepository.createQueryBuilder().delete().where('1=1').execute(); + await dbWalletRepository + .createQueryBuilder() + .delete() + .where('1=1') + .execute(); + await dbUserRepository.createQueryBuilder().delete().where('1=1').execute(); }); afterAll(async () => { @@ -110,14 +114,14 @@ describe('UsersRepository', () => { describe('created_at/updated_at', () => { it('should set created_at and updated_at when creating a User', async () => { const before = new Date().getTime(); - const user = await userRepository.insert({ + const user = await dbUserRepository.insert({ status: faker.helpers.enumValue(UserStatus), }); const after = new Date().getTime(); - const createdAt = (user.generatedMaps[0].created_at as Date).getTime(); - const updatedAt = (user.generatedMaps[0].updated_at as Date).getTime(); + const createdAt = user.generatedMaps[0].created_at; + const updatedAt = user.generatedMaps[0].updated_at; expect(createdAt).toEqual(updatedAt); @@ -129,14 +133,14 @@ describe('UsersRepository', () => { }); it('should update updated_at when updating a User', async () => { - const prevUser = await userRepository.insert({ + const prevUser = await dbUserRepository.insert({ status: UserStatus.PENDING, }); const userId = prevUser.identifiers[0].id as User['id']; - await userRepository.update(userId, { + await dbUserRepository.update(userId, { status: UserStatus.ACTIVE, }); - const updatedUser = await userRepository.findOneOrFail({ + const updatedUser = await dbUserRepository.findOneOrFail({ where: { id: userId }, }); @@ -159,7 +163,7 @@ describe('UsersRepository', () => { await usersRepository.createWithWallet({ status, authPayload }); - const wallet = await walletRepository.findOneOrFail({ + const wallet = await dbWalletRepository.findOneOrFail({ where: { address: authPayload.signer_address }, relations: { user: true }, }); @@ -231,7 +235,7 @@ describe('UsersRepository', () => { await usersRepository.createWithWallet({ status, authPayload }); - const wallet = await walletRepository.findOneOrFail({ + const wallet = await dbWalletRepository.findOneOrFail({ where: { address: authPayload.signer_address }, }); @@ -251,7 +255,7 @@ describe('UsersRepository', () => { await usersRepository.create(status, entityManager); }); - const users = await userRepository.find(); + const users = await dbUserRepository.find(); expect(users).toStrictEqual([ expect.objectContaining({ @@ -279,16 +283,16 @@ describe('UsersRepository', () => { const authPayloadDto = authPayloadDtoBuilder().build(); const authPayload = new AuthPayload(authPayloadDto); const status = faker.helpers.enumValue(UserStatus); - const userInsertResult = await userRepository.insert({ + const userInsertResult = await dbUserRepository.insert({ status, }); - await walletRepository.insert({ + await dbWalletRepository.insert({ user: { id: userInsertResult.identifiers[0].id, }, address: authPayloadDto.signer_address, }); - const wallet = await walletRepository.findOneOrFail({ + const wallet = await dbWalletRepository.findOneOrFail({ where: { address: authPayload.signer_address }, relations: { user: true }, }); @@ -311,7 +315,7 @@ describe('UsersRepository', () => { const authPayloadDto = authPayloadDtoBuilder().build(); const authPayload = new AuthPayload(authPayloadDto); const status = faker.helpers.enumValue(UserStatus); - await userRepository.insert({ status }); + await dbUserRepository.insert({ status }); await expect(usersRepository.getWithWallets(authPayload)).rejects.toThrow( `Wallet not found. Address=${authPayload.signer_address}`, @@ -327,16 +331,16 @@ describe('UsersRepository', () => { .build(); const authPayload = new AuthPayload(authPayloadDto); const status = faker.helpers.enumValue(UserStatus); - const userInsertResult = await userRepository.insert({ + const userInsertResult = await dbUserRepository.insert({ status, }); - await walletRepository.insert({ + await dbWalletRepository.insert({ user: { id: userInsertResult.identifiers[0].id, }, address: authPayloadDto.signer_address, }); - const wallet = await walletRepository.findOneOrFail({ + const wallet = await dbWalletRepository.findOneOrFail({ where: { address: getAddress(nonChecksummedAddress) }, relations: { user: true }, }); @@ -362,10 +366,10 @@ describe('UsersRepository', () => { const authPayloadDto = authPayloadDtoBuilder().build(); const authPayload = new AuthPayload(authPayloadDto); const status = faker.helpers.enumValue(UserStatus); - const userInsertResult = await userRepository.insert({ + const userInsertResult = await dbUserRepository.insert({ status, }); - await walletRepository.insert({ + await dbWalletRepository.insert({ user: { id: userInsertResult.identifiers[0].id, }, @@ -377,7 +381,7 @@ describe('UsersRepository', () => { walletAddress, }); - const wallet = await walletRepository.findOneOrFail({ + const wallet = await dbWalletRepository.findOneOrFail({ where: { address: walletAddress }, relations: { user: true }, }); @@ -401,11 +405,11 @@ describe('UsersRepository', () => { const authPayloadDto = authPayloadDtoBuilder().build(); const authPayload = new AuthPayload(authPayloadDto); const status = faker.helpers.enumValue(UserStatus); - const userInsertResult = await userRepository.insert({ + const userInsertResult = await dbUserRepository.insert({ status, }); - await walletRepository.insert({ + await dbWalletRepository.insert({ user: { id: userInsertResult.identifiers[0].id, }, @@ -429,7 +433,7 @@ describe('UsersRepository', () => { const authPayloadDto = authPayloadDtoBuilder().build(); const authPayload = new AuthPayload(authPayloadDto); const status = faker.helpers.enumValue(UserStatus); - await userRepository.insert({ status }); + await dbUserRepository.insert({ status }); await expect( usersRepository.addWalletToUser({ @@ -446,10 +450,10 @@ describe('UsersRepository', () => { const authPayloadDto = authPayloadDtoBuilder().build(); const authPayload = new AuthPayload(authPayloadDto); const status = faker.helpers.enumValue(UserStatus); - const userInsertResult = await userRepository.insert({ + const userInsertResult = await dbUserRepository.insert({ status, }); - await walletRepository.insert({ + await dbWalletRepository.insert({ user: { id: userInsertResult.identifiers[0].id, }, @@ -461,7 +465,7 @@ describe('UsersRepository', () => { walletAddress: nonChecksummedAddress as `0x${string}`, }); - const wallet = await walletRepository.findOneOrFail({ + const wallet = await dbWalletRepository.findOneOrFail({ where: { address: getAddress(nonChecksummedAddress) }, relations: { user: true }, }); @@ -479,36 +483,35 @@ describe('UsersRepository', () => { const authPayloadDto = authPayloadDtoBuilder().build(); const authPayload = new AuthPayload(authPayloadDto); const status = faker.helpers.enumValue(UserStatus); - const userInsertResult = await userRepository.insert({ + const userInsertResult = await dbUserRepository.insert({ status, }); const userId = userInsertResult.identifiers[0].id; - await walletRepository.insert({ + await dbWalletRepository.insert({ user: { id: userId, }, address: authPayloadDto.signer_address, }); - await walletRepository.insert({ + await dbWalletRepository.insert({ user: { id: userId, }, address: walletAddress, }); - await expect(walletRepository.find()).resolves.toHaveLength(2); + await expect(dbWalletRepository.find()).resolves.toHaveLength(2); await usersRepository.delete(authPayload); - await expect(userRepository.find()).resolves.toEqual([]); - // By cascade - await expect(walletRepository.find()).resolves.toEqual([]); + await expect(dbUserRepository.find()).resolves.toEqual([]); + await expect(dbWalletRepository.find()).resolves.toEqual([]); }); it('should throw if no user wallet is found', async () => { const authPayloadDto = authPayloadDtoBuilder().build(); const authPayload = new AuthPayload(authPayloadDto); const status = faker.helpers.enumValue(UserStatus); - await userRepository.insert({ status }); + await dbUserRepository.insert({ status }); await expect(usersRepository.delete(authPayload)).rejects.toThrow( `Wallet not found. Address=${authPayload.signer_address}`, @@ -522,30 +525,30 @@ describe('UsersRepository', () => { const authPayloadDto = authPayloadDtoBuilder().build(); const authPayload = new AuthPayload(authPayloadDto); const status = faker.helpers.enumValue(UserStatus); - const userInsertResult = await userRepository.insert({ + const userInsertResult = await dbUserRepository.insert({ status, }); const userId = userInsertResult.identifiers[0].id; - await walletRepository.insert({ + await dbWalletRepository.insert({ user: { id: userId, }, address: authPayloadDto.signer_address, }); - await walletRepository.insert({ + await dbWalletRepository.insert({ user: { id: userId, }, address: walletAddress, }); - await expect(walletRepository.find()).resolves.toHaveLength(2); + await expect(dbWalletRepository.find()).resolves.toHaveLength(2); await usersRepository.deleteWalletFromUser({ walletAddress, authPayload, }); - const wallets = await walletRepository.find({ + const wallets = await dbWalletRepository.find({ relations: { user: true }, }); expect(wallets).toEqual([ @@ -569,10 +572,10 @@ describe('UsersRepository', () => { const authPayloadDto = authPayloadDtoBuilder().build(); const authPayload = new AuthPayload(authPayloadDto); const status = faker.helpers.enumValue(UserStatus); - const userInsertResult = await userRepository.insert({ + const userInsertResult = await dbUserRepository.insert({ status, }); - await walletRepository.insert({ + await dbWalletRepository.insert({ user: { id: userInsertResult.identifiers[0].id, }, diff --git a/src/domain/wallets/wallets.repository.integration.spec.ts b/src/domain/wallets/wallets.repository.integration.spec.ts index a7b826d6a4..f7249a635a 100644 --- a/src/domain/wallets/wallets.repository.integration.spec.ts +++ b/src/domain/wallets/wallets.repository.integration.spec.ts @@ -38,8 +38,8 @@ describe('WalletsRepository', () => { migrationsTableName: testConfiguration.db.orm.migrationsTableName, entities: [User, Wallet], }); - const walletRepository = dataSource.getRepository(Wallet); - const userRepository = dataSource.getRepository(User); + const dbWalletRepository = dataSource.getRepository(Wallet); + const dbWserRepository = dataSource.getRepository(User); beforeAll(async () => { // Create database @@ -92,8 +92,12 @@ describe('WalletsRepository', () => { jest.resetAllMocks(); // Truncate tables - await walletRepository.createQueryBuilder().delete().where('1=1').execute(); - await userRepository.createQueryBuilder().delete().where('1=1').execute(); + await dbWalletRepository + .createQueryBuilder() + .delete() + .where('1=1') + .execute(); + await dbWserRepository.createQueryBuilder().delete().where('1=1').execute(); }); afterAll(async () => { @@ -105,10 +109,10 @@ describe('WalletsRepository', () => { describe('created_at/updated_at', () => { it('should set created_at and updated_at when creating a Wallet', async () => { const before = new Date().getTime(); - const user = await userRepository.insert({ + const user = await dbWserRepository.insert({ status: faker.helpers.enumValue(UserStatus), }); - const wallet = await walletRepository.insert({ + const wallet = await dbWalletRepository.insert({ address: getAddress(faker.finance.ethereumAddress()), user: { id: user.identifiers[0].id as User['id'], @@ -130,20 +134,20 @@ describe('WalletsRepository', () => { }); it('should update updated_at when updating a Wallet', async () => { - const user = await userRepository.insert({ + const user = await dbWserRepository.insert({ status: faker.helpers.enumValue(UserStatus), }); - const prevWallet = await walletRepository.insert({ + const prevWallet = await dbWalletRepository.insert({ address: getAddress(faker.finance.ethereumAddress()), user: { id: user.identifiers[0].id as User['id'], }, }); const walletId = prevWallet.identifiers[0].id as Wallet['id']; - await walletRepository.update(walletId, { + await dbWalletRepository.update(walletId, { address: getAddress(faker.finance.ethereumAddress()), }); - const updatedWallet = await walletRepository.findOneOrFail({ + const updatedWallet = await dbWalletRepository.findOneOrFail({ where: { id: walletId }, }); @@ -161,10 +165,10 @@ describe('WalletsRepository', () => { describe('findOneOrFail', () => { it('should find a wallet', async () => { const address = getAddress(faker.finance.ethereumAddress()); - const user = await userRepository.insert({ + const user = await dbWserRepository.insert({ status: faker.helpers.enumValue(UserStatus), }); - await walletRepository.insert({ + await dbWalletRepository.insert({ address, user: { id: user.identifiers[0].id as User['id'], @@ -195,10 +199,10 @@ describe('WalletsRepository', () => { describe('findOne', () => { it('should find a wallet', async () => { const address = getAddress(faker.finance.ethereumAddress()); - const user = await userRepository.insert({ + const user = await dbWserRepository.insert({ status: faker.helpers.enumValue(UserStatus), }); - await walletRepository.insert({ + await dbWalletRepository.insert({ address, user: { id: user.identifiers[0].id as User['id'], @@ -233,17 +237,17 @@ describe('WalletsRepository', () => { it('should find wallets', async () => { const address1 = getAddress(faker.finance.ethereumAddress()); const address2 = getAddress(faker.finance.ethereumAddress()); - const user = await userRepository.insert({ + const user = await dbWserRepository.insert({ status: faker.helpers.enumValue(UserStatus), }); const userId = user.identifiers[0].id as User['id']; - await walletRepository.insert({ + await dbWalletRepository.insert({ address: address1, user: { id: userId, }, }); - await walletRepository.insert({ + await dbWalletRepository.insert({ address: address2, user: { id: userId, @@ -281,15 +285,15 @@ describe('WalletsRepository', () => { it('should find wallets', async () => { const address1 = getAddress(faker.finance.ethereumAddress()); const address2 = getAddress(faker.finance.ethereumAddress()); - const user = await userRepository.insert({ + const user = await dbWserRepository.insert({ status: faker.helpers.enumValue(UserStatus), }); const userId = user.identifiers[0].id as User['id']; - await walletRepository.insert({ + await dbWalletRepository.insert({ address: address1, user: { id: userId }, }); - await walletRepository.insert({ + await dbWalletRepository.insert({ address: address2, user: { id: userId }, }); @@ -322,10 +326,10 @@ describe('WalletsRepository', () => { describe('findOneByAddressOrFail', () => { it('should find a wallet by address', async () => { const address = getAddress(faker.finance.ethereumAddress()); - const user = await userRepository.insert({ + const user = await dbWserRepository.insert({ status: faker.helpers.enumValue(UserStatus), }); - await walletRepository.insert({ + await dbWalletRepository.insert({ address, user: { id: user.identifiers[0].id as User['id'], @@ -348,10 +352,10 @@ describe('WalletsRepository', () => { const nonChecksummedAddress = faker.finance .ethereumAddress() .toLowerCase(); - const user = await userRepository.insert({ + const user = await dbWserRepository.insert({ status: faker.helpers.enumValue(UserStatus), }); - await walletRepository.insert({ + await dbWalletRepository.insert({ address: getAddress(nonChecksummedAddress), user: { id: user.identifiers[0].id as User['id'], @@ -384,10 +388,10 @@ describe('WalletsRepository', () => { describe('findOneByAddress', () => { it('should find a wallet by address', async () => { const address = getAddress(faker.finance.ethereumAddress()); - const user = await userRepository.insert({ + const user = await dbWserRepository.insert({ status: faker.helpers.enumValue(UserStatus), }); - await walletRepository.insert({ + await dbWalletRepository.insert({ address, user: { id: user.identifiers[0].id as User['id'], @@ -410,10 +414,10 @@ describe('WalletsRepository', () => { const nonChecksummedAddress = faker.finance .ethereumAddress() .toLowerCase(); - const user = await userRepository.insert({ + const user = await dbWserRepository.insert({ status: faker.helpers.enumValue(UserStatus), }); - await walletRepository.insert({ + await dbWalletRepository.insert({ address: getAddress(nonChecksummedAddress), user: { id: user.identifiers[0].id as User['id'], @@ -449,13 +453,13 @@ describe('WalletsRepository', () => { const address1 = getAddress(faker.finance.ethereumAddress()); const address2 = getAddress(faker.finance.ethereumAddress()); - const user = await userRepository.insert({ status }); + const user = await dbWserRepository.insert({ status }); const userId = user.identifiers[0].id as User['id']; - await walletRepository.insert({ + await dbWalletRepository.insert({ address: address1, user: { id: userId }, }); - await walletRepository.insert({ + await dbWalletRepository.insert({ address: address2, user: { id: userId }, }); @@ -519,7 +523,7 @@ describe('WalletsRepository', () => { ); }); - await expect(walletRepository.find()).resolves.toEqual([ + await expect(dbWalletRepository.find()).resolves.toEqual([ expect.objectContaining({ address: walletAddress, created_at: expect.any(Date), @@ -547,7 +551,7 @@ describe('WalletsRepository', () => { ); }); - await expect(walletRepository.find()).resolves.toEqual([ + await expect(dbWalletRepository.find()).resolves.toEqual([ expect.objectContaining({ address: getAddress(nonChecksummedAddress), created_at: expect.any(Date), @@ -559,10 +563,10 @@ describe('WalletsRepository', () => { it('should throw an error if wallet with the same address already exists', async () => { const walletAddress = getAddress(faker.finance.ethereumAddress()); - const user = await userRepository.insert({ + const user = await dbWserRepository.insert({ status: faker.helpers.enumValue(UserStatus), }); - await walletRepository.insert({ + await dbWalletRepository.insert({ address: walletAddress, user: { id: user.identifiers[0].id as User['id'], @@ -632,30 +636,30 @@ describe('WalletsRepository', () => { describe('deleteByAddress', () => { it('should delete a wallet by address', async () => { const address = getAddress(faker.finance.ethereumAddress()); - const user = await userRepository.insert({ + const user = await dbWserRepository.insert({ status: faker.helpers.enumValue(UserStatus), }); - await walletRepository.insert({ + await dbWalletRepository.insert({ address, user: { id: user.identifiers[0].id as User['id'], }, }); - await expect(walletRepository.find()).resolves.toHaveLength(1); + await expect(dbWalletRepository.find()).resolves.toHaveLength(1); await walletsRepository.deleteByAddress(address); - await expect(walletRepository.find()).resolves.toEqual([]); + await expect(dbWalletRepository.find()).resolves.toEqual([]); }); it('should delete by non-checksummed address', async () => { const nonChecksummedAddress = faker.finance .ethereumAddress() .toLowerCase(); - const user = await userRepository.insert({ + const user = await dbWserRepository.insert({ status: faker.helpers.enumValue(UserStatus), }); - await walletRepository.insert({ + await dbWalletRepository.insert({ address: getAddress(nonChecksummedAddress), user: { id: user.identifiers[0].id as User['id'], @@ -666,7 +670,7 @@ describe('WalletsRepository', () => { nonChecksummedAddress as `0x${string}`, ); - await expect(walletRepository.find()).resolves.toEqual([]); + await expect(dbWalletRepository.find()).resolves.toEqual([]); }); it('should throw if providing invalid wallet address', async () => { From 28b329a0475209d7084af6cbf3ece64b53ff278c Mon Sep 17 00:00:00 2001 From: Aaron Cook Date: Thu, 6 Feb 2025 09:18:15 +0100 Subject: [PATCH 08/12] fix: compare time --- src/domain/users/users.repository.integration.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/domain/users/users.repository.integration.spec.ts b/src/domain/users/users.repository.integration.spec.ts index 712c6a2ad3..ebd2ebfd0c 100644 --- a/src/domain/users/users.repository.integration.spec.ts +++ b/src/domain/users/users.repository.integration.spec.ts @@ -120,8 +120,8 @@ describe('UsersRepository', () => { const after = new Date().getTime(); - const createdAt = user.generatedMaps[0].created_at; - const updatedAt = user.generatedMaps[0].updated_at; + const createdAt = (user.generatedMaps[0].created_at as Date).getTime(); + const updatedAt = (user.generatedMaps[0].updated_at as Date).getTime(); expect(createdAt).toEqual(updatedAt); From e9de543feedb0fd10d5469bfc6a7f3f18d9f42e7 Mon Sep 17 00:00:00 2001 From: Aaron Cook Date: Thu, 6 Feb 2025 09:50:33 +0100 Subject: [PATCH 09/12] fix: throw if not `Date` --- .../users.repository.integration.spec.ts | 35 ++++++++++------- .../wallets.repository.integration.spec.ts | 39 ++++++++++++------- 2 files changed, 48 insertions(+), 26 deletions(-) diff --git a/src/domain/users/users.repository.integration.spec.ts b/src/domain/users/users.repository.integration.spec.ts index ebd2ebfd0c..7059da2b75 100644 --- a/src/domain/users/users.repository.integration.spec.ts +++ b/src/domain/users/users.repository.integration.spec.ts @@ -120,16 +120,23 @@ describe('UsersRepository', () => { const after = new Date().getTime(); - const createdAt = (user.generatedMaps[0].created_at as Date).getTime(); - const updatedAt = (user.generatedMaps[0].updated_at as Date).getTime(); + const createdAt = user.generatedMaps[0].created_at; + const updatedAt = user.generatedMaps[0].updated_at; + + if (!(createdAt instanceof Date) || !(updatedAt instanceof Date)) { + throw new Error('createdAt and/or updatedAt is not a Date'); + } expect(createdAt).toEqual(updatedAt); - expect(createdAt).toBeGreaterThanOrEqual(before); - expect(createdAt).toBeLessThanOrEqual(after); + const createdAtTime = createdAt.getTime(); + const updatedAtTime = updatedAt.getTime(); + + expect(createdAtTime).toBeGreaterThanOrEqual(before); + expect(createdAtTime).toBeLessThanOrEqual(after); - expect(updatedAt).toBeGreaterThanOrEqual(before); - expect(updatedAt).toBeLessThanOrEqual(after); + expect(updatedAtTime).toBeGreaterThanOrEqual(before); + expect(updatedAtTime).toBeLessThanOrEqual(after); }); it('should update updated_at when updating a User', async () => { @@ -144,14 +151,16 @@ describe('UsersRepository', () => { where: { id: userId }, }); - const prevUpdatedAt = ( - prevUser.generatedMaps[0].updated_at as Date - ).getTime(); - const createdAt = updatedUser.created_at.getTime(); - const updatedAt = updatedUser.updated_at.getTime(); + const prevUpdatedAt = prevUser.generatedMaps[0].updated_at; + + if (!(prevUpdatedAt instanceof Date)) { + throw new Error('prevUpdatedAt is not a Date'); + } + + const updatedAtTime = updatedUser.updated_at.getTime(); - expect(createdAt).toBeLessThan(updatedAt); - expect(prevUpdatedAt).toBeLessThanOrEqual(updatedAt); + expect(updatedUser.created_at.getTime()).toBeLessThan(updatedAtTime); + expect(prevUpdatedAt.getTime()).toBeLessThanOrEqual(updatedAtTime); }); }); diff --git a/src/domain/wallets/wallets.repository.integration.spec.ts b/src/domain/wallets/wallets.repository.integration.spec.ts index f7249a635a..fac6e97ea4 100644 --- a/src/domain/wallets/wallets.repository.integration.spec.ts +++ b/src/domain/wallets/wallets.repository.integration.spec.ts @@ -121,16 +121,27 @@ describe('WalletsRepository', () => { const after = new Date().getTime(); - const createdAt = (wallet.generatedMaps[0].created_at as Date).getTime(); - const updatedAt = (wallet.generatedMaps[0].updated_at as Date).getTime(); + const createdAt = wallet.generatedMaps[0].created_at; + const updatedAt = wallet.generatedMaps[0].updated_at; + + if (!(createdAt instanceof Date) || !(updatedAt instanceof Date)) { + throw new Error('created_at and/or updated_at is not a Date'); + } expect(createdAt).toEqual(updatedAt); - expect(createdAt).toBeGreaterThanOrEqual(before); - expect(createdAt).toBeLessThanOrEqual(after); + if (!(createdAt instanceof Date) || !(updatedAt instanceof Date)) { + throw new Error('createdAt and/or updatedAt is not a Date'); + } + + const createdAtTime = createdAt.getTime(); + const updatedAtTime = updatedAt.getTime(); - expect(updatedAt).toBeGreaterThanOrEqual(before); - expect(updatedAt).toBeLessThanOrEqual(after); + expect(createdAtTime).toBeGreaterThanOrEqual(before); + expect(createdAtTime).toBeLessThanOrEqual(after); + + expect(updatedAtTime).toBeGreaterThanOrEqual(before); + expect(updatedAtTime).toBeLessThanOrEqual(after); }); it('should update updated_at when updating a Wallet', async () => { @@ -151,14 +162,16 @@ describe('WalletsRepository', () => { where: { id: walletId }, }); - const prevUpdatedAt = ( - prevWallet.generatedMaps[0].updated_at as Date - ).getTime(); - const createdAt = updatedWallet.created_at.getTime(); - const updatedAt = updatedWallet.updated_at.getTime(); + const prevUpdatedAt = prevWallet.generatedMaps[0].updated_at; + + if (!(prevUpdatedAt instanceof Date)) { + throw new Error('prevUpdatedAt is not a Date'); + } + + const updatedAtTime = updatedWallet.updated_at.getTime(); - expect(createdAt).toBeLessThan(updatedAt); - expect(prevUpdatedAt).toBeLessThanOrEqual(updatedAt); + expect(updatedWallet.created_at.getTime()).toBeLessThan(updatedAtTime); + expect(prevUpdatedAt.getTime()).toBeLessThanOrEqual(updatedAtTime); }); }); From 668d5d22724d917a790bb6643b2bef0b08f8da47 Mon Sep 17 00:00:00 2001 From: Aaron Cook Date: Thu, 6 Feb 2025 10:13:01 +0100 Subject: [PATCH 10/12] fix: get repositories after migration --- .../users.repository.integration.spec.ts | 10 +++-- .../wallets.repository.integration.spec.ts | 42 ++++++++++--------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/domain/users/users.repository.integration.spec.ts b/src/domain/users/users.repository.integration.spec.ts index 7059da2b75..bdd9555e88 100644 --- a/src/domain/users/users.repository.integration.spec.ts +++ b/src/domain/users/users.repository.integration.spec.ts @@ -1,5 +1,6 @@ import { faker } from '@faker-js/faker'; import { DataSource } from 'typeorm'; +import { getAddress } from 'viem'; import configuration from '@/config/entities/__tests__/configuration'; import { postgresConfig } from '@/config/entities/postgres.config'; import { PostgresDatabaseService } from '@/datasources/db/v2/postgres-database.service'; @@ -11,9 +12,9 @@ import { authPayloadDtoBuilder } from '@/domain/auth/entities/__tests__/auth-pay import { AuthPayload } from '@/domain/auth/entities/auth-payload.entity'; import { UserStatus } from '@/domain/users/entities/user.entity'; import { Wallet } from '@/datasources/wallets/entities/wallets.entity.db'; +import type { Repository } from 'typeorm'; import type { ConfigService } from '@nestjs/config'; import type { ILoggingService } from '@/logging/logging.interface'; -import { getAddress } from 'viem'; const mockLoggingService = { debug: jest.fn(), @@ -41,8 +42,8 @@ describe('UsersRepository', () => { migrationsTableName: testConfiguration.db.orm.migrationsTableName, entities: [User, Wallet], }); - const dbWalletRepository = dataSource.getRepository(Wallet); - const dbUserRepository = dataSource.getRepository(User); + let dbWalletRepository: Repository; + let dbUserRepository: Repository; beforeAll(async () => { // Create database @@ -88,6 +89,9 @@ describe('UsersRepository', () => { ); await migrator.migrate(); + dbWalletRepository = dataSource.getRepository(Wallet); + dbUserRepository = dataSource.getRepository(User); + usersRepository = new UsersRepository( postgresDatabaseService, new WalletsRepository(postgresDatabaseService), diff --git a/src/domain/wallets/wallets.repository.integration.spec.ts b/src/domain/wallets/wallets.repository.integration.spec.ts index fac6e97ea4..c6529ed8c8 100644 --- a/src/domain/wallets/wallets.repository.integration.spec.ts +++ b/src/domain/wallets/wallets.repository.integration.spec.ts @@ -1,5 +1,6 @@ import { faker } from '@faker-js/faker'; import { DataSource } from 'typeorm'; +import { getAddress } from 'viem'; import configuration from '@/config/entities/__tests__/configuration'; import { postgresConfig } from '@/config/entities/postgres.config'; import { PostgresDatabaseService } from '@/datasources/db/v2/postgres-database.service'; @@ -7,10 +8,10 @@ import { DatabaseMigrator } from '@/datasources/db/v2/database-migrator.service' import { User } from '@/datasources/users/entities/users.entity.db'; import { WalletsRepository } from '@/domain/wallets/wallets.repository'; import { Wallet } from '@/datasources/wallets/entities/wallets.entity.db'; +import { UserStatus } from '@/domain/users/entities/user.entity'; import type { ConfigService } from '@nestjs/config'; +import type { Repository } from 'typeorm'; import type { ILoggingService } from '@/logging/logging.interface'; -import { getAddress } from 'viem'; -import { UserStatus } from '@/domain/users/entities/user.entity'; const mockLoggingService = { debug: jest.fn(), @@ -38,8 +39,8 @@ describe('WalletsRepository', () => { migrationsTableName: testConfiguration.db.orm.migrationsTableName, entities: [User, Wallet], }); - const dbWalletRepository = dataSource.getRepository(Wallet); - const dbWserRepository = dataSource.getRepository(User); + let dbWalletRepository: Repository; + let dbUserRepository: Repository; beforeAll(async () => { // Create database @@ -85,6 +86,9 @@ describe('WalletsRepository', () => { ); await migrator.migrate(); + dbWalletRepository = dataSource.getRepository(Wallet); + dbUserRepository = dataSource.getRepository(User); + walletsRepository = new WalletsRepository(postgresDatabaseService); }); @@ -97,7 +101,7 @@ describe('WalletsRepository', () => { .delete() .where('1=1') .execute(); - await dbWserRepository.createQueryBuilder().delete().where('1=1').execute(); + await dbUserRepository.createQueryBuilder().delete().where('1=1').execute(); }); afterAll(async () => { @@ -109,7 +113,7 @@ describe('WalletsRepository', () => { describe('created_at/updated_at', () => { it('should set created_at and updated_at when creating a Wallet', async () => { const before = new Date().getTime(); - const user = await dbWserRepository.insert({ + const user = await dbUserRepository.insert({ status: faker.helpers.enumValue(UserStatus), }); const wallet = await dbWalletRepository.insert({ @@ -145,7 +149,7 @@ describe('WalletsRepository', () => { }); it('should update updated_at when updating a Wallet', async () => { - const user = await dbWserRepository.insert({ + const user = await dbUserRepository.insert({ status: faker.helpers.enumValue(UserStatus), }); const prevWallet = await dbWalletRepository.insert({ @@ -178,7 +182,7 @@ describe('WalletsRepository', () => { describe('findOneOrFail', () => { it('should find a wallet', async () => { const address = getAddress(faker.finance.ethereumAddress()); - const user = await dbWserRepository.insert({ + const user = await dbUserRepository.insert({ status: faker.helpers.enumValue(UserStatus), }); await dbWalletRepository.insert({ @@ -212,7 +216,7 @@ describe('WalletsRepository', () => { describe('findOne', () => { it('should find a wallet', async () => { const address = getAddress(faker.finance.ethereumAddress()); - const user = await dbWserRepository.insert({ + const user = await dbUserRepository.insert({ status: faker.helpers.enumValue(UserStatus), }); await dbWalletRepository.insert({ @@ -250,7 +254,7 @@ describe('WalletsRepository', () => { it('should find wallets', async () => { const address1 = getAddress(faker.finance.ethereumAddress()); const address2 = getAddress(faker.finance.ethereumAddress()); - const user = await dbWserRepository.insert({ + const user = await dbUserRepository.insert({ status: faker.helpers.enumValue(UserStatus), }); const userId = user.identifiers[0].id as User['id']; @@ -298,7 +302,7 @@ describe('WalletsRepository', () => { it('should find wallets', async () => { const address1 = getAddress(faker.finance.ethereumAddress()); const address2 = getAddress(faker.finance.ethereumAddress()); - const user = await dbWserRepository.insert({ + const user = await dbUserRepository.insert({ status: faker.helpers.enumValue(UserStatus), }); const userId = user.identifiers[0].id as User['id']; @@ -339,7 +343,7 @@ describe('WalletsRepository', () => { describe('findOneByAddressOrFail', () => { it('should find a wallet by address', async () => { const address = getAddress(faker.finance.ethereumAddress()); - const user = await dbWserRepository.insert({ + const user = await dbUserRepository.insert({ status: faker.helpers.enumValue(UserStatus), }); await dbWalletRepository.insert({ @@ -365,7 +369,7 @@ describe('WalletsRepository', () => { const nonChecksummedAddress = faker.finance .ethereumAddress() .toLowerCase(); - const user = await dbWserRepository.insert({ + const user = await dbUserRepository.insert({ status: faker.helpers.enumValue(UserStatus), }); await dbWalletRepository.insert({ @@ -401,7 +405,7 @@ describe('WalletsRepository', () => { describe('findOneByAddress', () => { it('should find a wallet by address', async () => { const address = getAddress(faker.finance.ethereumAddress()); - const user = await dbWserRepository.insert({ + const user = await dbUserRepository.insert({ status: faker.helpers.enumValue(UserStatus), }); await dbWalletRepository.insert({ @@ -427,7 +431,7 @@ describe('WalletsRepository', () => { const nonChecksummedAddress = faker.finance .ethereumAddress() .toLowerCase(); - const user = await dbWserRepository.insert({ + const user = await dbUserRepository.insert({ status: faker.helpers.enumValue(UserStatus), }); await dbWalletRepository.insert({ @@ -466,7 +470,7 @@ describe('WalletsRepository', () => { const address1 = getAddress(faker.finance.ethereumAddress()); const address2 = getAddress(faker.finance.ethereumAddress()); - const user = await dbWserRepository.insert({ status }); + const user = await dbUserRepository.insert({ status }); const userId = user.identifiers[0].id as User['id']; await dbWalletRepository.insert({ address: address1, @@ -576,7 +580,7 @@ describe('WalletsRepository', () => { it('should throw an error if wallet with the same address already exists', async () => { const walletAddress = getAddress(faker.finance.ethereumAddress()); - const user = await dbWserRepository.insert({ + const user = await dbUserRepository.insert({ status: faker.helpers.enumValue(UserStatus), }); await dbWalletRepository.insert({ @@ -649,7 +653,7 @@ describe('WalletsRepository', () => { describe('deleteByAddress', () => { it('should delete a wallet by address', async () => { const address = getAddress(faker.finance.ethereumAddress()); - const user = await dbWserRepository.insert({ + const user = await dbUserRepository.insert({ status: faker.helpers.enumValue(UserStatus), }); await dbWalletRepository.insert({ @@ -669,7 +673,7 @@ describe('WalletsRepository', () => { const nonChecksummedAddress = faker.finance .ethereumAddress() .toLowerCase(); - const user = await dbWserRepository.insert({ + const user = await dbUserRepository.insert({ status: faker.helpers.enumValue(UserStatus), }); await dbWalletRepository.insert({ From 5b119ed7733c333b27e4164169a406df18d919fc Mon Sep 17 00:00:00 2001 From: Aaron Cook Date: Thu, 6 Feb 2025 10:29:37 +0100 Subject: [PATCH 11/12] fix: get DB repositories in tests --- .../users.repository.integration.spec.ts | 32 +++++++++++++--- .../wallets.repository.integration.spec.ts | 38 ++++++++++++++++--- 2 files changed, 58 insertions(+), 12 deletions(-) diff --git a/src/domain/users/users.repository.integration.spec.ts b/src/domain/users/users.repository.integration.spec.ts index bdd9555e88..b306b6bfd5 100644 --- a/src/domain/users/users.repository.integration.spec.ts +++ b/src/domain/users/users.repository.integration.spec.ts @@ -12,7 +12,6 @@ import { authPayloadDtoBuilder } from '@/domain/auth/entities/__tests__/auth-pay import { AuthPayload } from '@/domain/auth/entities/auth-payload.entity'; import { UserStatus } from '@/domain/users/entities/user.entity'; import { Wallet } from '@/datasources/wallets/entities/wallets.entity.db'; -import type { Repository } from 'typeorm'; import type { ConfigService } from '@nestjs/config'; import type { ILoggingService } from '@/logging/logging.interface'; @@ -42,8 +41,6 @@ describe('UsersRepository', () => { migrationsTableName: testConfiguration.db.orm.migrationsTableName, entities: [User, Wallet], }); - let dbWalletRepository: Repository; - let dbUserRepository: Repository; beforeAll(async () => { // Create database @@ -89,9 +86,6 @@ describe('UsersRepository', () => { ); await migrator.migrate(); - dbWalletRepository = dataSource.getRepository(Wallet); - dbUserRepository = dataSource.getRepository(User); - usersRepository = new UsersRepository( postgresDatabaseService, new WalletsRepository(postgresDatabaseService), @@ -101,6 +95,8 @@ describe('UsersRepository', () => { afterEach(async () => { jest.resetAllMocks(); + const dbWalletRepository = dataSource.getRepository(Wallet); + const dbUserRepository = dataSource.getRepository(User); await dbWalletRepository .createQueryBuilder() .delete() @@ -117,6 +113,7 @@ describe('UsersRepository', () => { // As the triggers are set on the database level, Jest's fake timers are not accurate describe('created_at/updated_at', () => { it('should set created_at and updated_at when creating a User', async () => { + const dbUserRepository = dataSource.getRepository(User); const before = new Date().getTime(); const user = await dbUserRepository.insert({ status: faker.helpers.enumValue(UserStatus), @@ -144,6 +141,7 @@ describe('UsersRepository', () => { }); it('should update updated_at when updating a User', async () => { + const dbUserRepository = dataSource.getRepository(User); const prevUser = await dbUserRepository.insert({ status: UserStatus.PENDING, }); @@ -170,6 +168,7 @@ describe('UsersRepository', () => { describe('createWithWallet', () => { it('should insert a new user and a linked wallet', async () => { + const dbWalletRepository = dataSource.getRepository(Wallet); const authPayloadDto = authPayloadDtoBuilder().build(); const authPayload = new AuthPayload(authPayloadDto); const status = faker.helpers.enumValue(UserStatus); @@ -237,6 +236,7 @@ describe('UsersRepository', () => { }); it('should checksum the inserted wallet address', async () => { + const dbWalletRepository = dataSource.getRepository(Wallet); const nonChecksummedAddress = faker.finance .ethereumAddress() .toLowerCase(); @@ -262,6 +262,7 @@ describe('UsersRepository', () => { describe('create', () => { it('should insert a new user', async () => { + const dbUserRepository = dataSource.getRepository(User); const status = faker.helpers.enumValue(UserStatus); await postgresDatabaseService.transaction(async (entityManager) => { @@ -293,6 +294,8 @@ describe('UsersRepository', () => { describe('getWithWallets', () => { it('should return a user with their wallets', async () => { + const dbWalletRepository = dataSource.getRepository(Wallet); + const dbUserRepository = dataSource.getRepository(User); const authPayloadDto = authPayloadDtoBuilder().build(); const authPayload = new AuthPayload(authPayloadDto); const status = faker.helpers.enumValue(UserStatus); @@ -325,6 +328,7 @@ describe('UsersRepository', () => { }); it('should throw if no user wallet is found', async () => { + const dbUserRepository = dataSource.getRepository(User); const authPayloadDto = authPayloadDtoBuilder().build(); const authPayload = new AuthPayload(authPayloadDto); const status = faker.helpers.enumValue(UserStatus); @@ -336,6 +340,8 @@ describe('UsersRepository', () => { }); it('should find by non-checksummed address', async () => { + const dbWalletRepository = dataSource.getRepository(Wallet); + const dbUserRepository = dataSource.getRepository(User); const nonChecksummedAddress = faker.finance .ethereumAddress() .toLowerCase(); @@ -375,6 +381,8 @@ describe('UsersRepository', () => { describe('addWalletToUser', () => { it('should add a wallet to a user', async () => { + const dbWalletRepository = dataSource.getRepository(Wallet); + const dbUserRepository = dataSource.getRepository(User); const walletAddress = getAddress(faker.finance.ethereumAddress()); const authPayloadDto = authPayloadDtoBuilder().build(); const authPayload = new AuthPayload(authPayloadDto); @@ -415,6 +423,8 @@ describe('UsersRepository', () => { }); it('should throw if the user wallet already exists', async () => { + const dbWalletRepository = dataSource.getRepository(Wallet); + const dbUserRepository = dataSource.getRepository(User); const authPayloadDto = authPayloadDtoBuilder().build(); const authPayload = new AuthPayload(authPayloadDto); const status = faker.helpers.enumValue(UserStatus); @@ -440,6 +450,7 @@ describe('UsersRepository', () => { }); it('should throw if an invalid wallet address is provided', async () => { + const dbUserRepository = dataSource.getRepository(User); const walletAddress = faker.string.hexadecimal({ length: { min: 41, max: 41 }, }); @@ -457,6 +468,8 @@ describe('UsersRepository', () => { }); it('should checksum the inserted wallet address', async () => { + const dbWalletRepository = dataSource.getRepository(Wallet); + const dbUserRepository = dataSource.getRepository(User); const nonChecksummedAddress = faker.finance .ethereumAddress() .toLowerCase(); @@ -492,6 +505,8 @@ describe('UsersRepository', () => { describe('delete', () => { it('should delete a user and their wallets', async () => { + const dbWalletRepository = dataSource.getRepository(Wallet); + const dbUserRepository = dataSource.getRepository(User); const walletAddress = getAddress(faker.finance.ethereumAddress()); const authPayloadDto = authPayloadDtoBuilder().build(); const authPayload = new AuthPayload(authPayloadDto); @@ -521,6 +536,7 @@ describe('UsersRepository', () => { }); it('should throw if no user wallet is found', async () => { + const dbUserRepository = dataSource.getRepository(User); const authPayloadDto = authPayloadDtoBuilder().build(); const authPayload = new AuthPayload(authPayloadDto); const status = faker.helpers.enumValue(UserStatus); @@ -534,6 +550,8 @@ describe('UsersRepository', () => { describe('deleteWalletFromUser', () => { it('should delete a wallet from a user', async () => { + const dbWalletRepository = dataSource.getRepository(Wallet); + const dbUserRepository = dataSource.getRepository(User); const walletAddress = getAddress(faker.finance.ethereumAddress()); const authPayloadDto = authPayloadDtoBuilder().build(); const authPayload = new AuthPayload(authPayloadDto); @@ -581,6 +599,8 @@ describe('UsersRepository', () => { }); it('should throw if no wallet is found', async () => { + const dbWalletRepository = dataSource.getRepository(Wallet); + const dbUserRepository = dataSource.getRepository(User); const walletAddress = getAddress(faker.finance.ethereumAddress()); const authPayloadDto = authPayloadDtoBuilder().build(); const authPayload = new AuthPayload(authPayloadDto); diff --git a/src/domain/wallets/wallets.repository.integration.spec.ts b/src/domain/wallets/wallets.repository.integration.spec.ts index c6529ed8c8..929175b652 100644 --- a/src/domain/wallets/wallets.repository.integration.spec.ts +++ b/src/domain/wallets/wallets.repository.integration.spec.ts @@ -10,7 +10,6 @@ import { WalletsRepository } from '@/domain/wallets/wallets.repository'; import { Wallet } from '@/datasources/wallets/entities/wallets.entity.db'; import { UserStatus } from '@/domain/users/entities/user.entity'; import type { ConfigService } from '@nestjs/config'; -import type { Repository } from 'typeorm'; import type { ILoggingService } from '@/logging/logging.interface'; const mockLoggingService = { @@ -39,8 +38,6 @@ describe('WalletsRepository', () => { migrationsTableName: testConfiguration.db.orm.migrationsTableName, entities: [User, Wallet], }); - let dbWalletRepository: Repository; - let dbUserRepository: Repository; beforeAll(async () => { // Create database @@ -86,9 +83,6 @@ describe('WalletsRepository', () => { ); await migrator.migrate(); - dbWalletRepository = dataSource.getRepository(Wallet); - dbUserRepository = dataSource.getRepository(User); - walletsRepository = new WalletsRepository(postgresDatabaseService); }); @@ -96,6 +90,8 @@ describe('WalletsRepository', () => { jest.resetAllMocks(); // Truncate tables + const dbWalletRepository = dataSource.getRepository(Wallet); + const dbUserRepository = dataSource.getRepository(User); await dbWalletRepository .createQueryBuilder() .delete() @@ -112,6 +108,8 @@ describe('WalletsRepository', () => { // As the triggers are set on the database level, Jest's fake timers are not accurate describe('created_at/updated_at', () => { it('should set created_at and updated_at when creating a Wallet', async () => { + const dbWalletRepository = dataSource.getRepository(Wallet); + const dbUserRepository = dataSource.getRepository(User); const before = new Date().getTime(); const user = await dbUserRepository.insert({ status: faker.helpers.enumValue(UserStatus), @@ -149,6 +147,8 @@ describe('WalletsRepository', () => { }); it('should update updated_at when updating a Wallet', async () => { + const dbWalletRepository = dataSource.getRepository(Wallet); + const dbUserRepository = dataSource.getRepository(User); const user = await dbUserRepository.insert({ status: faker.helpers.enumValue(UserStatus), }); @@ -181,6 +181,8 @@ describe('WalletsRepository', () => { describe('findOneOrFail', () => { it('should find a wallet', async () => { + const dbWalletRepository = dataSource.getRepository(Wallet); + const dbUserRepository = dataSource.getRepository(User); const address = getAddress(faker.finance.ethereumAddress()); const user = await dbUserRepository.insert({ status: faker.helpers.enumValue(UserStatus), @@ -215,6 +217,8 @@ describe('WalletsRepository', () => { describe('findOne', () => { it('should find a wallet', async () => { + const dbWalletRepository = dataSource.getRepository(Wallet); + const dbUserRepository = dataSource.getRepository(User); const address = getAddress(faker.finance.ethereumAddress()); const user = await dbUserRepository.insert({ status: faker.helpers.enumValue(UserStatus), @@ -252,6 +256,8 @@ describe('WalletsRepository', () => { describe('findOrFail', () => { it('should find wallets', async () => { + const dbWalletRepository = dataSource.getRepository(Wallet); + const dbUserRepository = dataSource.getRepository(User); const address1 = getAddress(faker.finance.ethereumAddress()); const address2 = getAddress(faker.finance.ethereumAddress()); const user = await dbUserRepository.insert({ @@ -300,6 +306,8 @@ describe('WalletsRepository', () => { describe('find', () => { it('should find wallets', async () => { + const dbWalletRepository = dataSource.getRepository(Wallet); + const dbUserRepository = dataSource.getRepository(User); const address1 = getAddress(faker.finance.ethereumAddress()); const address2 = getAddress(faker.finance.ethereumAddress()); const user = await dbUserRepository.insert({ @@ -342,6 +350,8 @@ describe('WalletsRepository', () => { describe('findOneByAddressOrFail', () => { it('should find a wallet by address', async () => { + const dbWalletRepository = dataSource.getRepository(Wallet); + const dbUserRepository = dataSource.getRepository(User); const address = getAddress(faker.finance.ethereumAddress()); const user = await dbUserRepository.insert({ status: faker.helpers.enumValue(UserStatus), @@ -366,6 +376,8 @@ describe('WalletsRepository', () => { }); it('should find a wallet by non-checksummed address', async () => { + const dbWalletRepository = dataSource.getRepository(Wallet); + const dbUserRepository = dataSource.getRepository(User); const nonChecksummedAddress = faker.finance .ethereumAddress() .toLowerCase(); @@ -404,6 +416,8 @@ describe('WalletsRepository', () => { describe('findOneByAddress', () => { it('should find a wallet by address', async () => { + const dbWalletRepository = dataSource.getRepository(Wallet); + const dbUserRepository = dataSource.getRepository(User); const address = getAddress(faker.finance.ethereumAddress()); const user = await dbUserRepository.insert({ status: faker.helpers.enumValue(UserStatus), @@ -428,6 +442,8 @@ describe('WalletsRepository', () => { }); it('should find a wallet by non-checksummed address', async () => { + const dbWalletRepository = dataSource.getRepository(Wallet); + const dbUserRepository = dataSource.getRepository(User); const nonChecksummedAddress = faker.finance .ethereumAddress() .toLowerCase(); @@ -466,6 +482,8 @@ describe('WalletsRepository', () => { describe('findByUser', () => { it('should find wallets by user', async () => { + const dbWalletRepository = dataSource.getRepository(Wallet); + const dbUserRepository = dataSource.getRepository(User); const status = faker.helpers.enumValue(UserStatus); const address1 = getAddress(faker.finance.ethereumAddress()); @@ -525,6 +543,7 @@ describe('WalletsRepository', () => { describe('create', () => { it('should create a wallet', async () => { + const dbWalletRepository = dataSource.getRepository(Wallet); const walletAddress = getAddress(faker.finance.ethereumAddress()); await postgresDatabaseService.transaction(async (entityManager) => { @@ -551,6 +570,7 @@ describe('WalletsRepository', () => { }); it('should checksum the address before saving', async () => { + const dbWalletRepository = dataSource.getRepository(Wallet); const nonChecksummedAddress = faker.finance .ethereumAddress() .toLowerCase(); @@ -579,6 +599,8 @@ describe('WalletsRepository', () => { }); it('should throw an error if wallet with the same address already exists', async () => { + const dbWalletRepository = dataSource.getRepository(Wallet); + const dbUserRepository = dataSource.getRepository(User); const walletAddress = getAddress(faker.finance.ethereumAddress()); const user = await dbUserRepository.insert({ status: faker.helpers.enumValue(UserStatus), @@ -652,6 +674,8 @@ describe('WalletsRepository', () => { describe('deleteByAddress', () => { it('should delete a wallet by address', async () => { + const dbWalletRepository = dataSource.getRepository(Wallet); + const dbUserRepository = dataSource.getRepository(User); const address = getAddress(faker.finance.ethereumAddress()); const user = await dbUserRepository.insert({ status: faker.helpers.enumValue(UserStatus), @@ -670,6 +694,8 @@ describe('WalletsRepository', () => { }); it('should delete by non-checksummed address', async () => { + const dbWalletRepository = dataSource.getRepository(Wallet); + const dbUserRepository = dataSource.getRepository(User); const nonChecksummedAddress = faker.finance .ethereumAddress() .toLowerCase(); From f592feb37cf4f47903b3d076b9fc24780ddb88a3 Mon Sep 17 00:00:00 2001 From: Aaron Cook Date: Thu, 13 Feb 2025 13:49:52 +0100 Subject: [PATCH 12/12] fix: tests --- .../users/users.repository.integration.spec.ts | 17 +++++++++++++---- .../wallets.repository.integration.spec.ts | 3 ++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/domain/users/users.repository.integration.spec.ts b/src/domain/users/users.repository.integration.spec.ts index f620ec241b..b1c39b328d 100644 --- a/src/domain/users/users.repository.integration.spec.ts +++ b/src/domain/users/users.repository.integration.spec.ts @@ -16,6 +16,7 @@ import type { ConfigService } from '@nestjs/config'; import type { ILoggingService } from '@/logging/logging.interface'; import { getStringEnumKeys } from '@/domain/common/utils/enum'; import { UserOrganization } from '@/datasources/users/entities/user-organizations.entity.db'; +import { Organization } from '@/datasources/organizations/entities/organizations.entity.db'; const mockLoggingService = { debug: jest.fn(), @@ -43,7 +44,7 @@ describe('UsersRepository', () => { database: testDatabaseName, }), migrationsTableName: testConfiguration.db.orm.migrationsTableName, - entities: [UserOrganization, User, Wallet], + entities: [UserOrganization, Organization, User, Wallet], }); beforeAll(async () => { @@ -221,7 +222,9 @@ describe('UsersRepository', () => { await expect( usersRepository.createWithWallet({ status, authPayload }), - ).rejects.toThrow(`invalid input syntax for type integer: "${status}"`); + ).rejects.toThrow( + 'null value in column "status" of relation "users" violates not-null constraint', + ); }); it('should throw if an invalid wallet address is provided', async () => { @@ -292,7 +295,9 @@ describe('UsersRepository', () => { postgresDatabaseService.transaction(async (entityManager) => { await usersRepository.create(status, entityManager); }), - ).rejects.toThrow(`invalid input syntax for type integer: "${status}"`); + ).rejects.toThrow( + 'null value in column "status" of relation "users" violates not-null constraint', + ); }); }); @@ -647,8 +652,10 @@ describe('UsersRepository', () => { await expect( usersRepository.findByWalletAddressOrFail(address), ).resolves.toEqual({ + createdAt: expect.any(Date), id: userInsertResult.identifiers[0].id, status, + updatedAt: expect.any(Date), }); }); @@ -680,8 +687,10 @@ describe('UsersRepository', () => { await expect( usersRepository.findByWalletAddressOrFail(address), ).resolves.toEqual({ + createdAt: expect.any(Date), id: userInsertResult.identifiers[0].id, status, + updatedAt: expect.any(Date), }); }); @@ -705,7 +714,7 @@ describe('UsersRepository', () => { await postgresDatabaseService.transaction(async (entityManager) => { await usersRepository.update({ userId, - user: { status: 'ACTIVE' }, + user: { id: userId, status: 'ACTIVE' }, entityManager, }); }); diff --git a/src/domain/wallets/wallets.repository.integration.spec.ts b/src/domain/wallets/wallets.repository.integration.spec.ts index 7572477c76..d1a49acaef 100644 --- a/src/domain/wallets/wallets.repository.integration.spec.ts +++ b/src/domain/wallets/wallets.repository.integration.spec.ts @@ -13,6 +13,7 @@ import { getStringEnumKeys } from '@/domain/common/utils/enum'; import type { ConfigService } from '@nestjs/config'; import type { ILoggingService } from '@/logging/logging.interface'; import { UserOrganization } from '@/datasources/users/entities/user-organizations.entity.db'; +import { Organization } from '@/datasources/organizations/entities/organizations.entity.db'; const mockLoggingService = { debug: jest.fn(), @@ -40,7 +41,7 @@ describe('WalletsRepository', () => { database: testDatabaseName, }), migrationsTableName: testConfiguration.db.orm.migrationsTableName, - entities: [UserOrganization, User, Wallet], + entities: [UserOrganization, Organization, User, Wallet], }); beforeAll(async () => {