How can i test my resolvers properly with jest? - javascript

I'm trying to test my resolvers but i'd like to test each field of the response, here's the code to call the response:
interface Options {
source: string;
variableValues?: Maybe<{ [key: string]: unknown | null }>;
}
let schema: GraphQLSchema;
const gCall = async ({
source,
variableValues,
}: Options): Promise<ExecutionResult> => {
if (!schema) {
schema = await createSchema();
}
return graphql({
schema,
source,
variableValues,
});
};
export default gCall;
And that's the code to test the resolver:
let connection: Connection;
const challengeMutation = `
mutation CreateChallenge($data: CreateChallengeInput!) {
createChallenge(data: $data) {
id
name
category
startDate
endDate
goal
description
}
}
`;
describe('Create Challenge', () => {
beforeAll(async () => {
connection = await databaseTestConnection();
await connection.createQueryBuilder().delete().from(Challenge).execute();
});
afterAll(async () => {
await connection.createQueryBuilder().delete().from(Challenge).execute();
await connection.close();
});
it('should create challenge', async () => {
const challenge = {
name: 'some awesome name',
category: 'distância',
startDate: new Date(2020, 7, 4).toISOString(),
endDate: new Date(2020, 7, 5).toISOString(),
goal: 5000,
description: 'some excelent challenge description',
};
const response = await gCall({
source: challengeMutation,
variableValues: {
data: challenge,
},
});
expect(response).toMatchObject({
data: {
createChallenge: {
name: challenge.name,
category: challenge.category,
startDate: challenge.startDate,
endDate: challenge.endDate,
goal: challenge.goal,
description: challenge.description,
},
},
});
});
});
What I'd like to do is test the fields separately, like this:
expect(response.data.createChallenge.name).toEqual(challenge.name);
But I'm getting the following error when I try to execute the above code:
Object is possibly 'null' or 'undefined'.
What can I do to solve this error and to make this test better?

Object is possibly 'null' or 'undefined'.
TypeScript warns you that the response data might not exist as the graphql "server" might return error instead. So you should use ! operator to assert it's not null.
You should also do that after checking it's not undefined with expect().

Related

uploading xlsx or csv files to firebase

Hello there,
im trying to upload files (.csv, .xlsx of 5000 row and 6 col) to firebase with a cloud functions that i call from the frontend, the cloud function start and after a few seconds of run its make an error "code":"4" "details":"Deadline exceeded"
(Blaze plan)
the upload is very slow
getting an Deadline exceeded error with code 4
its crashing the server after i get the error
explication of the code below:
i do a for of loop that iterate over an array of array that looks like this
[['Dufour', 'Annie', 'annie.duf#gmail.com', 33683333005, 'f'], ['john','Doe', 'john#gmail.com', 33223424, 'm']] then i format each row in order to
structure it as a firestore doc with many fields and try to add it to firestore
then its crash
i cant figure out when its crashing its really random
and when im trying a smaller file (1000 row) its also crashing after some seconds of execution
export const createNewLeads = functions.region("europe-west3").https.onCall(async (data, context) => {
if (!context.auth) {
throw new functions.https.HttpsError('failed-precondition', 'The function must be called' + 'while authenticated.');
}
const { category, subcategory, allLeads, downloadUrl, file_id, fileDetails } = data;
for (const lead of allLeads) {
const leads_infos = format_lead_infos(lead , fileDetails);
const leads_meta = format_lead_metadata(downloadUrl, file_id, fileDetails);
const db_lead_ref = admin.firestore().collection('leads');
const new_lead_doc_id = db_lead_ref.doc().id;
db_lead_ref
.doc(new_lead_doc_id).set({
lead_id: new_lead_doc_id,
category: category,
subcategory: subcategory,
createdOn: Date(),
uploaded_by: context.auth.uid,
purchased_by: null,
metadata: leads_meta,
infos: leads_infos
}).then(() => {
functions.logger.info('createLeads SUCCESS', { docId: new_lead_doc_id });
return {
user_id: new_lead_doc_id,
created: true,
};
}).catch((error: any) => {
functions.logger.info('createLeads ERROR', { error });
return {
error: true,
err_mess: error,
};
});
}
});
As far as I can see you are not handling the asynchronous nature of writing to Firestore correctly, as you're not awaiting the call to set(). You're also returning a result on the first successful document write in the loop, which seems suspicious.
This should be closer to what you need:
try {
for (const lead of allLeads) {
const leads_infos = format_lead_infos(lead , fileDetails);
const leads_meta = format_lead_metadata(downloadUrl, file_id, fileDetails);
const db_lead_ref = admin.firestore().collection('leads');
const new_lead_doc_id = db_lead_ref.doc().id;
await db_lead_ref.doc(new_lead_doc_id).set({
lead_id: new_lead_doc_id,
category: category,
subcategory: subcategory,
createdOn: Date(),
uploaded_by: context.auth.uid,
purchased_by: null,
metadata: leads_meta,
infos: leads_infos
});
}
}
catch (error: any) => {
functions.logger.info('createLeads ERROR', { error });
return {
error: true,
err_mess: error,
};
}
functions.logger.info('createLeads SUCCESS', { docId: new_lead_doc_id
return {
user_id: new_lead_doc_id,
created: true,
};
In this code we await each document write operation and then return an error if any of them fails, and success only once all of them succeed.
Hello Frank thanks for helping us i copy the code you give to us bu after a few seconds of running i got this error
error of the code from the firebase console log
export const createNewLeads = functions.region("europe-west3").https.onCall(async (data, context) => {
if (!context.auth) {
throw new functions.https.HttpsError('failed-precondition', 'The function must be called' + 'while authentificated.');
}
const { category, subcategory, allLeads, downloadUrl, file_id, fileDetails } = data;
const db_lead_ref = admin.firestore().collection('leads');
const new_lead_doc_id = db_lead_ref.doc().id;
try {
for (const lead of allLeads) {
const leads_infos = format_lead_infos(lead , fileDetails);
const leads_meta = format_lead_metadata(downloadUrl, file_id, fileDetails);
await db_lead_ref.doc(new_lead_doc_id).set({
lead_id: new_lead_doc_id,
category: category,
subcategory: subcategory,
createdOn: Date(),
uploaded_by: context.auth.uid,
purchased_by: null,
metadata: leads_meta,
infos: leads_infos
});
}
} catch (error:any) {
functions.logger.info('createLeads ERROR', { error });
return {
error: true,
err_mess: error,
}
}
functions.logger.info('createLeads SUCCESS', { docId: new_lead_doc_id })
return {
user_id: new_lead_doc_id,
created: true,
}
});

Jest mocking a mongodb record in a service

I'm trying to test a service in Nestjs which is responsible for getting one record out of a mongo database, using Jest.
As per common convention, when writing unit tests that test services we can mock a record that would sit in a database.
I'm trying the following implementation:
import { Test } from '#nestjs/testing';
import { QuestionsService } from './questions.service';
import { CreateQuestionRequestDto } from './dto/create-question-request.dto';
import { getModelToken } from '#nestjs/mongoose';
import { UpdateQuestionRequestDto } from './dto/update-question-request.dto';
import { NotFoundException } from '#nestjs/common';
import { DuplicateQuestionRequestDto } from './dto/duplicate-question-request.dto';
const testQuestion: CreateQuestionRequestDto = {
data: {
createdBy: { id: 0, name: '' },
lanugageTexts: undefined,
options: undefined,
status: undefined,
type: undefined,
entityId: 1,
propertyId: 'propHash1',
companyId: 1,
entityType: 'announcement',
},
};
describe('QuestionsService', () => {
let questionService: QuestionsService;
let findOne: jest.Mock;
let findOneAndUpdate: jest.Mock;
let find: jest.Mock;
beforeEach(async () => {
// save = jest.fn();
findOne = jest.fn();
findOneAndUpdate = jest.fn();
find = jest.fn();
const module = await Test.createTestingModule({
providers: [
QuestionsService,
{
provide: getModelToken('Question'),
useValue: {}
}
]
})
.compile();
questionService = await module.get<QuestionsService>(QuestionsService);
});
it('should be defined', () => {
expect(questionService).toBeDefined();
});
/**
* Question Get
*/
describe('when getting a question', () => {
describe('and the questionId does not exist', () => {
beforeEach(() => {
findOne.mockReturnValue(undefined);
})
it('should throw a NotFound exception', async () => {
const response = await questionService.get('announcement', 9136500000);
expect(response).toThrow(NotFoundException);
});
});
describe('and the questionId exists', () => {
beforeEach(() => {
findOne.mockResolvedValue(Promise.resolve(testQuestion));
});
it('should update the correct question', async() => {
const response = await questionService.get('announcement', 1);
expect(response).toMatchObject(updatedTestQuestion);
});
});
});
});
When I run this test I get the following error message.
● QuestionsService › when getting a question › and the questionId does not exist › should throw a NotFound exception
TypeError: this.questionModel.find is not a function
52 | const data: Question[] = [];
53 | const questions = await this.questionModel
> 54 | .find(
| ^
55 | { entityType: entityType, entityId: entityId, status: QuestionStatus.ACTIVE },
56 | { answers: 0 },
57 | {
at QuestionsService.get (questions/questions.service.ts:54:14)
at Object.<anonymous> (questions/questions.spec.ts:128:56)
The service method I'm testing is.
async get(entityType: string, entityId: number): Promise<any> {
const data: Question[] = [];
const questions = await this.questionModel
.find(
{ entityType: entityType, entityId: entityId, status: QuestionStatus.ACTIVE },
{ answers: 0 },
{
sort: { _id: -1 },
limit: 1,
}
)
.exec();
if (!questions.length) {
throw new NotFoundException();
}
questions.forEach((question) => {
data.push(question);
});
return { data };
}
find() is the mongoose method that fetches the record from the database. I believe for the test I need to somehow include these methods I'm using in the service and mock them but I cannot find one clear answer.

Unhandled promise rejection - Typescript wit hexpress and mongoose

Please bear with me, I am a beginner in node and async stuff is still no super clear for me.
I have the below piece of code and I a now working on the last part - the /new-comp route.
It is supposed to post in the database I connected above:
import { Schema } from 'mongoose'
export const mongoose = require('mongoose')
const express = require('express')
const app = express()
const bodyParser = require('body-parser')
const urlEncodedParser = bodyParser.urlencoded({ extended: false })
mongoose.connect('mongodb://localhost:27017/CompetitionEvent')
export const db = mongoose.connection
db.on('error', console.error.bind(console, 'An error has occured: '))
db.once('open', function () {
console.log('Connected to Mongodb')
})
const CompetitionSchema = new Schema({
id: String,
place: String,
time: String,
subscriptions: [],
date: Date,
cost: {
currency: String,
amount: Number,
},
})
const CompetitionModel = mongoose.model('CompetitionModel', CompetitionSchema)
app.use(bodyParser.json())
app.get('/events', (_req: any, res: any) => {
res.send(eventApplication.getAll())
})
app.post('/event', async (req: any, res: any) => {
await eventApplication.createAnEvent(req.body)
res.json({
success: true,
})
})
app.post('/new-comp', urlEncodedParser, async (res: any, req: any) => {
await eventApplication.createAnEvent(req.body)
const newComp = CompetitionModel(req.body)
newComp.save(function (error: any, data: any) {
if (error) throw error
res.json(data)
})
})
app.listen(8000)
I also have this file that has all my classes:
export interface Subscription {
id: string
event_id: string
name: string
surname: string
}
export interface EventDTO {
id: string
place: string
time: string
subscriptions: Subscription[]
date: Date
cost: EventCost
}
export interface EventCost {
amount: number
currency: string
}
export class CompetitionEvent {
public subscriptions: Subscription[]
public place: string
public time: string
public date: Date
public cost: EventCost
public id: string
static save: any
constructor(data: EventDTO) {
this.subscriptions = data.subscriptions
this.place = data.place
this.time = data.time
this.date = data.date
this.cost = data.cost
this.id = data.id
}
public isCompleted = () => this.place === 'Poznan' && this.date === new Date()
public getSubs = () => this.subscriptions
public subscribe = (sub: Subscription) => {
this.subscriptions = [...this.subscriptions, sub]
return this
}
public cancelSubscription(subscription: Subscription) {
const subExists = this.subscriptions.find(
(it) => it.id === subscription.id && it.name === subscription.name,
)
if (!subExists) {
throw new Error('Subscription does not exist.')
}
this.subscriptions = this.subscriptions.filter(
(it) => it.id !== subscription.id,
)
}
}
Now my issue is that when I post some data to my app using curl, I have anerror message from the server as follows:
(node:3264) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'subscriptions' of undefined
I am not sure how to understand this log. It seems that I have an unhandled promise somewhere (I get lines in the log but sometimes it points to empty lines in my program".
Do you have any idea how I should understand / manage this issue?
Thanks in advance

TypeError: Cannot read property 'sessions' of undefined

I am writing tests and I do not know js well, but I need to find the answer quickly. In the courses, I have not reached this point yet, or maybe I just don’t understand. Please help me figure out what I'm doing wrong.
I have two questions:
The test starts working as soon as I delete the line:
.then((response) => {
authId = response.body.result.sessions[0].authId;
});
if you do not delete this line, an error appears:
TypeError: Cannot read property 'sessions' of undefined
How to write so that the fields are optional, that is, you can pass, or you can not pass
(props: { authId: string; deviceId: string })
This is the response.body I am trying to parse:
{
"result": {
"sessions": [
{
"type": "web",
"authId": "jRXtO7oNiBR5Ldeq",
"platform": "platform",
"application": "application",
"seenAt": 1592052380
}
],
"integrations": []
},
"success": true
}
My code:
import { agent } from 'supertest';
import { config } from '../../config';
import { getSsoId } from '../../helpers/getUserFromGenesis';
describe('First', () => {
let id;
let authId;
beforeEach(async () => {
id = await getId();
});
const userAuthorizations = (fn: (response: any) => void) =>
agent(config.baseUrl)
.get(`users/${id}/authorizations?client_id=test`)
.auth(config.user, config.pass)
.expect((response) => {
fn(response);
});
const deleteUserAuthorizations = (props: { authId: string; deviceId: string }) =>
agent(config.baseUrl)
.delete(`users/authorizations`)
.auth(config.user, config.pass)
.send(props)
.expect((response) => {
expect(response.body.result.success).toEqual(true);
});
const getSession = () =>
agent(config.tokenQaUrl)
.post(`/token`)
.auth(config.user, config.pass)
.send({
clientId: 'test',
userId: id,
})
.expect((response) => {
expect(response.body.sessionId).not.toEqual('');
expect(response.body.sessionId).not.toEqual(null);
})
.then((response) => {
authId = response.body.result.sessions[0].authId;
});
it('test', async () => {
await getSession().then(() =>
userAuthorizations((response) => {
expect(response.body.result.sessions.length).toBeGreaterThan(0);
}),
);
});
});
As discussed in the comment
It seems like some time your response is getting the JSON data in the way which you are trying to access, but some time its not. Its better to add conditions before trying to access data where the JSON is not of fixed structure.
To make these property optional just add ?
it will make the properties option
props: { authId?: string; deviceId?: string }
I am a bit unclear on your question. There are some good answers up top.
In case you are trying to avoid a "property of undefined error" you can do something like this:
authId = response?.body?.result?.sessions[0]?.authId;
it prevents the error. Ideally you would type-check all of them.

Meteor & Mocha Chai : test insert function

I'm trying to test my insert function but It fail with UserAccount: Error: Cannot read property 'username' of undefined
I don't know how to make the test pass for inserting a post, here is my method:
Meteor.methods({
'posts.insert'(title, content) {
check(title, String);
check(content, String);
if (! this.userId) {
throw new Meteor.Error('not-authorized');
}
const urlSlug = getSlug(title);
Posts.insert({
title,
content,
urlSlug,
createdAt: new Date(),
owner: this.userId,
username: Meteor.users.findOne(this.userId).username,
});
},
});
And here is the test method I'm trying to test:
if (Meteor.isServer) {
describe('Posts', () => {
describe('methods', () => {
const userId = Random.id();
let postId;
beforeEach(() => {
Posts.remove({});
postId = Posts.insert({
title: 'test post',
content: 'test content',
urlSlug: 'test-post',
createdAt: new Date(),
owner: userId,
username: 'toto',
});
});
// TEST INSERT METHOD
it('can insert post', () => {
const title = "test blog 2";
const content = "test content blog 2";
const insertPost Meteor.server.method_handlers['posts.insert'];
const invocation = { userId };
insertPost.apply(invocation, [title, content]);
assert.equal(Posts.find().count(), 2);
});
});
});
}
Could you help me please ?
How about using sinon to stub the meteor call? Although I'm not sure if it works with nested object (if someone can confirm).
sinon.stub(Meteor, 'users.findOne').returns({ username: 'Foo' });
And don't forget to restore it after you have used it (in afterEach() for example).
Meteor.users.findOne.restore();

Categories