3

From all the documents and examples I've read, it should be possible to persist a session in supertest using an agent:

var app = require('../../../server'),
    should = require('should'),
    request = require('supertest'),
    mongoose = require('mongoose'),
    User = mongoose.model('User'),
    _ = require('lodash');

var user = {
    name: 'Sterling Archer',
    email: 'duchess@isis.com',
    password: 'guest'
};

describe('user.me', function() {

    var url = '/user';
    var agent = request.agent(app);
    var new_user = new User(user);
    new_user.save();

    it('should return a user object', function(done) {

        agent
            .post('/signin')
            .send(_.omit(user, 'name'))
            .expect(200).end(function(err, res) {
                console.log(res.headers['set-cookie']);
            });

        agent
            .get(url)
            .expect(200)
            .end(function(err, res) {
                should.not.exist(err);
                console.log(res.headers['set-cookie']);
                res.body.should.have.property('user');
                res.body.user.should.have.properties('name', 'email');
                done();
            });

    });
});

The session should persist since each request above is using the same agent. However that doesn't seem to be the case - the output from the set-cookie logs follows:

[ 'connect.sid=s%3AsFl1DQ4oOxC8MNAm79mnnr9q.gMkp8iEWtG8XlZZ2rkmheBwxKAyLyhixqDUOkYftwzA; Path=/; HttpOnly' ]
[ 'connect.sid=s%3AEzfbPyRGMff7yBXc9OAX3vGT.Ze2YoxZzuB6F6OwOk7mvrk96yPP2G4MGV%2Bt1rVjTyS8; Path=/; HttpOnly' ]

passport.js is being used for authentication and sessions. I would expect connect.sid above to be constant for both requests, but it looks like a new session is being created on each call so the agent isn't logged in on the second call and no user object is returned.

When I test my app manually in a browser connect.sid remains constant after login and the functionality I'm testing does work.

I must be doing something wrong with agent, and I'm hoping someone can spot it. Otherwise, suggestions on how I could debug the issue would be much appreciated.

Matt
  • 1,041
  • 1
  • 16
  • 28
  • `.set('Cookie',)` method on second agent call, does it work? – Gntem Apr 03 '14 at 22:38
  • I had tried setting the cookie because I'd seen that approach in some other examples. However, it shouldn't be necessary and didn't work anyway; set() would be called on the second agent before the first call completed (so the cookie value would not yet be available.) – Matt Apr 07 '14 at 12:36

2 Answers2

4

You're sending the second request without waiting for the first one to be responded; if you don't give the agent time to receive the Set-Cookie header in the response and use its value as a the Cookie header in the same request, a new session will be created. Try it this way:

it('should return a user object', function(done) {

    agent
        .post('/signin')
        .send(_.omit(user, 'name'))
        .expect(200).end(function(err, res) {
            console.log(res.headers['set-cookie']);
            agent
                .get(url)
                .expect(200)
                .end(function(err, res) {
                    should.not.exist(err);
                    console.log(res.headers['set-cookie']); // Should print nothing.
                    res.body.should.have.property('user');
                    res.body.user.should.have.properties('name', 'email');
                    done();
                });
        });
});
Esteban
  • 2,540
  • 21
  • 27
  • Your suggestion didn't work, but you did point out that I had overlooked the asynchronous nature of the code. I had tried this once before; though I now have a better understanding of what's going on, I don't understand why this doesn't work either! Again, different cookies are logged (even where you commented that it should print nothing.) However, I found a solution, see below. – Matt Apr 04 '14 at 03:30
2

Esteban's suggestion pointed out that I was overlooking the asynchronous nature of the code. Going back to this example I realized I missed the significance of logging in in a separate test; doing so solved my problem.

Though I'm now creating dependent tests, which I'm not crazy about.

var app = require('../../../server'),
    should = require('should'),
    request = require('supertest'),
    mongoose = require('mongoose'),
    User = mongoose.model('User'),
    _ = require('lodash');

var user = {
    name: 'Sterling Archer',
    email: 'duchess@isis.com',
    password: 'guest'
};

var agent = request.agent(app);

describe('User Controller', function() {

before(function(done) {
    var new_user = new User(user);
    new_user.save();
    done();
});

describe('user.signin', function() {

    var url = '/signin';

    it('should signin and return a user object', function(done) {
        agent
            .post(url)
            .send(_.omit(user, 'name'))
            .expect(200)
            .end(function(err, res) {
                should.not.exist(err);
                res.body.should.have.property('user');
                res.body.user.should.have.properties('name', 'email');
                done();
            });
    });
});

describe('user.me', function() {

    var url = '/user';

    it('should return a user object', function(done) {
        agent
            .get(url)
            .expect(200)
            .end(function(err, res) {
                should.not.exist(err);
                res.body.should.have.property('user');
                res.body.user.should.have.properties('name', 'email');
                done();
            });
    });
});

after(function(done) {
    User.remove().exec();
    done();
});
});
Community
  • 1
  • 1
Matt
  • 1,041
  • 1
  • 16
  • 28
  • 1
    It's great that you got that working! I think I know why that worked but don't have the time to test it right now, unfortunately (hint: when `end` calls your callback the agent isn't done managing the `set-cookie` header). I'd improve your solution by throwing the login into a `before` function instead of a different test (though testing login is still a good idea). – Esteban Apr 07 '14 at 23:46
  • 1
    I've just found you're a victim of [this bug](https://github.com/visionmedia/superagent/issues/314). Fortunately, a pull request has been issued to fix it. – Esteban Apr 10 '14 at 16:01