text: var keystone = require('../..');
  var sinon = require('sinon');
  
  describe('Keystone.session', function() {
  
  	describe('keystone.session.signinWithUser()', function() {
  		// mock args for signinWithUser(user, req, res, onSuccess)
  		var res = { cookie: sinon.stub() };
  		var onSuccess = sinon.stub();
  		var user;
  		var req;
  
  		function resetMocks() {
  			user = {
  				id: 'USERID',
  				password: 'PASSWORD'
  			};
  			req = {
  				user: null,
  				session: {
  					userId: null,
  					regenerate: function(callback) {
  						callback();
  					}
  				}
  			};
  		}
  
  		before(function() {
  			keystone.get('cookie secret', 'SECRET');
  			keystone.set('user model', 'User');
  		});
  
  		beforeEach(function() {
  			resetMocks();
  			sinon.spy(req.session, 'regenerate');
  		});
  
  		afterEach(function() {
  			req.session.regenerate.reset();
  			res.cookie.reset();
  			onSuccess.reset();
  		});
  
  		describe('with valid args, "cookie signin" on', function() {
  
  			it('should regenerate session, set user, session.userId, and res.cookie', function() {
  				keystone.set('cookie signin', true);
  				keystone.session.signinWithUser(user, req, res, onSuccess);
  
  				sinon.assert.calledOnce(req.session.regenerate);
  
  				req.user.must.equal(user);
  				req.session.userId.must.equal(user.id);
  
  				sinon.assert.calledOnce(res.cookie);
  				sinon.assert.calledWith(res.cookie, 'keystone.uid');
  
  				sinon.assert.calledOnce(onSuccess);
  				sinon.assert.calledWithExactly(onSuccess, user);
  			});
  
  		});
  
  		describe('with valid args, "cookie signin" off', function() {
  
  			it('should regenerate session, set user, session.userId', function() {
  				keystone.set('cookie signin', false);
  				keystone.session.signinWithUser(user, req, res, onSuccess);
  
  				sinon.assert.calledOnce(req.session.regenerate);
  
  				req.user.must.equal(user);
  				req.session.userId.must.equal(user.id);
  
  				sinon.assert.callCount(res.cookie, 0);
  
  				sinon.assert.calledOnce(onSuccess);
  				sinon.assert.calledWithExactly(onSuccess, user);
  			});
  
  		});
  
  		describe('with invalid args', function() {
  			it('should error when called less then 4 args', function() {
  				function callWithNoArgs() {
  					keystone.session.signinWithUser();
  				}
  				callWithNoArgs.must.throw('keystone.session.signinWithUser requires user, req and res objects, and an onSuccess callback.');
  
  				function callWithOneArg() {
  					keystone.session.signinWithUser(user);
  				}
  				callWithOneArg.must.throw('keystone.session.signinWithUser requires user, req and res objects, and an onSuccess callback.');
  
  				function callWithTwoArgs() {
  					keystone.session.signinWithUser(user, req);
  				}
  				callWithOneArg.must.throw('keystone.session.signinWithUser requires user, req and res objects, and an onSuccess callback.');
  
  				function callWithThreeArgs() {
  					keystone.session.signinWithUser(user, req, res);
  				}
  				callWithOneArg.must.throw('keystone.session.signinWithUser requires user, req and res objects, and an onSuccess callback.');
  			});
  
  			it('should error when user arg is not an object', function() {
  				function callWithInvalidUser() {
  					keystone.session.signinWithUser('user', req, res, onSuccess);
  				}
  				callWithInvalidUser.must.throw('keystone.session.signinWithUser requires user to be an object.');
  			});
  
  			it('should error when req arg is not an object', function() {
  				function callWithInvalidReq() {
  					keystone.session.signinWithUser(user, 'req', res, onSuccess);
  				}
  				callWithInvalidReq.must.throw('keystone.session.signinWithUser requires req to be an object.');
  			});
  
  			it('should error when res arg is not an object', function() {
  				function callWithInvalidRes() {
  					keystone.session.signinWithUser(user, req, 'res', onSuccess);
  				}
  				callWithInvalidRes.must.throw('keystone.session.signinWithUser requires res to be an object.');
  			});
  
  			it('should error when onSuccess arg is not a function', function() {
  				function callWithInvalidCallback() {
  					keystone.session.signinWithUser(user, req, res, 'onSuccess');
  				}
  				callWithInvalidCallback.must.throw('keystone.session.signinWithUser requires onSuccess to be a function.');
  			});
  
  		});
  
  	});
  
  	// TODO: need more test for keystone.session.signin()
  	describe('keystone.session.signin()', function() {
  
  		describe('case-insensitive email lookup', function() {
  
  			before(function() {
  				var self = this;
  				this.onSuccess = sinon.stub();
  				this.onFailure = sinon.stub();
  
  				// simulate User model
  				this.User = {
  					model: {
  						findOne: sinon.spy(function(query) {
  							self.query = query;
  							return this;
  						}),
  						exec: sinon.spy(function(callback) {
  							var email = 'test@test.com';
  							self.query.match = self.query.email.test(email);
  							if (self.query.match) {
  								return callback(null, self.user);
  							}
  							callback(new Error('not found'))
  						})
  					}
  				};
  
  				// simulate user instance
  				this.user = {
  					_: {
  						password: {
  							compare: sinon.spy(function(password, callback) {
  								callback(null, true);
  							})
  						}
  					}
  				};
  
  				keystone.set('user model', 'User');
  				sinon.stub(keystone, 'list').withArgs('User').returns(this.User);
  				sinon.stub(keystone.session, 'signinWithUser').callsArg(3);
  			});
  
  			after(function() {
  				keystone.list.restore();
  				keystone.session.signinWithUser.restore();
  			});
  
  			afterEach(function() {
  				delete this.query;
  
  				this.User.model.findOne.reset();
  				this.User.model.exec.reset();
  				this.onSuccess.reset();
  				this.onFailure.reset();
  
  				keystone.list.reset();
  				keystone.session.signinWithUser.reset();
  			});
  
  			it('shoud match email with mixed case', function (done) {
  				var lookup = { email: 'Test@Test.Com', password: 'password'};
  
  				keystone.session.signin(lookup, null, null, function(){
  					// make sure .findOne() is called with a regular expression
  					sinon.assert.calledOnce(this.User.model.findOne);
  					this.User.model.findOne.getCall(0).args[0].email.must.be.instanceof(RegExp);
  					// make sure .exec() is called after
  					sinon.assert.calledOnce(this.User.model.exec);
  					this.User.model.exec.calledAfter(this.User.model.findOne).must.be.true;
  					// make sure .signinWithUser() is called on successful match
  					sinon.assert.calledOnce(keystone.session.signinWithUser);
  					done();
  				}.bind(this), this.onFailure);
  
  			});
  
  			it('shoud match email with all uppercase', function (done) {
  				var lookup = { email: 'TEST@TEST.COM', password: 'password'};
  					keystone.session.signin(lookup, null, null, function(){
  					// make sure .findOne() is called with a regular expression
  					sinon.assert.calledOnce(this.User.model.findOne);
  					this.User.model.findOne.getCall(0).args[0].email.must.be.instanceof(RegExp);
  					// make sure .exec() is called after
  					sinon.assert.calledOnce(this.User.model.exec);
  					this.User.model.exec.calledAfter(this.User.model.findOne).must.be.true;
  					// make sure .signinWithUser() is called on successful match
  					sinon.assert.calledOnce(keystone.session.signinWithUser);
  					done();
  				}.bind(this), this.onFailure);
  
  			});
  
  			it('shoud match email with all lowercase', function (done) {
  				var lookup = { email: 'test@test.com', password: 'password'};
  				keystone.session.signin(lookup, null, null, function(){
  					// make sure .findOne() is called with a regular expression
  					sinon.assert.calledOnce(this.User.model.findOne);
  					this.User.model.findOne.getCall(0).args[0].email.must.be.instanceof(RegExp);
  					// make sure .exec() is called after
  					sinon.assert.calledOnce(this.User.model.exec);
  					this.User.model.exec.calledAfter(this.User.model.findOne).must.be.true;
  					// make sure .signinWithUser() is called on successful match
  					sinon.assert.calledOnce(keystone.session.signinWithUser);
  					done();
  				}.bind(this), this.onFailure);
  
  			});
  
  			it('should not match email when invalid', function (done) {
  				var lookup = { email: 'xxx', password: 'password'};
  				keystone.session.signin(lookup, null, null, this.onSuccess, function(err){
  					// make sure .findOne() was not called
  					sinon.assert.notCalled(this.User.model.findOne);
  					// make sure .exec() was not called
  					sinon.assert.notCalled(this.User.model.exec);
  					this.User.model.exec.calledAfter(this.User.model.findOne).must.be.true;
  					err.must.be.an.instanceof(Error)
  					// make sure .signinWithUser() is NOT called on failed match
  					sinon.assert.notCalled(keystone.session.signinWithUser);
  					done()
  				}.bind(this));
  			});
  
  			it('should not match email when just a regex', function (done) {
  				var lookup = { email: '\.', password: 'password'};
  				keystone.session.signin(lookup, null, null, this.onSuccess, function(err){
  					// make sure .findOne() was not called
  					sinon.assert.notCalled(this.User.model.findOne);
  					// make sure .exec() was not called
  					sinon.assert.notCalled(this.User.model.exec);
  					this.User.model.exec.calledAfter(this.User.model.findOne).must.be.true;
  					err.must.be.an.instanceof(Error)
  					// make sure .signinWithUser() is NOT called on failed match
  					sinon.assert.notCalled(keystone.session.signinWithUser);
  					done()
  				}.bind(this));
  			});
  
  		});
  
  	});
  
  	// TODO: test keystone.session.signout()
  	// describe('keystone.session.signout()');
  
  	// TODO: test keystone.session.persist()
  	// describe('keystone.session.persist()');
  
  	// TODO: test keystone.session.keystoneAuth()
  	// describe('keystone.session.keystoneAuth()');
  
  });
  