Unit test a nestJS interceptor - unit-testing

My interceptor intercepts a GET request and adds a header to the response based on some conditions.
My Controller
#UseInterceptors(ServiceHeaderInterceptor)
#Get('users')
public async users(
#Query('id') Id: number,
#Query('name') Name: string,
){
const setFlags: MetaFlags = {setCacheHeader: false, setServiceHeader: false};
const data = await this.service.getUsers(id, name, setFlags);
return data;
}
My interceptor looks like this
#Injectable()
export class HeaderInterceptor implements NestInterceptor {
public intercept(
context: ExecutionContext,
next: CallHandler,
): Observable<any> {
return next.handle().pipe(
map((data: { data: DataType, header: 'cache' | 'database' }) => {
const req = context.switchToHttp().getResponse();
if (data.header === 'database') {
res.setHeader('x-api-key', 'pretty secure');
} else {
res.setHeader(xyz, value);
}
return data.data;
}),
);
}
}
This is my test class
const users = [
{
'id': 1,
'name': 'Adam',
},
{
'id': 2,
'name': 'Evan',
},
]
const executionContext: any = {
switchToHttp: jest.fn().mockReturnThis(),
getResponse: jest.fn().mockReturnThis(),
setHeader: jest.fn().mockReturnThis(),
};
const next = {
handle: () => of(users),
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
{
provide: UserService,
useValue: mock<UserService>(),
},
ServiceHeaderInterceptor,
],
}).compile();
interceptor = module.get(ServiceHeaderInterceptor);
});
it('get users',async () => {
interceptor.intercept(executionContext, next).subscribe({
next: (value) => {
expect(value).toEqual(users);
},
error: (error) => {
throw error;
},
});
});
});
Is there a way to check what my header values are at this point?
I am able to access only the response body and not the headers.
I need to verify if the header I set in the interceptor is available and has the correct value

Related

Mock GRPC Clients in nestjs using jest

import { Injectable, Logger, OnModuleInit } from '#nestjs/common';
import { Client, ClientGrpc } from '#nestjs/microservices';
import { catchError, map } from 'rxjs';
import { ICmdUserUsecase } from 'src/adaptor/usecase/ICmdUserUsecase';
import { dataServiceGrpcOptions } from 'src/app/data.options';
import { ICmdRepo } from 'src/adaptor/repo/ICmdRepo';
#Injectable()
export class CmdUserUsecase implements ICmdUserUsecase, OnModuleInit {
private dataCmdRepo: ICmdRepo;
constructor(private readonly logger: Logger) {}
#Client({
...dataServiceGrpcOptions,
})
dataServiceClientGrpc: ClientGrpc;
onModuleInit() {
this.dataCmdRepo = this.dataServiceClientGrpc.getService<ICmdRepo>('DataService');
}
async createUserProfile(data: any) {
return new Promise((resolve, reject) => {
const userData = this.dataCmdRepo.createUser(data).pipe(
catchError((err) => {
if (err.code === 2) {
err.message = 'Data Server is down';
throw new Error(err.message);
} else {
throw new Error(err.message);
}
}),
map((results) => {
return results;
}),
);
userData.subscribe(
(rate) => {
resolve(rate);
},
(error) => {
reject(error);
},
);
});
}
async createUser(data: any): Promise<any> {
return await this.createUserProfile(data)
.then((res) => {
this.logger.log(res);
return res;
})
.catch((err) => {
return err;
});
}
}
here is the client service code which is utilizing the grpc services, every time I am getting connection not found from my spec file,
how can I mock this foresaid grpc service in spec files.
import { INestApplication, Logger } from '#nestjs/common';
import { ConfigModule } from '#nestjs/config';
import { ClientsModule } from '#nestjs/microservices';
import { Test, TestingModule } from '#nestjs/testing';
import { ICmdUserUsecase } from 'src/adaptor/usecase/ICmdUserUsecase';
import { IQueryUserUsecase } from 'src/adaptor/usecase/IQueryUserUsecase';
import { userServiceGrpcOptions } from 'src/app/data.options';
import { configuration } from 'src/utils/configuration';
import { Mock } from 'src/utils/Mocks/mock';
import { MetricsTelemetryModule } from 'telemetry-lib';
import { QueryUserUsecase } from '../query-user-usecase/query.user.usecase';
import { CmdUserUsecase } from './cmd.user.usecase';
describe('CMD user usecase ', () => {
let app: INestApplication;
let cmdUserUsecase: CmdUserUsecase;
beforeEach(async () => {
const moduleRef: TestingModule = await Test.createTestingModule({
imports: [
ConfigModule.forRoot({
load: [configuration],
isGlobal: true,
cache: true,
}),
MetricsTelemetryModule.forRoot({
apiMetrics: true,
}),
ClientsModule.register([
{
name: 'USER_SERVICE',
...userServiceGrpcOptions,
},
]),
],
providers: [
CmdUserUsecase,
{
provide: ICmdUserUsecase,
useClass: CmdUserUsecase,
},
{
provide: IQueryUserUsecase,
useClass: QueryUserUsecase,
},
{
provide: Logger,
useValue: {
log: jest.fn((data) => data),
},
},
],
exports: [],
}).compile();
app = moduleRef.createNestApplication();
await app.init();
cmdUserUsecase = moduleRef.get<CmdUserUsecase>(CmdUserUsecase);
});
afterAll(async () => {
await app.close();
});
describe('root', () => {
it('CmdUserUsecase should be defined', () => {
expect(cmdUserUsecase).toBeDefined();
});
it('CmdUserUsecase"s createUser should be called when invoked', async () => {
try {
await cmdUserUsecase.createUser(Mock.userMock);
expect(cmdUserUsecase.createUser).toHaveBeenCalled();
} catch (error) {
expect(error).toEqual(error);
}
});
it('CmdUserUsecase"s createUser should RETURN suceess message', async () => {
try {
const result = await cmdUserUsecase.createUser(Mock.userMock);
expect(result.status).toBe('success');
} catch (error) {
expect(error).toEqual(error);
}
});
it('CmdUserUsecase"s createUserProfile should be called when invoked', async () => {
try {
await cmdUserUsecase.createUserProfile(Mock.userMock);
expect(cmdUserUsecase.createUserProfile).toHaveBeenCalled();
} catch (error) {
expect(error).toEqual(error);
}
});
it('CmdUserUsecase"s createUserProfile should RETURN suceess message', async () => {
try {
const result = await cmdUserUsecase.createUserProfile(Mock.userMock);
expect(result).toBe('success');
} catch (error) {
expect(error).toEqual(error);
}
});
});
});
How can I modify the above spec file for so that it can contain the mock of the grpc clients as mentioned in the above code.
So after reading a lot of articles I found 2 ways to mock GRPC and use it in the client calls.
Use GRPC-MOCK npm package,
Create a Grpc mock server for the given spec file and add the services at run time. Like
import * as grpc from '#grpc/grpc-js';
import * as protoLoader from '#grpc/proto-loader';
import { INestApplication, Logger } from '#nestjs/common';
import { ConfigModule } from '#nestjs/config';
import { Test, TestingModule } from '#nestjs/testing';
import { IHandlebarJs } from 'src/adaptor/handlebarjs/IHandlebarJs';
import { IMatrixQueryRepo } from 'src/adaptor/repo/IMatrixQueryRepo';
import { Handlebars } from 'src/helper/HandlebarsJs';
import { MatrixQueryRepo } from 'src/infrastructure/repository/MatrixQueryRepo';
import { configuration } from 'src/utils/configuration';
import { Mock } from 'src/utils/mock';
import { QueryMatrixUsecase } from './matrix.service';
const packageDef = protoLoader.loadSync('src/app/proto/as.proto', {});
const grpcObject = grpc.loadPackageDefinition(packageDef);
const asdata: any = grpcObject.asdata;
const server = new grpc.Server();
server.bindAsync(process.env.AS_DATA_SERVICE_URL, grpc.ServerCredentials.createInsecure(), (e, r) => {
console.log(e, r);
server.start();
});
server.addService(asdata.DataService.service, {
SaveAddressGeocode: (call, callback) => {
console.log(call.request);
callback(null, Mock.dsMockResponse);
},
GetAddressGeocode: (call, callback) => {
console.log(call.request);
callback(null, Mock.dsMockResponse);
},
SaveMatrixDistanceAndETA: (call, callback) => {
console.log(call.request);
callback(null, Mock.dsMockResponse);
},
GetMatrixDistanceAndETA: (call, callback) => {
console.log(call.request);
callback(null, Mock.dsMockResponse);
},
});
describe('QueryMatrixUsecase', () => {
let service: QueryMatrixUsecase;
let app: INestApplication;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
ConfigModule.forRoot({
load: [configuration],
isGlobal: true,
cache: true,
}),
],
providers: [
QueryMatrixUsecase,
Logger,
{
provide: IMatrixQueryRepo,
useClass: MatrixQueryRepo,
},
{
provide: IHandlebarJs,
useClass: Handlebars,
},
],
}).compile();
app = module.createNestApplication();
await app.init();
service = module.get<QueryMatrixUsecase>(QueryMatrixUsecase);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
it('QueryMatrixUsecase GetRouteMatrix should return a object with status as success', async () => {
const res = await service.GetRouteMatrix(Mock.routeDTOMock);
expect(res.success).toBe('true');
});
});

unit test with mocking custom repository of typeorm

When I tried with basic repository provided typeorm, I think test is completed.
But to do 'unit test' with custom repository of typeorm is not working.
I think mocking custom repository has problem.
What I have to do for mocking custom repostitory?
Next are test file and source file to test.
Thanks.
quests.service.spec.ts
import { Test } from '#nestjs/testing';
import { getRepositoryToken } from '#nestjs/typeorm';
import { QuestsService } from 'src/quests/quests.service';
import { QuestRepository } from 'src/quests/repositories/quest.repository';
import { Repository } from 'typeorm';
import { Complete } from 'src/quests/entities/complete.entity';
import { Player } from 'src/players/entities/player.entity';
const mockRepository = () => ({
create: jest.fn(),
save: jest.fn(),
findOne: jest.fn(),
});
const mockQuestsRepository = {
save: jest.fn(),
findOne: jest.fn(),
findAllWithCompletes: jest.fn(),
findOneWithCompletes: jest.fn(),
};
type MockRepository<T = any> = Partial<Record<keyof Repository<T>, jest.Mock>>;
type MockQuestRepository = Partial<Record<keyof QuestRepository, jest.Mock>>;
describe('QuestsService', () => {
let service: QuestsService;
let playersRepository: MockRepository<Player>;
let completeRepository: MockRepository<Complete>;
let questsRepository: MockQuestRepository;
beforeAll(async () => {
const module = await Test.createTestingModule({
providers: [
QuestsService,
{
provide: getRepositoryToken(Player),
useValue: mockRepository(),
},
{
provide: getRepositoryToken(Complete),
useValue: mockRepository(),
},
{
provide: QuestRepository,
useValue: mockQuestsRepository,
},
],
}).compile();
service = module.get<QuestsService>(QuestsService);
playersRepository = module.get(getRepositoryToken(Player));
completeRepository = module.get(getRepositoryToken(Complete));
questsRepository = module.get(QuestRepository);
});
describe('questComplete', () => {
it('should fail if quest does not exist', async () => {
questsRepository.findOne.mockResolvedValue(undefined);
const result = await service.questComplete(-1, 1);
expect(result).toEqual({
ok: false,
message: 'cant find requested Quest.',
});
});
it('should fail if player does not exist', async () => {
questsRepository.findOne.mockResolvedValue(true);
playersRepository.findOne.mockResolvedValue(undefined);
const result = await service.questComplete(1, 1);
expect(result).toEqual({
ok: false,
message: 'cant find player.',
});
});
});
});
quests.service.ts
#Injectable()
export class QuestsService {
constructor(
#InjectRepository(Complete)
private readonly completes: Repository<Complete>,
private readonly quests: QuestRepository
) {}
async questComplete(questId: number, playerId: number) {
try {
const quest = await this.quests.findOne({ id: questId });
if (!quest)
return { ok: false, message: 'cant find requested Quest.' };
const player = await Player.findOne({ where: { id: playerId } });
if (!player)
return { ok: false, message: 'cant find player.' };
const isCompleted = await this.completes.findOne({ quest, player });
if (isCompleted)
return { ok: false, message: 'quest is already completed.' };
await this.completes.save(this.completes.create({ quest, player }));
return { ok: true };
} catch (error) {
return { ok: false, message: 'quest cant be completed.' };
}
}
}

How to implement auth guard for graphql subscriptions (passportjs + cookies)

How I can pass user to the request?
Is there any possible way to implement something like SubscriptionAuthGuard?
without the subscription, everything works fine
Code:
GraphQLModule.forRoot({
installSubscriptionHandlers: true,
subscriptions: {
'subscriptions-transport-ws': {
onConnect: (connectionParams, webSocket) =>
new Promise((resolve) => {
passportInit(webSocket.upgradeReq, {} as any, () => {
resolve(webSocket.upgradeReq);
});
}),
},
},
context: ({ req }) => ({ req }),
}),
Error:
TypeError: Cannot set property 'authInfo' of undefined
This worked for me, I'm using JWT and bearer tokens.
GraphQL.module:
'subscriptions-transport-ws': {
path: '/graphql',
onConnect: (connectionParams) => {
return {
req: {
headers: { authorization: connectionParams.Authorization },
},
};
},
},
Guard:
#Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
async canActivate(context: ExecutionContext): Promise<boolean> {
try {
return (await super.canActivate(context)) as boolean;
} catch (e) {
throw new AuthenticationError(generalErrorMessages.invalidToken);
}
}
getRequest(context: ExecutionContext): Request {
const ctx = GqlExecutionContext.create(context);
return ctx.getContext().req;
}
}

Jest & AWS.DynamoDB.DocumentClient mocking

I'm trying to mock a call to AWS.DynamoDB.DocumentClient. I tried several solutions I found online, but I cannot get it to work.
This is my best effort so far:
import * as AWS from 'aws-sdk';
import * as dynamoDbUtils from '../../src/utils/dynamo-db.utils';
jest.mock("aws-sdk");
describe('dynamo-db.utils', () => {
describe('updateEntity', () => {
it('Should return', async () => {
AWS.DynamoDB.DocumentClient.prototype.update.mockImplementation((_, cb) => {
cb(null, user);
});
await dynamoDbUtils.updateEntity('tableName', 'id', 2000);
});
});
});
I get error message
Property 'mockImplementation' does not exist on type '(params: UpdateItemInput, callback?: (err: AWSError, data: UpdateItemOutput) => void) => Request<UpdateItemOutput, AWSError>'.ts(2339)
My source file:
import AWS from 'aws-sdk';
let db: AWS.DynamoDB.DocumentClient;
export function init() {
db = new AWS.DynamoDB.DocumentClient({
region: ('region')
});
}
export async function updateEntity(tableName: string, id: string, totalNumberOfCharacters: number): Promise<AWS.DynamoDB.UpdateItemOutput> {
try {
const params = {
TableName: tableName,
Key: { 'id': id },
UpdateExpression: 'set totalNumberOfCharacters = :totalNumberOfCharacters',
ExpressionAttributeValues: {
':totalNumberOfCharacters': totalNumberOfCharacters
},
ReturnValues: 'UPDATED_NEW'
};
const updatedItem = await db.update(params).promise();
return updatedItem;
} catch (err) {
throw err;
}
}
Please advise how can I properly mock the response of AWS.DynamoDB.DocumentClient.update
Have some way to do the that thing (I think so).
This is one of them:
You use AWS.DynamoDB.DocumentClient, then we will mock AWS object to return an object with DocumentClient is mocked object.
jest.mock("aws-sdk", () => {
return {
DynamoDB: {
DocumentClient: jest.fn(),
},
};
});
Now, AWS.DynamoDB.DocumentClient is mocked obj. Usage of update function like update(params).promise() => Call with params, returns an "object" with promise is a function, promise() returns a Promise. Do step by step.
updateMocked = jest.fn();
updatePromiseMocked = jest.fn();
updateMocked.mockReturnValue({
promise: updatePromiseMocked,
});
mocked(AWS.DynamoDB.DocumentClient).mockImplementation(() => {
return { update: updateMocked } as unknown as AWS.DynamoDB.DocumentClient;
});
mocked import from ts-jest/utils, updateMocked to check the update will be call or not, updatePromiseMocked to control result of update function (success/ throw error).
Complete example:
import * as AWS from 'aws-sdk';
import * as dynamoDbUtils from './index';
import { mocked } from 'ts-jest/utils'
jest.mock("aws-sdk", () => {
return {
DynamoDB: {
DocumentClient: jest.fn(),
},
};
});
describe('dynamo-db.utils', () => {
describe('updateEntity', () => {
let updateMocked: jest.Mock;
let updatePromiseMocked: jest.Mock;
beforeEach(() => {
updateMocked = jest.fn();
updatePromiseMocked = jest.fn();
updateMocked.mockReturnValue({
promise: updatePromiseMocked,
});
mocked(AWS.DynamoDB.DocumentClient).mockImplementation(() => {
return { update: updateMocked } as unknown as AWS.DynamoDB.DocumentClient;
});
dynamoDbUtils.init();
});
it('Should request to Dynamodb with correct param and forward result from Dynamodb', async () => {
const totalNumberOfCharacters = 2000;
const id = 'id';
const tableName = 'tableName';
const updatedItem = {};
const params = {
TableName: tableName,
Key: { 'id': id },
UpdateExpression: 'set totalNumberOfCharacters = :totalNumberOfCharacters',
ExpressionAttributeValues: {
':totalNumberOfCharacters': totalNumberOfCharacters
},
ReturnValues: 'UPDATED_NEW'
};
updatePromiseMocked.mockResolvedValue(updatedItem);
const result = await dynamoDbUtils.updateEntity(tableName, id, totalNumberOfCharacters);
expect(result).toEqual(updatedItem);
expect(updateMocked).toHaveBeenCalledWith(params);
});
});
});

mock axios request jest network error

I am trying to create async tests with axios-mock and jest.
This is my test file:
var axios = require('axios');
var MockAdapter = require('axios-mock-adapter');
const middlewares = [thunk,axiosMiddleware]
const mockStore = configureMockStore(middlewares)
describe('async-actions', () => {
var instance;
var mock;
beforeEach(function() {
instance = axios.create();
mock = new MockAdapter(instance);
});
afterEach(() => {
mock.reset()
mock.restore()
})
it('creates FETCH_BOOKINGS_SUCCESS when fetch bookings has been done', () => {
mock
.onGet('/bookings').reply(200, {
data: [
{ id: 1, name: 'test booking' }
]
});
const expectedActions = [
{type: "FETCH_BOOKINGS_START" },
{type: "FETCH_BOOKINGS_SUCCESS", }
]
const store = mockStore({
session: {
token: {
token: "test_token"
}
}
})
return store.dispatch(actions.fetchBookingsTest())
.then(
() => {
expect(store.getActions()).toEqual(expectedActions)
})
// return of async actions
})
})
And my action:
export function fetchBookingsTest() {
return (dispatch) => {
dispatch(async.fetchDataStart(namedType));
return dispatch(rest.get(BOOKINGS))
.then(
(data) => {
dispatch(async.fetchDataSuccess(data,namedType));
},
(error) => {
dispatch(async.fetchDataFailure(error,namedType));
}
)
}
}
I have middleware setup that uses the authentication token from the redux store for each get request. That is why I have setup "test_token" in the mock store.
When I run this test I receive the response
[{"type": "FETCH_BOOKINGS_START"}, {"payload": [Error: Network Error], "type": "FETCH_BOOKINGS_FAILURE"}]
Why am I getting a network error? Do i need to do more setup with Jest to avoid authentication with mock-axios?