2

I have a frisbyjs test inside another frisbyjs test's afterJSON() that is failing. When I debug the server code, it appears that the x-access-token and x-key HTTP headers are not getting sent. Am I sending them wrong? Surely I'm doing something silly.

Below is the outer test. The first test in afterJSON() is the one that fails:

frisby.create('Should be able to log in with test user')
.post(appHost + '/login',
    {
        username:'test@voxoid.com',
        password:'pass123'
    },
    {json: true},
    {headers: {'Content-Type': 'application/json'}})
.expectStatus(200)
.expectJSONTypes({ token: String })
.expectJSON({
    user: {
        name:'test',
        role:'admin',
        username:'test@voxoid.com'
    }
})
.afterJSON(function(res) {
    // TODO: The functionality works, but this test does not; the headers do not get sent.
    console.log('x-access-token: ' + res.token);
    console.log('x-key: ' + res.user.username);

    // **************** THIS IS THE TEST THAT FAILS ********************
    frisby.create('Should allow access with valid token')
        .get(appHost + '/api/v1/products',{},
            {json:true},
            {headers:{
                'x-access-token': res.token,
                'x-key': res.user.username
            }})
        .inspectJSON()
        .expectStatus(200)
        .toss();

    frisby.create('Should not allow access with invalid token')
        .get(appHost + '/api/v1/products',{},
        {json:true},
        {headers:{
            'x-access-token': res.token + '123',
            'x-key': res.user.username
        }})
        .expectStatus(401)
        .toss();
})
.toss();

The inspectJSON() results in:

{ status: 401, message: 'Invalid Token or Key' }

And here is the server code (an express middleware) handling the request, where token and key both end up 'undefined' while debugging, and res.headers does not contain the x-access-token nor x-key headers:

var jwt = require('jwt-simple');
var validateUser = require('../routes/auth').validateUser;

module.exports = function(req, res, next) {

  // When performing a cross domain request, you will recieve
  // a preflighted request first. This is to check if our the app
  // is safe. 

  // We skip the token outh for [OPTIONS] requests.
  //if(req.method == 'OPTIONS') next();

  var token = (req.body && req.body.access_token) || (req.query && req.query.access_token) || req.headers['x-access-token'];
  var key = (req.body && req.body.x_key) || (req.query && req.query.x_key) || req.headers['x-key'];

  if (token || key) {
    try {
      var decoded = jwt.decode(token, require('../config/secret.js')());

      if (decoded.exp <= Date.now()) {
        res.status(400);
        res.json({
          "status": 400,
          "message": "Token Expired"
        });
        return;
      }

      // Authorize the user to see if s/he can access our resources

      var dbUser = validateUser(key); // The key would be the logged in user's username
      if (dbUser) {


        if ((req.url.indexOf('admin') >= 0 && dbUser.role == 'admin') || (req.url.indexOf('admin') < 0 && req.url.indexOf('/api/v1/') >= 0)) {
          next(); // To move to next middleware
        } else {
          res.status(403);
          res.json({
            "status": 403,
            "message": "Not Authorized"
          });
          return;
        }
      } else {
        // No user with this name exists, respond back with a 401
        res.status(401);
        res.json({
          "status": 401,
          "message": "Invalid User"
        });
        return;
      }

    } catch (err) {
      res.status(500);
      res.json({
        "status": 500,
        "message": "Oops something went wrong",
        "error": err
      });
    }
  } else {
    res.status(401);
    res.json({
      "status": 401,
      "message": "Invalid Token or Key"
    });
    return;
  }
};
voxoid
  • 1,164
  • 1
  • 14
  • 31

1 Answers1

1

Yes, it's something simple - "almost a typo". Here's the working code:

frisby.create('Should allow access with valid token')
  .get(appHost + '/api/v1/products', {
     json: true,
     headers: {
       'x-access-token': res.token,
       'x-key': res.user.username
     }
  })
  .inspectJSON()
  .expectStatus(200)
  .toss();

Notice how we're passing single options object to .get(), instead of three separate ones (for json and headers, and one empty at the beginning).

Also: if most of your requests will contain these headers, it might be useful to set them globally. This applies to other options, too:

frisby.globalSetup({
  request: { 
    json: true,
    headers: {
      'x-access-token': res.token,
      'x-key': res.user.username
    }
  }
});

frisby.create('Should allow access with valid token')
  .get(appHost + '/api/v1/products') //no need for options - they're already set!
  .inspectJSON()
  .expectStatus(200)
  .toss();

frisby.create('Should not allow access with invalid token')
  .get(appHost + '/api/v1/products', {
    // ...but you still can override them - when needed
    headers: {
      'x-access-token': res.token + '123',
      'x-key': res.user.username
    }
  })
  .expectStatus(401)
  .toss();
bardzusny
  • 3,788
  • 7
  • 30
  • 30
  • Thank you for the detailed response! I made the changes you recommended, yet even after that, the headers are still not passed. Any other ideas? I wish the frisbyjs documentation were as detailed as your response. – voxoid May 19 '15 at 11:22
  • 1
    Yes, Frisby.js documentation is far from perfect. It used to be my playtoy, but I abandoned it some time ago - I guess mostly because it's based on jasmine-node, which still isn't using Jasmine 2.0 (it's in beta) - and switched to using SuperTest: https://github.com/visionmedia/supertest . About your case - I'm afraid I'm out of ideas, only thing I can suggest is omitting `json: true` option for `.get()` requests. I see this option breaking them down here (errors on Frisby side). Also remember to pass options object as *second* argument to `.get()` calls, and *third* to `.post()`. Good luck! – bardzusny May 19 '15 at 16:35
  • Thanks again bardzusny! frisbyjs was eating more time than it was worth to me, so I switched to using plain jasmine-node and request, and found i don't miss whatever extra frisbyjs might provide. But supertest does look nice, less verbose than plain jasmine-node/request. – voxoid May 20 '15 at 10:51