it('When refreshing a token with used access token, then make sure exception is thrown', async () => {
//Arrange
const user = await createUser();
const { data } = await anonymousClient.post<LoginResponse>(
'auth/refresh',
user.response,
);
//Act
const post = () =>
anonymousClient.post<LoginResponse>('auth/refresh', {
accessToken: user.response.accessToken,
refreshToken: data.refreshToken,
});
//Assert
expect(post()).rejects.toMatchObject({ status: 401 }); // 200
});
My server throws 401, but for some reason when I also expect 200 or any other number it still pass the test.
How can I expect the error status from the server.
I don't want write .catch(e => expect(e.status).toEqual(401) I think its ugly....
Thanks
Related
I'm trying to implement a Firebase function that generates a custom token for my app. But I keep getting the following error message :
Error: could not handle the request
Or it ends up in timeout.
Do you have any idea of what could be wrong with my code hereafter ? I'm trying it with a 'test' uid.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const serviceAccount = require('./serviceAccountKey.json');
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
});
exports.customFunction = functions.https.onRequest((data, context) => {
return admin.auth().createCustomToken('test')
.then(customToken => {
console.log(`The customToken is: ${customToken}`);
return {
status: 'success',
customToken: customToken
};
})
.catch(error => {
console.error(`Something happened buddy: ${error}`)
return {
status: 'error'
};
});
});
Your Cloud Function is an HTTPS one. In order to terminate it you need to call res.redirect(), res.send(), or res.end() as explained in the doc.
In your code you actually return the Promises chain: this is the correct way to terminate Cloud function triggered by background events (which is not the case of an HTTPS Cloud Function which is triggered by a call to the URL it exposes).
So, the following changes should do the trick (untested):
exports.customFunction = functions.https.onRequest((req, res)(data, context) => {
admin.auth().createCustomToken('test') // No need to return
.then(customToken => {
console.log(`The customToken is: ${customToken}`);
response.status(200).send({
status: 'success',
customToken: customToken
});
})
.catch(error => {
console.error(`Something happened buddy: ${error}`)
response.status(500).send(error);
});
});
Note that with an HTTPS Cloud Function, the objects you pass to the handler are not the Firebase data and context objects but the Express.js request and response objects.
So it is more clear to write
exports.customFunction = functions.https.onRequest((req, res) => {...});
instead of
exports.customFunction = functions.https.onRequest((data, context) => {...});
I am testing API'S with JEST. I don't understand how I'm going to pass values to parameters in GET request.
describe("Refresh Token", () => {
it("Refresh Token", async() => {
const response = await request(app).get("/refreshtoken");
expect(response.status).toEqual(200);
expect(response.body.data).toEqual("hd$snndm12cdj2#Efvvxv");
});
})
In the above case, the output is expected as the given string. But the output is undefined. Besides that what I should do if I have multiple parameters. Below code is my post request code which is working perfectly. I want to pass multiple parameters as I defined in the post request.
describe('Set Profile Image', () => {
it('Set Profile Image', async() => {
const res = await request(app)
.post('/setProfileImage')
.send({
profileID: "1234",
profileImage: "fnsdjnfsnf"
})
expect(res.status).toBe(200)
})
});
Try passing the params in the URL of your request:
const response = await request(app).get("/refreshtoken?param1=123")
To pass multiple parameters, just do this:
const response = await request(app).get("/refreshtoken?param1=123¶m2=234")
The why
We're using the axios-retry library, which uses this code internally:
axios.interceptors.response.use(null, error => {
Since it only specifies the error callback, the Axios documentation says:
Any status codes that falls outside the range of 2xx cause this function to trigger
Unfortunately we're calling a non-RESTful API that can return 200 with an error code in the body, and we need to retry that.
We've tried adding an Axios interceptor before axios-retry does and changing the result status in this case; that did not trigger the subsequent interceptor error callback though.
What did work was specifying a custom adapter. However this is not well-documented and our code does not handle every case.
The code
const axios = require('axios');
const httpAdapter = require('axios/lib/adapters/http');
const settle = require('axios/lib/core/settle');
const axiosRetry = require('axios-retry');
const myAdapter = async function(config) {
return new Promise((resolve, reject) => {
// Delegate to default http adapter
return httpAdapter(config).then(result => {
// We would have more logic here in the production code
if (result.status === 200) result.status = 500;
settle(resolve, reject, result);
return result;
});
});
}
const axios2 = axios.create({
adapter: myAdapter
});
function isErr(error) {
console.log('retry checking response', error.response.status);
return !error.response || (error.response.status === 500);
}
axiosRetry(axios2, {
retries: 3,
retryCondition: isErr
});
// httpstat.us can return various status codes for testing
axios2.get('http://httpstat.us/200')
.then(result => {
console.log('Result:', result.data);
})
.catch(e => console.error('Service returned', e.message));
This works in the error case, printing:
retry checking response 500
retry checking response 500
retry checking response 500
retry checking response 500
Service returned Request failed with status code 500
It works in the success case too (change the URL to http://httpstat.us/201):
Result: { code: 201, description: 'Created' }
The issue
Changing the URL to http://httpstat.us/404, though, results in:
(node:19759) UnhandledPromiseRejectionWarning: Error: Request failed with status code 404
at createError (.../node_modules/axios/lib/core/createError.js:16:15)
at settle (.../node_modules/axios/lib/core/settle.js:18:12)
A catch on the httpAdapter call will catch that error, but how do we pass that down the chain?
What is the correct way to implement an Axios adapter?
If there is a better way to handle this (short of forking the axios-retry library), that would be an acceptable answer.
Update
A coworker figured out that doing .catch(e => reject(e)) (or just .catch(reject)) on the httpAdapter call appears to handle the issue. However we'd still like to have a canonical example of implementing an Axios adapter that wraps the default http adapter.
Here's what worked (in node):
const httpAdapter = require('axios/lib/adapters/http');
const settle = require('axios/lib/core/settle');
const customAdapter = config =>
new Promise((resolve, reject) => {
httpAdapter(config).then(response => {
if (response.status === 200)
// && response.data contains particular error
{
// log if desired
response.status = 503;
}
settle(resolve, reject, response);
}).catch(reject);
});
// Then do axios.create() and pass { adapter: customAdapter }
// Now set up axios-retry and its retryCondition will be checked
Workaround with interceptor and custom error
const axios = require("axios").default;
const axiosRetry = require("axios-retry").default;
axios.interceptors.response.use(async (response) => {
if (response.status == 200) {
const err = new Error("I want to retry");
err.config = response.config; // axios-retry using this
throw err;
}
return response;
});
axiosRetry(axios, {
retries: 1,
retryCondition: (error) => {
console.log("retryCondition");
return false;
},
});
axios
.get("https://example.com/")
.catch((err) => console.log(err.message)); // gonna be here anyway as we'll fail due to interceptor logic
I need to store my values from the request body to the cloud firestore and sent back the foruminsertdata.Name back in the response. But I am not able to do this.
const functions = require('firebase-functions');
const admin =require("firebase-admin");
admin.initializeApp(functions.config().firebase);
const db = admin.firestore();
exports.helloWorld = functions.https.onRequest((req, res) => {
if(req.method === 'POST'){
foruminsertdata = req.body;
db.collection('forum').add({
Name: foruminsertdata.Name,
Description: foruminsertdata.Description,
Heading: foruminsertdata.Heading,
PostedOn: foruminsertdata.PostedOn,
Status: foruminsertdata.Status,
})
.then(ref => {
console.log('Added document with ID: ', ref.id);
return res.status(200).json(
{
message: foruminsertdata.Name
});
})
.catch(err => {
console.log('Error getting documents', err);
});
res.json({
message: foruminsertdata.Status,
});
}
})
I don't know what is happening...Whatever I do I always get the output as
{
message: foruminsertdata.Status,
}
in which "foruminsertdata.Status" has some value that I give
but what I expect the output as
{
message: foruminsertdata.Name
}
Your function is immediately returning foruminsertdata.Status to the client without waiting for the promise from the database operations to resolve. Any function that returns a promise is asynchronous and returns immediately. Execution will continue in the callbacks you attach to it.
I'm not sure why you have two calls to res.json() in your code, but if you want to send a response only after your query completes, you'll remove the second one and just send a response after the query is done. You will probably also want to send a response in the catch callback as well to indicate an error.
I have some promise
getSomeInfo(data) {
return new Promise((resolve, reject) => {
/* ...some code... */
someObject.getData((err, info) => {
if (info) {
resolve(info)
}
else {
reject("Error")
}
})
})
}
I use this promise and want to send response to client from Controller (AdonisJS):
async create ({ request, response }) {
this.getSomeInfo(data).then(info => {
console.log(info) // It's work, i get the data from promise
response.status(201).json({ // but this is not work
code: 201,
message: "Data received!",
data: info
})
})
}
Why response is not work?
Simply do this.
async create ({ request, response }) {
const info = await this.getSomeInfo(data)
console.log(info)
response.status(201).json({
code: 201,
message: "Data received!",
data: info
})
}
When marking a function as async the function must return a Promise, this can be done explicitly.
async create({ request, response }) {
return this.getSomeInfo(data).then(info => {
console.log(info) // It's work, i get the data from promise
response.status(201).json({ // but this is not work
code: 201,
message: "Data received!",
data: info
})
})
}
Or implicitly using the await keyword.
async create({ request, response }) {
const info = await this.getSomeInfo(data)
console.log(info) // It's work, i get the data from promise
response.status(201).json({ // but this is not work
code: 201,
message: "Data received!",
data: info
})
}
If your console.log(info) inside of create() works and shows the data you want, but the response.status(201).json(...) does not send a response, then I can see the following possibilities:
You've already sent a response to this request (and thus cannot send another one)
The .json() method is having trouble converting info to JSON (perhaps because of circular references) and throwing an exception.
You aren't passing the arguments request and response properly and thus response isn't what it is supposed to be.
You can test for the second case like this:
create ({ request, response }) {
this.getSomeInfo(data).then(info => {
console.log(info) // It's work, i get the data from promise
response.status(201).json({ // but this is not work
code: 201,
message: "Data received!",
data: info
});
}).catch(e => {
console.log("Error in create()", e);
response.sendStatus(500);
});
}
Also, there is no reason for this method to be declared async as you don't show that you're using await or any of the features of an async function.
In the comments, you say that this function is called directly by a router (I assume an Express router). If that's the case, then the function arguments are not declared properly as they come as two separate arguments, not as properties of an object. Change the function declaration to this:
create (request, response) { ... }