Jest and NestJs how how to close the prisma connections - jestjs

I have an issue, where my test suites are not closing connections to prisma when I use app.close or prisma.$disconnect. This means that I am running into the error when running my test suites.
Error querying the database: db error: FATAL: sorry, too many clients already
As well as
Jest did not exit one second after the test run has completed.
This usually means that there are asynchronous operations that weren't stopped in your tests. Consider running Jest with `--detectOpenHandles` to troubleshoot this issue.
This is the blueprint to what my typical test suite looks like:
import { Test, TestingModule } from '#nestjs/testing';
import { INestApplication } from '#nestjs/common';
import { AppModule } from '../../src/app.module';
import { PrismaService } from '../../src/prisma.service';
describe('Description', () => {
let app: INestApplication;
let prismaService: PrismaService;
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
providers: [PrismaService],
}).compile();
app = moduleFixture.createNestApplication();
prismaService = moduleFixture.get(PrismaService);
await app.init();
});
afterAll(async () => {
await prismaService.$disconnect();
await app.close();
});
it('Should do something', async () => {
expect(1).toEqual(1);
});
});
My implementation of Prisma Service
import { INestApplication, Injectable, OnModuleInit } from '#nestjs/common';
import { PrismaClient } from '#prisma/client';
#Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
async onModuleInit() {
await this.$connect();
}
async enableShutdownHooks(app: INestApplication) {
this.$on('beforeExit', async () => {
await app.close();
});
}
}
I was using TypeOrm previously, and app.close() sorted out this issue, however after moving to prisma I cannot for the life of me figure out how to overcome this.
Any help will be greatly appreciated. Thanks

You can take a look at this issue which discusses connection issues in various contexts (including tests, serverless environments, etc).
One workaround (That worked for the original poster of this question) is to manually set the connection limit by appending ?connection_limit=1 to the connection string.

Updating to the latest major version of Prisma has sorted out this issue. In my case the exact version I am now using is 3.4.2.

Related

NestJS testing that interception has been called on a controller

I'm looking to see if there is a recommended way to write a JEST test that an Interceptor has been called. In the example below LoggingInterceptor was called? The purpose of test is verify that NestJS Binding interceptors is in place.
import { Controller, Get, UseInterceptors } from '#nestjs/common';
import { AppService } from './app.service';
import { LoggingInterceptor, TransformInterceptor } from './transform.interceptor';
#Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
#UseInterceptors(LoggingInterceptor)
#Get()
getHello(): string {
return this.appService.getHello();
}
}```
I would advise against testing that. Think about what you're testing: you're testing that the framework is doing what it says it will, something that the framework itself already tests. Sure you could use the Reflect API and verify that that metadata does exist, but that's something the framework should assert, it's a feature you should just be able to use with peace of mind.
One option I used to create a Jest test to verify that a binding interceptor(Which transformed the response) on a controller was called and produced the expected response was by using NestJS Supertest lib to simulate an end to end test.
Related NestJS doc:
List item
https://docs.nestjs.com/fundamentals/testing#end-to-end-testing
Test Code Sample:
import { INestApplication } from '#nestjs/common';
import { Test } from '#nestjs/testing';
import * as request from 'supertest';
import { CatsModule } from '../../src/cats/cats.module';
import { CatsService } from '../../src/cats/cats.service';
import { CoreModule } from '../../src/core/core.module';
describe('Test interceptor response binding was triggerred', () => {
const aServiceResponse = { findAll: () => ['test'] };
const expectedResponseAfterTransformation = { code: 200, data: [ 'test' ] };
let app: INestApplication;
beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
imports: [CatsModule, CoreModule],
})
.overrideProvider(CatsService)
.useValue(aServiceResponse)
.compile();
app = moduleRef.createNestApplication();
await app.init();
});
it(`/GET cats returns transformed data respsone`, () => {
return request(app.getHttpServer()).get('/cats').expect(200).expect({
data: expectedResponseAfterTransformation,
});
});
afterAll(async () => {
await app.close();
});
});

NestJS Jest error: TypeError: Cannot read properties of undefined (reading '[any variable from required config]')

While trying to cover out project in unit tests using nest's jest I've bumped into a problem of a testing module not being able to pull variables from config.
Basically, I have an EmailService, I want to test it, I use it as a Provider in my testing module. Naturally, as EmailService takes ConfigService in its constructor to pull some variables from config (that initially come from env) I put ConfigService into the providers array as well... well, then upon initialization testing module drops
NestJS Jest error: TypeError: Cannot read properties of undefined (reading 'region')
note: region variable is taken from env in a registered config module
code example of my test that throws
describe('EmailService', () => {
let emailService: EmailService;
let configService: ConfigService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [EmailService, ConfigService],
}).compile();
emailService = module.get<EmailService>(EmailService);
configService = module.get<ConfigService>(ConfigService);
});
it('should be defined', () => {
expect(emailService).toBeDefined();
});
});
I have came to the conclusion that it throws an error specifically because EmailService takes ConfigService in it's constructor in this way:
export class EmailService {
private readonly config: IAwsConfig;
private readonly region: IRegion;
constructor(private readonly configService: ConfigService) {
this.config = this.configService.get('aws');
this.region = this.config.region;
}
aditional info: both EmailService and ConfigService work just fine during a normal runtime, it only fails during jest testing
seems like this.configService.get method returns 'undefined' during a test run and i'm, not sure why or how to fix it. Any ideas?
In case you don't want to import the entire ConfigService but just the config values themselves, then you use them in the test as follows :)
// my-config.ts
import { registerAs } from '#nestjs/config';
export default registerAs('myConfig', () => ({ propA: 'aa', propB: 123 }));
import { Inject } from '#nestjs/common';
import { ConfigType } from '#nestjs/config';
import myConfig from './my-config.ts';
export class EmailService {
private propA: string;
private propB: number;
constructor(
#Inject(myConfig.KEY) config: ConfigType<typeof myConfig>
) {
this.propA = config.propA;
this.propB = config.propB;
}
}
import { ConfigModule, registerAs } from '#nestjs/config';
import { Test, TestingModule } from '#nestjs/testing';
describe('Test', () => {
const configValues = { propA: 'aa', proprB: 123 };
const config = registerAs('testConfig', () => configValues);
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
imports: [ConfigModule.forFeature(config)],
providers: [EmailService],
}).compile();
});
});
Was not able to find an answer for 2 hours straight, but then, 10 minutes after asking a question, there you go, an answer.
Seems like ConfigService doesn't provide configs during jest testing so you have to provide it in the testing module with replaced get method, something like such:
providers: [
EmailService,
{
provide: ConfigService,
useValue: {
get: jest.fn((key: string) => {
return hardcodedConfigFromWithinTheTestFile;
}),
},
},
],

Mock nestjs decorator

I am using a custom Firewall decorator that provides some utility functionality for many of my endpoints (e.g. throttling, authorization etc.) and I want be able to mock this decorator in my endpoints:
#Controller('some')
export class SomeController {
#Firewall() // mock it and check that it's called with correct arguments
async testEndpoint() {
return 'test';
}
}
I want to mock it and check that it's called with the correct parameters, but I can't figure out how I can do this in my test cases:
import * as request from 'supertest';
import { Test } from '#nestjs/testing';
import { INestApplication } from '#nestjs/common';
import { AppModule } from 'src/app.module';
describe('Some Controller', () => {
let app: INestApplication;
beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleRef.createNestApplication();
await app.init();
});
it('some testcase', () => {
// What do I do here to mock my Firewall decorator? // <--- <--- <---
return request(app.getHttpServer()).get('/some/testEndpoint').expect(401);
});
afterAll(async () => {
await app.close();
});
});
If it can help, here is a short version of the Firewall decorator:
import { applyDecorators } from '#nestjs/common';
import { Throttle, SkipThrottle } from '#nestjs/throttler';
export function Firewall(options?: { skipThrottle?: boolean }) {
const { skipThrottle } = options || {
anonymous: false,
};
const decorators = [];
if (skipThrottle) {
decorators.push(SkipThrottle());
} else {
decorators.push(Throttle(10, 10));
}
return applyDecorators(...decorators);
}
I have checked other answers (including this one) but they didn't help.
Thanks in advance for your time!
The #Throttle() and #SkipThrottle() decorators only apply metadata to the controller / controller method they decorate. They don't do anything on their own. Your custom #Firewall() is a utility decorator to combine these into a single decorator for convenience.
If you take a look at the source code of the nestjs/throttler package you'll see it is the #ThrottlerGuard() guard that retrieves this metadata and actually does the throttling.
I suspect you configured this one as a global app guard, so it is applied for all requests.
#Module({
imports: [
ThrottlerModule.forRoot({...}),
],
providers: [
{
provide: APP_GUARD,
useClass: ThrottlerGuard,
},
],
})
export class AppModule {}
In your test you need to mock the ThrottlerGuard.
const ThrottlerGuardMock = {
canActivate(ctx) {
const request = ctx.switchToHttp().getRequest();
// mock implementation goes here
// ...
return true;
}
} as ThrottlerGuard;
const module = await Test.createTestModule({
imports: [AppModule]
})
.overrideProvider(ThrottlerGuard)
.useValue(ThrottlerGuardMock) // <-- magic happens here
.compile();
app = moduleRef.createNestApplication();
await app.init();
You could setup some spies, in the mocked guard retrieve the metadata set by the decorators applied by the #Firewall() decorator and then invoke the spies with that metadata. Then you could just verify if the spies were called with the expected values. That would verify that your custom decorator passed down the expected values to the throttle decorators. Actually testing the #ThrottleGuard() decorator is the nestjs/throttler package's responsibility.

Prisma is opening too many connections with PostgrsQL when running Jest end to end testing

I'm trying here to do end to end testing with Jest on a NestJS/GraphQL app and Prisma as my ORM.
What happens here is Prisma is opening too many connections with Postgres, I've tried to fix this problem by using prisma.$disconnect() after each test but it doesn't seem to work...
This is what I've tried so far.
import { PrismaService } from '../src/prisma.service';
describe('Users', () => {
let app: INestApplication;
const gql = '/graphql';
let prisma;
beforeEach(async () => {
prisma = new PrismaService();
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
afterEach(async () => {
await prisma.$disconnect();
});
it('Query - Users', async () => {
//Using prisma to query database
});
});
My Prisma service (I got it from NestJS documentation Text ):
import { INestApplication, Injectable, OnModuleInit } from '#nestjs/common';
import { PrismaClient } from '#prisma/client';
#Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
async onModuleInit() {
await this.$connect();
}
async enableShutdownHooks(app: INestApplication) {
this.$on('beforeExit', async () => {
await app.close();
});
}
}
These are the errors I'm getting after running all the tests (serially):
I've already looked through Prisma documentation but didn't find anything about how to setup properly a Jest environment with NestJS and Prisma.
Thanks for your help !
Explicitly instructing Prisma to open only one connection with your database should solve this issue.
You can do it by appending connection_limit=1 to your connection string.
Demo Connection String:
postgresql://USER:PASSWORD#HOST:PORT/DATABASE?schema=public&connection_limit=1
Reference to connection management in prisma should be helpful as well.

How to mock getMongoRepository in service nestjs

I write unit test for my service in nestjs. In my function delete i use getMongoRepository to delete. But i stuck in write the unit test
I've tried write the mock but it's not work
my service
async delete(systemId: string): Promise<DeleteWriteOpResultObject> {
const systemRepository = getMongoRepository(Systems);
return await systemRepository.deleteOne({ systemId });
}
my mock
import { Mock } from './mock.type';
import { Repository, getMongoRepository } from 'typeorm';
// #ts-ignore
export const mockRepositoryFactory: () => Mock<Repository<any>> = jest.fn(
() => ({
save: jest.fn(Systems => Systems),
delete: jest.fn(Systems => Systems),
deleteOne: jest.fn(Systems => Systems),
}),
);
my test
import { ExternalSystemService } from '../external-system.service';
import { Systems } from '../entities/external-system.entity';
module = await Test.createTestingModule({
providers: [
ExternalSystemService,
{
provide: getRepositoryToken(Systems),
useFactory: mockRepositoryFactory,
},
],
}).compile();
service = module.get<ExternalSystemService>(ExternalSystemService);
mockRepository = module.get(getRepositoryToken(Systems));
describe('delete', () => {
it('should delete the system', async () => {
mockRepository.delete.mockReturnValue(undefined);
const deletedSystem = await service.delete(systemOne.systemId);
expect(mockRepository.delete).toBeCalledWith({ systemId: systemOne.systemId });
expect(deletedSystem).toBe(Object);
});
I got this error
ExternalSystemService › delete › should not delete the system
ConnectionNotFoundError: Connection "default" was not found.
at new ConnectionNotFoundError (error/ConnectionNotFoundError.ts:8:9)
at ConnectionManager.Object.<anonymous>.ConnectionManager.get (connection/ConnectionManager.ts:40:19)
at Object.getMongoRepository (index.ts:300:35)
at Object.<anonymous> (external-system/tests/external-system.service.spec.ts:176:33)
at external-system/tests/external-system.service.spec.ts:7:71
at Object.<anonymous>.__awaiter (external-system/tests/external-system.service.spec.ts:3:12)
at Object.<anonymous> (external-system/tests/external-system.service.spec.ts:175:51)
You should avoid using global functions and instead use the dependency injection system; this makes testing much easier and is one of the main features of nest.
The nest typeorm module already provides a convenient way of injecting a repository:
1) Inject the repository in your service's constructor:
constructor(
#InjectRepository(Systems)
private readonly systemsRepository: MongoRepository<Systems>,
) {}
2) Use the injected repository
async delete(systemId: string): Promise<DeleteWriteOpResultObject> {
return this.systemsRepository.deleteOne({ systemId });
}
Now your mocked repository will be used in your test.

Resources