sharedb 3.2.4 → 3.3.0
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.
- package/lib/agent.js +2 -1
- package/lib/backend.js +44 -7
- package/lib/client/doc.js +18 -0
- package/lib/error.js +4 -0
- package/lib/message-actions.js +1 -0
- package/lib/query-emitter.js +34 -23
- package/lib/submit-request.js +45 -1
- package/package.json +2 -2
- package/test/client/presence/presence.js +0 -25
- package/test/client/query-subscribe.js +781 -493
- package/test/client/submit.js +48 -0
- package/test/logger.js +0 -4
- package/test/middleware.js +356 -0
- package/test/setup.js +5 -0
|
@@ -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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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', '
|
|
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(['
|
|
23
|
-
expect(util.pluck(docs, 'data')).eql([{age:
|
|
24
|
-
expect(index).equal(0);
|
|
25
|
-
|
|
26
|
-
expect(util.pluck(
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
},
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
355
|
+
query.destroy(function(err) {
|
|
103
356
|
if (err) return done(err);
|
|
104
|
-
|
|
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
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
},
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
|
|
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
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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
|
-
|
|
192
|
-
|
|
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
|
-
|
|
448
|
+
connection2.get('dogs', 'fido').fetch(function(err) {
|
|
199
449
|
if (err) return done(err);
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
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.
|
|
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
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
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
|
-
|
|
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
|
-
|
|
311
|
-
|
|
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
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
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
|
-
|
|
320
|
-
|
|
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
|
-
|
|
545
|
+
}
|
|
546
|
+
if (total === 10) {
|
|
324
547
|
// first document is its own batch; then subsequent creates
|
|
325
|
-
// are debounced
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
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
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
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
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
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
|
-
|
|
355
|
-
|
|
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
|
-
|
|
595
|
+
}
|
|
596
|
+
if (total === 10) {
|
|
359
597
|
// first document is its own batch; then subsequent creates
|
|
360
|
-
// are debounced
|
|
361
|
-
|
|
362
|
-
|
|
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
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
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
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
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
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
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
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
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
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
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
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
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
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
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
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
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
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
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
|
-
|
|
755
|
+
connection.get('dogs', 'fido').create({age: 3});
|
|
432
756
|
});
|
|
433
757
|
|
|
434
|
-
it('
|
|
435
|
-
|
|
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
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
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').
|
|
772
|
+
connection.get('dogs', 'fido').create({age: 3});
|
|
461
773
|
});
|
|
774
|
+
});
|
|
462
775
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
connection.
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
var
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
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
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
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
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
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
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
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
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
},
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
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:
|
|
566
|
-
|
|
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
|
-
|
|
572
|
-
|
|
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
|
+
}
|