sharedb 3.2.4 → 3.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,6 +2,7 @@ var expect = require('chai').expect;
2
2
  var async = require('async');
3
3
  var util = require('../util');
4
4
  var sinon = require('sinon');
5
+ var ShareDBError = require('../../lib/error');
5
6
 
6
7
  module.exports = function(options) {
7
8
  var getQuery = options.getQuery;
@@ -11,596 +12,883 @@ module.exports = function(options) {
11
12
  this.matchAllDbQuery = getQuery({query: {}});
12
13
  });
13
14
 
14
- it('creating a document updates a subscribed query', function(done) {
15
- var connection = this.backend.connect();
16
- var query = connection.createSubscribeQuery('dogs', this.matchAllDbQuery, null, function(err) {
15
+ afterEach(function() {
16
+ sinon.restore();
17
+ });
18
+
19
+ commonTests(options);
20
+
21
+ describe('custom channels', function() {
22
+ it('only informs subscribed channels', function(done) {
23
+ this.backend.use('connect', function(context, next) {
24
+ context.agent.custom = context.req;
25
+ next();
26
+ });
27
+
28
+ this.backend.use('commit', function(context, next) {
29
+ var user = context.agent.custom;
30
+
31
+ if (user === 'sending-user-1') {
32
+ context.channels.push('channel-1');
33
+ }
34
+ if (user === 'sending-user-2') {
35
+ context.channels.push('channel-2');
36
+ }
37
+ next();
38
+ });
39
+
40
+ this.backend.use('query', function(context, next) {
41
+ var user = context.agent.custom;
42
+ if (user === 'receiving-user') {
43
+ context.channels = ['channel-1', 'channel-2'];
44
+ } else if (user === 'not-receiving-user') {
45
+ context.channels = ['different-channel'];
46
+ }
47
+ next();
48
+ });
49
+
50
+ var receivingUserConnection = this.backend.connect(null, 'receiving-user');
51
+ var notReceivingUserConnection = this.backend.connect(null, 'not-receiving-user');
52
+ var sendingUser1Connection = this.backend.connect(null, 'sending-user-1');
53
+ var sendingUser2Connection = this.backend.connect(null, 'sending-user-2');
54
+
55
+ var notReceivingQuery = notReceivingUserConnection.createSubscribeQuery(
56
+ 'dogs',
57
+ this.matchAllDbQuery,
58
+ null,
59
+ function(err) {
60
+ if (err) return done(err);
61
+ }
62
+ );
63
+
64
+ notReceivingQuery.on('error', done);
65
+ notReceivingQuery.on('insert', function() {
66
+ done('User who didn\'t subscribed to sending channels shouldn\'t get the message');
67
+ });
68
+
69
+ var receivingQuery = receivingUserConnection.createSubscribeQuery(
70
+ 'dogs',
71
+ this.matchAllDbQuery,
72
+ null,
73
+ function(err) {
74
+ if (err) return done(err);
75
+ sendingUser1Connection.get('dogs', '1').on('error', done).create({});
76
+ sendingUser2Connection.get('dogs', '2').on('error', done).create({});
77
+ }
78
+ );
79
+ var receivedDogsCount = 0;
80
+ receivingQuery.on('error', done);
81
+ receivingQuery.on('insert', function() {
82
+ receivedDogsCount++;
83
+ if (receivedDogsCount === 2) {
84
+ var allDocsIds = receivingQuery.results.map(function(doc) {
85
+ return doc.id;
86
+ });
87
+ expect(allDocsIds.sort()).to.be.deep.equal(['1', '2']);
88
+ done();
89
+ } else if (receivedDogsCount > 2) {
90
+ done('It should not duplicate messages');
91
+ }
92
+ });
93
+ });
94
+
95
+ describe('one common channel', function() {
96
+ beforeEach(function() {
97
+ this.backend.use('commit', function(context, next) {
98
+ context.channels.push('channel-1');
99
+ context.channels.push('channel-3');
100
+ next();
101
+ });
102
+ this.backend.use('query', function(context, next) {
103
+ context.channels = ['channel-1', 'channel-2'];
104
+ next();
105
+ });
106
+ });
107
+
108
+ commonCustomChannelsErrorHandlingTests();
109
+ commonTests(options);
110
+ });
111
+
112
+ describe('multiple common channels', function() {
113
+ beforeEach(function() {
114
+ this.backend.use('commit', function(context, next) {
115
+ context.channels.push('channel-1');
116
+ context.channels.push('channel-2');
117
+ next();
118
+ });
119
+ this.backend.use('query', function(context, next) {
120
+ context.channels = ['channel-1', 'channel-2'];
121
+ next();
122
+ });
123
+ });
124
+
125
+ it('does not duplicate messages', function(done) {
126
+ var connection = this.backend.connect();
127
+ var count = 0;
128
+ var query = connection.createSubscribeQuery(
129
+ 'dogs',
130
+ this.matchAllDbQuery,
131
+ {pollInterval: 0, pollDebounce: 0},
132
+ function(err) {
133
+ if (err) return done(err);
134
+ connection.get('dogs', '1').on('error', done).create({});
135
+ connection.get('dogs', '2').on('error', done).create({});
136
+ connection.get('dogs', '3').on('error', done).create({});
137
+ }
138
+ );
139
+ query.on('error', done);
140
+ query.on('insert', function() {
141
+ count++;
142
+ if (count === 3) {
143
+ var allDocsIds = query.results.map(function(doc) {
144
+ return doc.id;
145
+ });
146
+ expect(allDocsIds.sort()).to.be.deep.equal(['1', '2', '3']);
147
+ done();
148
+ } else if (count > 3) {
149
+ done('It should not duplicate messages');
150
+ }
151
+ });
152
+ });
153
+
154
+ commonCustomChannelsErrorHandlingTests();
155
+ commonTests(options);
156
+ });
157
+
158
+ describe('backward compatibility', function() {
159
+ beforeEach(function() {
160
+ this.backend.use('commit', function(context, next) {
161
+ context.channels.push('channel-1');
162
+ next();
163
+ });
164
+ this.backend.use('query', function(context, next) {
165
+ context.channel = 'channel-1';
166
+ next();
167
+ });
168
+ });
169
+ commonTests(options);
170
+ });
171
+ });
172
+ });
173
+ };
174
+
175
+ function commonCustomChannelsErrorHandlingTests() {
176
+ it('should throw if not channels provided in query', function(done) {
177
+ this.backend.use('query', function(context, next) {
178
+ context.channels = null;
179
+ next();
180
+ });
181
+ var connection = this.backend.connect();
182
+ var matchAllDbQuery = this.matchAllDbQuery;
183
+ async.parallel([
184
+ function(cb) {
185
+ connection.get('dogs', 'fido').on('error', done).create({age: 3}, cb);
186
+ },
187
+ function(cb) {
188
+ connection.get('dogs', 'spot').on('error', done).create({age: 5}, cb);
189
+ }
190
+ ], function(err) {
191
+ if (err) return done(err);
192
+ connection.createSubscribeQuery('dogs', matchAllDbQuery, null, function(err) {
193
+ if (!err) return done('Should throw Required minimum one query channel error');
194
+ expect(err.message).to.be.equal('Required minimum one query channel.');
195
+ expect(err.code).to.be.equal(ShareDBError.CODES.ERR_QUERY_CHANNEL_MISSING);
196
+ done();
197
+ });
198
+ });
199
+ });
200
+
201
+ it('should throw if channels provided in query is an empty array', function(done) {
202
+ this.backend.use('query', function(context, next) {
203
+ context.channels = [];
204
+ next();
205
+ });
206
+ var connection = this.backend.connect();
207
+ var matchAllDbQuery = this.matchAllDbQuery;
208
+ async.parallel([
209
+ function(cb) {
210
+ connection.get('dogs', 'fido').on('error', done).create({age: 3}, cb);
211
+ },
212
+ function(cb) {
213
+ connection.get('dogs', 'spot').on('error', done).create({age: 5}, cb);
214
+ }
215
+ ], function(err) {
216
+ if (err) return done(err);
217
+ connection.createSubscribeQuery('dogs', matchAllDbQuery, null, function(err) {
218
+ if (!err) return done('Should throw Required minimum one query channel error');
219
+ expect(err.message).to.be.equal('Required minimum one query channel.');
220
+ expect(err.code).to.be.equal(ShareDBError.CODES.ERR_QUERY_CHANNEL_MISSING);
221
+ done();
222
+ });
223
+ });
224
+ });
225
+ }
226
+
227
+ function commonTests(options) {
228
+ var getQuery = options.getQuery;
229
+
230
+ it('creating a document updates a subscribed query', function(done) {
231
+ var connection = this.backend.connect();
232
+ var query = connection.createSubscribeQuery('dogs', this.matchAllDbQuery, null, function(err) {
233
+ if (err) return done(err);
234
+ connection.get('dogs', 'fido').on('error', done).create({age: 3});
235
+ });
236
+ query.on('error', done);
237
+ query.on('insert', function(docs, index) {
238
+ expect(util.pluck(docs, 'id')).eql(['fido']);
239
+ expect(util.pluck(docs, 'data')).eql([{age: 3}]);
240
+ expect(index).equal(0);
241
+ expect(util.pluck(query.results, 'id')).eql(['fido']);
242
+ expect(util.pluck(query.results, 'data')).eql([{age: 3}]);
243
+ done();
244
+ });
245
+ });
246
+
247
+ it('creating an additional document updates a subscribed query', function(done) {
248
+ var connection = this.backend.connect();
249
+ var matchAllDbQuery = this.matchAllDbQuery;
250
+ async.parallel([
251
+ function(cb) {
252
+ connection.get('dogs', 'fido').on('error', done).create({age: 3}, cb);
253
+ },
254
+ function(cb) {
255
+ connection.get('dogs', 'spot').on('error', done).create({age: 5}, cb);
256
+ }
257
+ ], function(err) {
258
+ if (err) return done(err);
259
+ var query = connection.createSubscribeQuery('dogs', matchAllDbQuery, null, function(err) {
17
260
  if (err) return done(err);
18
- connection.get('dogs', 'fido').on('error', done).create({age: 3});
261
+ connection.get('dogs', 'taco').on('error', done).create({age: 2});
19
262
  });
20
263
  query.on('error', done);
21
264
  query.on('insert', function(docs, index) {
22
- expect(util.pluck(docs, 'id')).eql(['fido']);
23
- expect(util.pluck(docs, 'data')).eql([{age: 3}]);
24
- expect(index).equal(0);
25
- expect(util.pluck(query.results, 'id')).eql(['fido']);
26
- expect(util.pluck(query.results, 'data')).eql([{age: 3}]);
265
+ expect(util.pluck(docs, 'id')).eql(['taco']);
266
+ expect(util.pluck(docs, 'data')).eql([{age: 2}]);
267
+ expect(query.results[index]).equal(docs[0]);
268
+ var results = util.sortById(query.results);
269
+ expect(util.pluck(results, 'id')).eql(['fido', 'spot', 'taco']);
270
+ expect(util.pluck(results, 'data')).eql([{age: 3}, {age: 5}, {age: 2}]);
27
271
  done();
28
272
  });
29
273
  });
274
+ });
30
275
 
31
- it('creating an additional document updates a subscribed query', function(done) {
32
- var connection = this.backend.connect();
33
- var matchAllDbQuery = this.matchAllDbQuery;
34
- async.parallel([
35
- function(cb) {
36
- connection.get('dogs', 'fido').on('error', done).create({age: 3}, cb);
37
- },
38
- function(cb) {
39
- connection.get('dogs', 'spot').on('error', done).create({age: 5}, cb);
40
- }
41
- ], function(err) {
276
+ it('deleting a document updates a subscribed query', function(done) {
277
+ var connection = this.backend.connect();
278
+ var matchAllDbQuery = this.matchAllDbQuery;
279
+ async.parallel([
280
+ function(cb) {
281
+ connection.get('dogs', 'fido').on('error', done).create({age: 3}, cb);
282
+ },
283
+ function(cb) {
284
+ connection.get('dogs', 'spot').on('error', done).create({age: 5}, cb);
285
+ }
286
+ ], function(err) {
287
+ if (err) return done(err);
288
+ var query = connection.createSubscribeQuery('dogs', matchAllDbQuery, null, function(err) {
42
289
  if (err) return done(err);
43
- var query = connection.createSubscribeQuery('dogs', matchAllDbQuery, null, function(err) {
44
- if (err) return done(err);
45
- connection.get('dogs', 'taco').on('error', done).create({age: 2});
46
- });
47
- query.on('error', done);
48
- query.on('insert', function(docs, index) {
49
- expect(util.pluck(docs, 'id')).eql(['taco']);
50
- expect(util.pluck(docs, 'data')).eql([{age: 2}]);
51
- expect(query.results[index]).equal(docs[0]);
52
- var results = util.sortById(query.results);
53
- expect(util.pluck(results, 'id')).eql(['fido', 'spot', 'taco']);
54
- expect(util.pluck(results, 'data')).eql([{age: 3}, {age: 5}, {age: 2}]);
55
- done();
56
- });
290
+ connection.get('dogs', 'fido').del();
291
+ });
292
+ query.on('error', done);
293
+ query.on('remove', function(docs, index) {
294
+ expect(util.pluck(docs, 'id')).eql(['fido']);
295
+ expect(util.pluck(docs, 'data')).eql([undefined]);
296
+ expect(index).a('number');
297
+ var results = util.sortById(query.results);
298
+ expect(util.pluck(results, 'id')).eql(['spot']);
299
+ expect(util.pluck(results, 'data')).eql([{age: 5}]);
300
+ done();
57
301
  });
58
302
  });
303
+ });
59
304
 
60
- it('deleting a document updates a subscribed query', function(done) {
61
- var connection = this.backend.connect();
62
- var matchAllDbQuery = this.matchAllDbQuery;
63
- async.parallel([
64
- function(cb) {
65
- connection.get('dogs', 'fido').on('error', done).create({age: 3}, cb);
66
- },
67
- function(cb) {
68
- connection.get('dogs', 'spot').on('error', done).create({age: 5}, cb);
69
- }
70
- ], function(err) {
305
+ it('subscribed query removes document from results before sending delete op to other clients', function(done) {
306
+ var connection1 = this.backend.connect();
307
+ var connection2 = this.backend.connect();
308
+ var matchAllDbQuery = this.matchAllDbQuery;
309
+ async.parallel([
310
+ function(cb) {
311
+ connection1.get('dogs', 'fido').on('error', done).create({age: 3}, cb);
312
+ },
313
+ function(cb) {
314
+ connection1.get('dogs', 'spot').on('error', done).create({age: 5}, cb);
315
+ }
316
+ ], function(err) {
317
+ if (err) return done(err);
318
+ var query = connection2.createSubscribeQuery('dogs', matchAllDbQuery, null, function(err) {
71
319
  if (err) return done(err);
72
- var query = connection.createSubscribeQuery('dogs', matchAllDbQuery, null, function(err) {
73
- if (err) return done(err);
74
- connection.get('dogs', 'fido').del();
75
- });
76
- query.on('error', done);
77
- query.on('remove', function(docs, index) {
78
- expect(util.pluck(docs, 'id')).eql(['fido']);
79
- expect(util.pluck(docs, 'data')).eql([undefined]);
80
- expect(index).a('number');
81
- var results = util.sortById(query.results);
82
- expect(util.pluck(results, 'id')).eql(['spot']);
83
- expect(util.pluck(results, 'data')).eql([{age: 5}]);
84
- done();
85
- });
320
+ connection1.get('dogs', 'fido').del();
321
+ });
322
+ query.on('error', done);
323
+ var removed = false;
324
+ connection2.get('dogs', 'fido').on('del', function() {
325
+ expect(removed).equal(true);
326
+ done();
327
+ });
328
+ query.on('remove', function(docs, index) {
329
+ removed = true;
330
+ expect(util.pluck(docs, 'id')).eql(['fido']);
331
+ expect(util.pluck(docs, 'data')).eql([{age: 3}]);
332
+ expect(index).a('number');
333
+ var results = util.sortById(query.results);
334
+ expect(util.pluck(results, 'id')).eql(['spot']);
335
+ expect(util.pluck(results, 'data')).eql([{age: 5}]);
86
336
  });
87
337
  });
338
+ });
88
339
 
89
- it('subscribed query removes document from results before sending delete op to other clients', function(done) {
90
- var connection1 = this.backend.connect();
91
- var connection2 = this.backend.connect();
92
- var matchAllDbQuery = this.matchAllDbQuery;
93
- async.parallel([
94
- function(cb) {
95
- connection1.get('dogs', 'fido').on('error', done).create({age: 3}, cb);
96
- },
97
- function(cb) {
98
- connection1.get('dogs', 'spot').on('error', done).create({age: 5}, cb);
99
- }
100
- ], function(err) {
340
+ it('subscribed query does not get updated after destroyed', function(done) {
341
+ var connection = this.backend.connect();
342
+ var connection2 = this.backend.connect();
343
+ var matchAllDbQuery = this.matchAllDbQuery;
344
+ async.parallel([
345
+ function(cb) {
346
+ connection.get('dogs', 'fido').on('error', done).create({age: 3}, cb);
347
+ },
348
+ function(cb) {
349
+ connection.get('dogs', 'spot').on('error', done).create({age: 5}, cb);
350
+ }
351
+ ], function(err) {
352
+ if (err) return done(err);
353
+ var query = connection.createSubscribeQuery('dogs', matchAllDbQuery, null, function(err) {
101
354
  if (err) return done(err);
102
- var query = connection2.createSubscribeQuery('dogs', matchAllDbQuery, null, function(err) {
355
+ query.destroy(function(err) {
103
356
  if (err) return done(err);
104
- connection1.get('dogs', 'fido').del();
105
- });
106
- query.on('error', done);
107
- var removed = false;
108
- connection2.get('dogs', 'fido').on('del', function() {
109
- expect(removed).equal(true);
110
- done();
111
- });
112
- query.on('remove', function(docs, index) {
113
- removed = true;
114
- expect(util.pluck(docs, 'id')).eql(['fido']);
115
- expect(util.pluck(docs, 'data')).eql([{age: 3}]);
116
- expect(index).a('number');
117
- var results = util.sortById(query.results);
118
- expect(util.pluck(results, 'id')).eql(['spot']);
119
- expect(util.pluck(results, 'data')).eql([{age: 5}]);
357
+ connection2.get('dogs', 'taco').on('error', done).create({age: 2}, done);
120
358
  });
121
359
  });
360
+ query.on('error', done);
361
+ query.on('insert', function() {
362
+ done();
363
+ });
122
364
  });
365
+ });
123
366
 
124
- it('subscribed query does not get updated after destroyed', function(done) {
125
- var connection = this.backend.connect();
126
- var connection2 = this.backend.connect();
127
- var matchAllDbQuery = this.matchAllDbQuery;
128
- async.parallel([
129
- function(cb) {
130
- connection.get('dogs', 'fido').on('error', done).create({age: 3}, cb);
131
- },
132
- function(cb) {
133
- connection.get('dogs', 'spot').on('error', done).create({age: 5}, cb);
134
- }
135
- ], function(err) {
367
+ it('subscribed query does not get updated after connection is disconnected', function(done) {
368
+ var connection = this.backend.connect();
369
+ var connection2 = this.backend.connect();
370
+ var matchAllDbQuery = this.matchAllDbQuery;
371
+ async.parallel([
372
+ function(cb) {
373
+ connection.get('dogs', 'fido').on('error', done).create({age: 3}, cb);
374
+ },
375
+ function(cb) {
376
+ connection.get('dogs', 'spot').on('error', done).create({age: 5}, cb);
377
+ }
378
+ ], function(err) {
379
+ if (err) return done(err);
380
+ var query = connection.createSubscribeQuery('dogs', matchAllDbQuery, null, function(err) {
136
381
  if (err) return done(err);
137
- var query = connection.createSubscribeQuery('dogs', matchAllDbQuery, null, function(err) {
138
- if (err) return done(err);
139
- query.destroy(function(err) {
140
- if (err) return done(err);
141
- connection2.get('dogs', 'taco').on('error', done).create({age: 2}, done);
142
- });
143
- });
144
- query.on('error', done);
145
- query.on('insert', function() {
146
- done();
147
- });
382
+ connection.close();
383
+ connection2.get('dogs', 'taco').on('error', done).create({age: 2}, done);
384
+ });
385
+ query.on('error', done);
386
+ query.on('insert', function() {
387
+ done();
148
388
  });
149
389
  });
390
+ });
150
391
 
151
- it('subscribed query does not get updated after connection is disconnected', function(done) {
152
- var connection = this.backend.connect();
153
- var connection2 = this.backend.connect();
154
- var matchAllDbQuery = this.matchAllDbQuery;
155
- async.parallel([
156
- function(cb) {
157
- connection.get('dogs', 'fido').on('error', done).create({age: 3}, cb);
158
- },
159
- function(cb) {
160
- connection.get('dogs', 'spot').on('error', done).create({age: 5}, cb);
161
- }
162
- ], function(err) {
392
+ it('subscribed query gets update after reconnecting', function(done) {
393
+ var backend = this.backend;
394
+ var connection = backend.connect();
395
+ var connection2 = backend.connect();
396
+ var matchAllDbQuery = this.matchAllDbQuery;
397
+ async.parallel([
398
+ function(cb) {
399
+ connection.get('dogs', 'fido').on('error', done).create({age: 3}, cb);
400
+ },
401
+ function(cb) {
402
+ connection.get('dogs', 'spot').on('error', done).create({age: 5}, cb);
403
+ }
404
+ ], function(err) {
405
+ if (err) return done(err);
406
+
407
+ var wait = 2;
408
+ function finish() {
409
+ if (--wait) return;
410
+ expect(util.pluck(query.results, 'id')).to.have.members(['fido', 'spot', 'taco']);
411
+ done();
412
+ }
413
+
414
+ var query = connection.createSubscribeQuery('dogs', matchAllDbQuery, null, function(err) {
163
415
  if (err) return done(err);
164
- var query = connection.createSubscribeQuery('dogs', matchAllDbQuery, null, function(err) {
165
- if (err) return done(err);
166
- connection.close();
167
- connection2.get('dogs', 'taco').on('error', done).create({age: 2}, done);
168
- });
169
- query.on('error', done);
170
- query.on('insert', function() {
171
- done();
416
+ connection.close();
417
+ connection2.get('dogs', 'taco').on('error', done).create({age: 2});
418
+ process.nextTick(function() {
419
+ backend.connect(connection);
420
+ query.on('ready', finish);
172
421
  });
173
422
  });
423
+ query.on('error', done);
424
+ query.on('insert', finish);
174
425
  });
426
+ });
175
427
 
176
- it('subscribed query gets update after reconnecting', function(done) {
177
- var backend = this.backend;
178
- var connection = backend.connect();
179
- var connection2 = backend.connect();
180
- var matchAllDbQuery = this.matchAllDbQuery;
181
- async.parallel([
182
- function(cb) {
183
- connection.get('dogs', 'fido').on('error', done).create({age: 3}, cb);
184
- },
185
- function(cb) {
186
- connection.get('dogs', 'spot').on('error', done).create({age: 5}, cb);
187
- }
188
- ], function(err) {
189
- if (err) return done(err);
428
+ it('subscribed query gets simultaneous insert and remove after reconnecting', function(done) {
429
+ var backend = this.backend;
430
+ var connection = backend.connect();
431
+ var connection2 = backend.connect();
432
+ var matchAllDbQuery = this.matchAllDbQuery;
433
+ async.parallel([
434
+ function(cb) {
435
+ connection.get('dogs', 'fido').on('error', done).create({age: 3}, cb);
436
+ },
437
+ function(cb) {
438
+ connection.get('dogs', 'spot').on('error', done).create({age: 5}, cb);
439
+ }
440
+ ], function(err) {
441
+ if (err) return done(err);
442
+ var query = connection.createSubscribeQuery('dogs', matchAllDbQuery);
443
+ query.on('error', done);
190
444
 
191
- var wait = 2;
192
- function finish() {
193
- if (--wait) return;
194
- expect(util.pluck(query.results, 'id')).to.have.members(['fido', 'spot', 'taco']);
195
- done();
196
- }
445
+ query.once('ready', function() {
446
+ connection.close();
197
447
 
198
- var query = connection.createSubscribeQuery('dogs', matchAllDbQuery, null, function(err) {
448
+ connection2.get('dogs', 'fido').fetch(function(err) {
199
449
  if (err) return done(err);
200
- connection.close();
201
- connection2.get('dogs', 'taco').on('error', done).create({age: 2});
202
- process.nextTick(function() {
450
+ async.parallel([
451
+ function(cb) {
452
+ connection2.get('dogs', 'fido').del(cb);
453
+ },
454
+ function(cb) {
455
+ connection2.get('dogs', 'taco').create({age: 2}, cb);
456
+ }
457
+ ], function(error) {
458
+ if (error) return done(error);
203
459
  backend.connect(connection);
204
- query.on('ready', finish);
205
- });
206
- });
207
- query.on('error', done);
208
- query.on('insert', finish);
209
- });
210
- });
211
-
212
- it('subscribed query gets simultaneous insert and remove after reconnecting', function(done) {
213
- var backend = this.backend;
214
- var connection = backend.connect();
215
- var connection2 = backend.connect();
216
- var matchAllDbQuery = this.matchAllDbQuery;
217
- async.parallel([
218
- function(cb) {
219
- connection.get('dogs', 'fido').on('error', done).create({age: 3}, cb);
220
- },
221
- function(cb) {
222
- connection.get('dogs', 'spot').on('error', done).create({age: 5}, cb);
223
- }
224
- ], function(err) {
225
- if (err) return done(err);
226
- var query = connection.createSubscribeQuery('dogs', matchAllDbQuery);
227
- query.on('error', done);
228
-
229
- query.once('ready', function() {
230
- connection.close();
231
-
232
- connection2.get('dogs', 'fido').fetch(function(err) {
233
- if (err) return done(err);
234
- async.parallel([
235
- function(cb) {
236
- connection2.get('dogs', 'fido').del(cb);
237
- },
238
- function(cb) {
239
- connection2.get('dogs', 'taco').create({age: 2}, cb);
240
- }
241
- ], function(error) {
242
- if (error) return done(error);
243
- backend.connect(connection);
244
- query.once('ready', function() {
245
- finish();
246
- });
460
+ query.once('ready', function() {
461
+ finish();
247
462
  });
248
463
  });
249
464
  });
465
+ });
250
466
 
251
- var wait = 3;
252
- function finish() {
253
- if (--wait) return;
254
- var results = util.sortById(query.results);
255
- expect(util.pluck(results, 'id')).eql(['spot', 'taco']);
256
- expect(util.pluck(results, 'data')).eql([{age: 5}, {age: 2}]);
257
- done();
258
- }
259
- query.once('insert', function(docs) {
260
- expect(util.pluck(docs, 'id')).eql(['taco']);
261
- expect(util.pluck(docs, 'data')).eql([{age: 2}]);
262
- finish();
263
- });
264
- query.once('remove', function(docs) {
265
- expect(util.pluck(docs, 'id')).eql(['fido']);
266
- // We don't assert the value of data, because the del op could be
267
- // applied by the client before or after the query result is removed.
268
- // Order of ops & query result updates is not currently guaranteed
269
- finish();
270
- });
467
+ var wait = 3;
468
+ function finish() {
469
+ if (--wait) return;
470
+ var results = util.sortById(query.results);
471
+ expect(util.pluck(results, 'id')).eql(['spot', 'taco']);
472
+ expect(util.pluck(results, 'data')).eql([{age: 5}, {age: 2}]);
473
+ done();
474
+ }
475
+ query.once('insert', function(docs) {
476
+ expect(util.pluck(docs, 'id')).eql(['taco']);
477
+ expect(util.pluck(docs, 'data')).eql([{age: 2}]);
478
+ finish();
479
+ });
480
+ query.once('remove', function(docs) {
481
+ expect(util.pluck(docs, 'id')).eql(['fido']);
482
+ // We don't assert the value of data, because the del op could be
483
+ // applied by the client before or after the query result is removed.
484
+ // Order of ops & query result updates is not currently guaranteed
485
+ finish();
271
486
  });
272
487
  });
488
+ });
273
489
 
274
- it('creating an additional document updates a subscribed query', function(done) {
275
- var connection = this.backend.connect();
276
- var matchAllDbQuery = this.matchAllDbQuery;
277
- async.parallel([
278
- function(cb) {
279
- connection.get('dogs', 'fido').on('error', done).create({age: 3}, cb);
280
- },
281
- function(cb) {
282
- connection.get('dogs', 'spot').on('error', done).create({age: 5}, cb);
283
- }
284
- ], function(err) {
490
+ it('creating an additional document updates a subscribed query', function(done) {
491
+ var connection = this.backend.connect();
492
+ var matchAllDbQuery = this.matchAllDbQuery;
493
+ async.parallel([
494
+ function(cb) {
495
+ connection.get('dogs', 'fido').on('error', done).create({age: 3}, cb);
496
+ },
497
+ function(cb) {
498
+ connection.get('dogs', 'spot').on('error', done).create({age: 5}, cb);
499
+ }
500
+ ], function(err) {
501
+ if (err) return done(err);
502
+ var query = connection.createSubscribeQuery('dogs', matchAllDbQuery, null, function(err) {
285
503
  if (err) return done(err);
286
- var query = connection.createSubscribeQuery('dogs', matchAllDbQuery, null, function(err) {
287
- if (err) return done(err);
288
- connection.get('dogs', 'taco').on('error', done).create({age: 2});
289
- });
290
- query.on('error', done);
291
- query.on('insert', function(docs, index) {
292
- expect(util.pluck(docs, 'id')).eql(['taco']);
293
- expect(util.pluck(docs, 'data')).eql([{age: 2}]);
294
- expect(query.results[index]).equal(docs[0]);
295
- var results = util.sortById(query.results);
296
- expect(util.pluck(results, 'id')).eql(['fido', 'spot', 'taco']);
297
- expect(util.pluck(results, 'data')).eql([{age: 3}, {age: 5}, {age: 2}]);
298
- done();
299
- });
504
+ connection.get('dogs', 'taco').on('error', done).create({age: 2});
300
505
  });
301
- });
302
-
303
- it('pollDebounce option reduces subsequent poll interval', function(done) {
304
- var connection = this.backend.connect();
305
- this.backend.db.canPollDoc = function() {
306
- return false;
307
- };
308
- var query = connection.createSubscribeQuery('items', this.matchAllDbQuery, {pollDebounce: 1000});
309
506
  query.on('error', done);
310
- var batchSizes = [];
311
- var total = 0;
507
+ query.on('insert', function(docs, index) {
508
+ expect(util.pluck(docs, 'id')).eql(['taco']);
509
+ expect(util.pluck(docs, 'data')).eql([{age: 2}]);
510
+ expect(query.results[index]).equal(docs[0]);
511
+ var results = util.sortById(query.results);
512
+ expect(util.pluck(results, 'id')).eql(['fido', 'spot', 'taco']);
513
+ expect(util.pluck(results, 'data')).eql([{age: 3}, {age: 5}, {age: 2}]);
514
+ done();
515
+ });
516
+ });
517
+ });
312
518
 
313
- query.on('insert', function(docs) {
314
- batchSizes.push(docs.length);
315
- total += docs.length;
316
- if (total === 1) {
519
+ it('pollDebounce option reduces subsequent poll interval', function(done) {
520
+ var clock = sinon.useFakeTimers();
521
+ var connection = this.backend.connect();
522
+ this.backend.db.canPollDoc = function() {
523
+ return false;
524
+ };
525
+ var query = connection.createSubscribeQuery('items', this.matchAllDbQuery, {pollDebounce: 2000});
526
+ query.on('error', done);
527
+ var batchSizes = [];
528
+ var total = 0;
529
+
530
+ query.on('insert', function(docs) {
531
+ batchSizes.push(docs.length);
532
+ total += docs.length;
533
+
534
+ if (total === 1) {
317
535
  // first write received by client. we're debouncing. create 9
318
536
  // more documents.
319
- for (var i = 1; i < 10; i++) {
320
- connection.get('items', i.toString()).on('error', done).create({});
321
- }
537
+ var counter = 0;
538
+ for (var i = 1; i < 10; i++) {
539
+ connection.get('items', i.toString()).on('error', done).create({}, function(err) {
540
+ if (err) return done(err);
541
+ counter++;
542
+ if (counter === 9) clock.tickAsync(10000);
543
+ });
322
544
  }
323
- if (total === 10) {
545
+ }
546
+ if (total === 10) {
324
547
  // first document is its own batch; then subsequent creates
325
- // are debounced until after all other 9 docs are created
326
- expect(batchSizes).eql([1, 9]);
327
- done();
328
- }
329
- });
330
-
331
- // create an initial document. this will lead to the 'insert'
332
- // event firing the first time, while sharedb is definitely
333
- // debouncing
334
- connection.get('items', '0').on('error', done).create({});
548
+ // are debounced and batched
549
+ expect(batchSizes[0]).eql(1);
550
+ batchSizes.shift();
551
+ var sum = batchSizes.reduce(function(sum, batchSize) {
552
+ return sum + batchSize;
553
+ }, 0);
554
+ expect(batchSizes.length).to.lessThan(9);
555
+ expect(sum).eql(9);
556
+ done();
557
+ }
335
558
  });
336
559
 
337
- it('db.pollDebounce option reduces subsequent poll interval', function(done) {
338
- var connection = this.backend.connect();
339
- this.backend.db.canPollDoc = function() {
340
- return false;
341
- };
342
- this.backend.db.pollDebounce = 1000;
343
- var query = connection.createSubscribeQuery('items', this.matchAllDbQuery);
344
- query.on('error', done);
345
- var batchSizes = [];
346
- var total = 0;
560
+ // create an initial document. this will lead to the 'insert'
561
+ // event firing the first time, while sharedb is definitely
562
+ // debouncing
563
+ connection.get('items', '0').on('error', done).create({}, function() {
564
+ clock.tickAsync(3000);
565
+ });
566
+ });
347
567
 
348
- query.on('insert', function(docs) {
349
- batchSizes.push(docs.length);
350
- total += docs.length;
351
- if (total === 1) {
568
+ it('db.pollDebounce option reduces subsequent poll interval', function(done) {
569
+ var clock = sinon.useFakeTimers();
570
+ var connection = this.backend.connect();
571
+ this.backend.db.canPollDoc = function() {
572
+ return false;
573
+ };
574
+ this.backend.db.pollDebounce = 2000;
575
+ var query = connection.createSubscribeQuery('items', this.matchAllDbQuery);
576
+ query.on('error', done);
577
+ var batchSizes = [];
578
+ var total = 0;
579
+
580
+ query.on('insert', function(docs) {
581
+ batchSizes.push(docs.length);
582
+ total += docs.length;
583
+
584
+ if (total === 1) {
352
585
  // first write received by client. we're debouncing. create 9
353
586
  // more documents.
354
- for (var i = 1; i < 10; i++) {
355
- connection.get('items', i.toString()).on('error', done).create({});
356
- }
587
+ var counter = 0;
588
+ for (var i = 1; i < 10; i++) {
589
+ connection.get('items', i.toString()).on('error', done).create({}, function(err) {
590
+ if (err) return done(err);
591
+ counter++;
592
+ if (counter === 9) clock.tickAsync(10000);
593
+ });
357
594
  }
358
- if (total === 10) {
595
+ }
596
+ if (total === 10) {
359
597
  // first document is its own batch; then subsequent creates
360
- // are debounced until after all other 9 docs are created
361
- expect(batchSizes).eql([1, 9]);
362
- done();
363
- }
364
- });
598
+ // are debounced and batched
599
+ expect(batchSizes[0]).eql(1);
600
+ batchSizes.shift();
601
+ var sum = batchSizes.reduce(function(sum, batchSize) {
602
+ return sum + batchSize;
603
+ }, 0);
604
+ expect(batchSizes.length).to.lessThan(9);
605
+ expect(sum).eql(9);
606
+ done();
607
+ }
608
+ });
365
609
 
366
- // create an initial document. this will lead to the 'insert'
367
- // event firing the first time, while sharedb is definitely
368
- // debouncing
369
- connection.get('items', '0').on('error', done).create({});
610
+ // create an initial document. this will lead to the 'insert'
611
+ // event firing the first time, while sharedb is definitely
612
+ // debouncing
613
+ connection.get('items', '0').on('error', done).create({}, function() {
614
+ clock.tickAsync(3000);
370
615
  });
616
+ });
371
617
 
372
- it('pollInterval updates a subscribed query after an unpublished create', function(done) {
373
- var connection = this.backend.connect();
374
- this.backend.suppressPublish = true;
375
- var query = connection.createSubscribeQuery('dogs', this.matchAllDbQuery, {pollInterval: 50}, function(err) {
618
+ it('pollInterval updates a subscribed query after an unpublished create', function(done) {
619
+ var clock = sinon.useFakeTimers();
620
+ var connection = this.backend.connect();
621
+ this.backend.suppressPublish = true;
622
+ var query = connection.createSubscribeQuery(
623
+ 'dogs',
624
+ this.matchAllDbQuery,
625
+ {pollDebounce: 0, pollInterval: 50},
626
+ function(err) {
376
627
  if (err) return done(err);
377
- connection.get('dogs', 'fido').on('error', done).create({});
378
- });
379
- query.on('error', done);
380
- query.on('insert', function(docs) {
381
- expect(util.pluck(docs, 'id')).eql(['fido']);
382
- done();
628
+ connection.get('dogs', 'fido').on('error', done).create({}, function() {
629
+ clock.tickAsync(51);
630
+ });
631
+ }
632
+ );
633
+ query.on('error', done);
634
+ query.on('insert', function(docs) {
635
+ expect(util.pluck(docs, 'id')).eql(['fido']);
636
+ done();
637
+ });
638
+ });
639
+
640
+ it('db.pollInterval updates a subscribed query after an unpublished create', function(done) {
641
+ var clock = sinon.useFakeTimers();
642
+ var connection = this.backend.connect();
643
+ this.backend.suppressPublish = true;
644
+ this.backend.db.pollDebounce = 0;
645
+ this.backend.db.pollInterval = 50;
646
+ var query = connection.createSubscribeQuery('dogs', this.matchAllDbQuery, null, function(err) {
647
+ if (err) return done(err);
648
+ connection.get('dogs', 'fido').on('error', done).create({}, function() {
649
+ clock.tickAsync(51);
383
650
  });
384
651
  });
652
+ query.on('error', done);
653
+ query.on('insert', function(docs) {
654
+ expect(util.pluck(docs, 'id')).eql(['fido']);
655
+ done();
656
+ });
657
+ });
385
658
 
386
- it('db.pollInterval updates a subscribed query after an unpublished create', function(done) {
387
- var connection = this.backend.connect();
388
- this.backend.suppressPublish = true;
389
- this.backend.db.pollInterval = 50;
390
- var query = connection.createSubscribeQuery('dogs', this.matchAllDbQuery, null, function(err) {
391
- if (err) return done(err);
392
- connection.get('dogs', 'fido').on('error', done).create({});
659
+ it('pollInterval captures additional unpublished creates', function(done) {
660
+ var clock = sinon.useFakeTimers();
661
+ var connection = this.backend.connect();
662
+ this.backend.suppressPublish = true;
663
+ var count = 0;
664
+
665
+ var query = connection.createSubscribeQuery('dogs', this.matchAllDbQuery, {pollInterval: 1000}, function(err) {
666
+ if (err) return done(err);
667
+ var doc = connection.get('dogs', count.toString()).on('error', done);
668
+ doc.create({}, function(e) {
669
+ if (e) return done(e);
670
+ clock.tickAsync(2000);
393
671
  });
394
- query.on('error', done);
395
- query.on('insert', function(docs) {
396
- expect(util.pluck(docs, 'id')).eql(['fido']);
397
- done();
672
+ });
673
+ query.on('error', done);
674
+ query.on('insert', function() {
675
+ count++;
676
+ if (count === 3) return done();
677
+ var doc = connection.get('dogs', count.toString()).on('error', done);
678
+ doc.create({}, function(e) {
679
+ if (e) return done(e);
680
+ clock.tickAsync(10000);
398
681
  });
399
682
  });
683
+ clock.tickAsync(1);
684
+ });
400
685
 
401
- it('pollInterval captures additional unpublished creates', function(done) {
402
- var connection = this.backend.connect();
403
- this.backend.suppressPublish = true;
404
- var count = 0;
405
- var query = connection.createSubscribeQuery('dogs', this.matchAllDbQuery, {pollInterval: 50}, function(err) {
406
- if (err) return done(err);
407
- connection.get('dogs', count.toString()).on('error', done).create({});
686
+ it('query extra is returned to client', function(done) {
687
+ var connection = this.backend.connect();
688
+ this.backend.db.query = function(collection, query, fields, options, callback) {
689
+ process.nextTick(function() {
690
+ callback(null, [], {colors: ['brown', 'gold']});
408
691
  });
409
- query.on('error', done);
410
- query.on('insert', function() {
411
- count++;
412
- if (count === 3) return done();
413
- connection.get('dogs', count.toString()).on('error', done).create({});
692
+ };
693
+ var query = connection.createSubscribeQuery('dogs', this.matchAllDbQuery, null, function(err, results, extra) {
694
+ if (err) return done(err);
695
+ expect(results).eql([]);
696
+ expect(extra).eql({colors: ['brown', 'gold']});
697
+ expect(query.extra).eql({colors: ['brown', 'gold']});
698
+ done();
699
+ });
700
+ query.on('error', done);
701
+ });
702
+
703
+ it('query extra is updated on change', function(done) {
704
+ var connection = this.backend.connect();
705
+ this.backend.db.query = function(collection, query, fields, options, callback) {
706
+ process.nextTick(function() {
707
+ callback(null, [], 1);
414
708
  });
709
+ };
710
+ this.backend.db.queryPoll = function(collection, query, options, callback) {
711
+ process.nextTick(function() {
712
+ callback(null, [], 2);
713
+ });
714
+ };
715
+ this.backend.db.canPollDoc = function() {
716
+ return false;
717
+ };
718
+ var query = connection.createSubscribeQuery('dogs', this.matchAllDbQuery, null, function(err, results, extra) {
719
+ if (err) return done(err);
720
+ expect(extra).eql(1);
721
+ expect(query.extra).eql(1);
722
+ });
723
+ query.on('error', done);
724
+ query.on('extra', function(extra) {
725
+ expect(extra).eql(2);
726
+ expect(query.extra).eql(2);
727
+ done();
415
728
  });
729
+ connection.get('dogs', 'fido').on('error', done).create({age: 3});
730
+ });
416
731
 
417
- it('query extra is returned to client', function(done) {
418
- var connection = this.backend.connect();
419
- this.backend.db.query = function(collection, query, fields, options, callback) {
420
- process.nextTick(function() {
421
- callback(null, [], {colors: ['brown', 'gold']});
422
- });
732
+ describe('passing agent.custom to the DB adapter', function() {
733
+ var connection;
734
+ var expectedArg = {
735
+ agentCustom: {foo: 'bar'}
736
+ };
737
+ beforeEach('set up', function() {
738
+ connection = this.backend.connect();
739
+ connection.agent.custom = {
740
+ foo: 'bar'
423
741
  };
424
- var query = connection.createSubscribeQuery('dogs', this.matchAllDbQuery, null, function(err, results, extra) {
425
- if (err) return done(err);
426
- expect(results).eql([]);
427
- expect(extra).eql({colors: ['brown', 'gold']});
428
- expect(query.extra).eql({colors: ['brown', 'gold']});
742
+ });
743
+
744
+ it('sends agentCustom to the db\'s getSnapshot call', function(done) {
745
+ var query = connection.createSubscribeQuery('dogs', this.matchAllDbQuery);
746
+ var getSnapshotSpy = sinon.spy(this.backend.db, 'getSnapshot');
747
+
748
+ query.on('insert', function() {
749
+ // The first call to getSnapshot is when the document is created
750
+ // The seconds call is when the event is triggered, and is the one we are testing here
751
+ expect(getSnapshotSpy.callCount).to.equal(2);
752
+ expect(getSnapshotSpy.getCall(1).args[3]).to.deep.equal(expectedArg);
429
753
  done();
430
754
  });
431
- query.on('error', done);
755
+ connection.get('dogs', 'fido').create({age: 3});
432
756
  });
433
757
 
434
- it('query extra is updated on change', function(done) {
435
- var connection = this.backend.connect();
436
- this.backend.db.query = function(collection, query, fields, options, callback) {
437
- process.nextTick(function() {
438
- callback(null, [], 1);
439
- });
440
- };
441
- this.backend.db.queryPoll = function(collection, query, options, callback) {
442
- process.nextTick(function() {
443
- callback(null, [], 2);
444
- });
445
- };
758
+ it('sends agentCustom to the db\'s getSnapshotBulk call', function(done) {
759
+ // Ensures that getSnapshotBulk is called, instead of getSnapshot
446
760
  this.backend.db.canPollDoc = function() {
447
761
  return false;
448
762
  };
449
- var query = connection.createSubscribeQuery('dogs', this.matchAllDbQuery, null, function(err, results, extra) {
450
- if (err) return done(err);
451
- expect(extra).eql(1);
452
- expect(query.extra).eql(1);
453
- });
454
- query.on('error', done);
455
- query.on('extra', function(extra) {
456
- expect(extra).eql(2);
457
- expect(query.extra).eql(2);
763
+
764
+ var query = connection.createSubscribeQuery('dogs', this.matchAllDbQuery);
765
+ var getSnapshotBulkSpy = sinon.spy(this.backend.db, 'getSnapshotBulk');
766
+
767
+ query.on('insert', function() {
768
+ expect(getSnapshotBulkSpy.callCount).to.equal(1);
769
+ expect(getSnapshotBulkSpy.getCall(0).args[3]).to.deep.equal(expectedArg);
458
770
  done();
459
771
  });
460
- connection.get('dogs', 'fido').on('error', done).create({age: 3});
772
+ connection.get('dogs', 'fido').create({age: 3});
461
773
  });
774
+ });
462
775
 
463
- describe('passing agent.custom to the DB adapter', function() {
464
- var connection;
465
- var expectedArg = {
466
- agentCustom: {foo: 'bar'}
467
- };
468
- beforeEach('set up', function() {
469
- connection = this.backend.connect();
470
- connection.agent.custom = {
471
- foo: 'bar'
472
- };
473
- });
474
-
475
- it('sends agentCustom to the db\'s getSnapshot call', function(done) {
476
- var query = connection.createSubscribeQuery('dogs', this.matchAllDbQuery);
477
- var getSnapshotSpy = sinon.spy(this.backend.db, 'getSnapshot');
478
-
479
- query.on('insert', function() {
480
- // The first call to getSnapshot is when the document is created
481
- // The seconds call is when the event is triggered, and is the one we are testing here
482
- expect(getSnapshotSpy.callCount).to.equal(2);
483
- expect(getSnapshotSpy.getCall(1).args[3]).to.deep.equal(expectedArg);
484
- done();
485
- });
486
- connection.get('dogs', 'fido').create({age: 3});
776
+ it('changing a filtered property removes from a subscribed query', function(done) {
777
+ var connection = this.backend.connect();
778
+ async.parallel([
779
+ function(cb) {
780
+ connection.get('dogs', 'fido').on('error', done).create({age: 3}, cb);
781
+ },
782
+ function(cb) {
783
+ connection.get('dogs', 'spot').on('error', done).create({age: 3}, cb);
784
+ }
785
+ ], function(err) {
786
+ if (err) return done(err);
787
+ var dbQuery = getQuery({query: {age: 3}});
788
+ var query = connection.createSubscribeQuery('dogs', dbQuery, null, function(err, results) {
789
+ if (err) return done(err);
790
+ var sorted = util.sortById(results);
791
+ expect(util.pluck(sorted, 'id')).eql(['fido', 'spot']);
792
+ expect(util.pluck(sorted, 'data')).eql([{age: 3}, {age: 3}]);
793
+ connection.get('dogs', 'fido').submitOp({p: ['age'], na: 2});
487
794
  });
488
-
489
- it('sends agentCustom to the db\'s getSnapshotBulk call', function(done) {
490
- // Ensures that getSnapshotBulk is called, instead of getSnapshot
491
- this.backend.db.canPollDoc = function() {
492
- return false;
493
- };
494
-
495
- var query = connection.createSubscribeQuery('dogs', this.matchAllDbQuery);
496
- var getSnapshotBulkSpy = sinon.spy(this.backend.db, 'getSnapshotBulk');
497
-
498
- query.on('insert', function() {
499
- expect(getSnapshotBulkSpy.callCount).to.equal(1);
500
- expect(getSnapshotBulkSpy.getCall(0).args[3]).to.deep.equal(expectedArg);
501
- done();
502
- });
503
- connection.get('dogs', 'fido').create({age: 3});
795
+ query.on('error', done);
796
+ query.on('remove', function(docs, index) {
797
+ expect(util.pluck(docs, 'id')).eql(['fido']);
798
+ expect(util.pluck(docs, 'data')).eql([{age: 5}]);
799
+ expect(index).a('number');
800
+ var results = util.sortById(query.results);
801
+ expect(util.pluck(results, 'id')).eql(['spot']);
802
+ expect(util.pluck(results, 'data')).eql([{age: 3}]);
803
+ done();
504
804
  });
505
805
  });
806
+ });
506
807
 
507
- it('changing a filtered property removes from a subscribed query', function(done) {
508
- var connection = this.backend.connect();
509
- async.parallel([
510
- function(cb) {
511
- connection.get('dogs', 'fido').on('error', done).create({age: 3}, cb);
512
- },
513
- function(cb) {
514
- connection.get('dogs', 'spot').on('error', done).create({age: 3}, cb);
515
- }
516
- ], function(err) {
808
+ it('changing a filtered property inserts to a subscribed query', function(done) {
809
+ var connection = this.backend.connect();
810
+ async.parallel([
811
+ function(cb) {
812
+ connection.get('dogs', 'fido').on('error', done).create({age: 3}, cb);
813
+ },
814
+ function(cb) {
815
+ connection.get('dogs', 'spot').on('error', done).create({age: 5}, cb);
816
+ }
817
+ ], function(err) {
818
+ if (err) return done(err);
819
+ var dbQuery = getQuery({query: {age: 3}});
820
+ var query = connection.createSubscribeQuery('dogs', dbQuery, null, function(err, results) {
517
821
  if (err) return done(err);
518
- var dbQuery = getQuery({query: {age: 3}});
519
- var query = connection.createSubscribeQuery('dogs', dbQuery, null, function(err, results) {
520
- if (err) return done(err);
521
- var sorted = util.sortById(results);
522
- expect(util.pluck(sorted, 'id')).eql(['fido', 'spot']);
523
- expect(util.pluck(sorted, 'data')).eql([{age: 3}, {age: 3}]);
524
- connection.get('dogs', 'fido').submitOp({p: ['age'], na: 2});
525
- });
526
- query.on('error', done);
527
- query.on('remove', function(docs, index) {
528
- expect(util.pluck(docs, 'id')).eql(['fido']);
529
- expect(util.pluck(docs, 'data')).eql([{age: 5}]);
530
- expect(index).a('number');
531
- var results = util.sortById(query.results);
532
- expect(util.pluck(results, 'id')).eql(['spot']);
533
- expect(util.pluck(results, 'data')).eql([{age: 3}]);
534
- done();
535
- });
822
+ var sorted = util.sortById(results);
823
+ expect(util.pluck(sorted, 'id')).eql(['fido']);
824
+ expect(util.pluck(sorted, 'data')).eql([{age: 3}]);
825
+ connection.get('dogs', 'spot').submitOp({p: ['age'], na: -2});
826
+ });
827
+ query.on('error', done);
828
+ query.on('insert', function(docs, index) {
829
+ expect(util.pluck(docs, 'id')).eql(['spot']);
830
+ expect(util.pluck(docs, 'data')).eql([{age: 3}]);
831
+ expect(index).a('number');
832
+ var results = util.sortById(query.results);
833
+ expect(util.pluck(results, 'id')).eql(['fido', 'spot']);
834
+ expect(util.pluck(results, 'data')).eql([{age: 3}, {age: 3}]);
835
+ done();
536
836
  });
537
837
  });
838
+ });
538
839
 
539
- it('changing a filtered property inserts to a subscribed query', function(done) {
540
- var connection = this.backend.connect();
541
- async.parallel([
542
- function(cb) {
543
- connection.get('dogs', 'fido').on('error', done).create({age: 3}, cb);
544
- },
545
- function(cb) {
546
- connection.get('dogs', 'spot').on('error', done).create({age: 5}, cb);
547
- }
548
- ], function(err) {
549
- if (err) return done(err);
550
- var dbQuery = getQuery({query: {age: 3}});
551
- var query = connection.createSubscribeQuery('dogs', dbQuery, null, function(err, results) {
840
+ it('changing a sorted property moves in a subscribed query', function(done) {
841
+ var connection = this.backend.connect();
842
+
843
+ async.parallel([
844
+ function(cb) {
845
+ connection.get('dogs', 'fido').on('error', done).create({age: 3}, cb);
846
+ },
847
+ function(cb) {
848
+ connection.get('dogs', 'spot').on('error', done).create({age: 5}, cb);
849
+ }
850
+ ], function(err) {
851
+ if (err) return done(err);
852
+ var dbQuery = getQuery({query: {}, sort: [['age', 1]]});
853
+ var query = connection.createSubscribeQuery(
854
+ 'dogs',
855
+ dbQuery,
856
+ null,
857
+ function(err, results) {
552
858
  if (err) return done(err);
553
- var sorted = util.sortById(results);
554
- expect(util.pluck(sorted, 'id')).eql(['fido']);
555
- expect(util.pluck(sorted, 'data')).eql([{age: 3}]);
556
- connection.get('dogs', 'spot').submitOp({p: ['age'], na: -2});
557
- });
558
- query.on('error', done);
559
- query.on('insert', function(docs, index) {
560
- expect(util.pluck(docs, 'id')).eql(['spot']);
561
- expect(util.pluck(docs, 'data')).eql([{age: 3}]);
562
- expect(index).a('number');
563
- var results = util.sortById(query.results);
564
859
  expect(util.pluck(results, 'id')).eql(['fido', 'spot']);
565
- expect(util.pluck(results, 'data')).eql([{age: 3}, {age: 3}]);
566
- done();
860
+ expect(util.pluck(results, 'data')).eql([{age: 3}, {age: 5}]);
861
+ connection.get('dogs', 'spot').submitOp({p: ['age'], na: -3});
567
862
  });
863
+ query.on('error', done);
864
+ query.on('move', function(docs, from, to) {
865
+ expect(docs.length).eql(1);
866
+ expect(from).a('number');
867
+ expect(to).a('number');
868
+ expect(util.pluck(query.results, 'id')).eql(['spot', 'fido']);
869
+ expect(util.pluck(query.results, 'data')).eql([{age: 2}, {age: 3}]);
870
+ done();
568
871
  });
569
872
  });
873
+ });
570
874
 
571
- it('changing a sorted property moves in a subscribed query', function(done) {
572
- var connection = this.backend.connect();
573
-
574
- async.parallel([
575
- function(cb) {
576
- connection.get('dogs', 'fido').on('error', done).create({age: 3}, cb);
577
- },
578
- function(cb) {
579
- connection.get('dogs', 'spot').on('error', done).create({age: 5}, cb);
580
- }
581
- ], function(err) {
582
- if (err) return done(err);
583
- var dbQuery = getQuery({query: {}, sort: [['age', 1]]});
584
- var query = connection.createSubscribeQuery(
585
- 'dogs',
586
- dbQuery,
587
- null,
588
- function(err, results) {
589
- if (err) return done(err);
590
- expect(util.pluck(results, 'id')).eql(['fido', 'spot']);
591
- expect(util.pluck(results, 'data')).eql([{age: 3}, {age: 5}]);
592
- connection.get('dogs', 'spot').submitOp({p: ['age'], na: -3});
593
- });
594
- query.on('error', done);
595
- query.on('move', function(docs, from, to) {
596
- expect(docs.length).eql(1);
597
- expect(from).a('number');
598
- expect(to).a('number');
599
- expect(util.pluck(query.results, 'id')).eql(['spot', 'fido']);
600
- expect(util.pluck(query.results, 'data')).eql([{age: 2}, {age: 3}]);
601
- done();
602
- });
603
- });
875
+ it('returns pubSub error if fails to subscribe to channel', function(done) {
876
+ sinon.stub(this.backend.pubsub, 'subscribe').callsFake(function(_channel, callback) {
877
+ callback(new Error('TEST_ERROR'));
604
878
  });
879
+ var connection = this.backend.connect();
880
+ connection.createSubscribeQuery(
881
+ 'dogs',
882
+ this.matchAllDbQuery,
883
+ {pollInterval: 0, pollDebounce: 0},
884
+ function(err) {
885
+ if (err) {
886
+ expect(err.message).to.be.equal('TEST_ERROR');
887
+ return done();
888
+ } else {
889
+ done('Should call callback with pubsub subscribe error');
890
+ }
891
+ }
892
+ );
605
893
  });
606
- };
894
+ }