diff --git a/lib/redis.js b/lib/redis.js index f11f1b0..c1abe82 100644 --- a/lib/redis.js +++ b/lib/redis.js @@ -6,7 +6,7 @@ module.exports = CoreObject.extend({ init: function(options, lib) { this._super(); var redisOptions = {}; - var redisLib = lib; + var RedisLib = lib; if (options.url) { redisOptions = this._stripUsernameFromConfigUrl(options.url); @@ -21,15 +21,15 @@ module.exports = CoreObject.extend({ } if (options.database) { - redisOptions.database = options.database; + redisOptions.db = options.database; } } - if (!redisLib) { - redisLib = require('then-redis'); + if (!RedisLib) { + RedisLib = require('ioredis'); } - this._client = redisLib.createClient(redisOptions); + this._client = new RedisLib(redisOptions); this._maxRecentUploads = options.maxRecentUploads; this._allowOverwrite = options.allowOverwrite; @@ -204,13 +204,16 @@ module.exports = CoreObject.extend({ if (!revisions) { return; } + var promises = []; revisions.forEach(function(revision) { if (revision !== current) { - client.del(keyPrefix + ":" + revision); - client.del(keyPrefix + ":revision-data:" + revision); - client.zrem(listKey, revision); + promises.push(client.del(keyPrefix + ":" + revision)); + promises.push(client.del(keyPrefix + ":revision-data:" + revision)); + promises.push(client.zrem(listKey, revision)); } }); + + return RSVP.all(promises); }); }, diff --git a/package.json b/package.json index 84d409f..204bf05 100644 --- a/package.json +++ b/package.json @@ -23,8 +23,10 @@ "eslint": "^3.18.0", "github": "^6.1.0", "glob": "^7.1.1", + "ioredis-mock": "^3.14.0", "mocha": "^3.2.0", - "multiline": "^1.0.2" + "multiline": "^1.0.2", + "sinon": "^6.1.5" }, "keywords": [ "ember-addon", @@ -34,9 +36,9 @@ "chalk": "^1.1.3", "core-object": "^2.0.6", "ember-cli-deploy-plugin": "^0.2.9", + "ioredis": "^3.2.2", "redis": "^2.6.3", - "rsvp": "^3.0.18", - "then-redis": "^2.0.1" + "rsvp": "^3.0.18" }, "ember-addon": { "configPath": "tests/dummy/config" diff --git a/tests/unit/index-test.js b/tests/unit/index-test.js index eb30169..fe58421 100644 --- a/tests/unit/index-test.js +++ b/tests/unit/index-test.js @@ -1,41 +1,46 @@ -'use strict'; +"use strict"; -var RSVP = require('rsvp'); -var assert = require('../helpers/assert'); -var FakeRedis = require('../helpers/fake-redis-lib'); +var RSVP = require("rsvp"); +var assert = require("../helpers/assert"); +var IoRedis = require("ioredis"); +var sandbox = require("sinon").createSandbox(); var stubProject = { - name: function(){ - return 'my-project'; + name: function() { + return "my-project"; } }; -describe('redis plugin', function() { +describe("redis plugin", function() { var subject, mockUi; beforeEach(function() { - subject = require('../../index'); + subject = require("../../index"); mockUi = { verbose: true, messages: [], - write: function() { }, + write: function() {}, writeLine: function(message) { this.messages.push(message); } }; }); - it('has a name', function() { + afterEach(function() { + sandbox.restore(); + }); + + it("has a name", function() { var result = subject.createDeployPlugin({ - name: 'test-plugin' + name: "test-plugin" }); - assert.equal(result.name, 'test-plugin'); + assert.equal(result.name, "test-plugin"); }); - it('implements the correct hooks', function() { + it("implements the correct hooks", function() { var plugin = subject.createDeployPlugin({ - name: 'test-plugin' + name: "test-plugin" }); assert.ok(plugin.configure); assert.ok(plugin.upload); @@ -43,10 +48,10 @@ describe('redis plugin', function() { assert.ok(plugin.didDeploy); }); - describe('configure hook', function() { - it('runs without error if config is ok', function() { + describe("configure hook", function() { + it("runs without error if config is ok", function() { var plugin = subject.createDeployPlugin({ - name: 'redis' + name: "redis" }); var context = { @@ -54,7 +59,7 @@ describe('redis plugin', function() { project: stubProject, config: { redis: { - host: 'somehost', + host: "somehost", port: 1234, database: 4 } @@ -65,101 +70,107 @@ describe('redis plugin', function() { assert.ok(true); // didn't throw an error }); - it('passes through config options', function () { + it("passes through config options", function() { var plugin = subject.createDeployPlugin({ - name: 'redis' + name: "redis" }); - var redisLib = new FakeRedis(); + var redisLibStub = sandbox.stub(IoRedis, "constructor"); var context = { ui: mockUi, project: stubProject, config: { redis: { - host: 'somehost', + host: "somehost", port: 1234, database: 4 } }, - _redisLib: redisLib + _redisLib: redisLibStub }; plugin.beforeHook(context); plugin.configure(context); - plugin.readConfig('redisDeployClient'); + plugin.readConfig("redisDeployClient"); - assert.equal(redisLib.createdClient.options.host, 'somehost'); - assert.equal(redisLib.createdClient.options.port, 1234); - assert.equal(redisLib.createdClient.options.database, 4); + assert.isTrue( + redisLibStub.calledWith({ + host: "somehost", + port: 1234, + db: 4 + }) + ); }); - describe('handles redis urls appropriately', function() { - it('handles pre-stripped urls without a username', function () { - + describe("handles redis urls appropriately", function() { + it("handles pre-stripped urls without a username", function() { var plugin = subject.createDeployPlugin({ - name: 'redis' + name: "redis" }); - var redisLib = new FakeRedis(); + var redisLibStub = sandbox.stub(IoRedis, "constructor"); var context = { ui: mockUi, project: stubProject, config: { redis: { - url: 'redis://:password@host.amazonaws.com:6379/4' + url: "redis://:password@host.amazonaws.com:6379/4" } }, - _redisLib: redisLib + _redisLib: redisLibStub }; plugin.beforeHook(context); plugin.configure(context); - plugin.readConfig('redisDeployClient'); + plugin.readConfig("redisDeployClient"); - assert.equal(redisLib.createdClient.options, 'redis://:password@host.amazonaws.com:6379/4'); + assert.isTrue( + redisLibStub.calledWith("redis://:password@host.amazonaws.com:6379/4") + ); }); - it('strips Redis username from a Heroku url to work with our upstream redis library', function () { - + it("strips Redis username from a Heroku url to work with our upstream redis library", function() { var plugin = subject.createDeployPlugin({ - name: 'redis' + name: "redis" }); - var redisLib = new FakeRedis(); + var redisLibStub = sandbox.stub(IoRedis, "constructor"); var context = { ui: mockUi, project: stubProject, config: { redis: { - url: 'redis://username:password@host.amazonaws.com:6379/4' + url: "redis://username:password@host.amazonaws.com:6379/4" } }, - _redisLib: redisLib + _redisLib: redisLibStub }; plugin.beforeHook(context); plugin.configure(context); - plugin.readConfig('redisDeployClient'); + plugin.readConfig("redisDeployClient"); - assert.equal(redisLib.createdClient.options, 'redis://:password@host.amazonaws.com:6379/4'); + assert.isTrue( + redisLibStub.calledWith("redis://:password@host.amazonaws.com:6379/4") + ); }); it('throws if the Redis URL is missing the "redis://" protocol', function() { var plugin = subject.createDeployPlugin({ - name: 'redis' + name: "redis" }); - var redisLib = new FakeRedis(); + var redisLibStub = sandbox.stub(IoRedis, "constructor"); var context = { ui: mockUi, project: stubProject, config: { redis: { - url: 'host.amazonaws.com:6379/4' + url: "host.amazonaws.com:6379/4" } }, - _redisLib: redisLib + _redisLib: redisLibStub }; plugin.beforeHook(context); @@ -170,15 +181,15 @@ describe('redis plugin', function() { }); }); - describe('resolving port from the pipeline', function() { - it('uses the config data if it already exists', function() { + describe("resolving port from the pipeline", function() { + it("uses the config data if it already exists", function() { var plugin = subject.createDeployPlugin({ - name: 'redis' + name: "redis" }); var config = { - host: 'somehost', - port: 1234, + host: "somehost", + port: 1234 }; var context = { ui: mockUi, @@ -187,22 +198,22 @@ describe('redis plugin', function() { redis: config }, tunnel: { - srcPort: '2345' + srcPort: "2345" } }; plugin.beforeHook(context); plugin.configure(context); - assert.equal(plugin.readConfig('port'), '1234'); + assert.equal(plugin.readConfig("port"), "1234"); }); - it('uses the context value if it exists and config doesn\'t', function() { + it("uses the context value if it exists and config doesn't", function() { var plugin = subject.createDeployPlugin({ - name: 'redis' + name: "redis" }); var config = { - host: 'somehost', + host: "somehost" }; var context = { ui: mockUi, @@ -211,22 +222,22 @@ describe('redis plugin', function() { redis: config }, tunnel: { - srcPort: '2345' + srcPort: "2345" } }; plugin.beforeHook(context); plugin.configure(context); - assert.equal(plugin.readConfig('port'), '2345'); + assert.equal(plugin.readConfig("port"), "2345"); }); - it('uses the default port if config and context don\'t exist', function() { + it("uses the default port if config and context don't exist", function() { var plugin = subject.createDeployPlugin({ - name: 'redis' + name: "redis" }); var config = { - host: 'somehost', + host: "somehost" }; var context = { ui: mockUi, @@ -238,20 +249,20 @@ describe('redis plugin', function() { plugin.beforeHook(context); plugin.configure(context); - assert.equal(plugin.readConfig('port'), '6379'); + assert.equal(plugin.readConfig("port"), "6379"); }); }); - describe('resolving revisionKey from the pipeline', function() { - it('uses the config data if it already exists', function() { + describe("resolving revisionKey from the pipeline", function() { + it("uses the config data if it already exists", function() { var plugin = subject.createDeployPlugin({ - name: 'redis' + name: "redis" }); var config = { - host: 'somehost', + host: "somehost", port: 1234, - revisionKey: '12345' + revisionKey: "12345" }; var context = { ui: mockUi, @@ -260,22 +271,22 @@ describe('redis plugin', function() { redis: config }, revisionData: { - revisionKey: 'something-else' + revisionKey: "something-else" } }; plugin.beforeHook(context); plugin.configure(context); - assert.equal(plugin.readConfig('revisionKey'), '12345'); + assert.equal(plugin.readConfig("revisionKey"), "12345"); }); - it('uses the commandOptions value if it exists', function() { + it("uses the commandOptions value if it exists", function() { var plugin = subject.createDeployPlugin({ - name: 'redis' + name: "redis" }); var config = { - host: 'somehost', + host: "somehost", port: 1234 }; var context = { @@ -285,26 +296,26 @@ describe('redis plugin', function() { redis: config }, commandOptions: { - revision: 'abcd' + revision: "abcd" }, revisionData: { - revisionKey: 'something-else' + revisionKey: "something-else" } }; plugin.beforeHook(context); plugin.configure(context); - assert.typeOf(config.revisionKey, 'function'); - assert.equal(config.revisionKey(context), 'abcd'); + assert.typeOf(config.revisionKey, "function"); + assert.equal(config.revisionKey(context), "abcd"); }); - it('uses the context value if it exists and commandOptions doesn\'t', function() { + it("uses the context value if it exists and commandOptions doesn't", function() { var plugin = subject.createDeployPlugin({ - name: 'redis' + name: "redis" }); var config = { - host: 'somehost', + host: "somehost", port: 1234 }; var context = { @@ -313,24 +324,24 @@ describe('redis plugin', function() { config: { redis: config }, - commandOptions: { }, + commandOptions: {}, revisionData: { - revisionKey: 'something-else' + revisionKey: "something-else" } }; plugin.beforeHook(context); plugin.configure(context); - assert.typeOf(config.revisionKey, 'function'); - assert.equal(config.revisionKey(context), 'something-else'); + assert.typeOf(config.revisionKey, "function"); + assert.equal(config.revisionKey(context), "something-else"); }); }); - describe('without providing config', function () { + describe("without providing config", function() { var config, plugin, context; beforeEach(function() { - config = { }; + config = {}; plugin = subject.createDeployPlugin({ - name: 'redis' + name: "redis" }); context = { ui: mockUi, @@ -339,7 +350,7 @@ describe('redis plugin', function() { }; plugin.beforeHook(context); }); - it('warns about missing optional config', function() { + it("warns about missing optional config", function() { plugin.configure(context); var messages = mockUi.messages.reduce(function(previous, current) { if (/- Missing config:\s.*, using default:\s/.test(current)) { @@ -350,7 +361,7 @@ describe('redis plugin', function() { }, []); assert.equal(messages.length, 12); }); - it('adds default config to the config object', function() { + it("adds default config to the config object", function() { plugin.configure(context); assert.isDefined(config.redis.host); assert.isDefined(config.redis.port); @@ -360,16 +371,16 @@ describe('redis plugin', function() { }); }); - describe('with a keyPrefix provided', function () { + describe("with a keyPrefix provided", function() { var config, plugin, context; beforeEach(function() { config = { redis: { - keyPrefix: 'proj:home' + keyPrefix: "proj:home" } }; plugin = subject.createDeployPlugin({ - name: 'redis' + name: "redis" }); context = { ui: mockUi, @@ -378,7 +389,7 @@ describe('redis plugin', function() { }; plugin.beforeHook(context); }); - it('warns about missing optional filePattern, distDir, activationSuffix, revisionKey, didDeployMessage, maxNumberOfRecentUploads, and connection info', function() { + it("warns about missing optional filePattern, distDir, activationSuffix, revisionKey, didDeployMessage, maxNumberOfRecentUploads, and connection info", function() { plugin.configure(context); var messages = mockUi.messages.reduce(function(previous, current) { if (/- Missing config:\s.*, using default:\s/.test(current)) { @@ -389,27 +400,27 @@ describe('redis plugin', function() { }, []); assert.equal(messages.length, 11); }); - it('does not add default config to the config object', function() { + it("does not add default config to the config object", function() { plugin.configure(context); assert.isDefined(config.redis.host); assert.isDefined(config.redis.port); assert.isDefined(config.redis.filePattern); assert.isDefined(config.redis.activationSuffix); assert.isDefined(config.redis.didDeployMessage); - assert.equal(config.redis.keyPrefix, 'proj:home'); + assert.equal(config.redis.keyPrefix, "proj:home"); }); }); - describe('with an activationSuffix provided', function () { + describe("with an activationSuffix provided", function() { var config, plugin, context; beforeEach(function() { config = { redis: { - activationSuffix: 'special:suffix' + activationSuffix: "special:suffix" } }; plugin = subject.createDeployPlugin({ - name: 'redis' + name: "redis" }); context = { ui: mockUi, @@ -418,7 +429,7 @@ describe('redis plugin', function() { }; plugin.beforeHook(context); }); - it('warns about missing optional filePattern, distDir, keyPrefix, revisionKey, didDeployMessage, maxNumberOfRecentUploads, and connection info', function() { + it("warns about missing optional filePattern, distDir, keyPrefix, revisionKey, didDeployMessage, maxNumberOfRecentUploads, and connection info", function() { plugin.configure(context); var messages = mockUi.messages.reduce(function(previous, current) { if (/- Missing config:\s.*, using default:\s/.test(current)) { @@ -427,29 +438,29 @@ describe('redis plugin', function() { return previous; }, []); - assert.equal(messages.length, 11) + assert.equal(messages.length, 11); }); - it('does not add default config to the config object', function() { + it("does not add default config to the config object", function() { plugin.configure(context); assert.isDefined(config.redis.host); assert.isDefined(config.redis.port); assert.isDefined(config.redis.filePattern); assert.isDefined(config.redis.keyPrefix); assert.isDefined(config.redis.didDeployMessage); - assert.equal(config.redis.activationSuffix, 'special:suffix'); + assert.equal(config.redis.activationSuffix, "special:suffix"); }); }); - describe('with a url provided', function () { + describe("with a url provided", function() { var config, plugin, context; beforeEach(function() { config = { redis: { - url: 'redis://localhost:6379' + url: "redis://localhost:6379" } }; plugin = subject.createDeployPlugin({ - name: 'redis' + name: "redis" }); context = { ui: mockUi, @@ -458,7 +469,7 @@ describe('redis plugin', function() { }; plugin.beforeHook(context); }); - it('warns about missing optional filePattern, distDir, keyPrefix, activationSuffix, revisionKey, maxNumberOfRecentUploads, and didDeployMessage only', function() { + it("warns about missing optional filePattern, distDir, keyPrefix, activationSuffix, revisionKey, maxNumberOfRecentUploads, and didDeployMessage only", function() { plugin.configure(context); var messages = mockUi.messages.reduce(function(previous, current) { if (/- Missing config:\s.*, using default:\s/.test(current)) { @@ -470,7 +481,7 @@ describe('redis plugin', function() { assert.equal(messages.length, 10); }); - it('does not add default config to the config object', function() { + it("does not add default config to the config object", function() { plugin.configure(context); assert.isUndefined(config.redis.host); assert.isUndefined(config.redis.port); @@ -479,13 +490,13 @@ describe('redis plugin', function() { }); }); - describe('with aliases', function () { - it('passes config for specified alias to redis', function () { + describe("with aliases", function() { + it("passes config for specified alias to redis", function() { var plugin = subject.createDeployPlugin({ - name: 'foobar' + name: "foobar" }); - var redisLib = new FakeRedis(); + var redisLibStub = sandbox.stub(IoRedis, "constructor"); var config = { database: 7 @@ -496,25 +507,25 @@ describe('redis plugin', function() { config: { foobar: config }, - _redisLib: redisLib + _redisLib: redisLibStub }; plugin.beforeHook(context); plugin.configure(context); - plugin.readConfig('redisDeployClient'); + plugin.readConfig("redisDeployClient"); - assert.equal(redisLib.createdClient.options.database, 7); + assert.isTrue(redisLibStub.calledWithMatch({ db: 7 })); }); }); }); - describe('upload hook', function() { + describe("upload hook", function() { var plugin; var context; - it('uploads the index', function() { + it("uploads the index", function() { plugin = subject.createDeployPlugin({ - name: 'redis' + name: "redis" }); context = { @@ -522,14 +533,14 @@ describe('redis plugin', function() { project: stubProject, config: { redis: { - keyPrefix: 'test-prefix', - filePattern: 'index.html', - distDir: 'tests', - revisionKey: '123abc', + keyPrefix: "test-prefix", + filePattern: "index.html", + distDir: "tests", + revisionKey: "123abc", redisDeployClient: function(/* context */) { return { upload: function(keyPrefix, revisionKey) { - return RSVP.resolve(keyPrefix + ':' + revisionKey); + return RSVP.resolve(keyPrefix + ":" + revisionKey); } }; } @@ -539,19 +550,18 @@ describe('redis plugin', function() { plugin.beforeHook(context); plugin.configure(context); - return assert.isFulfilled(plugin.upload(context)) - .then(function(result) { - assert.deepEqual(result, { redisKey: 'test-prefix:123abc' }); - }); + return assert.isFulfilled(plugin.upload(context)).then(function(result) { + assert.deepEqual(result, { redisKey: "test-prefix:123abc" }); + }); }); }); - describe('activate hook', function() { - it('activates revision', function() { + describe("activate hook", function() { + it("activates revision", function() { var activateCalled = false; var plugin = subject.createDeployPlugin({ - name: 'redis' + name: "redis" }); var context = { @@ -559,11 +569,11 @@ describe('redis plugin', function() { project: stubProject, config: { redis: { - keyPrefix: 'test-prefix', - filePattern: 'index.html', - distDir: 'tests', - revisionKey: '123abc', - redisDeployClient: function(/* context */){ + keyPrefix: "test-prefix", + filePattern: "index.html", + distDir: "tests", + revisionKey: "123abc", + redisDeployClient: function(/* context */) { return { activate: function() { activateCalled = true; @@ -575,16 +585,17 @@ describe('redis plugin', function() { }; plugin.beforeHook(context); - return assert.isFulfilled(plugin.activate(context)) + return assert + .isFulfilled(plugin.activate(context)) .then(function(result) { assert.ok(activateCalled); - assert.equal(result.revisionData.activatedRevisionKey, '123abc'); + assert.equal(result.revisionData.activatedRevisionKey, "123abc"); }); }); - it('rejects if an error is thrown when activating', function() { + it("rejects if an error is thrown when activating", function() { var plugin = subject.createDeployPlugin({ - name: 'redis' + name: "redis" }); var context = { @@ -592,14 +603,14 @@ describe('redis plugin', function() { project: stubProject, config: { redis: { - keyPrefix: 'test-prefix', - filePattern: 'index.html', - distDir: 'tests', - revisionKey: '123abc', + keyPrefix: "test-prefix", + filePattern: "index.html", + distDir: "tests", + revisionKey: "123abc", redisDeployClient: function(/* context */) { return { activate: function() { - return RSVP.reject('some-error'); + return RSVP.reject("some-error"); } }; } @@ -608,57 +619,59 @@ describe('redis plugin', function() { }; plugin.beforeHook(context); - return assert.isRejected(plugin.activate(context)) - .then(function(error) { - assert.equal(error, 'some-error'); - }); + return assert.isRejected(plugin.activate(context)).then(function(error) { + assert.equal(error, "some-error"); + }); }); }); - describe('didDeploy hook', function() { - it('prints default message about lack of activation when revision has not been activated', function() { - var messageOutput = ''; + describe("didDeploy hook", function() { + it("prints default message about lack of activation when revision has not been activated", function() { + var messageOutput = ""; var plugin = subject.createDeployPlugin({ - name: 'redis' + name: "redis" }); - plugin.upload = function(){}; - plugin.activate = function(){}; + plugin.upload = function() {}; + plugin.activate = function() {}; var context = { - deployTarget: 'qa', + deployTarget: "qa", ui: { - write: function(message){ + write: function(message) { messageOutput = messageOutput + message; }, - writeLine: function(message){ - messageOutput = messageOutput + message + '\n'; + writeLine: function(message) { + messageOutput = messageOutput + message + "\n"; } }, project: stubProject, config: { - redis: { } + redis: {} }, revisionData: { - revisionKey: '123abc', + revisionKey: "123abc" } }; plugin.beforeHook(context); plugin.configure(context); plugin.beforeHook(context); plugin.didDeploy(context); - assert.match(messageOutput, /Deployed but did not activate revision 123abc./); + assert.match( + messageOutput, + /Deployed but did not activate revision 123abc./ + ); assert.match(messageOutput, /To activate, run/); assert.match(messageOutput, /ember deploy:activate qa --revision=123abc/); }); }); - describe('fetchInitialRevisions hook', function() { - it('fills the initialRevisions variable on context', function() { + describe("fetchInitialRevisions hook", function() { + it("fills the initialRevisions variable on context", function() { var plugin; var context; plugin = subject.createDeployPlugin({ - name: 'redis' + name: "redis" }); context = { @@ -666,17 +679,19 @@ describe('redis plugin', function() { project: stubProject, config: { redis: { - keyPrefix: 'test-prefix', - filePattern: 'index.html', - distDir: 'tests', - revisionKey: '123abc', + keyPrefix: "test-prefix", + filePattern: "index.html", + distDir: "tests", + revisionKey: "123abc", redisDeployClient: function(/* context */) { return { fetchRevisions: function(/* keyPrefix, revisionKey */) { - return RSVP.resolve([{ - revision: 'a', - active: false - }]); + return RSVP.resolve([ + { + revision: "a", + active: false + } + ]); } }; } @@ -686,25 +701,28 @@ describe('redis plugin', function() { plugin.beforeHook(context); plugin.configure(context); - return assert.isFulfilled(plugin.fetchInitialRevisions(context)) + return assert + .isFulfilled(plugin.fetchInitialRevisions(context)) .then(function(result) { assert.deepEqual(result, { - initialRevisions: [{ - "active": false, - "revision": "a" - }] + initialRevisions: [ + { + active: false, + revision: "a" + } + ] }); }); }); }); - describe('fetchRevisions hook', function() { - it('fills the revisions variable on context', function() { + describe("fetchRevisions hook", function() { + it("fills the revisions variable on context", function() { var plugin; var context; plugin = subject.createDeployPlugin({ - name: 'redis' + name: "redis" }); context = { @@ -712,17 +730,19 @@ describe('redis plugin', function() { project: stubProject, config: { redis: { - keyPrefix: 'test-prefix', - filePattern: 'index.html', - distDir: 'tests', - revisionKey: '123abc', + keyPrefix: "test-prefix", + filePattern: "index.html", + distDir: "tests", + revisionKey: "123abc", redisDeployClient: function(/* context */) { return { fetchRevisions: function(/* keyPrefix, revisionKey */) { - return RSVP.resolve([{ - revision: 'a', - active: false - }]); + return RSVP.resolve([ + { + revision: "a", + active: false + } + ]); } }; } @@ -732,13 +752,16 @@ describe('redis plugin', function() { plugin.beforeHook(context); plugin.configure(context); - return assert.isFulfilled(plugin.fetchRevisions(context)) + return assert + .isFulfilled(plugin.fetchRevisions(context)) .then(function(result) { assert.deepEqual(result, { - revisions: [{ - "active": false, - "revision": "a" - }] + revisions: [ + { + active: false, + revision: "a" + } + ] }); }); }); diff --git a/tests/unit/lib/redis-test.js b/tests/unit/lib/redis-test.js index a0b286a..f2fe270 100644 --- a/tests/unit/lib/redis-test.js +++ b/tests/unit/lib/redis-test.js @@ -1,391 +1,353 @@ 'use strict'; -var FakeRedis = require('../../helpers/fake-redis-lib'); -var FakeClient = require('../../helpers/fake-redis-client'); - +var IoredisMock = require('ioredis-mock'); var RSVP = require('rsvp'); -var assert = require('../../helpers/assert'); -var CoreObject = require('core-object'); +var assert = require('../../helpers/assert'); +var sandbox = require('sinon').createSandbox(); -describe('redis', function() { +describe('redis', function () { var Redis; - before(function() { + before(function () { Redis = require('../../../lib/redis'); }); - describe('#upload', function() { - it('rejects if the key already exists in redis', function() { - var redis = new Redis({}, new FakeRedis()); + afterEach(function () { + sandbox.restore(); + }); - var promise = redis.upload('key', 'value'); - return assert.isRejected(promise, /^Value already exists for key: key:default$/); - }); + describe('#upload', function () { + it('rejects if the key already exists in redis', function () { + var redis = new Redis({}, IoredisMock); - it('uploads the contents if the key does not already exist', function() { - var fileUploaded = false; - - var redis = new Redis({}, new FakeRedis(FakeClient.extend({ - get: function(key) { - return RSVP.resolve(null); - }, - set: function(key, value) { - fileUploaded = true; - }, - zadd: function(key, value) { - assert(key.match(/:revisions$/)); - }, - zrange: function(key, value) { - assert(key.match(/:revisions$/)); - } - }))); - - var promise = redis.upload('key', 'value'); - return assert.isFulfilled(promise) - .then(function() { - assert.ok(fileUploaded); - }); + return redis.upload('key', 'value').then(() => { + var promise = redis.upload('key', 'value'); + assert.isRejected(promise, /^Value already exists for key: key:default$/); + }); }); - it('uploads the contents if the key already exists but allowOverwrite is true', function() { - var fileUploaded = false; + it('uploads the contents if the key does not already exist', function () { + var redis = new Redis({}, IoredisMock); - var redis = new Redis({ - allowOverwrite: true - }, new FakeRedis(FakeClient.extend({ - set: function(key, value) { - fileUploaded = true; - } - }))); - - var promise = redis.upload('key', 'value'); - return assert.isFulfilled(promise) - .then(function() { - assert.ok(fileUploaded); - }); + var promise = redis.upload('key', 'value', 'filecontents'); + return assert.isFulfilled(promise).then(() => { + return redis._client.get('key:value'); + }).then((value) => { + assert.equal(value, 'filecontents'); + }); }); - it('updates the list of recent uploads once upload is successful', function() { - var redis = new Redis({}, new FakeRedis(FakeClient.extend({ - get: function(key) { - return RSVP.resolve(null); - } - }))); - - var promise = redis.upload('key', 'value'); - return assert.isFulfilled(promise) - .then(function() { - assert.equal(redis._client.recentRevisions.length, 1); - assert.equal(redis._client.recentRevisions[0], 'key:default'); - }); + it('uploads the contents if the key already exists but allowOverwrite is true', function () { + var redis = new Redis({ + allowOverwrite: true + }, IoredisMock); + + return redis.upload('key', 'value', 'firstfilecontents').then(() => { + return redis.upload('key', 'value', 'secondfilecontents'); + }).then(() => { + return redis._client.get('key:value'); + }).then((value) => { + assert.equal(value, 'secondfilecontents'); + }); }); - it('trims the list of recent uploads and removes the index key and the revisionData', function() { - var finalUploads = ['3','4','5','6','7','8','9','10','11','key:12']; - - var redis = new Redis({}, new FakeRedis(FakeClient.extend({ - get: function(key) { - return RSVP.resolve(null); - }, - del: function(key) { - assert(key === 'key:1' || key === 'key:2' || key === 'key:revision-data:1' || key === 'key:revision-data:2'); - }, - zrange: function() { - return this.recentRevisions.slice(0,2); - }, - zrem: function(key) { - assert(key.match(/:revisions$/)); - return this._super.apply(this, arguments); - } - }))); - - redis._client.recentRevisions = ['1','2','3','4','5','6','7','8','9','10','11']; - - var promise = redis.upload('key', '12', 'value'); - return assert.isFulfilled(promise) - .then(function() { - assert.equal(redis._client.recentRevisions.length, 10); - assert.deepEqual(redis._client.recentRevisions, finalUploads); + it('trims the list of recent uploads and removes the index key and the revisionData', function () { + var redis = new Redis({ + maxRecentUploads: 2 + }, IoredisMock); + + return RSVP.resolve() + .then(() => { + return redis.upload('key', 1, '1value'); + }) + .then(() => { + return redis.upload('key', 2, '2value'); + }) + .then(() => { + return redis.upload('key', 3, '3value'); + }) + .then(() => { + return redis._client.mget('key:1', 'key:revision-data:1') + }) + .then((values) => { + assert.equal(values.filter(Boolean).length, 0, 'Expected key:1 and key:revision-data:1 to be deleted.'); + return redis._client.zrange('key:revisions', 0, -1); + }) + .then((value) => { + assert.deepEqual(value, ['2', '3']); }); }); - it('trims the list of recent uploads but leaves the active one', function() { - var finalUploads = ['1','3','4','5','6','7','8','9','10','11','key:12']; - - var redis = new Redis({}, new FakeRedis(FakeClient.extend({ - get: function(key) { - if (key == 'key:current') { - return RSVP.resolve('1'); - } - return RSVP.resolve(null); - }, - zrange: function() { - return this.recentRevisions.slice(0,2); - } - }))); - - redis._client.recentRevisions = ['1','2','3','4','5','6','7','8','9','10','11']; - - var promise = redis.upload('key', '12', 'value'); - return assert.isFulfilled(promise) - .then(function() { - assert.equal(redis._client.recentRevisions.length, 11); - assert.deepEqual(redis._client.recentRevisions, finalUploads); + it('trims the list of recent uploads but leaves the active one', function () { + var redis = new Redis({ + maxRecentUploads: 2 + }, IoredisMock); + + return RSVP.resolve() + .then(() => { + return redis.upload('key', 1, '1value'); + }) + .then(() => { + return redis._client.set('key:current', '1'); + }) + .then(() => { + return redis.upload('key', 2, '2value'); + }) + .then(() => { + return redis.upload('key', 3, '3value'); + }) + .then(() => { + return redis.upload('key', 4, '4value'); + }) + .then(() => { + return redis._client.keys('*'); + }) + .then((values) => { + assert.deepEqual(values, [ + 'key:1', + 'key:revisions', + 'key:current', + 'key:3', // key 2 was trimmed + 'key:4' + ]); }); }); - it('trims the list of recent uploads if maxRecentUploads exists', function() { - var finalUploads = ['2','3','4','5','key:6']; - - var redis = new Redis({ maxRecentUploads: 5 }, new FakeRedis(FakeClient.extend({ - get: function(/* key */) { - return RSVP.resolve(null); - }, - zrange: function(listKey, startIndex, stopIndex) { - var end = this.recentRevisions.length - (Math.abs(stopIndex) - 1); - return this.recentRevisions.slice(0, end); - } - }))); - - redis._client.recentRevisions = ['1','2','3','4','5']; - - var promise = redis.upload('key', '6', 'value'); - return assert.isFulfilled(promise) - .then(function() { - assert.equal(redis._client.recentRevisions.length, 5); - assert.deepEqual(redis._client.recentRevisions, finalUploads); - }); - }); - - describe('generating the redis key', function() { - it('will use just the default tag if the tag is not provided', function() { - var redisKey = null; - - var redis = new Redis({}, new FakeRedis(FakeClient.extend({ - get: function(key) { - redisKey = key; - return RSVP.resolve('some-other-value'); - } - }))); + describe('generating the redis key', function () { + it('will use just the default tag if the tag is not provided', function () { + var redis = new Redis({}, IoredisMock); - var promise = redis.upload('key', 'value'); - return assert.isRejected(promise) - .then(function() { - assert.equal(redisKey, 'key:default'); + return RSVP.resolve() + .then(() => { + return redis.upload('key', undefined, 'filecontents'); + }) + .then(() => { + return redis._client.get('key:default'); }) + .then((value) => { + assert.equal(value, 'filecontents'); + }); }); - it('will use the key and the tag if the tag is provided', function() { - var redisKey = null; - var redis = new Redis({}, new FakeRedis(FakeClient.extend({ - get: function(key) { - redisKey = key; - return RSVP.resolve('some-other-value'); - } - }))); + it('will use the key and the tag if the tag is provided', function () { + var redis = new Redis({}, IoredisMock); - var promise = redis.upload('key', 'tag', 'value'); - return assert.isRejected(promise) - .then(function() { - assert.equal(redisKey, 'key:tag'); + return RSVP.resolve() + .then(() => { + return redis.upload('key', 'tag', 'filecontents'); + }) + .then(() => { + return redis._client.get('key:tag'); }) + .then((value) => { + assert.equal(value, 'filecontents'); + }); }); }); }); - describe('#willActivate', function() { - it('sets the previous revision to the current revision', function() { - var currentRevision = 'q'; - - var redis = new Redis({}, new FakeRedis(FakeClient.extend({ - get: function() { - return currentRevision; - } - }))); - - var result = redis.activeRevision('key-prefix'); - assert.equal(result, 'q'); - }); - }), - - describe('#activate', function() { - it('rejects if the revisionKey doesn\'t exist in list of uploaded revisions', function() { - var redis = new Redis({}, new FakeRedis(FakeClient.extend({ - zrevrange: function() { - return this.recentRevisions; - } - }))); - - redis._client.recentRevisions = ['a', 'b', 'c']; - - var promise = redis.activate('key-prefix', 'revision-key'); - return assert.isRejected(promise) - .then(function(error) { - assert.equal(error, '`revision-key` is not a valid revision key'); - }); - }); + describe('#willActivate', function () { + it('sets the previous revision to the current revision', function () { + var redis = new Redis({}, IoredisMock); - it('resolves and sets the current revision to the revision key provided', function() { - var redisKey, redisValue; - - var redis = new Redis({}, new FakeRedis(FakeClient.extend({ - set: function(key, value) { - redisKey = key; - redisValue = value; - } - }))); - - redis._client.recentRevisions = ['a', 'b', 'c']; - - var promise = redis.activate('key-prefix', 'c', 'current'); - return assert.isFulfilled(promise) - .then(function() { - assert.equal(redisKey, 'key-prefix:current'); - assert.equal(redisValue, 'c'); - }); - }); + return RSVP.resolve() + .then(() => { + return redis.upload('key', '1', 'filecontents1'); + }) + .then(() => { + return redis.upload('key', '2', 'filecontents2'); + }) + .then(() => { + return redis.activate('key', '1', 'current'); + }) + .then(() => { + return redis.activeRevision('key'); + }) + .then((activeRevision) => { + assert.equal(activeRevision, '1'); + }); + }); + }), - it('copies revision to the activeContentSuffix', function() { - var redisKey, redisValue; + describe('#activate', function () { + it('rejects if the revisionKey doesn\'t exist in list of uploaded revisions', function () { + var redis = new Redis({}, IoredisMock); - var redisClient = new FakeRedis(FakeClient.extend({ - _db: { - "key-prefix:a": "first revision content", - "key-prefix:b": "second revision content", - "key-prefix:c": "third revision content" - }, + return RSVP.resolve() + .then(() => { + return redis.upload('key', '1', 'filecontents1'); + }) + .then(() => { + return redis.upload('key', '2', 'filecontents2'); + }) + .then(() => { + var promise = redis.activate('key', '3', 'current'); + return assert.isRejected(promise); + }) + }); - get: function(key) { - return RSVP.resolve(this._db[key]); - }, - set: function(key, value) { - this._db[key] = value; - return RSVP.resolve(value); - }, - })); + it('resolves and sets the current revision to the revision key provided', function () { + var redis = new Redis({}, IoredisMock); - var redis = new Redis({}, redisClient); + return RSVP.resolve() + .then(() => { + return redis.upload('key', '1', 'filecontents1'); + }) + .then(() => { + return redis.upload('key', '2', 'filecontents2'); + }) + .then(() => { + return redis.activate('key', '1', 'current'); + }) + .then(() => { + return redis.activeRevision('key'); + }) + .then((activeRevision) => { + assert.equal(activeRevision, '1'); + return redis._client.get('key:1'); + }).then((keyContents) => { + assert.equal(keyContents, 'filecontents1'); + }); + }); - redis._client.recentRevisions = ['a', 'b', 'c']; + it('copies revision to the activeContentSuffix', function () { + var redis = new Redis({}, IoredisMock); - var activate = redis.activate('key-prefix', 'c', 'current-id', 'current-content').then(function() { - return redis._client.get('key-prefix:current-content'); + return RSVP.resolve() + .then(() => { + return redis.upload('key', '1', 'filecontents1'); + }) + .then(() => { + return redis.upload('key', '2', 'filecontents2'); + }) + .then(() => { + return redis.upload('key', '3', 'filecontents3'); + }) + .then(() => { + return redis.activate('key', '1', 'current-id', 'current-content'); + }) + .then(() => { + return redis._client.get('key:current-content'); + }) + .then((currentContent) => { + assert.equal(currentContent, 'filecontents1'); + return redis._client.get('key:current-id'); + }).then((currentId) => { + assert.equal(currentId, '1'); + }); }); - - return assert.isFulfilled(activate) - .then(function(result) { - assert.equal(result, "third revision content"); - }); }); - }); - - describe('#fetchRevisions', function() { - it('lists the last existing revisions', function() { - var redis = new Redis({}, new FakeRedis(FakeClient.extend({ - }))); - redis._client.recentRevisions = ['a', 'b', 'c']; - - var promise = redis.fetchRevisions('key-prefix'); - return assert.isFulfilled(promise) - .then(function(result) { - assert.deepEqual(result, [ - { - revision: 'a', - active: false + describe('#fetchRevisions', function () { + it('lists the last existing revisions', function () { + var redis = new Redis({}, IoredisMock); + + return RSVP.resolve() + .then(() => { + return redis.upload('key', '1', 'filecontents1'); + }) + .then(() => { + return redis.upload('key', '2', 'filecontents2'); + }) + .then(() => { + return redis.upload('key', '3', 'filecontents3'); + }) + .then(() => { + return redis.fetchRevisions('key'); + }) + .then((recentRevisions) => { + assert.deepEqual(recentRevisions, [{ + revision: '3', + active: false, + revisionData: null }, { - revision: 'b', - active: false + revision: '2', + active: false, + revisionData: null }, { - revision: 'c', - active: false + revision: '1', + active: false, + revisionData: null } - ] - ); - }); + ]); + }); }); - it('lists revisions and marks the active one', function() { - var currentRevision = 'b'; - - var redis = new Redis({}, new FakeRedis(FakeClient.extend({ - get: function() { - return currentRevision; - }, - zrevrange: function(key) { - assert(key.match(/:revisions$/)); - return this._super.apply(this, arguments); - } - }))); - - redis._client.recentRevisions = ['a', 'b']; - - var promise = redis.fetchRevisions('key-prefix'); - return assert.isFulfilled(promise) - .then(function(result) { - assert.deepEqual(result, [ + it('lists revisions and marks the active one', function () { + var redis = new Redis({}, IoredisMock); + + return RSVP.resolve() + .then(() => { + return redis.upload('key', '1', 'filecontents1'); + }) + .then(() => { + return redis.activate('key', '1', 'current'); + }) + .then(() => { + return redis.upload('key', '2', 'filecontents2'); + }) + .then(() => { + return redis.upload('key', '3', 'filecontents3'); + }) + .then(() => { + return redis.fetchRevisions('key'); + }) + .then((recentRevisions) => { + assert.deepEqual(recentRevisions, [{ + revision: '3', + active: false, + revisionData: null + }, { - revision: 'a', - active: false + revision: '2', + active: false, + revisionData: null }, { - revision: 'b', - active: true + revision: '1', + active: true, + revisionData: null } - ] - ); - }); + ]); + }); }); - it('retrieves revisionData', function() { - var redis = new Redis({}, new FakeRedis(FakeClient.extend({ - get: function() { - }, - mget: function(keys) { - return RSVP.resolve(['{"revisionKey":"a","timestamp":"2016-03-13T14:25:40.563Z","scm":{"sha":"9101968710f18a6720c48bf032fd82efd5743b7d","email":"mattia@mail.com","name":"Mattia Gheda","timestamp":"2015-12-22T12:44:48.000Z","branch":"master"}}']); - } - }))); - - redis._client.recentRevisions = ['a']; - - var promise = redis.fetchRevisions('key-prefix'); - return assert.isFulfilled(promise) - .then(function(result) { - assert.deepEqual(result, [ - { - revision: 'a', - active: false, - revisionData: { - revisionKey: 'a', - timestamp: '2016-03-13T14:25:40.563Z', - scm: - { sha: '9101968710f18a6720c48bf032fd82efd5743b7d', - email: 'mattia@mail.com', - name: 'Mattia Gheda', - timestamp: '2015-12-22T12:44:48.000Z', - branch: 'master' } - } - } - ]); + it('retrieves revisionData', function () { + var redis = new Redis({}, IoredisMock); + var revisionData = '{"revisionKey":"a","timestamp":"2016-03-13T14:25:40.563Z","scm":{"sha":"9101968710f18a6720c48bf032fd82efd5743b7d","email":"mattia@mail.com","name":"Mattia Gheda","timestamp":"2015-12-22T12:44:48.000Z","branch":"master"}}'; + + return RSVP.resolve() + .then(() => { + return redis.upload('key', '1', revisionData, 'filecontents1'); + }) + .then(() => { + return redis.fetchRevisions('key'); + }) + .then((revisions) => { + assert.deepEqual(revisions, [{ + revision: '1', + active: false, + revisionData: revisionData + }]); }); }); - it('uses activationSuffix in order to get the right activeRevision', function() { + it('uses activationSuffix in order to get the right activeRevision', function () { var redis = new Redis({ activationSuffix: 'active-key' - }, new FakeRedis(FakeClient.extend({ - get: function(key) { - return RSVP.resolve(key); - } - }))); - - var promise = redis.activeRevision('key-prefix'); - return assert.isFulfilled(promise) - .then(function(result) { - assert.equal(result, 'key-prefix:active-key'); + }, IoredisMock); + + var redisGetStub = sandbox.stub(redis._client, 'get').returns(RSVP.Promise.resolve()); + + return RSVP.resolve() + .then(() => { + return redis.activeRevision('key-prefix'); + }) + .then(() => { + assert.isTrue(redisGetStub.calledWith('key-prefix:active-key')); }); }); }); diff --git a/yarn.lock b/yarn.lock index 548d22b..5d16f7a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,22 @@ # yarn lockfile v1 +"@sinonjs/commons@^1.0.1": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.0.2.tgz#3e0ac737781627b8844257fadc3d803997d0526e" + dependencies: + type-detect "4.0.8" + +"@sinonjs/formatio@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-2.0.0.tgz#84db7e9eb5531df18a8c5e0bfb6e449e55e654b2" + dependencies: + samsam "1.3.0" + +"@sinonjs/samsam@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-2.0.0.tgz#9163742ac35c12d3602dece74317643b35db6a80" + abbrev@1: version "1.1.0" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.0.tgz#d0554c2256636e2f56e7c2e5ad183f859428d81f" @@ -166,6 +182,10 @@ array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" +array-from@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/array-from/-/array-from-2.1.1.tgz#cfe9d8c26628b9dc5aecc62a9f5d8f1f352c1195" + array-index@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-index/-/array-index-1.0.0.tgz#ec56a749ee103e4e08c790b9c353df16055b97f9" @@ -413,6 +433,10 @@ balanced-match@^0.4.1: version "0.4.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + base64-arraybuffer@0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8" @@ -469,6 +493,10 @@ bluebird@^3.1.1, bluebird@^3.4.6: version "3.5.0" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c" +bluebird@^3.3.4, bluebird@^3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" + body@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/body/-/body-5.1.0.tgz#e4ba0ce410a46936323367609ecb4e6553125069" @@ -505,6 +533,13 @@ brace-expansion@^1.0.0: balanced-match "^0.4.1" concat-map "0.0.1" +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + braces@^1.8.2: version "1.8.5" resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" @@ -925,6 +960,10 @@ clone@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.1.tgz#d217d1e961118e3ac9a4b8bba3285553bf647cdb" +cluster-key-slot@^1.0.6: + version "1.0.12" + resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.0.12.tgz#d5deff2a520717bc98313979b687309b2d368e29" + cmd-shim@~2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-2.0.2.tgz#6fcbda99483a8fd15d7d30a196ca69d688a2efdb" @@ -1176,6 +1215,12 @@ debug@2.6.1: dependencies: ms "0.7.2" +debug@^2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + dependencies: + ms "2.0.0" + debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" @@ -1239,6 +1284,10 @@ delegates@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" +denque@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/denque/-/denque-1.3.0.tgz#681092ef44a630246d3f6edb2a199230eae8e76b" + depd@1.1.0, depd@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.0.tgz#e1bd82c6aab6ced965b97b88b17ed3e528ca18c3" @@ -1279,6 +1328,10 @@ diff@1.4.0, diff@^1.3.1: version "1.4.0" resolved "https://registry.yarnpkg.com/diff/-/diff-1.4.0.tgz#7f28d2eb9ee7b15a97efd89ce63dcfdaa3ccbabf" +diff@^3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" + doctrine@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.0.0.tgz#c73d8d2909d22291e1a007a395804da8b665fe63" @@ -1642,7 +1695,7 @@ es6-iterator@2, es6-iterator@^2.0.1, es6-iterator@~2.0.1: es5-ext "^0.10.14" es6-symbol "^3.1" -es6-map@^0.1.3: +es6-map@^0.1.3, es6-map@^0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0" dependencies: @@ -1653,7 +1706,7 @@ es6-map@^0.1.3: es6-symbol "~3.1.1" event-emitter "~0.3.5" -es6-set@~0.1.5: +es6-set@^0.1.5, es6-set@~0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1" dependencies: @@ -2027,6 +2080,10 @@ flat-cache@^1.2.1: graceful-fs "^4.1.2" write "^0.2.1" +flexbuffer@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/flexbuffer/-/flexbuffer-0.0.6.tgz#039fdf23f8823e440c38f3277e6fef1174215b30" + follow-redirects@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-0.0.7.tgz#34b90bab2a911aa347571da90f22bd36ecd8a919" @@ -2405,6 +2462,10 @@ has-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + has-unicode@^2.0.0, has-unicode@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" @@ -2615,6 +2676,46 @@ invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" +ioredis-mock@^3.14.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/ioredis-mock/-/ioredis-mock-3.14.0.tgz#89d01e94aa2d5e30b717d7938b358b689e68e6e3" + dependencies: + array-from "^2.1.1" + bluebird "^3.5.1" + es6-map "^0.1.5" + es6-set "^0.1.5" + lodash "^4.17.4" + minimatch "^3.0.4" + object-assign "^4.1.1" + +ioredis@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-3.2.2.tgz#b7d5ff3afd77bb9718bb2821329b894b9a44c00b" + dependencies: + bluebird "^3.3.4" + cluster-key-slot "^1.0.6" + debug "^2.6.9" + denque "^1.1.0" + flexbuffer "0.0.6" + lodash.assign "^4.2.0" + lodash.bind "^4.2.1" + lodash.clone "^4.5.0" + lodash.clonedeep "^4.5.0" + lodash.defaults "^4.2.0" + lodash.difference "^4.5.0" + lodash.flatten "^4.4.0" + lodash.foreach "^4.5.0" + lodash.isempty "^4.4.0" + lodash.keys "^4.2.0" + lodash.noop "^3.0.1" + lodash.partial "^4.2.1" + lodash.pick "^4.4.0" + lodash.sample "^4.2.1" + lodash.shuffle "^4.2.0" + lodash.values "^4.3.0" + redis-commands "^1.2.0" + redis-parser "^2.4.0" + ipaddr.js@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.3.0.tgz#1e03a52fdad83a8bbb2b25cbf4998b4cffcd3dec" @@ -2872,6 +2973,10 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.3.6" +just-extend@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-3.0.0.tgz#cee004031eaabf6406da03a7b84e4fe9d78ef288" + kind-of@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.1.0.tgz#475d698a5e49ff5e53d14e3e732429dc8bf4cf47" @@ -3064,10 +3169,22 @@ lodash.assign@^3.2.0: lodash._createassigner "^3.0.0" lodash.keys "^3.0.0" +lodash.assign@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" + lodash.assignin@^4.1.0: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.assignin/-/lodash.assignin-4.2.0.tgz#ba8df5fb841eb0a3e8044232b0e263a8dc6a28a2" +lodash.bind@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/lodash.bind/-/lodash.bind-4.2.1.tgz#7ae3017e939622ac31b7d7d7dcb1b34db1690d35" + +lodash.clone@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clone/-/lodash.clone-4.5.0.tgz#195870450f5a13192478df4bc3d23d2dea1907b6" + lodash.clonedeep@^4.4.1, lodash.clonedeep@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" @@ -3093,6 +3210,14 @@ lodash.debounce@^3.1.1: dependencies: lodash._getnative "^3.0.0" +lodash.defaults@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" + +lodash.difference@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.difference/-/lodash.difference-4.5.0.tgz#9ccb4e505d486b91651345772885a2df27fd017c" + lodash.find@^4.5.1: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.find/-/lodash.find-4.6.0.tgz#cb0704d47ab71789ffa0de8b97dd926fb88b13b1" @@ -3104,6 +3229,18 @@ lodash.flatten@^3.0.2: lodash._baseflatten "^3.0.0" lodash._isiterateecall "^3.0.0" +lodash.flatten@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" + +lodash.foreach@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53" + +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + lodash.isarguments@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" @@ -3112,6 +3249,10 @@ lodash.isarray@^3.0.0: version "3.0.4" resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" +lodash.isempty@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.isempty/-/lodash.isempty-4.4.0.tgz#6f86cbedd8be4ec987be9aaf33c9684db1b31e7e" + lodash.isplainobject@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-3.2.0.tgz#9a8238ae16b200432960cd7346512d0123fbf4c5" @@ -3132,6 +3273,10 @@ lodash.keys@^3.0.0: lodash.isarguments "^3.0.0" lodash.isarray "^3.0.0" +lodash.keys@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-4.2.0.tgz#a08602ac12e4fb83f91fc1fb7a360a4d9ba35205" + lodash.keysin@^3.0.0: version "3.0.8" resolved "https://registry.yarnpkg.com/lodash.keysin/-/lodash.keysin-3.0.8.tgz#22c4493ebbedb1427962a54b445b2c8a767fb47f" @@ -3159,6 +3304,10 @@ lodash.merge@^4.3.0, lodash.merge@^4.4.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.0.tgz#69884ba144ac33fe699737a6086deffadd0f89c5" +lodash.noop@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash.noop/-/lodash.noop-3.0.1.tgz#38188f4d650a3a474258439b96ec45b32617133c" + lodash.omit@^4.1.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.omit/-/lodash.omit-4.5.0.tgz#6eb19ae5a1ee1dd9df0b969e66ce0b7fa30b5e60" @@ -3181,10 +3330,26 @@ lodash.pairs@^3.0.0: dependencies: lodash.keys "^3.0.0" +lodash.partial@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/lodash.partial/-/lodash.partial-4.2.1.tgz#49f3d8cfdaa3bff8b3a91d127e923245418961d4" + +lodash.pick@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" + lodash.restparam@^3.0.0: version "3.6.1" resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" +lodash.sample@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/lodash.sample/-/lodash.sample-4.2.1.tgz#5e4291b0c753fa1abeb0aab8fb29df1b66f07f6d" + +lodash.shuffle@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.shuffle/-/lodash.shuffle-4.2.0.tgz#145b5053cf875f6f5c2a33f48b6e9948c6ec7b4b" + lodash.template@^4.2.5: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.4.0.tgz#e73a0385c8355591746e020b99679c690e68fba0" @@ -3227,6 +3392,10 @@ lodash.uniq@~3.2.2: lodash._isiterateecall "^3.0.0" lodash.isarray "^3.0.0" +lodash.values@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.values/-/lodash.values-4.3.0.tgz#a3a6c2b0ebecc5c2cba1c17e6e620fe81b53d347" + lodash.without@~3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/lodash.without/-/lodash.without-3.2.1.tgz#d69614b3512e52294b6abab782e7ca96538ce816" @@ -3242,6 +3411,14 @@ lodash@^4.0.0, lodash@^4.14.0, lodash@^4.3.0, lodash@^4.6.1: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" +lodash@^4.17.4: + version "4.17.10" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" + +lolex@^2.3.2, lolex@^2.7.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/lolex/-/lolex-2.7.1.tgz#e40a8c4d1f14b536aa03e42a537c7adbaf0c20be" + longest@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" @@ -3390,6 +3567,12 @@ minimatch@^2.0.1, minimatch@^2.0.3: dependencies: brace-expansion "^1.0.0" +minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + dependencies: + brace-expansion "^1.1.7" + minimist@0.0.8, minimist@~0.0.1: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" @@ -3456,6 +3639,10 @@ ms@0.7.2: version "0.7.2" resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765" +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + multiline@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/multiline/-/multiline-1.0.2.tgz#69b1f25ff074d2828904f244ddd06b7d96ef6c93" @@ -3482,6 +3669,16 @@ negotiator@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" +nise@^1.4.2: + version "1.4.4" + resolved "https://registry.yarnpkg.com/nise/-/nise-1.4.4.tgz#b8d9dd35334c99e514b75e46fcc38e358caecdd0" + dependencies: + "@sinonjs/formatio" "^2.0.0" + just-extend "^3.0.0" + lolex "^2.3.2" + path-to-regexp "^1.7.0" + text-encoding "^0.6.4" + node-fetch@^1.3.3: version "1.6.3" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.6.3.tgz#dc234edd6489982d58e8f0db4f695029abcd8c04" @@ -3724,7 +3921,7 @@ object-assign@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" -object-assign@^4.0.1, object-assign@^4.1.0: +object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -3920,6 +4117,12 @@ path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" +path-to-regexp@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d" + dependencies: + isarray "0.0.1" + pify@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -4176,6 +4379,10 @@ redis-commands@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.3.1.tgz#81d826f45fa9c8b2011f4cd7a0fe597d241d442b" +redis-parser@^2.4.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-2.6.0.tgz#52ed09dacac108f1a631c07e9b69941e7a19504b" + redis-parser@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-2.5.0.tgz#79fc2b1d4a6e4d2870b35368433639271fca2617" @@ -4366,6 +4573,10 @@ safe-json-parse@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/safe-json-parse/-/safe-json-parse-1.0.1.tgz#3e76723e38dfdda13c9b1d29a1e07ffee4b30b57" +samsam@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.3.0.tgz#8d1d9350e25622da30de3e44ba692b5221ab7c50" + sane@^1.1.1: version "1.6.0" resolved "https://registry.yarnpkg.com/sane/-/sane-1.6.0.tgz#9610c452307a135d29c1fdfe2547034180c46775" @@ -4480,6 +4691,20 @@ simple-is@~0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/simple-is/-/simple-is-0.2.0.tgz#2abb75aade39deb5cc815ce10e6191164850baf0" +sinon@^6.1.5: + version "6.1.5" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-6.1.5.tgz#41451502d43cd5ffb9d051fbf507952400e81d09" + dependencies: + "@sinonjs/commons" "^1.0.1" + "@sinonjs/formatio" "^2.0.0" + "@sinonjs/samsam" "^2.0.0" + diff "^3.5.0" + lodash.get "^4.4.2" + lolex "^2.7.1" + nise "^1.4.2" + supports-color "^5.4.0" + type-detect "^4.0.8" + slash@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" @@ -4731,6 +4956,12 @@ supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" +supports-color@^5.4.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + dependencies: + has-flag "^3.0.0" + symlink-or-copy@^1.0.0, symlink-or-copy@^1.0.1, symlink-or-copy@^1.1.8: version "1.1.8" resolved "https://registry.yarnpkg.com/symlink-or-copy/-/symlink-or-copy-1.1.8.tgz#cabe61e0010c1c023c173b25ee5108b37f4b4aa3" @@ -4804,6 +5035,10 @@ testem@^1.8.1: tap-parser "^5.1.0" xmldom "^0.1.19" +text-encoding@^0.6.4: + version "0.6.4" + resolved "https://registry.yarnpkg.com/text-encoding/-/text-encoding-0.6.4.tgz#e399a982257a276dae428bb92845cb71bdc26d19" + text-table@~0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -4812,10 +5047,6 @@ text-table@~0.2.0: version "2.0.1" resolved "https://registry.yarnpkg.com/textextensions/-/textextensions-2.0.1.tgz#be8cf22d65379c151319f88f0335ad8f667abdca" -then-redis@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/then-redis/-/then-redis-2.0.1.tgz#e797f18fff7f3c50bfc2ae95a35db5ad0b0816ff" - through@^2.3.6, through@^2.3.8, through@~2.3.8: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" @@ -4903,6 +5134,10 @@ type-detect@0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-0.1.1.tgz#0ba5ec2a885640e470ea4e8505971900dac58822" +type-detect@4.0.8, type-detect@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + type-detect@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-1.0.0.tgz#762217cc06db258ec48908a1298e8b95121e8ea2"