1

I'm facing a strange problem in my automated tests written using Protractor. We need to test a bunch of API endpoints that return JSON over HTTP, as opposed to actual websites so instead of relying on Protractor, my team decided to use Chakram.

I have a page object responsible for accessing the API:

const qs = require('querystring')
const chakram = require('chakram');

function MyApi() {

  const domain = // read from a configuration file

  this.readImportantBusinessData = (queryParams) => {
    const serviceUrl = `${domain}/services/seriousBusiness.json`;
    const queryString = qs.stringify(queryParams);
    const fullUrl = `${serviceUrl}?${queryString}`;
    return chakram.get(fullUrl).then((response) => {
      return response;
    });
  };
};

module.exports = new MyApi();

then, in one of my specs, I call the readImportantBusinessData function to check if it returns the expected data.

return MyApi.readImportantBusinessData(validParameters).then((response) => {
        chakramExpect(response).to.have.status(HTTP_200_OK);
        chakramExpect(response).to.have.json({
            "foo" : "bar"
        });
      });

Depending on the enviornment where I run this code, the test passes or fails with an error message that basically means that no response has been received.

Failed: Cannot read property 'statusCode' of undefined

I can confirm that the server I'm hitting is running and I can get a correct response when using a web browser.

The rquest made in my tests succeeds when I use a shared server hosted in AWS and fails when I use a local server running in VirtualBox.

Why could Chakram not recieve a response at all?

toniedzwiedz
  • 17,895
  • 9
  • 86
  • 131
  • I tried this same thing but `${serviceUrl}?${queryString}` doesn't seem to pick up the `queryString`. Any idea why? – demouser123 May 01 '18 at 09:51
  • @demouser123 what do you mean? Does it show as empty? In my snippet, `queryString` is just a string value I build based on a JSON object in the first few lines of that function. If you're using the same library, perhaps you're passing an empty object? – toniedzwiedz May 01 '18 at 09:54
  • Yeah it's coming as empty. I passed the required parameters in the `MyApi.readImportantBusinessData(validParameters)` call, but it shows the empty parameters. Let me know if you want to elaborate it with a question. I can provide with the example – demouser123 May 01 '18 at 10:11
  • @demouser123 this does sound like a separate question. Probably a minor error that's tough to spot. Ask away. – toniedzwiedz May 01 '18 at 10:13

2 Answers2

1

The root cause

The calls to expect just showed undefined but I managed to log the whole response object, as returned by chakram.get

{ error: { Error: self signed certificate
  at TLSSocket.<anonymous> (_tls_wrap.js:1103:38)
  at emitNone (events.js:106:13)
  at TLSSocket.emit (events.js:208:7)
  at TLSSocket.finishInit (_tls_wrap.js:637:8)
  at TLSWrap.ssl.onhandshakedone (_tls_wrap.js:467:38) code: 'DEPTH_ZERO_SELF_SIGNED_CERT' },
  response: undefined,
  body: undefined,
  jar:
       RequestJar {
         jar: CookieJar { enableLooseMode: true, store: { idx: {} } } 
       },
  url: 'https://www.example.com/myService.json?foo=bar',
  responseTime: 29.524212 
}

This means Chakram had an issue with the self-signed certificate I was using for my local dev environment. The other servers are hosted in the cloud and have their certificates set up properly.

A workaround

A quick Google search returned a lot of suggestions to modify a global parameter that drives this behaviour by setting:

process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

However, this affects the whole node process, not just my test. These are just tests that I'm running locally and not something that has to do with actual user data. Still, if I am to make the security of these calls more lax, I need this to limit the scope of such a change as much as possible.

Thankfully, chakram.get uses the request library which makes it possible to customize requests quite heavily.

chakram.get allows me to pass:

params Object optional additional request options, see the popular request library for options

These options, in turn allow me to specify:

agentOptions - and pass its options. Note: for HTTPS see tls API doc for TLS/SSL options and the documentation above.

Finally, in the agentOptions, one can pass a rejectUnauthorized value that allows the certificate error to be ignored for the single request being made.

Therefore, in my page object, I could use:

return chakram.get(fullUrl, {
  agentOptions : {
    //Hack to allow requests to the local env with a self-signed cert
    rejectUnauthorized : false
  }
}).then((response) => {
  return response;
});

This allows the test to succeed despite using a self-signed certificate.

The solution

The solution is to provide a valid certificate for every environment and domain in which case the problem does not exist in the first place.

toniedzwiedz
  • 17,895
  • 9
  • 86
  • 131
  • How did you log the response from chakram.get in the first part of your answer? – Mnebuerquo May 26 '22 at 12:48
  • @Mnebuerquo I can't remember but I"m assuming I attached a debugger or just printed `JSON.stringify(response)` before calling `chakramExpect`. The response object is passed as a parameter to the anonymous function passed to `then` – toniedzwiedz May 27 '22 at 09:12
-1

This is because its asynchronous and ur function is getting executed without even completing your chakram request. wait for request completion to get response.

Ritdhwaj
  • 16
  • 1
  • That's not the case. The `get` function returns a promise. The block I pass to `then` is only evaluated after the request completes. – toniedzwiedz Mar 25 '18 at 17:04