var assert = require('assert'); var connectMongoDBSession = require('../'); var ee = require('events').EventEmitter; var mongodb = require('mongodb'); var strawman = require('strawman'); describe('connectMongoDBSession', function() { var client = {"db": {}}; var db; var StoreStub; beforeEach(function() { db = strawman({ collection: { argumentNames: ['collection'], chain: true }, createIndex: { argumentNames: ['index', 'options', 'callback'] }, findOne: { argumentNames: ['query', 'callback'] }, deleteOne: { argumentNames: ['query', 'callback'] }, deleteMany: { argumentNames: ['query', 'callback'] }, updateOne: { argumentNames: ['query', 'update', 'options', 'callback' ] } }); client.db = function(n) {return db;}; mongodb.MongoClient.connect = function(uri, options, callback) { process.nextTick(function() { callback(null, client); }); }; StoreStub = function() {}; StoreStub.prototype = { connectMongoDB: 1 }; }); describe('options', function() { it('can specify uri', function(done) { var SessionStore = connectMongoDBSession({ Store: StoreStub }); var session = new SessionStore({ uri: 'mongodb://host:port/db' }); assert.equal(session.options.uri, 'mongodb://host:port/db'); assert.equal(session.options.idField, '_id'); done(); }); it('can specify collection', function(done) { var SessionStore = connectMongoDBSession({ Store: StoreStub }); var session = SessionStore({ collection: 'notSessions' }); assert.equal(session.options.uri, 'mongodb://localhost:27017/test'); assert.equal(session.options.collection, 'notSessions'); done(); }); it('can specify expires', function(done) { var SessionStore = connectMongoDBSession({ Store: StoreStub }); var session = new SessionStore({ expires: 25 }); assert.equal(session.options.uri, 'mongodb://localhost:27017/test'); assert.equal(session.options.expires, 25); done(); }); it('can specify idField', function(done) { var SessionStore = connectMongoDBSession({ Store: StoreStub }); var session = new SessionStore({ idField: 'sessionId' }); assert.equal(session.options.uri, 'mongodb://localhost:27017/test'); assert.deepEqual(session._generateQuery('1234'), { sessionId: '1234' }); done(); }); it('can specify databaseName', function(done) { var SessionStore = connectMongoDBSession({ Store: StoreStub }); var session = new SessionStore({ databaseName: 'other_db' }); assert.equal(session.options.databaseName, 'other_db'); done(); }); }); it('can get Store object from Express 3', function(done) { var SessionStore = connectMongoDBSession({ session: { Store: StoreStub } }); assert.ok(SessionStore.prototype.connectMongoDB); done(); }); it('specifying options is optional', function(done) { var SessionStore = connectMongoDBSession({ Store: StoreStub }); var numIndexCalls = 0; db.createIndex.on('called', function(args) { assert.equal(++numIndexCalls, 1); assert.equal(args.index.expires, 1); args.callback(); }); var session = new SessionStore(function(error) { assert.ifError(error); done(); }); assert.equal(session.options.uri, 'mongodb://localhost:27017/test'); }); it('uses default options and no callback if no args passed', function(done) { var SessionStore = connectMongoDBSession({ Store: StoreStub }); var numIndexCalls = 0; db.createIndex.on('called', function(args) { assert.equal(++numIndexCalls, 1); assert.equal(args.index.expires, 1); args.callback(); }); var session = new SessionStore(); assert.equal(session.options.uri, 'mongodb://localhost:27017/test'); session.on('connected', function() { done(); }); }); it('throws an error when connection fails and no callback', function(done) { mongodb.MongoClient.connect = function(uri, options, callback) { // purposely make callback sync callback(new Error('Cant connect')); }; var SessionStore = connectMongoDBSession({ Store: StoreStub }); var threw = false; try { new SessionStore(); } catch (error) { threw = true; assert.equal(error.message, 'Error connecting to db: Cant connect'); } done(); }); it('passes error to callback if specified', function(done) { mongodb.MongoClient.connect = function(uri, options, callback) { process.nextTick(function() { callback(new Error('Cant connect')); }); }; var SessionStore = connectMongoDBSession({ Store: StoreStub }); var numSources = 2; var store = new SessionStore(function(error) { assert.ok(error); --numSources || done(); }); store.once('error', function(error) { assert.ok(error); --numSources || done(); }); }); it('handles index errors', function(done) { var SessionStore = connectMongoDBSession({ Store: StoreStub }); var numIndexCalls = 0; db.createIndex.on('called', function(args) { assert.equal(++numIndexCalls, 1); assert.equal(args.index.expires, 1); args.callback(new Error('Index fail')); }); var session = new SessionStore(function(error) { assert.equal(error.message, 'Error creating index: Index fail'); done(); }); }); describe('get()', function() { var numIndexCalls; beforeEach(function() { numIndexCalls = 0; db.createIndex.on('called', function(args) { assert.equal(++numIndexCalls, 1); assert.equal(args.index.expires, 1); args.callback(); }); }); it('buffers get() calls', function(done) { var SessionStore = connectMongoDBSession({ Store: StoreStub }); var emitter = new ee(); mongodb.MongoClient.connect = function(uri, options, callback) { emitter.on('success', function() { callback(null, client); }); }; var session = new SessionStore(); db.findOne.on('called', function(args) { args.callback(null, { expires: new Date('2040-06-01T00:00:00.000Z'), session: { data: 1 } }); }); session.get('1234', function(error) { assert.ifError(error); assert.equal(numIndexCalls, 1); done(); }); setImmediate(function() { emitter.emit('success'); }); }); it('handles get() errors', function(done) { var SessionStore = connectMongoDBSession({ Store: StoreStub }); var session = new SessionStore(); db.findOne.on('called', function(args) { args.callback(new Error('fail!')); }); session.get('1234', function(error) { assert.ok(error); assert.equal(error.message, 'Error finding 1234: fail!'); done(); }); }); it('calls destroy() on stale sessions', function(done) { var SessionStore = connectMongoDBSession({ Store: StoreStub }); var numRemoveCalls = 0; var session = new SessionStore(); db.findOne.on('called', function(args) { args.callback(null, { expires: new Date('2011-06-01T00:00:00.000Z') }); }); db.deleteOne.on('called', function(args) { ++numRemoveCalls; assert.equal(args.query._id, '1234'); args.callback(); }); session.get('1234', function(error, doc) { assert.ifError(error); assert.ok(!doc); assert.equal(numRemoveCalls, 1); done(); }); }); it('returns empty if no session found', function(done) { var SessionStore = connectMongoDBSession({ Store: StoreStub }); var session = new SessionStore(); db.findOne.on('called', function(args) { args.callback(null, null); }); session.get('1234', function(error, doc) { assert.ifError(error); assert.ok(!doc); done(); }); }); }); describe('destroy()', function() { var numIndexCalls; beforeEach(function() { numIndexCalls = 0; db.createIndex.on('called', function(args) { assert.equal(++numIndexCalls, 1); assert.equal(args.index.expires, 1); args.callback(); }); }); it('buffers until connected', function(done) { var SessionStore = connectMongoDBSession({ Store: StoreStub }); var emitter = new ee(); mongodb.MongoClient.connect = function(uri, options, callback) { emitter.on('success', function() { callback(null, client); }); }; var session = new SessionStore(); db.deleteOne.on('called', function(args) { args.callback(null); }); session.destroy('1234', function(error) { assert.ifError(error); assert.equal(numIndexCalls, 1); done(); }); setImmediate(function() { emitter.emit('success'); }); }); it('reports driver errors', function(done) { var SessionStore = connectMongoDBSession({ Store: StoreStub }); var session = new SessionStore(); db.deleteOne.on('called', function(args) { args.callback(new Error('fail!')); }); session.destroy('1234', function(error) { assert.ok(error); assert.equal(error.message, 'Error destroying 1234: fail!'); done(); }); }); }); describe('set()', function(done) { var numIndexCalls; beforeEach(function() { numIndexCalls = 0; db.createIndex.on('called', function(args) { assert.equal(++numIndexCalls, 1); assert.equal(args.index.expires, 1); args.callback(); }); }); it('buffers until connected', function(done) { var SessionStore = connectMongoDBSession({ Store: StoreStub }); var emitter = new ee(); mongodb.MongoClient.connect = function(uri, options, callback) { emitter.on('success', function() { callback(null, client); }); }; var session = new SessionStore(); db.updateOne.on('called', function(args) { args.callback(null); }); session.set('1234', { test: 1 }, function(error) { assert.ifError(error); assert.equal(numIndexCalls, 1); done(); }); setImmediate(function() { emitter.emit('success'); }); }); it('converts expires to a date', function(done) { var SessionStore = connectMongoDBSession({ Store: StoreStub }); var session = new SessionStore(); db.updateOne.on('called', function(args) { assert.ok(args.update.$set.expires instanceof Date); assert.equal(args.update.$set.expires.getTime(), new Date('2011-06-01T00:00:00.000Z').getTime()); args.callback(null); }); var update = { test: 1, cookie: { expires: '2011-06-01T00:00:00.000Z' } }; session.set('1234', update, function(error) { assert.ifError(error); assert.equal(db.updateOne.calls.length, 1); done(); }); }); it('handles set() errors', function(done) { var SessionStore = connectMongoDBSession({ Store: StoreStub }); var session = new SessionStore(); db.updateOne.on('called', function(args) { args.callback(new Error('fail!')); }); session.set('1234', {}, function(error) { assert.ok(error); assert.equal(error.message, 'Error setting 1234 to {}: fail!'); done(); }); }); /** For backwards compatibility with connect-mongo */ it('converts cookies to JSON strings', function(done) { var SessionStore = connectMongoDBSession({ Store: StoreStub }); var session = new SessionStore(); db.updateOne.on('called', function(args) { assert.equal(args.update.$set.session.cookie, 'put that cookie down!'); args.callback(null); }); var update = { test: 1, cookie: { toJSON: function() { return 'put that cookie down!'; } } }; session.set('1234', update, function(error) { assert.ifError(error); assert.equal(db.updateOne.calls.length, 1); done(); }); }); /** For backwards compatibility with connect-mongo */ it('unless they do not have a toJSON()', function(done) { var SessionStore = connectMongoDBSession({ Store: StoreStub }); var session = new SessionStore(); db.updateOne.on('called', function(args) { assert.deepEqual(args.update.$set.session.cookie, { test: 2 }); args.callback(null); }); var update = { test: 1, cookie: { test: 2 } }; session.set('1234', update, function(error) { assert.ifError(error); assert.equal(db.updateOne.calls.length, 1); done(); }); }); }); describe('clear()', function(done){ var numIndexCalls; beforeEach(function() { numIndexCalls = 0; db.createIndex.on('called', function(args) { assert.equal(++numIndexCalls, 1); assert.equal(args.index.expires, 1); args.callback(); }); }); it('clears the session store', function(done) { var SessionStore = connectMongoDBSession({ Store: StoreStub }); var session = new SessionStore(); db.deleteMany.on('called', function(args) { args.callback(null); }); session.clear(function(error) { assert.ifError(error); done(); }); }); it('handles set() errors', function(done) { var SessionStore = connectMongoDBSession({ Store: StoreStub }); var session = new SessionStore(); db.deleteMany.on('called', function(args) { args.callback(new Error('fail!')); }); session.clear(function(error) { assert.ok(error); assert.equal(error.message, 'Error clearing all sessions: fail!'); done(); }); }); }); });