Unable to make HTTP Post from custom Karma reporter - karma-runner

I need to publish my Karma test results to a custom REST API. To handle this automatically, I've written a custom Karma reporter. I'm trying to use the run_complete event so that the POST happens after all browsers finish. However, no HTTP call is being made.
I'm using Axios 0.19.2 to do the actual HTTP call, but the same thing happens with node-fetch. The tests are being run by the Angular cli via ng test. My Karma config is lengthy but other than having a million different reporters and possible browser configs, is pretty much standard.
This is my onRunComplete method:
self.onRunComplete = function () {
var report = ... ; // logic to generate a JSON object, not relevant
var url = '...'; // the endpoint for the request
try {
console.log('Sending report to ' + url);
axios.post(url, report, {headers: {'Content-Type': 'application/json'}})
.then(function(response) {
console.log('Success!');
console.log(response);
})
.catch(function(error) {
console.log('Failure!');
console.log(error);
});
} catch (err) {
console.log('Error!');
console.log(err);
}
}
At the end of the test run, it writes to console the 'Sending report to...' message, and then immediately ends. The server does not receive the request at all.
I also tried adding explicit blocking using a 'inProgress' boolean flag and while-loop, but that pretty much just leaves the entire test run hanging since it never completes. (Since the request is never made, the 'inProgress' flag is always true and we never hit the then/catch promise handlers or the catch block.)
I have verified that the Axios POST request works by taking the entire contents of the onRunComplete as shown here, putting it in its own JS file, and calling it directly. The report logs as expected. It's only when I call from inside of Karma that it's somehow blocked.
Since Karma's documentation pretty much boils down to "go read how other people did similar things!" I'm having trouble figuring out how to get this to work. Is there a trick to getting an HTTP request to happen inside of a custom reporter? Why does my implementation not work?

Looks like the post request is made asynchronously - that is the request is made and control resumes almost immediately to the method which completes... try instead:
self.onRunComplete = function () {
var report = ... ; // logic to generate a JSON object, not relevant
var url = '...'; // the endpoint for the request
try {
console.log('Sending report to ' + url);
await axios.post(url, report, {headers: {'Content-Type': 'application/json'}})
...
}
}

Related

Protractor not wait for https.request callback

I hit a Protractor issue, if I run this within "ts-node demo.js" it works well and can output the response code and response body.
But if I run this in Protractor it block, then can't get the expected output response code and body, seems like Protractor won't wait for the callback.
In this situation, how can I output the response code and boday?
it(Check manifests V2 api, async() => {
const https = require('https')
const options = {
hostname: 'demo-quayecosystem-quay-quay.com',
port: 443,
path: '/v2/quay/multiarchdemo/manifests/latest',
method: 'GET',
headers: {
'Accept': 'application/vnd.docker.distribution.manifest.list.v2+json'
}
}
https.request(options, res => {
browser.getTitle().then(()=>{
console.log("starting.........");
console.log(`statusCode: ${res.statusCode}`)
res.on('data', d => {
process.stdout.write(d)
})
})
})
})
The function above is an async and when you make your function async, then you should use the await keyword in front of the https.request or return it like this.
return https.request...
And another thing that could lead to problems are...
https.request is making a direct HTTP request, without using a browser
broser.getTitle() is using the browser to interact with the web page.
Be aware - browser uses the browser, and http.request uses direct HTTP from node.js - these are two different things. And it will lead to unpredictable things to mix them. So consider if you want to "test as a user" and then use the browser, or if you want to do the fastest possible test and "test as a website or javascript" and use HTTP.request.
Try reading the async/await page on the Protractor website.
https://www.protractortest.org/#/async-await
If you are puzzled about async/await consider seeing the video from Fun Fun Function on promises https://www.youtube.com/watch?v=568g8hxJJp4&t=251s.

error.response is undefined if axios request fails

I cannot access the error (response) status code if an axios request has failed in my Vue.js app. I cannot figure out why the response is undefined in both '.catch' and 'axios.interceptors.response'. I followed this instruction that demonstrates that 'error.response' can be easily accessed with a code like this:
axios.interceptors.response.use(
(response) => {
console.log(response);
return response;
},
(error) => {
handleApiFail(error.response);
});
If I add this code to 'main.js' in my app, 'handleApiFail' is called when a request fails, but error.response is undefined in the second lambda and the first lambda is not called. If a request succeeded the 'response' in the first lambda is defined and has the status code.
EDIT1: this is not an option because my OPTIONS requests do not require authorization. Also there are various posts describing the same situation.
The lack of
access-control-allow-origin: *
header in the response caused the browser to block my request.
Adding the header makes axios work fine.
I have code like this:
axios.interceptors.response.use(null, (error) => {
.........
return Promise.reject();
});
Then, I found I miss to return my "error" in promise reject, correct like this:
return Promise.reject(error);
This is an idiosyncrasy of axios. A quick solution to this is to serialize the response:
JSON.stringify(error)
Please refer to this GitHub issue for more info: https://github.com/axios/axios/issues/960
As someone pointed out there, you can check the error status code in the action and run some other commit depending on it.

vertx timeout if async result is failed

I am seeing a timeout in the browser when the server-side service ends in a failed result. Everything works fine if the service call succeeds but it seems as though the browser never receives a response if the call fails.
My service passes a result handler to a DAO containing the following code:
final SQLConnection conn = ar.result();
conn.updateWithParams(INSERT_SQL, params, insertAsyncResult -> {
if (insertAsyncResult.failed()) {
conn.close();
resultHandler.handle(ServiceException.fail(1, "TODO"));
} else {
resultHandler.handle(Future.succeededFuture());
}
});
I'm not sure where to go from here. How do I debug what the framework is sending back to the client?
The problem was that I needed to register a ServiceExceptionMessageCodec in an intermediate Verticle, one that was sitting between the browser and the Verticle that was performing the database operation.

Sails v1 new machine-based actions and custom responses

I'm in the middle of upgrading our API from Sails v0.12 -> v1, which was prompted by the use of self-validating machines for controller actions. After finally getting through a ton of headache replacing deprecated code, I've landed in a rough spot...
With v0.12 (rather, with the older "req, res" controller style), one could use custom response handlers across the board. I've taken advantage of this, and have request logging at the end of all our response types (with some additional sugaring of data). This was done to log all requests in the database, so we can get insights into what our production servers are doing (because they are load-balanced, having a central place to view this is a must, and this was an easy route to take).
So now, my problem is moving forward with "Actions2" machine-style actions. How does one use these custom response types in these things? Are we being forced to repeat ourselves in our exists? I can't find any good documentation to help guide this process, nor can I find a consistent way to "hook" into the end of a response using machines as actions. I can't find any documentation on what kind of options machines can give to Sails.
#Nelson yes, I understand that, but at the time, that isn't what I wanted at all. I wanted all of the benefits of Actions2.
EDIT: While the original, crossed-out comment below does still work, the prefered way to use Actions2 and the custom responses folder paradigm, is to do something similar to the following in an Actions2 file:
module.exports = {
friendlyName: 'Human-friendly name of function',
description: 'Long description of function and what it does.',
inputs: {
userCommand: {
type: 'string',
required: true,
description: 'Long, human-readable description of the input'
}
},
exits: {
success: {
responseType: 'chatbotResponse'
}
},
fn: async function(inputs, exits){
// do some crazy stuff with the inputs, which has already been validated.
return exits.success('Woot');
}
}
This ultimately will route through the responses/chatbotResponse.js, which looks something similar to this:
module.exports = async function chatbotResponse(data){
let res = this.res,
req = this.req;
if (!data) {
data = 'Something didn\'t go as planned...';
}
// how to call a Node Machine style helper with named inputs
await sails.helpers.finalizeRequestLog.with({req: req, res: res, body: {plainString: data}});
return res.json(data);
};
ORIGINAL:
As it turns out, in the Actions2 function, you just need to add the env param async function(inputs, exists, env). The env will give you access to the req and res. So, if you have custom responses, that perform special tasks (like request logging), you can just use return await env.res.customResponse('Hurray, you made a successful call!');

Empty response on long running query SailsJS

I'm currently running SailsJS on a Raspberry Pi and all is working well however when I execute a sails.models.nameofmodel.count() when I attempt to respond with the result I end up getting a empty response.
getListCount: function(req,res)
{
var mainsource = req.param("source");
if(mainsource)
{
sails.models.gatherer.find({source: mainsource}).exec(
function(error, found)
{
if(error)
{
return res.serverError("Error in call");
}
else
{
sails.log("Number found "+found.length);
return res.ok({count: found.length});
}
}
);
}
else
{
return res.ok("Error in parameter");
}
},
I am able to see in the logs the number that was found (73689). However when responding I still get an empty response. I am using the default stock ok.js file, however I did stick in additional logging to try to debug and make sure it is going through the correct paths. I was able to confirm that the ok.js was going through this path
if (req.wantsJSON) {
return res.jsonx(data);
}
I also tried adding .populate() to the call before the .exec(), res.status(200) before I sent out a res.send() instead of res.ok(). I've also updated Sails to 11.5 and still getting the same empty response. I've also used a sails.models.gatherer.count() call with the same result.
You can try to add some logging to the beginning of your method to capture the value of mainsource. I do not believe you need to use an explicit return for any response object calls.
If all looks normal there, try to eliminate the model's find method and just evaluate the request parameter and return a simple response:
getListCount: function(req, res) {
var mainsource = req.param("source");
sails.log("Value of mainsource:" + mainsource);
if (mainsource) {
res.send("Hello!");
} else {
res.badRequest("Sorry, missing source.");
}
}
If that does not work, then your model data may not actually be matching on the criteria that you are providing and the problem may lie there; in which case, your response would be null. You mentioned that you do see the resulting count of the query within the log statement. If the res.badRequest is also null, then you may have a problem with the version of express that is installed within sailsjs. You mention that you have 11.5 of sailsjs. I will assume you mean 0.11.5.
This is what is found in package.json of 0.11.5
"express": "^3.21.0",
Check for any possible bugs within the GitHub issues for sailsjs regarding express and response object handling and the above version of express.
It may be worthwhile to perform a clean install using the latest sailsjs version (0.12.0) and see if that fixes your issue.
Another issue may be in how you are handling the response. In this case .exec should execute the query immediately (i.e. a synchronous call) and return the response when complete. So there should be no asynchronous processing there.
If you can show the code that is consuming the response, that would be helpful. I am assuming that there is a view that is showing the response via AJAX or some kind of form POST that is being performed. If that is where you are seeing the null response, then perhaps the problem lies in the view layer rather than the controller/model.
If you are experiencing a true timeout error via HTTP even though your query returns with a result just in time, then you may need to consider using async processing with sailjs. Take a look at this post on using a Promise instead.