Nestjs with Fastify e2e test with jest mocking is not working - node.js

I have Nodejs API that use Fastify. The e2e test is written with jest.
Please find my e2e test. It works and test passes.
Here I find the response is same as the request parameter instead of mocking object.
The actual API respond the request parameter but here I am mocking different object but it is not using it for response.
I am not sure whether passing request body is correct for POST.
Why Jest mocking is not working. The similar mocking works for unit test.
import {
FastifyAdapter,
NestFastifyApplication,
} from '#nestjs/platform-fastify';
import { Test, TestingModule } from '#nestjs/testing';
import request from 'supertest';
import { AppModule } from '../src/app/app.module';
import Axios, { AxiosResponse } from 'axios';
import { HttpService } from '#nestjs/common';
import { Observable, of, observable } from 'rxjs';
import { OracleDBService } from 'synapse.bff.core';
describe('AppController (e2e)', () => {
let app: NestFastifyApplication;
let httpService: HttpService;
let oracleDBService: OracleDBService;
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication<NestFastifyApplication>(
new FastifyAdapter(),
);
httpService = moduleFixture.get<HttpService>(HttpService);
oracleDBService = moduleFixture.get<OracleDBService>(OracleDBService);
await app.init();
jest.setTimeout(30000);
});
it('/ (GET)', () => {
const data = [
{ name: 'item1', code: '100', category: 'cat1', price: 1250 },
{ name: 'item2', code: '101', category: 'cat1', price: 1250 },
{ name: 'item3', code: '102', category: 'cat1', price: 1250 },
{ name: 'item4', code: '103', category: 'cat1', price: 1250 },
];
const response: AxiosResponse<any> = {
data,
headers: {},
config: { url: 'http://localhost:3001/item/getitem' },
status: 200,
statusText: 'OK',
};
jest.spyOn(httpService, 'get').mockReturnValue(of(response));
return app
.inject({
method: 'GET',
url: '/item/100',
})
.then(onresponse =>
expect(JSON.parse(onresponse.payload)).toEqual(response.data),
);
});
it('/ (POST)', () => {
const data = {
Name: 'item1Mock',
Code: '500',
Category: 'cat1Mock',
Price: 9250,
};
const response: AxiosResponse<any> = {
data,
headers: {},
config: { url: 'http://localhost:3001/item/getitem' },
status: 200,
statusText: 'OK',
};
jest.spyOn(httpService, 'post').mockReturnValue(of(response));
return app
.inject({
method: 'POST',
url: '/item/create',
payload: {
body: {
Name: 'item1',
Code: '200',
Category: 'cat1',
Price: 1250,
},
},
})
.then(onResponse =>
expect(
JSON.parse(onResponse.payload).message.Detail.message[0].target.body,
).toEqual(response.data),
);
});
I am getting the following error
expect(received).toEqual(expected) // deep equality
- Expected
+ Received
Object {
- "Category": "cat1Mock",
- "Code": "500",
- "Name": "item1Mock",
- "Price": 9250,
+ "Category": "cat1",
+ "Code": "200",
+ "Name": "item1",
+ "Price": 1250,
}
90 | expect(
91 | JSON.parse(onResponse.payload).message.Detail.message[0].target.body,
> 92 | ).toEqual(response.data),
| ^
93 | );
94 | });
95 |
at item.controller.e2e-spec.ts:92:11

Related

joi validation is not working as i expected

I'm newbie to the nestjs here i wanna to do a joi validation ,if i hit the request
in my terminal it's showing the joi validation errors but not returning any joi error in the response
i have found that controller invoked even before joi middleware
here is my controller,validation and joi schema,
controller
#Post()
#UsePipes(new ValidationPipe(subReportSchema))
cardsteps(#Body() payload:createCardStepsDto):Promise<any>{
return this.cardStepservice.createCardSteps(payload);
}
joi schema
import * as Joi from 'joi';
export const subReportSchema = Joi.object({
stepId: Joi.string().optional(),
stepTitle: Joi.string().when('stepId', { is: 'null', then: Joi.required() }),
order: Joi.number().when('stepId', { is: 'null', then: Joi.required() }),
draft: Joi.boolean().when('stepId', { is: 'null', then: Joi.required() }),
time: Joi.string().when('stepId', { is: 'null', then: Joi.required() }),
date: Joi.string().when('stepId', { is: 'null', then: Joi.required() }),
cardId: Joi.string().when('stepId', { is: 'null', then: Joi.required() }),
counters: Joi.object({
attendees: Joi.number().optional(),
children: Joi.number().optional(),
adults: Joi.number().optional(),
boys: Joi.number().optional(),
girls: Joi.number().optional(),
over18: Joi.number().optional(),
under18: Joi.number().optional(),
over60: Joi.number().optional()
}).when('stepId', { is: 'null', then: Joi.required() }),
}).options({
abortEarly: false, allowUnknown: true
});
validation schema
import {
PipeTransform,
BadRequestException,
ArgumentMetadata,
} from '#nestjs/common';
import { ObjectSchema } from 'joi';
export class ValidationPipe implements PipeTransform {
constructor(private readonly schema: ObjectSchema) { }
transform(value: Record<string, any>) {
const { error } = this.schema.validate(value)
if (error) {
throw new BadRequestException({
error: "validation failed",
message: error.message.replace(/(\"|[\d])/g, ''),
});
}
return value;
}
}
output reflects in my terminal
ValidationError [SequelizeValidationError]: notNull Violation: cardStep.date cannot be null
at InstanceValidator._validate (/home/user-89/projects/report-back2/node_modules/sequelize/src/instance-validator.js:78:13)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at InstanceValidator._validateAndRunHooks (/home/user-89/projects/report-back2/node_modules/sequelize/src/instance-validator.js:111:7)
at InstanceValidator.validate (/home/user-89/projects/report-back2/node_modules/sequelize/src/instance-validator.js:93:12)
at cardStep.save (/home/user-89/projects/report-back2/node_modules/sequelize/src/model.js:3996:7)
at Function.create (/home/user-89/projects/report-back2/node_modules/sequelize/src/model.js:2280:12)
at CardStepService.createCardSteps (/home/user-89/projects/report-back2/src/card-step/card-step.service.ts:30:41)
at /home/user-89/projects/report-back2/node_modules/#nestjs/core/router/router-execution-context.js:46:28
at /home/user-89/projects/report-back2/node_modules/#nestjs/core/router/router-proxy.js:9:17 {
errors: [
ValidationErrorItem {
message: 'cardStep.date cannot be null',
type: 'notNull Violation',
path: 'date',
value: null,
origin: 'CORE',
instance: [cardStep],
validatorKey: 'is_null',
validatorName: null,
validatorArgs: []
}
]
}

Can't connect to Atlas cluster from my lambda using mongoose

I am trying to connect to a cluster using the last example from mongoose site
Here are my files using node14 and typescript
src/index.ts
import { APIGatewayProxyHandler } from "aws-lambda"
export { list as productsList } from "./products"
export const list: APIGatewayProxyHandler = (event, context, callback) => {
callback(null, {
statusCode: 200,
body: `Hello from ${process.env.AWS_SAM_BRANCH}`,
})
}
src/utils.ts
import mongoose from "mongoose"
import { APIGatewayProxyResult, Callback } from "aws-lambda"
let mongoConnection: Promise<typeof mongoose> | null = null
export const connectMongoose = async () => {
if (mongoConnection == null) {
const mongoURI = `mongodb+srv://USER:PASS#cluster0.ohjoj.mongodb.net/myFirstDB?retryWrites=true&w=majority`
mongoConnection = mongoose
.connect(mongoURI, { serverSelectionTimeoutMS: 3000 })
.then((mongooseReply) => {
console.log({ mongooseReply })
return mongoose
})
.catch((mongooseError) => {
console.log({ mongooseError })
return mongoose
})
await mongoConnection
}
return mongoConnection
}
export const errorHandler = (error: unknown, callback: Callback<APIGatewayProxyResult>) => {
console.error("catchedError", error)
if (error instanceof Error) {
callback(null, {
statusCode: 400,
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ error: error.message }),
})
} else {
callback(null, {
statusCode: 500,
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ error: "Internal server error" }),
})
}
}
src/products/index.ts
import { APIGatewayProxyHandler } from "aws-lambda"
import Model from "./model"
import { connectMongoose, errorHandler } from "../utils"
export const list: APIGatewayProxyHandler = (event, context, callback) => {
try {
connectMongoose()
Model.find({}, (error: unknown, reply: unknown) => {
if (error) throw error
callback(null, {
statusCode: 200,
headers: { "Content-Type": "application/json" },
body: JSON.stringify(reply),
})
})
} catch (error) {
errorHandler(error, callback)
}
}
src/products/model.ts
import mongoose from "mongoose"
const model = mongoose.model(
"Product",
new mongoose.Schema(
{
title: {
type: String,
required: true,
maxLength: 256,
},
description: {
type: String,
required: true,
maxLength: 2048,
},
count: {
type: Number,
required: true,
min: 0,
max: 1000 * 1000,
},
},
{
timestamps: true,
versionKey: false,
}
)
)
export default model
Here is the code in a repo is includes commands used to deploy with AWS SAM
https://github.com/LuisEnMarroquin/aws-sam-mongo
There are 2 routes in my app
https://2us58gl430.execute-api.us-east-2.amazonaws.com/
This works and returns Hello from test with status 200
https://2us58gl430.execute-api.us-east-2.amazonaws.com/products
This doesn't work and returns {"message":"Internal Server Error"} with status 500
Here are the CloudWatch logs exported as CSV
timestamp,message
1647203544609,"START RequestId: 83fd3fc8-1134-4ff4-a5f7-7e83a65159ce Version: $LATEST
"
1647203545742,"2022-03-13T20:32:25.685Z 83fd3fc8-1134-4ff4-a5f7-7e83a65159ce INFO {
mongooseReply: <ref *1> Mongoose {
connections: [ [NativeConnection] ],
models: { Product: Model { Product } },
events: EventEmitter {
_events: [Object: null prototype] {},
_eventsCount: 0,
_maxListeners: undefined,
[Symbol(kCapture)]: false
},
options: {
pluralization: true,
autoIndex: true,
autoCreate: true,
[Symbol(mongoose:default)]: true
},
_pluralize: [Function: pluralize],
Schema: [Function: Schema] {
reserved: [Object: null prototype],
Types: [Object],
ObjectId: [Function]
},
model: [Function (anonymous)],
plugins: [ [Array], [Array], [Array], [Array], [Array], [Array] ],
default: [Circular *1],
mongoose: [Circular *1]
}
}
"
1647203549616,"END RequestId: 83fd3fc8-1134-4ff4-a5f7-7e83a65159ce
"
1647203549616,"REPORT RequestId: 83fd3fc8-1134-4ff4-a5f7-7e83a65159ce Duration: 5005.75 ms Billed Duration: 5000 ms Memory Size: 128 MB Max Memory Used: 76 MB Init Duration: 366.30 ms
"
1647203549616,"2022-03-13T20:32:29.616Z 83fd3fc8-1134-4ff4-a5f7-7e83a65159ce Task timed out after 5.01 seconds
"
As explained in this GitHub issue
A few suggestions:
You should either choose between a full callback approach and a full promise approach
Don't mix async / await with .then syntax when you can avoid it
import mongoose from "mongoose"
import { APIGatewayProxyHandler } from "aws-lambda"
let mongoConnection: Promise<typeof mongoose> | null = null
const connectMongoose = async () => {
if (mongoConnection == null) {
const mongoURI = `mongodb+srv://YOUR_CLUSTER_URL`
mongoConnection = mongoose
.connect(mongoURI, { serverSelectionTimeoutMS: 3000 })
.then((mongooseReply) => {
console.log({ mongooseReply })
return mongoose
})
.catch((mongooseError) => {
console.log({ mongooseError })
return mongoose
})
await mongoConnection
}
return mongoConnection
}
const Model = mongoose.model(
"Product",
new mongoose.Schema(
{
title: String,
description: String,
},
{
timestamps: true,
versionKey: false,
}
)
)
export const myRoute: APIGatewayProxyHandler = async (event, context) => {
try {
await connectMongoose();
const reply = await Model.find({}).exec();
return {
statusCode: 200,
headers: { "Content-Type": "application/json" },
body: JSON.stringify(reply),
};
} catch (error) {
return {
statusCode: 400,
headers: { "Content-Type": "application/json" },
body: "Server error",
};
}
}

how to test Router.push options with Jest / React

I'm new to testing with routing and was wondering how I would go about testing this
const handleFilter = (items: ParsedUrlQueryInput) => {
push({ pathname: '/games', query: items }, undefined, { scroll: false });
return;
};
<S.ShowMoreButton role="button" onClick={handleShowMore}
I made some mocks and the following test worked without the options, but when I add options, it's impossible to test
mock
const useRouter = jest.spyOn(require('next/router'), 'useRouter');
const push = jest.fn();
useRouter.mockImplementation(() => ({
push,
query: '',
asPath: '',
route: '/',
}));
test
expect(push).toHaveBeenCalledWith({
pathname: '/games',
query: { platforms: ['windows', 'linux'], sort_by: 'low-to-high' },
});
error in test
Expected: {"pathname": "/games", "query": {"platforms": ["windows", "linux"], "sort_by": "low-to-high"}}
Received
1
Object {
"pathname": "/games",
- "query": Object {
- "platforms": Array [
- "windows",
- "linux",
- ],
- "sort_by": "low-to-high",
- },
+ "query": Object {},
},
+ undefined,
+ {"scroll": false}

Apollo GraphQL Server - Access query params from cache plugin

I have an Apollo GraphQL server using the apollo-server-plugin-response-cache plugin and I need to determine whether or not I'm going to write to the cache based on incoming parameters. I have the plugin set up and I'm using the shouldWriteToCache hook. I can print out the GraphQLRequestContext object that gets passed into the hook, and I can see the full request source, but request.variables is empty. Other than parsing the query itself, how can I access the actual params for the resolver in this hook? (In the example below, I need the value of param2.)
Apollo Server:
new ApolloServer({
introspection: true,
playground: true,
subscriptions: false,
typeDefs,
resolvers,
cacheControl: {
defaultMaxAge: 60
},
plugins: [
apolloServerPluginResponseCache({
cache, // This is a "apollo-server-cache-redis" instance
shouldWriteToCache: (requestContext) => {
// I get a lot of info here, including the source query, but not the
// parsed out query variables
console.log(requestContext.request);
// What I want to do here is:
return !context.request.variables.param2
// but `variables` is empty, and I can't see that value parsed anywhere else
}
})
]
})
Here is my resolver:
export async function exapi(variables, context) {
// in here I use context.param1 and context.param2
// ...
}
I have also tried:
export async function exapi(variables, { param1, param2 }) {
// ...
}
Here is what I get logged out from the code above:
{
query: '{\n' +
' exapi(param1: "value1", param2: true) {\n' +
' records\n' +
' }\n' +
'}\n',
operationName: null,
variables: {}, // <-- this is empty?! How can I get param2's value??
extensions: undefined,
http: Request {
size: 0,
timeout: 0,
follow: 20,
compress: true,
counter: 0,
agent: undefined,
[Symbol(Body internals)]: { body: null, disturbed: false, error: null },
[Symbol(Request internals)]: {
method: 'POST',
redirect: 'follow',
headers: [Headers],
parsedURL: [Url],
signal: null
}
}
}
If you didn't provide variables for GraphQL query, you could get the arguments from the GraphQL query string via ArgumentNode of AST
If you provide variables for GraphQL query, you will get them from requestContext.request.variables.
E.g.
server.js:
import apolloServerPluginResponseCache from 'apollo-server-plugin-response-cache';
import { ApolloServer, gql } from 'apollo-server';
import { RedisCache } from 'apollo-server-cache-redis';
const typeDefs = gql`
type Query {
exapi(param1: String, param2: Boolean): String
}
`;
const resolvers = {
Query: {
exapi: (_, { param1, param2 }) => 'teresa teng',
},
};
const cache = new RedisCache({ host: 'localhost', port: 6379 });
const server = new ApolloServer({
introspection: true,
playground: true,
subscriptions: false,
typeDefs,
resolvers,
cacheControl: {
defaultMaxAge: 60,
},
plugins: [
apolloServerPluginResponseCache({
cache,
shouldWriteToCache: (requestContext) => {
console.log(requestContext.document.definitions[0].selectionSet.selections[0].arguments);
return true;
},
}),
],
});
server.listen().then(({ url }) => console.log(`πŸš€ Server ready at ${url}`));
GraphQL query:
query{
exapi(param1: "value1", param2: true)
}
Server logs print param1 and param2 arguments:
πŸš€ Server ready at http://localhost:4000/
[]
[ { kind: 'Argument',
name: { kind: 'Name', value: 'param1', loc: [Object] },
value:
{ kind: 'StringValue',
value: 'value1',
block: false,
loc: [Object] },
loc: { start: 15, end: 31 } },
{ kind: 'Argument',
name: { kind: 'Name', value: 'param2', loc: [Object] },
value: { kind: 'BooleanValue', value: true, loc: [Object] },
loc: { start: 33, end: 45 } } ]

error using datalisting react-redux-datatable package

In the view I put all code for datatable and also using react redux method ,when I run this code then give me error look like this :
Your render method should have return statement
react/require-render-return
How to fix it?
service.js file
import React, { Component } from 'react';
import DataTable from 'react-redux-datatable';
import 'react-redux-datatable/dist/styles.css';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import {Link} from 'react-router-dom';
import {getServices} from '../../actions/servicesActions';
import Spinner from '../Spinner';
class Services extends Component{
componentDidMount(){
const api= this.props.getServices();
}
render(){
const services=this.props.services;
var tableSettings = {
tableID: 'AdvancedFeaturesTable',
wrapperType: 'section',
displayTitle: 'Requests Table',
keyField: '_id',
defaultSort: ['_id', 'desc'],
minWidth: 880,
useLocalStorage: true,
tableColumns: [
{
title: '_id',
key: '_id',
width: 90,
},
{
title: 'Name',
key: 'name',
width: 90,
},
{
title: 'Description',
key: 'description',
width: 90,
},
{
title: 'Status',
key: 'status',
width: 164,
},
{
title: 'Subscription',
key: 'subscription',
width: 90,
},
],
};
var DataTable = () => (
<DataTable
tableSettings={tableSettings}
apiLocation={services}
/>
)
}
};
const mapStateToProps = state => {
return {
service: state.services.service
};
};
export default connect(
mapStateToProps,
{
getServices
}
)(Services);
getservices.js
export const getServices=() =>dispatch => {
dispatch(setLoading());
axios.get('/api/admin/services')
.then(res =>
dispatch({
type:GET_SERVICES,
payload:res.data
})
)
.catch(err =>
dispatch({
type: GET_SERVICES,
payload: {}
})
)
}
error display
Line 199: Your render method should have return statement react/require-render-return
referral link
https://www.npmjs.com/package/react-redux-datatable
The error is clear. A component in react must return a jsx element or set of jsx elements.
In your Services component the render method isn’t returning anything
Looks like you want Services component to render DataTable so replace below code in your Services component render
render(){
const tableSettings = {
tableID: 'AdvancedFeaturesTable',
wrapperType: 'section',
displayTitle: 'Requests Table',
keyField: '_id',
defaultSort: ['_id', 'desc'],
minWidth: 880,
useLocalStorage: true,
tableColumns: [
{
title: '_id',
key: '_id',
width: 90,
},
{
title: 'Name',
key: 'name',
width: 90,
},
{
title: 'Description',
key: 'description',
width: 90,
},
{
title: 'Status',
key: 'status',
width: 164,
},
{
title: 'Subscription',
key: 'subscription',
width: 90,
},
],
};
return(
<div><DataTable
tableSettings={tableSettings}
apiLocation={this.props.services}
/>
</div>
)}
This will resolve your issue.

Resources