UNPKG

10.9 kBJavaScriptView Raw
1'use strict';
2
3var assert = require('chai').assert;
4var Promise = require('bluebird');
5var Redlock = require('./redlock');
6
7test('https://www.npmjs.com/package/redis', [require('redis').createClient()]);
8test('https://www.npmjs.com/package/ioredis', [new (require('ioredis'))()]);
9
10/* istanbul ignore next */
11function test(name, clients){
12 var redlock = new Redlock(clients, {
13 retryCount: 2,
14 retryDelay: 150
15 });
16
17 var resource = 'Redlock:test:resource';
18
19 describe('Redlock: ' + name, function(){
20
21 describe('callbacks', function(){
22 before(function(done){
23 var err;
24 var l = clients.length; function cb(e){ if(e) err = e; l--; if(l === 0) done(err); }
25 for (var i = clients.length - 1; i >= 0; i--) {
26 clients[i].del(resource, cb);
27 }
28 });
29
30 it('should throw an error if not passed any clients', function(){
31 assert.throws(function(){
32 new Redlock([], {
33 retryCount: 2,
34 retryDelay: 150
35 });
36 });
37 });
38
39 var one;
40 it('should lock a resource', function(done){
41 redlock.lock(resource, 200, function(err, lock){
42 if(err) throw err;
43 assert.isObject(lock);
44 assert.isAbove(lock.expiration, Date.now()-1);
45 one = lock;
46 done();
47 });
48 });
49
50 var two;
51 var two_expiration;
52 it('should wait until a lock expires before issuing another lock', function(done){
53 assert(one, 'Could not run because a required previous test failed.');
54 redlock.lock(resource, 800, function(err, lock){
55 if(err) throw err;
56 assert.isObject(lock);
57 assert.isAbove(lock.expiration, Date.now()-1);
58 assert.isAbove(Date.now()+1, one.expiration);
59 two = lock;
60 two_expiration = lock.expiration;
61 done();
62 });
63 });
64
65 it('should unlock a resource', function(done){
66 assert(two, 'Could not run because a required previous test failed.');
67 two.unlock(done);
68 });
69
70 it('should silently fail to unlock an already-unlocked resource', function(done){
71 assert(two, 'Could not run because a required previous test failed.');
72 two.unlock(done);
73 });
74
75 it('should fail to extend a lock on an already-unlocked resource', function(done){
76 assert(two, 'Could not run because a required previous test failed.');
77 two.extend(200, function(err, lock){
78 assert.isNotNull(err);
79 assert.equal(err.name, 'LockError');
80 done();
81 });
82 });
83
84 var three;
85 it('should issue another lock immediately after a resource is unlocked', function(done){
86 assert(two_expiration, 'Could not run because a required previous test failed.');
87 redlock.lock(resource, 800, function(err, lock){
88 if(err) throw err;
89 assert.isObject(lock);
90 assert.isAbove(lock.expiration, Date.now()-1);
91 assert.isBelow(Date.now()-1, two_expiration);
92 three = lock;
93 done();
94 });
95 });
96
97 var four;
98 it('should extend an unexpired lock', function(done){
99 assert(three, 'Could not run because a required previous test failed.');
100 three.extend(800, function(err, lock){
101 if(err) throw err;
102 assert.isObject(lock);
103 assert.isAbove(lock.expiration, Date.now()-1);
104 assert.isAbove(lock.expiration, three.expiration-1);
105 assert.equal(three, lock);
106 four = lock;
107 done();
108 });
109 });
110
111 it('should fail after the maximum retry count is exceeded', function(done){
112 assert(four, 'Could not run because a required previous test failed.');
113 redlock.lock(resource, 200, function(err, lock){
114 assert.isNotNull(err);
115 assert.equal(err.name, 'LockError');
116 done();
117 });
118 });
119
120 it('should fail to extend an expired lock', function(done){
121 assert(four, 'Could not run because a required previous test failed.');
122 setTimeout(function(){
123 three.extend(800, function(err, lock){
124 assert.isNotNull(err);
125 assert.equal(err.name, 'LockError');
126 done();
127 });
128 }, four.expiration - Date.now() + 100);
129 });
130
131 it('should issue another lock immediately after a resource is expired', function(done){
132 assert(four, 'Could not run because a required previous test failed.');
133 redlock.lock(resource, 800, function(err, lock){
134 if(err) throw err;
135 assert.isObject(lock);
136 assert.isAbove(lock.expiration, Date.now()-1);
137 done();
138 });
139 });
140
141 after(function(done){
142 var err;
143 var l = clients.length; function cb(e){ if(e) err = e; l--; if(l === 0) done(err); }
144 for (var i = clients.length - 1; i >= 0; i--) {
145 clients[i].del(resource, cb);
146 }
147 });
148 });
149
150 describe('promises', function(){
151 before(function(done){
152 var err;
153 var l = clients.length; function cb(e){ if(e) err = e; l--; if(l === 0) done(err); }
154 for (var i = clients.length - 1; i >= 0; i--) {
155 clients[i].del(resource, cb);
156 }
157 });
158
159 it('should throw an error if not passed any clients', function(){
160 assert.throws(function(){
161 new Redlock([], {
162 retryCount: 2,
163 retryDelay: 150
164 });
165 });
166 });
167
168 var one;
169 it('should lock a resource', function(done){
170 redlock.lock(resource, 200)
171 .done(function(lock){
172 assert.isObject(lock);
173 assert.isAbove(lock.expiration, Date.now()-1);
174 one = lock;
175 done();
176 }, done);
177 });
178
179 var two;
180 var two_expiration;
181 it('should wait until a lock expires before issuing another lock', function(done){
182 assert(one, 'Could not run because a required previous test failed.');
183 redlock.lock(resource, 800)
184 .done(function(lock){
185 assert.isObject(lock);
186 assert.isAbove(lock.expiration, Date.now()-1);
187 assert.isAbove(Date.now()+1, one.expiration);
188 two = lock;
189 two_expiration = lock.expiration;
190 done();
191 }, done);
192 });
193
194 it('should unlock a resource', function(done){
195 assert(two, 'Could not run because a required previous test failed.');
196 two.unlock().done(done, done);
197 });
198
199 it('should silently fail to unlock an already-unlocked resource', function(done){
200 assert(two, 'Could not run because a required previous test failed.');
201 two.unlock().done(done, done);
202 });
203
204 it('should fail to extend a lock on an already-unlocked resource', function(done){
205 assert(two, 'Could not run because a required previous test failed.');
206 two.extend(200)
207 .done(function(){
208 done(new Error('Should have failed with a LockError'));
209 }, function(err){
210 assert.equal(err.name, 'LockError');
211 done();
212 });
213 });
214
215 var three;
216 it('should issue another lock immediately after a resource is unlocked', function(done){
217 assert(two_expiration, 'Could not run because a required previous test failed.');
218 redlock.lock(resource, 800)
219 .done(function(lock){
220 assert.isObject(lock);
221 assert.isAbove(lock.expiration, Date.now()-1);
222 assert.isBelow(Date.now()-1, two_expiration);
223 three = lock;
224 done();
225 }, done);
226 });
227
228 var four;
229 it('should extend an unexpired lock', function(done){
230 assert(three, 'Could not run because a required previous test failed.');
231 three.extend(800)
232 .done(function(lock){
233 assert.isObject(lock);
234 assert.isAbove(lock.expiration, Date.now()-1);
235 assert.isAbove(lock.expiration, three.expiration-1);
236 assert.equal(three, lock);
237 four = lock;
238 done();
239 }, done);
240 });
241
242 it('should fail after the maximum retry count is exceeded', function(done){
243 assert(four, 'Could not run because a required previous test failed.');
244 redlock.lock(resource, 200)
245 .done(function(){
246 done(new Error('Should have failed with a LockError'));
247 }, function(err){
248 assert.equal(err.name, 'LockError');
249 done();
250 });
251 });
252
253 it('should fail to extend an expired lock', function(done){
254 assert(four, 'Could not run because a required previous test failed.');
255 setTimeout(function(){
256 three.extend(800)
257 .done(function(){
258 done(new Error('Should have failed with a LockError'));
259 }, function(err){
260 assert.equal(err.name, 'LockError');
261 done();
262 });
263 }, four.expiration - Date.now() + 100);
264 });
265
266 after(function(done){
267 var err;
268 var l = clients.length; function cb(e){ if(e) err = e; l--; if(l === 0) done(err); }
269 for (var i = clients.length - 1; i >= 0; i--) {
270 clients[i].del(resource, cb);
271 }
272 });
273 });
274
275 describe('disposer', function(){
276 before(function(done){
277 var err;
278 var l = clients.length; function cb(e){ if(e) err = e; l--; if(l === 0) done(err); }
279 for (var i = clients.length - 1; i >= 0; i--) {
280 clients[i].del(resource, cb);
281 }
282 });
283
284 var one;
285 var one_expiration;
286 it('should automatically release a lock after the using block', function(done){
287 Promise.using(
288 redlock.disposer(resource, 200),
289 function(lock){
290 assert.isObject(lock);
291 assert.isAbove(lock.expiration, Date.now()-1);
292 one = lock;
293 one_expiration = lock.expiration;
294 }
295 ).done(done, done);
296 });
297
298 var two;
299 var two_expiration;
300 it('should issue another lock immediately after a resource is unlocked', function(done){
301 assert(one_expiration, 'Could not run because a required previous test failed.');
302 Promise.using(
303 redlock.disposer(resource, 800),
304 function(lock){
305 assert.isObject(lock);
306 assert.isAbove(lock.expiration, Date.now()-1);
307 assert.isBelow(Date.now()-1, one_expiration);
308 two = lock;
309 two_expiration = lock.expiration;
310 }
311 ).done(done, done);
312 });
313
314 var three_original, three_extended;
315 var three_original_expiration;
316 var three_extended_expiration;
317 it('should automatically release an extended lock', function(done){
318 assert(two_expiration, 'Could not run because a required previous test failed.');
319 Promise.using(
320 redlock.disposer(resource, 200),
321 function(lock){
322 assert.isObject(lock);
323 assert.isAbove(lock.expiration, Date.now()-1);
324 assert.isBelow(Date.now()-1, two_expiration);
325 three_original = lock;
326 three_original_expiration = lock.expiration;
327
328 return Promise.delay(100)
329 .then(function(){ return lock.extend(200); })
330 .then(function(extended) {
331 assert.isObject(extended);
332 assert.isAbove(extended.expiration, Date.now()-1);
333 assert.isBelow(Date.now()-1, three_original_expiration);
334 assert.isAbove(extended.expiration, three_original_expiration);
335 assert.equal(extended, lock);
336 three_extended = extended;
337 three_extended_expiration = extended.expiration;
338 });
339 }
340 )
341 .then(function(){
342 assert.equal(three_original.expiration, 0);
343 assert.equal(three_extended.expiration, 0);
344 }).done(done, done);
345 });
346
347 after(function(done){
348 var err;
349 var l = clients.length; function cb(e){ if(e) err = e; l--; if(l === 0) done(err); }
350 for (var i = clients.length - 1; i >= 0; i--) {
351 clients[i].del(resource, cb);
352 }
353 });
354 });
355
356 });
357}
\No newline at end of file