How to establish database connection within a Nest.js e2e test - node.js

I'm having an issue connecting to CosmosDB within a Nest.js e2e test.
I've been following the Nest.js documentation for creating e2e tests but it doesn't contain any documentation on connecting to a test database. Some of the articles I found (both on SO, and numerous other websites) stated that I should just initialize the database as I would within a regular module, but inside of the testing module, but it didn't work for me.
Module for CosmosDB that I'm using is #nestjs/azure-database.
I tried to initialize the AzureCosmosDbModule through the imports of the testing module as mentioned above but without any success.
Below is a snippet of what I tried. (I changed the actual name of the controller, module, etc. to a random "cats" one)
Environment variable loading is set up correctly, as it works outside of the tests.
describe('CatsController (e2e)', () => {
let app: INestApplication;
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [
ConfigModule.forRoot({
validationSchema: schema,
isGlobal: true,
}),
AzureCosmosDbModule.forRootAsync({
imports: [ConfigModule],
useFactory: async (cfg: ConfigService) => ({
endpoint: cfg.get<string>('AZURE_COSMOS_DB_ENDPOINT'),
dbName: cfg.get<string>('AZURE_COSMOS_DB_TEST_NAME'),
key: cfg.get<string>('AZURE_COSMOS_DB_KEY'),
}),
inject: [ConfigService],
}),
CatsModule,
],
}).compile();
app = moduleFixture.createNestApplication();
app.setGlobalPrefix('api');
await app.init();
});
afterAll(async () => {
await app.close();
});
});
Error message I get after running e2e test.
[Nest] 2355 - 08/25/2022, 11:27:33 AM ERROR [AzureCosmosDbModule] Unable to connect to the cosmos db database. Retrying (1)...
[Nest] 2355 - 08/25/2022, 11:27:36 AM ERROR [AzureCosmosDbModule] Unable to connect to the cosmos db database. Retrying (2)...

Related

NestJS - Reflect.getMetadata TypeError while running e2e tests

The error:
[Nest] 163363 - 10/03/2022, 11:59:37 AM LOG [NestFactory] Starting Nest application...
[Nest] 163363 - 10/03/2022, 11:59:37 AM ERROR [ExceptionHandler]
TypeError
at Reflect.getMetadata (/home/mszan/foo/source/node_modules/reflect-metadata/Reflect.js:354:23)
at DependenciesScanner.isInjectable (/home/mszan/foo/source/node_modules/#nestjs/core/scanner.js:302:26)
at DependenciesScanner.insertModule (/home/mszan/foo/source/node_modules/#nestjs/core/scanner.js:73:18)
at DependenciesScanner.scanForModules (/home/mszan/foo/source/node_modules/#nestjs/core/scanner.js:34:43)
at DependenciesScanner.scan (/home/mszan/foo/source/node_modules/#nestjs/core/scanner.js:27:20)
at async /home/mszan/foo/source/node_modules/#nestjs/core/nest-factory.js:95:17
at async Function.asyncRun (/home/mszan/foo/source/node_modules/#nestjs/core/errors/exceptions-zone.js:22:13)
at async NestFactoryStatic.initialize (/home/mszan/foo/source/node_modules/#nestjs/core/nest-factory.js:94:13)
at async NestFactoryStatic.create (/home/mszan/foo/source/node_modules/#nestjs/core/nest-factory.js:37:9)
● process.exit called with "1"
at DEFAULT_TEARDOWN (../../node_modules/#nestjs/core/errors/exceptions-zone.js:6:40)
at Function.asyncRun (../../node_modules/#nestjs/core/errors/exceptions-zone.js:29:13)
at async NestFactoryStatic.initialize (../../node_modules/#nestjs/core/nest-factory.js:94:13)
at async NestFactoryStatic.create (../../node_modules/#nestjs/core/nest-factory.js:37:9)
The app.e2e-spec.ts:
describe('app - e2e', () => {
let app: INestApplication;
beforeAll(async () => {
const moduleRef: TestingModule = await Test.createTestingModule({
...appModuleMetadata,
imports: appModuleMetadata.imports.map((x) => {
if (x == CommonModule) {
return CommonModuleMock;
}
return x;
}),
}).compile();
app = moduleRef.createNestApplication();
await app.init();
});
});
The CommonModule contains a provider that imports fs and path packages. When I remove these imports, the tests work.
As you can see, I tried replacing CommonModule with CommonModuleMock that does not contain mentioned imports but the tests still fail.
Nest version is 9.0.11. Jest is 29.0.3. Supertest is 6.2.4. Node is 16.17.0.
What are my options?

Nest could not find Sequelize element when calling `app.close()`

We have a web server running Nest 8.0.8. With the given module setup:
#Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
cache: true,
validate,
load: [configuration]
}),
SequelizeModule.forRootAsync({
imports: [ConfigModule],
useFactory: (config: ConfigService) => config.get('db1'),
inject: [ConfigService]
}),
SequelizeModule.forRootAsync({
name: 'db2',
imports: [ConfigModule],
useFactory: (config: ConfigService) => config.get('db2'),
inject: [ConfigService],
}),
controllers: [...],
providers: [...]
})
To start the application, we have this main.ts file:
async function bootstrap() {
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter(...)
);
useContainer(app.select(AppModule), { fallbackOnErrors: true });
app.useGlobalPipes(
new ValidationPipe({
exceptionFactory: (errors) => new ValidationException(errors),
transform: true,
whitelist: true,
validationError: {
target: false,
value: false
}
})
);
app.setGlobalPrefix('v1');
app.useWebSocketAdapter(new RedisIoAdapter(app));
app.enableCors({ origin: '*' });
/**
* When SIGTERM, run functions before shutting down
*/
app.enableShutdownHooks();
if (process.env.SETUP) {
const service = app.get(FooService);
await service.setup();
await app.close();
return;
}
await app.listen(3000, '0.0.0.0');
}
bootstrap().then();
When running out unit tests, we use Nest to automatically configure the database. Initially after initializing the server with the command SETUP=true nest start that will force Nest to not listen the port and early return after running service.setup(). (note that this problem also happens when calling the app.listen(...) and app.close() functions)
The problem happens at the call app.close() we expect Nest to close all connections, including the two SequelizeModule of the 2 databases connected.
Unfortunaly Nest closes the first connection and tries to close the second, but the module (we suspect) is already closed. Giving the error:
This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason:
Error: Nest could not find Sequelize element (this provider does not exist in the current context)
at InstanceLinksHost.get (/Users/me/Repository/project_nest/node_modules/#nestjs/core/injector/instance-links-host.js:21:19)
at Object.find (/Users/me/Repository/project_nest/node_modules/#nestjs/core/injector/module-ref.js:38:55)
at Object.get (/Users/me/Repository/project_nest/node_modules/#nestjs/core/injector/module.js:345:28)
at SequelizeCoreModule.<anonymous> (/Users/me/Repository/project_nest/node_modules/#nestjs/sequelize/dist/sequelize-core.module.js:81:47)
at Generator.next (<anonymous>)
at /Users/me/Repository/project_nest/node_modules/#nestjs/sequelize/dist/sequelize-core.module.js:20:71
at new Promise (<anonymous>)
at __awaiter (/Users/me/Repository/project_nest/node_modules/#nestjs/sequelize/dist/sequelize-core.module.js:16:12)
at SequelizeCoreModule.onApplicationShutdown (/Users/me/Repository/project_nest/node_modules/#nestjs/sequelize/dist/sequelize-core.module.js:80:16)
at Object.callAppShutdownHook (/Users/me/Repository/project_nest/node_modules/#nestjs/core/hooks/on-app-shutdown.hook.js:51:35)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at NestApplication.callShutdownHook (/Users/me/Repository/project_nest/node_modules/#nestjs/core/nest-application-context.js:199:13)
at NestApplication.close (/Users/me/Repository/project_nest/node_modules/#nestjs/core/nest-application-context.js:83:9)
at bootstrap (/Users/me/Repository/project_nest/src/main.ts:143:5)
We initially thing that is a problem on Nest but we are not sure. This used to work in the past but somehow after a update the issue started happening and we cannot downgrade the library due some constraints with other dependencies.
I would like to know what can be done to either debug or sort the issue, we tried to debug the application but nothing points out to issues in our codebase. Could you please help?
Regards, Pedro
you need to give name for you second configuration SequelizeModule.
Example for your situation
SequelizeModule.forRootAsync({
name: 'db2',
imports: [ConfigModule],
useFactory: (config: ConfigService) => {
name: 'db2',
...config.get('db2')
},
inject: [ConfigService],
}),
The reason of this problem is getConnectionToken function which uses for getting connection by name from options. And if you not use specific name, SequelizeModule will use one connection between two modules and will close them both

NestJs with Fastify doesn't execute code after app.listen()

this is my first question here, so i'd like to apologize in advance if i miss something important.
So this is pretty basic, but i didn't find an answer anywhere, as the title states, my NestJs application running with Fastify, simply does not execute any code after the app.listen('port') line, this doesn't happen with Express. OBS: I'm referring to the main.ts file.
relevant code below:
async function bootstrap() {
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter({ logger: { level: 'warn' } }),
);
const configService = app.get(ConfigService);
await app.listen(configService.get<number>(ENV_KEYS.PORT), '0.0.0.0');
console.log(
`Application is running on: ${await app.getUrl()}\nApplication Version: ${version}`,
);
}
bootstrap();
The console.log() after await app.listen never executes, even if the app is working normally, and as far as my tests show, no code after await app.listen() ever executes.
I'd like to know how to overcome this issue, because i need to run some code after the app is already bootstrapped.
So thanks to #Micael Levi pointing me to an issue on github (github.com/nestjs/nest/issues/7572) i started to look into problems within my controllers, and the reason to freeze the application on app.listen() was because of "an double instance" of my AuthController, let me explain
I had my AuthController defined on AuthModule:
// auth.module.ts
#Module({
imports: [
PassportModule,
JwtModule.register({
secret: JWT_CONSTANTS.SECRET,
}),
],
controllers: [AuthController],
providers: [AuthService, LocalStrategy, JwtStrategy],
exports: [AuthService, LocalStrategy, JwtStrategy],
})
And in the AppModule, i was importing AuthModule while also declaring AuthController AGAIN:
// app.module.ts
#Module({
imports: [
ConfigModule.forRoot(getConfigServiceConfiguration()),
TypeOrmModule.forRootAsync({
useFactory: async (configService: ConfigService) =>
getDatabaseModuleOptionsAsync(configService),
inject: [ConfigService],
}),
AuthModule, // <-AuthController is declared within AuthModule scope
UsuarioModule,
ClienteModule,
],
controllers: [AppController, AuthController] // <- AuthController here again,
providers: [AppService],
})
Removing AuthController from controllers:[] in AppModule solved my problem. Rookie mistake, but for some reason this isn't a problem with express, and doesn't raise any compilation errors with Fastify either!

NestJS: Unit Test Joi Validation

I'am new using Node and Nest framework, and I have a problem when testing modules that import a Custom Config Module that uses Joi to validate env vars:
yarn test
Test suite failed to run
Config validation error: "APP_PORT" is required
app-config.module.ts
#Module({
imports: [
ConfigModule.forRoot({
expandVariables: true,
load: [configuration],
validationSchema: Joi.object({
APP_PORT: Joi.number().required()
}),
}),
],
providers: [AppConfigService],
exports: [AppConfigService],
})
export class AppConfigModule { }
app.config.ts
import { registerAs } from '#nestjs/config';
export default registerAs('app', () => ({
env: process.env.NODE_ENV,
port: process.env.APP_PORT || 3000
...
}));
invoice.service.spec.ts
describe('InvoiceService', () => {
let service: InvoiceService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
ConfigModule.forRoot({
load: [configuration],
ignoreEnvFile: true,
}),
AppConfigModule
],
providers: [
InvoiceService,
....
],
}).compile();
service = module.get<InvoiceService>(InvoiceService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
I use env file to deploy locally, and I only set up ignoreEnvFile: true in test class because env file is ignore from github repo, and project integrate github actions that run unit test.
How is the best way to solved this problem? I would not like add env file to repo.
Exist any way to disable/fake/mock Joi validation method. I saw some examples using setupFiles but I'm not sure if it's a good practice.
May you please show the package.json file?
you must have installed npm i --save #nestjs/config then create .env file inside root directory. Moreover you should import that into you app module.
import { Module } from '#nestjs/common';
import { ConfigModule } from '#nestjs/config';
#Module({
imports: [
ConfigModule.forRoot({
envFilePath: ".development.env",
}),
],
})
export class AppModule {}
Ref: https://docs.nestjs.com/techniques/configuration

NestJS, using typeorm on providers

I want to make a provider which queries an entity from db. (what interests me actually is to make typeorm available when I create a provider).
I get
[ExceptionHandler] Connection "default" was not found. - {"trace":"ConnectionNotFoundError: Connection \"default\" was not found.\n
I tried using service, using custom repository, nothing works. I have something like this in module:
{
provide: MICROSERVICE_ID,
useFactory: async (): Promise<Microservice> => {
//ignore logic
return await getRepository(Microservice).findOne(1);
},
inject: []
}
TypeOrm is imported on app.module.ts
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (configService: ConfigService) => (configService.get('database'))
}),
Any help is appreciated.
Check your database file. The Entity may not load as expected.
For newest NestJS version. We should load from ./dist/
Fixed by injecting Connection from typeorm.
import { getRepository, Connection } from 'typeorm';
{
provide: MICROSERVICE_ID,
useFactory: async (): Promise<Microservice> => {
//ignore logic
return await getRepository(Microservice).findOne(1);
},
inject: [Connection]
}

Resources