UNPKG

passwordless-tokenstore-test

Version:

Provides a generic test suite for Passwordless TokenStore implementations

419 lines (373 loc) 15 kB
'use strict'; var expect = require('chai').expect; var uuid = require('node-uuid'); var chance = new require('chance')(); /** * Runs the standardized tests for any TokenStore implementation * @param {Object} A new, completely initalized instance of the TokenStore * @param {Function} function(done) Function that will be executed before * every test (e.g. to setup demo records). done() has to be called after * all steps have been finalized * @param {Function} function(done) Function that will be executed after * every test (e.g. to clear all records). done() has to be called after * all steps have been finalized * @param {Number} optional. Timeout in ms. Used as a baseline for all * timeout-sensitive tests to allow the testing of slower systems / DBs. * Defaults to 200. */ module.exports = function(TokenStoreFactory, beforeEachTest, afterEachTest, defaultTimeout) { defaultTimeout = defaultTimeout || 200; describe('General TokenStore tests (no need to modify)', function() { beforeEach(function(done) { beforeEachTest(done); }) afterEach(function(done) { afterEachTest(done); }) describe('Tests', function() { describe('storeOrUpdate()', function() { it('should allow the storage of a new token', function () { expect(function() { TokenStoreFactory().storeOrUpdate(uuid.v4(), chance.email(), 1000*60, 'http://' + chance.domain() + '/page.html', function() {}) }).to.not.throw(); }) it('should allow the update of details if the same UID is provided', function (done) { var store = TokenStoreFactory(); var uid = chance.email(); var token1 = uuid.v4(), token2 = uuid.v4(); // Storage of first token for uid store.storeOrUpdate(token1, uid, 1000*60, 'http://www.example.com/alice', function(err) { expect(err).to.not.exist; // Update of uid with new details (incl. new token) store.storeOrUpdate(token2, uid, 1000*60, 'http://www.example.com/tom', function(err) { expect(err).to.not.exist; // the old token should not be valid anymore store.authenticate(token1, uid, function(err, valid, ret_ref) { expect(err).to.not.exist; expect(valid).to.equal(false); expect(ret_ref).to.not.exist; // but the new token should be valid and also return the new referrer store.authenticate(token2, uid, function(err, valid, ret_ref) { expect(err).to.not.exist; expect(valid).to.equal(true); expect(ret_ref).to.equal('http://www.example.com/tom') done(); }); }) }) }); }) it('should throw exceptions for missing data', function () { var store = TokenStoreFactory(); expect(function() { store.storeOrUpdate('', chance.email(), 1000*60, 'http://' + chance.domain() + '/page.html', function() {})}).to.throw(Error); expect(function() { store.storeOrUpdate(uuid.v4(), '', 1000*60, 'http://' + chance.domain() + '/page.html', function() {})}).to.throw(Error); expect(function() { store.storeOrUpdate(uuid.v4(), chance.email(), '', 'http://' + chance.domain() + '/page.html', function() {})}).to.throw(Error); expect(function() { store.storeOrUpdate(uuid.v4(), chance.email(), 1000*60, '', function() {})}).to.not.throw(); expect(function() { store.storeOrUpdate(uuid.v4(), chance.email(), 1000*60, 'http://' + chance.domain() + '/page.html')}).to.throw(Error); }) it('should callback in the format of callback() in case of success', function (done) { TokenStoreFactory().storeOrUpdate(uuid.v4(), chance.email(), 1000*60, 'http://' + chance.domain() + '/page.html', function(err) { expect(err).to.not.exist; done(); }); }) }) describe('authenticate()', function() { it('should allow the authentication of a token / uid combination', function () { expect(function() { TokenStoreFactory().authenticate(uuid.v4(), chance.email(), function() {}) }).to.not.throw(); }) it('should throw exceptions for missing data', function () { var store = TokenStoreFactory(); expect(function() { store.authenticate('', chance.email(), function() {})}).to.throw(Error); expect(function() { store.authenticate(uuid.v4(), '', function() {})}).to.throw(Error); expect(function() { store.authenticate(uuid.v4(), chance.email()) }).to.throw(Error); expect(function() { store.authenticate(uuid.v4()) }).to.throw(Error); }) it('should not authenticate a valid token for the wrong uid: callback(null, false, null)', function (done) { var store = TokenStoreFactory(); var uid1 = chance.email(), uid2 = chance.email(); var token1 = uuid.v4(), token2 = uuid.v4(); var ref1 = 'http://www.example.com/path', ref2 = 'http://www.example.com/other'; store.storeOrUpdate(token1, uid1, 1000*60, ref1, function(err) { expect(err).to.not.exist; store.storeOrUpdate(token2, uid2, 1000*60, ref2, function(err2) { expect(err2).to.not.exist; store.authenticate(token1, uid2, function(err, valid, ret_ref) { expect(err).to.not.exist; expect(valid).to.equal(false); expect(ret_ref).to.not.exist; done(); }) }) }); }) it('should callback in the format of callback(null, true, referrer) in case of success', function (done) { var store = TokenStoreFactory(); var uid = chance.email(); var token = uuid.v4(); var ref = 'http://www.example.com/path'; store.storeOrUpdate(token, uid, 1000*60, ref, function(err) { expect(err).to.not.exist; store.authenticate(token, uid, function(err, valid, ret_ref) { expect(err).to.not.exist; expect(valid).to.equal(true); expect(ret_ref).to.equal(ref); done(); }) }); }) it('should callback in the format of callback(null, true, "") in case of a null referrer', function (done) { var store = TokenStoreFactory(); var uid = chance.email(); var token = uuid.v4(); var ref = null; store.storeOrUpdate(token, uid, 1000*60, ref, function(err) { expect(err).to.not.exist; store.authenticate(token, uid, function(err, valid, ret_ref) { expect(err).to.not.exist; expect(valid).to.equal(true); expect(ret_ref).to.equal(""); done(); }) }); }) it('should callback with callback(null, false, null) in case of an unknown token / uid', function (done) { TokenStoreFactory().authenticate(uuid.v4(), chance.email(), function(err, valid, ret_ref) { expect(err).to.not.exist; expect(valid).to.equal(false); expect(ret_ref).to.not.exist; done(); }); }) }) describe('invalidateUser()', function() { it('should fail silently for uids that do not exist', function (done) { var store = TokenStoreFactory(); store.invalidateUser(chance.email(), function(err) { expect(err).to.not.exist; done(); }); }) it('should invalidate an existing user', function (done) { var store = TokenStoreFactory(); var token = uuid.v4(); var uid = chance.email(); store.storeOrUpdate(token, uid, 1000*60, 'http://' + chance.domain() + '/page.html', function() { store.invalidateUser(uid, function(err) { expect(err).to.not.exist; store.authenticate(token, uid, function(err, valid, ref) { expect(err).to.not.exist; expect(valid).to.equal(false); expect(ref).to.not.exist; done(); }) }) }) }) it('should throw exceptions for missing data', function () { var store = TokenStoreFactory(); expect(function() { store.invalidateUser(chance.email())}).to.throw(Error); expect(function() { store.invalidateUser()}).to.throw(Error); }) }) describe('clear()', function() { it('should remove all data', function (done) { var store = TokenStoreFactory(); store.storeOrUpdate(uuid.v4(), chance.email(), 1000*60, 'http://' + chance.domain() + '/page.html', function() { store.storeOrUpdate(uuid.v4(), chance.email(), 1000*60, 'http://' + chance.domain() + '/page.html', function() { store.clear(function(err) { expect(err).to.not.exist; store.length(function(err, length) { expect(err).to.not.exist; expect(length).to.equal(0); done(); }) }) }) }) }) it('should run smoothly for an empty TokenStore', function (done) { var store = TokenStoreFactory(); store.clear(function(err) { expect(err).to.not.exist; store.length(function(err, length) { expect(err).to.not.exist; expect(length).to.equal(0); done(); }) }) }) it('should throw exceptions for missing data', function () { var store = TokenStoreFactory(); expect(function() { store.clear()}).to.throw(Error); }) }) describe('length()', function() { it('should return 0 for an empty TokenStore', function (done) { var store = TokenStoreFactory(); store.length(function(err, count) { expect(count).to.equal(0); done(); }); }) it('should return 2 after 2 tokens have been stored', function (done) { var store = TokenStoreFactory(); store.storeOrUpdate(uuid.v4(), chance.email(), 1000*60, 'http://' + chance.domain() + '/page.html', function() { store.storeOrUpdate(uuid.v4(), chance.email(), 1000*60, 'http://' + chance.domain() + '/page.html', function() { store.length(function(err, count) { expect(count).to.equal(2); done(); }); }) }); }) }) describe('flow', function() { it('should validate an existing token / uid combination', function (done) { var store = TokenStoreFactory(); var uid = chance.email(); var token = uuid.v4(); var referrer = 'http://' + chance.domain() + '/page.html'; store.storeOrUpdate(token, uid, 1000*60, referrer, function(err) { expect(err).to.not.exist; store.authenticate(token, uid, function(error, valid, ref) { expect(valid).to.equal(true); expect(ref).to.equal(referrer); expect(error).to.not.exist; done() }) }) }) it('should validate an existing token / uid several times if still valid', function (done) { var store = TokenStoreFactory(); var uid = chance.email(); var token = uuid.v4(); var referrer = 'http://' + chance.domain() + '/page.html'; store.storeOrUpdate(token, uid, 1000*60, referrer, function(err) { expect(err).to.not.exist; store.authenticate(token, uid, function(error, valid, ref) { expect(valid).to.equal(true); expect(ref).to.equal(referrer); expect(error).to.not.exist; store.authenticate(token, uid, function(error, valid, ref) { expect(valid).to.equal(true); expect(ref).to.equal(referrer); expect(error).to.not.exist; done(); }) }) }) }) it('should not validate a not existing token / uid combination', function (done) { var store = TokenStoreFactory(); var uid = chance.email(); var token = uuid.v4(); var referrer = 'http://' + chance.domain() + '/page.html'; store.storeOrUpdate(token, uid, 1000*60, referrer, function(err) { expect(err).to.not.exist; store.authenticate(uuid.v4(), uid, function(error, valid, ref) { expect(valid).to.equal(false); expect(ref).to.not.exist; expect(error).to.not.exist; done(); }) }) }) it('should not validate a token / uid combination which time has run up', function (done) { this.timeout(defaultTimeout*10); var store = TokenStoreFactory(); var uid = chance.email(); var token = uuid.v4(); var referrer = 'http://' + chance.domain() + '/page.html'; store.storeOrUpdate(token, uid, defaultTimeout, referrer, function(err) { expect(err).to.not.exist; setTimeout(function() { store.authenticate(token, uid, function(error, valid, ref) { expect(valid).to.equal(false); expect(ref).to.not.exist; expect(error).to.not.exist; done(); }) }, defaultTimeout*2); }) }) it('should validate token/uid if still valid, but not validate anymore if time has run up', function (done) { this.timeout(defaultTimeout*10); var store = TokenStoreFactory(); var uid = chance.email(); var token = uuid.v4(); var referrer = 'http://' + chance.domain() + '/page.html'; store.storeOrUpdate(token, uid, defaultTimeout, referrer, function(err) { expect(err).to.not.exist; store.authenticate(token, uid, function(error, valid, ref) { expect(valid).to.equal(true); expect(ref).to.equal(referrer); expect(error).to.not.exist; setTimeout(function() { store.authenticate(token, uid, function(error, valid, ref) { expect(valid).to.equal(false); expect(ref).to.not.exist; expect(error).to.not.exist; done(); }) }, defaultTimeout*2); }) }) }) it('should allow the extension of time for a token/uid combination', function (done) { this.timeout(defaultTimeout*10); var store = TokenStoreFactory(); var uid = chance.email(); var token1 = uuid.v4(), token2 = uuid.v4(); var referrer = 'http://' + chance.domain() + '/page.html'; // First storage of a token for uid store.storeOrUpdate(token1, uid, defaultTimeout, referrer, function(err) { expect(err).to.not.exist; // should authenticate store.authenticate(token1, uid, function(error, valid, ref) { expect(valid).to.equal(true); expect(ref).to.equal(referrer); expect(error).to.not.exist; // update of uid token with a new one, which is valid for much longer store.storeOrUpdate(token2, uid, defaultTimeout*5*60, referrer, function(err) { expect(err).to.not.exist; // authenticate with the new token after 200ms which is beyond the validity of token1 setTimeout(function() { store.authenticate(token2, uid, function(error, valid, ref) { expect(valid).to.equal(true); expect(ref).to.equal(referrer); expect(error).to.not.exist; // ... but token1 shouldn't work anymore store.authenticate(token1, uid, function(error, valid, ref) { expect(valid).to.equal(false); expect(ref).to.not.exist; expect(error).to.not.exist; done(); }) }) }, defaultTimeout*2); }) }) }) }) }) }) }) };