Accessing JSON values with jasmine-node / frisby - javascript

I am trying to construct a test that will validate the error message received for a bad request to an API. I am using node-jasmine and frisby to write the tests but having trouble accessing the JSON values with dot notation.
var frisby = require('frisby');
frisby.create('JSON Error')
.put('url', {
}, {json: true})
.expectStatus(400)
.expectHeaderContains('content-type', 'application/json')
.auth('username', 'password')
.afterJSON(function (body) {
//changed values
expect(body.error.newEmail).toMatch('missing-required-key'),
expect(body.error.oldEmail).toMatch('missing-required-key')
})
.toss();
Expected JSON response body
{
"error": {
"newEmail": "missing-required-key",
"oldEmail": "missing-required-key"
}
}
Test results in following error:
Cannot read property newEmail of undefined
I have wrote a similar test in Postman which works, I am trying to convert these tests into something that can be used by node.js.
Postman working example:
var body = JSON.parse(responseBody);
//error messages
var newEmailError = body.error.newEmail;
var oldEmailError = body.error.oldEmail;
tests["New Email Error Message"] = newEmailError === "missing-required-key";
tests["Old Email Error Message"] = oldEmailError === "missing-required-key";

Turns out the endpoint supplied was incorrect, test is valid with the correct endpoint!

you need too do that:
frisby.create('GET JSON data with parameters')
.get('your_json_url.com')
.expectStatus(200)
.expectHeader('Content-Type', 'application/json')
.expectJSON({ 'error': {
'newEmail': 'missing-required-key'
}})
.toss();

Related

Jest test that simulates throwing an Axios exception fails with empty error message

I am trying to write a Jest test to cover a scenario whereby an axios.post (in the code I am trying to test) throws and handles an exception. The test successfully throws an error for a mocked axios instance and the appropriate catch block in the code I am wishing to test is reached. However, the error/exception message is empty. This then causes some assertions that I am trying to do in the test to fail.
The relevant section of the code to test looks as follows:
try {
// Call the token endpoint with the client/user credentials and check the response.
const { status, data } = axios.post(authenticationConfig.tokenEndpoint,
'grant_type=client_credentials', { headers });
if (status === StatusCodes.OK) {
...
}
}
catch(err) {
console.log(JSON.stringify(err));
res.status(StatusCodes.UNAUTHORIZED);
res.json(err.response.data.error);
}
The corresponding test looks like:
it('cannot get access token', async () => {
const response = {
response: {
data: {
error: 'My error'
}
}
};
const req = {
headers: {
'authorization': 'Basic client_id:client_secret'
}
};
mockedAxios.mockImplementation(() => {
throw new Error(response);
});
const provide = await authenticationMiddleware.provide(req, res, next);
await provide(req, res, next);
expect(mockedAxios).toBeCalledTimes(1);
expect(res.status).toHaveBeenCalledTimes(1);
expect(res.status).toHaveBeenCalledWith(StatusCodes.UNAUTHORIZED);
});
The err object in the catch block is logged out as an empty object even though from the test I'm throwing an error with a fully populated object. The test passes if I remove the 'res.json' statement from the catch block.
● authentication middleware › cannot get access token
TypeError: Cannot read property 'data' of undefined
89 | console.log(JSON.stringify(err));
90 | res.status(StatusCodes.UNAUTHORIZED);
> 91 | res.json(err.response.data.error);
Any ideas most welcome please. No doubt the way that I'm mocking Axios and causing it to throw an exception is wrong. The code does enter the catch block but the 'err' object is empty for some reason.
Many thanks.

PG-Promise Proc Erroring Out with Unknown Parameter Type

We are attempting to write a PostgreSQL Procedure to insert data into a table. We have created the procedure and ran said procedure with the variables below and it inserts just fine. However, when we try to use pg-promise with our express server, our string parameters are being read as unknown. When we iterate over the post body, we see that each parameter is reading as the type we expect to go in, and PostgreSQL is reading integer correctly, but it isn't reading string correctly. I've attempted to use the as.text function and it sends in the parameter value as "''" but that still reads as unknown. Is there something we are missing to successfully post the data?
let createInspection = async (req, res, next) => {
try {
let params = [];
for (let prop in req.body) {
console.log(typeof req.body[prop]);
params.push(req.body[prop]);
}
console.log(params)
let data = await db.proc('Inspections_Create', params);
res.status(200)
.json({
status: 'success',
data: data,
message: 'Inserted Inspection'
});
}
catch (error) {
return next(error);
}
}

Return XML in Firebase Cloud Function

I am trying to set up a cloud function that returns an xml. I am able to create and log the xml, but it crashes with the following error when I try to return it.
TypeError: Converting circular structure to JSON
at Object.stringify (native)
at stringify (/var/tmp/worker/node_modules/express/lib/response.js:1119:12)
at ServerResponse.json (/var/tmp/worker/node_modules/express/lib/response.js:260:14)
at ServerResponse.send (/var/tmp/worker/node_modules/express/lib/response.js:158:21)
at cors (/user_code/index.js:663:21)
at cors (/user_code/node_modules/cors/lib/index.js:188:7)
at /user_code/node_modules/cors/lib/index.js:224:17
at originCallback (/user_code/node_modules/cors/lib/index.js:214:15)
at /user_code/node_modules/cors/lib/index.js:219:13
at optionsCallback (/user_code/node_modules/cors/lib/index.js:199:9)
My Function
exports.sendXMLResponeSample = functions.https.onRequest((request, response) => {
cors(request, response, () => {
// import xmlbuilder
const builder = require('xmlbuilder');
// create my object to convert to xml
var myFeedObject = {
"somekey": "some value",
"age": 59,
"eye color": "brown"
}
// convert myFeedObject to xml
const feed = builder.create(myFeedObject, { encoding: 'utf-8' })
console.log("feed.end({ pretty: true }) = (below)");
console.log(feed.end({ pretty: true }));
// return xml
return response.send(200, feed) // <<< error occurs here
})
})
I believe the error suggests the the firebase cloud function is expecting I return a JSON object in the response rather than an xml object, but I am unsure how to tell it to expect an xml object in the response.
Does anyone understand how to return an xml object in a firebase cloud function?
EDIT: The object is converted to an xml object without any issue. The error occurs when the xml object is attempted to be returned.
You can use the .contentType(type: string) on the response object that the cloud function returns to the caller.
Like so:
res.status(200)
.contentType('text/xml; charset=utf8')
.send(xmlString);
You may install the object-to-xml library, and then set the response data type in the response header to text/XML, something like res.header('Content-type','text/xml').
This is what I'm doing.
const xmlString =
'<?xml version="1.0" encoding="UTF-8"?><Response><Message><Body>This is the
response</Body></Message></Response>';
res
.set("Content-Type", "text/xml; charset=utf8")
.status(200)
.send(xmlString);
Works for me. I'm sure there is a better way to convert your XML to a string.

Node.Js Slack App Https request 'Uncaught Exception: Converting circular structure to JSON'

I am trying to create a slack app, using stdlib that can take a simple text argument and then use that information to make an https request to a url and get a json response back. However, when I try to make this request and output the string to slack as a text response, I get the following error:
Critical Error: TypeError: Converting circular structure to JSON
at JSON.stringify (<anonymous>)
at Domain.criticalExecution.run ([eval]:86:45)
at Domain.run (domain.js:242:14)
at callback ([eval]:66:23)
at func.index.apply.params.concat ([eval]:199:51)
at module.exports (/var/task/functions/commands/price.js:23:3)
at Domain.functionExecution.run ([eval]:197:22)
at Domain.run (domain.js:242:14)
at process.nextTick ([eval]:196:27)
at _combinedTickCallback (internal/process/next_tick.js:131:7)
Now, I can't see anywhere in the response json that makes it cyclical, since it is just a text response, I don't add to it, so it confuses me.
Below is the source code:
module.exports = (user, channel, text = '', command = {}, botToken = null, callback) => {
callback(null, {
response_type: 'in_channel',
text: getString()
});
};
function getString() {
return getNasaData('DEMO_KEY');
}
function getNasaData(key = 'DEMO_KEY', callback) {
return https.get({
host: 'api.nasa.gov',
path: '/planetary/apod?api_key=${key}'
}, (response) => {
let body = '';
response.on('data', (data) => {
body += data;
});
response.on('end', () => {
let parsed = JSON.parse(body);
callback({
copyright: parsed.copyright,
date: parsed.date
});
});
});
};
I've looked around for solutions and its unclear what would cause this exception other than a cyclical reference. Perhaps it is to do with the callbacks I'm using in the two methods?
Thank you for any help.

Custom Boom error messages

On my Hapi.js server, I'd like to send a specific message if an account does not have permission rights to access an api endpoint. The Boom message I have right now looks like this:
return reply(Boom.unauthorized("unauthorized access to this API."));
This returns a message that looks like this:
{
"statusCode": 401,
"error": "Unauthorized",
"message": "unauthorized access to this API."
}
I would like to make it more customized to look like this:
{
"success": false,
"message": "unauthorized access to this API.",
"csrf-decorator": "",
"redirect": ""
}
Do we have access to customize the Boom error messages?
Thanks!
Boom comes with built in response error transformation. So in order to achieve my results I had reformat my error reply in the following way:
const error = Boom.forbidden("Sorry, you are restricted in accesssing this API. No soup for you!.");
error.output.statusCode = 403; // Assign a custom error code
error.output.payload["csrf-decorator"] = request.headers["csrf-decorator"];
error.reformat();
return reply(error);
According to the Hapijs documentation you can reformat error messages to customize them:
Error transformation
Errors can be customized by changing their output content. The boom error object includes the following properties:
isBoom - if true, indicates this is a Boom object instance.
message - the error message.
output - the formatted response. Can be directly manipulated after object construction to return a custom error response. Allowed root keys:
statusCode - the HTTP status code (typically 4xx or 5xx).
headers - an object containing any HTTP headers where each key is a header name and value is the header content.
payload - the formatted object used as the response payload (stringified). Can be directly manipulated but any changes will be lost if reformat() is called. Any content allowed and by default includes the following content:
statusCode - the HTTP status code, derived from
error.output.statusCode.
error - the HTTP status message (e.g. 'Bad Request', 'Internal Server
Error') derived from statusCode.
message - the error message derived from error.message.
inherited Error properties.
It also supports the following method:
reformat() - rebuilds error.output using the other object properties.
const Boom = require('boom');
const handler = function (request, h) {
const error = Boom.badRequest('Cannot feed after midnight');
error.output.statusCode = 499; // Assign a custom error code
error.reformat();
error.output.payload.custom = 'abc_123'; // Add custom key
throw error;
});
When a different error representation is desired, such as an HTML page or a different payload format, the onPreResponse extension point may be used to identify errors and replace them with a different response object.
const Hapi = require('hapi');
const Vision = require('vision');
const server = Hapi.server({ port: 80 });
server.register(Vision, (err) => {
server.views({
engines: {
html: require('handlebars')
}
});
});
const preResponse = function (request, h) {
const response = request.response;
if (!response.isBoom) {
return h.continue;
}
// Replace error with friendly HTML
const error = response;
const ctx = {
message: (error.output.statusCode === 404 ? 'page not found' : 'something went wrong')
};
return h.view('error', ctx).code(error.output.statusCode);
};
server.ext('onPreResponse', preResponse);

Categories

Resources