Jest run async endpoints beforeall, afterall tests - node.js

[Junior dev!!]
->Node v18.08
->jest 29.0.1
->MySQL
Hi i m running several jest test in diferent folders of each endpoint on my API. I have several files where i m creating an account => test several endpoints => clean DB.. I have at least 14 files like these. So.. When an endpoint in those 14 file is not working the test doesn't go until the end and clean the DB so i need to go back and change the user name.. A clearly waste of time..
I'm learning about the Hook beforeAll and afterAll but they are not working.
i will show here my code without the hooks (test working)
const axios = require("axios");
const API_URL = process.env.API_TEST_URL;
let TOKEN;
//Creating User
test("POST test register user", async () => {
const data = {
pseudo: "TEST",
password: "123",
date: "2022-08-29 16:31:25",
};
const url = `${API_URL}/register`;
const response = await axios.post(url, data);
expect(response.status).toBe(200);
expect(response.data).toHaveProperty("token");
TOKEN = response.data.token;
});
//Endpoints to test
test("POST/test", async () => {
const url = `${API_URL}/data/actions`;
const data = {
action: "running",
date: "2021-09-30 18:14:24",
};
const headers = { Authorization: `Bearer ${TOKEN}` };
const response = await axios.post(url, data, { headers });
expect(response.status).toBe(200);
});
//Deleting all info from DB
test("DELETE test account", async () => {
const url = `${API_URL}/user/delete/all-data`;
const headers = { Authorization: `Bearer ${TOKEN}` };
const response = await axios.delete(url, { headers });
expect(response.status).toBe(200);
});
And when i want to add the hooks doesn't work
const axios = require("axios");
const API_URL = process.env.API_TEST_URL;
let TOKEN;
//creating before all an user
beforeAll(() => {
test("POST test register user", async () => {
const data = {
pseudo: "TEST",
password: "123",
date: "2022-08-29 16:31:25",
};
const url = `${API_URL}/register`;
const response = await axios.post(url, data);
expect(response.status).toBe(200);
expect(response.data).toHaveProperty("token");
TOKEN = response.data.token;
});
});
//testing endpoints
test("POST/action test", async () => {
const url = `${API_URL}/data/actions`;
const data = {
action: "running",
date: "2021-09-30 18:14:24",
};
const headers = { Authorization: `Bearer ${TOKEN}` };
const response = await axios.post(url, data, { headers });
expect(response.status).toBe(200);
});
// if the endpoint doesn't work afterAll clean all DB
afterAll(() => {
test("DELETE test account", async () => {
const url = `${API_URL}/user/delete/all-data`;
const headers = { Authorization: `Bearer ${TOKEN}` };
const response = await axios.delete(url, { headers });
expect(response.status).toBe(200);
});
});
what do you think. Could you help me ? Because the info on jest is only showing how to do it with a function.. Can we test something inside beforeAll & afterAll ??

I have something similar in a project that I'm working on.
I had to make sure that the beforeAll was an async function and put the await.
beforeAll(async () => {
connection = await createConnection();
await connection.runMigrations();
const id = uuidv4();
const password = await hash('admin', 8);
await connection.query(
`insert into users (id, name, email, password, is_admin, created_at, driver_license)
values ('${id}', 'Admin', 'admin#test.com.br', '${password}', true, 'now()', 'XXXXXXX')`
);
});
afterAll(async () => {
await connection.dropDatabase();
await connection.close();
});
Another thing is that I have done a test using a test function inside the beforeAll and it has returned an error, so maybe just executing the post without the test may work.
Edit: Reading the docs about the test inside the beforeAll and beforeEach, it says that you can not use a test inside it.

Related

Setting headers for JEST integration testing on Apollo

Hi i'm facing an error in my code, this is the code.
I am not able to set the headers inside executeOperation function of Apollo as i need accessToken to run next mutation or a query.
loginToken is being set and i can retrieve it's value . But when i set headers before query "getClientConfiguration" it gives me unauthorized response because of header.
const { createServerWithSchema } = require('../../gql/server')
let loginToken;
describe('Space test suite', () => {
it('returns login token', async () => {
const testServer = await createServerWithSchema()
const result = await testServer.executeOperation({
query: `
query loginFromEmail{
loginFromEmail(
email:"qa+stadiumexperiencehost#pavemint.com"
password:"Password1"
)
{
accessToken
}
}
`
})
const { accessToken } = result.data.loginFromEmail
loginToken = accessToken
expect(result.errors).toEqual(undefined)
})
it('returns clientConfiguration', async () => {
const testServer = await createServerWithSchema()
const result = await testServer.executeOperation({
http: {
headers : {
"Authorization": `Bearer ${loginToken}`
}
},
query: 'query getClientConfiguration { getClientConfiguration { GOOGLE_MAPS_API_KEY } }',
})
console.log("result", result)
})
})
For
Setting headers for JEST integration testing on Apollo
Here is the answer.

Firebase functions take MINUTES to write in Firestore

I'm building a mobile app and on some actions (like userCreate), I trigger some Firebase functions to perform an API call to a third service, and then write something in the Firestore database.
Everything works in theory, but in practice, the API calls are quite fast (even with cold start scenarios), but the database writes can take several MINUTES to complete (if they do at all, I suspect that sometimes it takes too long and times out).
Since the API call work just fine, and so does the DB write on some occasion, I suspect that this is simply due to very poor async management from me since I know about nothing in JS.
Here are two of many example functions which are concerned by this issue, just to showcase that it happens whereas I'm triggering functions onCreate or on HTTPS.
onCreate function
const functions = require("firebase-functions");
const axios = require('axios')
// The Firebase Admin SDK to access the Firestore.
const admin = require('firebase-admin');
admin.initializeApp();
// Third party service credentials generation during onCreate request
exports.
buildCredentials = functions.auth.user().onCreate((user) => {
// Request to Third party to generate an Client Access Token
axios({
method: "post",
url: "https://api.ThirdParty.com/api/v1/oauth/token",
data: "client_id=xxx&client_secret=yyy&grant_type=client_credentials&scope=all:all",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
})
.then(function (response) {
//handle success
console.log('new user created: ')
console.log(user.id);
console.log(response.data);
// We write a new document in the users collection with the user ID and the Client Access Token
const db = admin.firestore();
const newUser = {
uid: user.uid,
clientAccessToken: response.data.access_token
};
db.collection('users').doc(user.uid).set(newUser)
.catch(function (error) {
//handle error
console.log(error);
});
})
.catch(function (response) {
//handle error
console.log(response);
});
})
HTTPS onCall function
exports.paymentRequest = functions.https.onCall(async (data, context) => {
const clientAccessToken = data.clientAccessToken;
const recipientIban = data.recipientIban;
const recipientName = data.recipientName;
const paymentDescription = data.paymentDescription;
const paymentReference = data.paymentReference;
const productPrice = parseInt(data.productPrice);
const uid = data.uid;
const jsonData = {
"destinations": [
{
"accountNumber": recipientIban,
"type": "iban"
}
],
"amount": productPrice,
"currency": "EUR",
"market": "FR",
"recipientName": recipientName,
"sourceMessage": paymentDescription,
"remittanceInformation": {
"type": "UNSTRUCTURED",
"value": paymentReference
},
"paymentScheme": "SEPA_INSTANT_CREDIT_TRANSFER"
};
(async function(){
response = await axios({
method: "post",
url: "https://api.ThirdParty.com/api/v1/pay",
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
Authorization: `Bearer ${clientAccessToken}`,},
data: jsonData,
})
// We write a the payment request ID in the user's document
const db = admin.firestore();
const paymentRequestID = response.data.id;
db.collection('users').doc(uid).set({
paymentRequestID: paymentRequestID
}, { merge: true })
.catch(function (error) {
//handle error
console.log(error);
});
console.log(response.data)
return response.data
})()
})
Am I on the right track thinking that this is an async problem?
Or is it a Firebase/Firestore issue?
Thanks
You are not returning the promises returned by the asynchronous methods (axios() and set()), potentially generating some "erratic" behavior of the Cloud Function.
As you will see in the three videos about "JavaScript Promises" from the official Firebase video series you MUST return a Promise or a value in a background triggered Cloud Function, to indicate to the platform that it has completed, and to avoid it is terminated before the asynchronous operations are done or it continues running after the work has been completed.
The following adaptations should do the trick (untested):
onCreate Function:
buildCredentials = functions.auth.user().onCreate((user) => {
// Request to Third party to generate an Client Access Token
return axios({
method: "post",
url: "https://api.ThirdParty.com/api/v1/oauth/token",
data: "client_id=xxx&client_secret=yyy&grant_type=client_credentials&scope=all:all",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
})
.then(function (response) {
//handle success
console.log('new user created: ')
console.log(user.id);
console.log(response.data);
// We write a new document in the users collection with the user ID and the Client Access Token
const db = admin.firestore();
const newUser = {
uid: user.uid,
clientAccessToken: response.data.access_token
};
return db.collection('users').doc(user.uid).set(newUser)
})
.catch(function (response) {
//handle error
console.log(response);
return null;
});
})
Callable Function:
exports.paymentRequest = functions.https.onCall(async (data, context) => {
try {
const clientAccessToken = data.clientAccessToken;
const recipientIban = data.recipientIban;
const recipientName = data.recipientName;
const paymentDescription = data.paymentDescription;
const paymentReference = data.paymentReference;
const productPrice = parseInt(data.productPrice);
const uid = data.uid;
const jsonData = {
// ...
};
const response = await axios({
method: "post",
url: "https://api.ThirdParty.com/api/v1/pay",
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
Authorization: `Bearer ${clientAccessToken}`,
},
data: jsonData,
})
// We write a the payment request ID in the user's document
const db = admin.firestore();
const paymentRequestID = response.data.id;
await db.collection('users').doc(uid).set({
paymentRequestID: paymentRequestID
}, { merge: true })
console.log(response.data)
return response.data
} catch (error) {
// See https://firebase.google.com/docs/functions/callable#handle_errors
}
})

Extracting values from API data in node.js

Thank you for your time.
I'm trying to use OAuth2 in discord, but I'm having a hard time figuring out how to retrieve the username.
Can someone please tell me how to do it?
code
const fetch = require('node-fetch');
const express = require('express');
const app = express();
app.get('/', async ({ query }, response) => {
const { code } = query;
if (code) {
try {
const oauthResult = await fetch('https://discord.com/api/oauth2/token', {
method: 'POST',
body: new URLSearchParams({
client_id: process.env['ci'],
client_secret: process.env['cs'],
code,
grant_type: 'authorization_code',
redirect_uri: `https://oauth.aiueominato1111.repl.co`,
scope: 'identify',
}),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
});
const oauthData = await oauthResult.json();
const userResult = await fetch('https://discord.com/api/users/#me', {
headers: {
authorization: `${oauthData.token_type} ${oauthData.access_token}`,
},
});
console.log( await userResult.json());
} catch (error) {
// NOTE: An unauthorized token will not throw an error;
// it will return a 401 Unauthorized response in the try block above
console.error(error);
}
}
return response.sendFile('index.html', { root: '.' });
});
app.listen(port, () => console.log(`App listening at http://localhost:${port}`));
thank you
You already have the JSON data (from await userResult.json()) with it you can either get the property from the object or destructure it.
Getting property
const user = await userResult.json();
const username = user.username
Destructuring
const { username, id } = await userResult.json()
You can find out more about what properties you can extract from the Discord documentation

I get undefined when reading my response but there is a response in React.js

I can't figure it out, the answer comes in the network table but when I want to console.log it, this will display undefined. Do you have any idea why? I attach the pictures and the code.
Here is a image with my codes and response
Here is the code - first one is where I send the response. As I said, it's going well on network tab, I get a 200 status.
export const getAccountStatus = async (req, res) => {
const user = await User.findById(req.user._id).exec();
const account = await stripe.accounts.retrieve(user.stripe_account_id);
// console.log("user account retrieve", account);
const updatedUser = await User.findByIdAndUpdate(
user._id,
{
stripe_seller: account
},
{ new: true }
)
.select("-password")
.exec();
console.log(updatedUser);
res.send(updatedUser);
};
Here is the page where i want to console.log it:
const StripeCallback = ({ history }) => {
const { auth } = useSelector(state => ({ ...state }));
const dispatch = useDispatch();
useEffect(() => {
if (auth && auth.token) accountStatus();
}, [auth]);
const accountStatus = async () => {
try {
const res = await getAccountStatus(auth.token);
console.log(res);
} catch (err) {
console.log(err);
}
};
return <div>test</div>;
};
Ang here is the Axios.post (which is working well as I know):
export const getAccountStatus = async token => {
await axios.post(
`${process.env.REACT_APP_API}/get-account-status`,
{},
{
headers: {
Authorization: `Bearer ${token}`
}
}
);
};
Thank you!
getAccountStatus doesn't have a return statement, so res in const res = await getAccountStatus(auth.token); will always be undefined.
export const getAccountStatus = async token => {
return axios.post( // <----- added return
`${process.env.REACT_APP_API}/get-account-status`,
{},
{
headers: {
Authorization: `Bearer ${token}`
}
}
);
};

Jest mocking a function within another function

I have searched quite a bit, and whilst there are many questions asked about mocking functions, none of the solutions I've found work for my setup, and I'm not quite sure why. I have the following:
questionnaire-service.js
const service = require('./request-service')();
function questionnaireService() {
function createQuestionnaire() {
const opts = {
url: `http://some-url.com`,
body: {
data: "some-data"
}
};
return service.post(opts);
}
return Object.freeze({
createQuestionnaire
});
}
module.exports = questionnaireService;
request-service.js
const got = require('got');
const merge = require('lodash.merge');
function requestService() {
function post(options) {
let opts = {
method: 'POST',
headers: {
accept: 'application/json',
'Content-Type': 'application/json'
},
json: true,
body: options.body
};
opts = merge(opts, options);
return got(opts.url, opts);
}
return Object.freeze({
post
});
}
module.exports = requestService;
I am trying to write tests for the questionnaire service, and want to mock the 'post' function. I have tried the following
questionnaire-service.test.js
const requestService = require('./request-service')();
const questionnaireService = require('./questionnaire-service')();
const createdQuestionnaire = require('./test-fixtures/res/get_questionnaire.json');
describe('questionnaire service routes', () => {
it('Should create a new questionnaire', async () => {
const spy = jest.spyOn(requestService.post);
spy.mockReturnValue(createdQuestionnaire);
const response = await questionnaireService.createQuestionnaire();
expect(requestService.post).toBeCalled();
expect(response).toMatch(createdQuestionnaire);
}
it('Should create a new questionnaire', async () => {
jest.doMock('./questionnaire-service', () =>
jest.fn(() => ({
createQuestionnaire: () => createdQuestionnaire
}))
);
const response = await questionnaireService.createQuestionnaire();
expect(questionnaireService.createQuestionnaire).toBeCalled();
expect(response).toMatch(createdQuestionnaire);
}
it('Should create a new questionnaire', async () => {
jest.doMock('./request-service', () =>
jest.fn(() => ({
post: () => createdQuestionnaire
}))
);
const response = await questionnaireService.createQuestionnaire();
expect(requestService.post).toBeCalled();
expect(response).toMatch(createdQuestionnaire);
}
});
All of the above yield the same error: RequestError: getaddrinfo ENOTFOUND some_token some_token:443 which sounds like it's being thrown by the 'GOT' module not finding a url to hit. Can someone shed some light on how to get this to work correctly?
When you make a require("somemodule") it loads this module and executes it, so it executes require calls inside this module. Then all the modules are cached, so whenever you make a require again, it doesn't execute again, and returns already cached dependencies.
When you do doMock, you need to reset this cache, and make new require in your test for everything, that depends on mocked module.
jest.doMock('./questionnaire-service', () =>
jest.fn(() => ({
createQuestionnaire: () => createdQuestionnaire
}))
);
jest.resetModules();
const questionnaireService = require(`questionnaire-service`)();
const response = await questionnaireService.createQuestionnaire();

Resources