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.
- 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/milestone-db.js +8 -4
- package/test/setup.js +5 -0
package/lib/agent.js
CHANGED
|
@@ -718,9 +718,10 @@ Agent.prototype._unsubscribeBulk = function(collection, ids, callback) {
|
|
|
718
718
|
|
|
719
719
|
Agent.prototype._submit = function(collection, id, op, callback) {
|
|
720
720
|
var agent = this;
|
|
721
|
-
this.backend.submit(this, collection, id, op, null, function(err, ops) {
|
|
721
|
+
this.backend.submit(this, collection, id, op, null, function(err, ops, request) {
|
|
722
722
|
// Message to acknowledge the op was successfully submitted
|
|
723
723
|
var ack = {src: op.src, seq: op.seq, v: op.v};
|
|
724
|
+
if (request._fixupOps.length) ack[ACTIONS.fixup] = request._fixupOps;
|
|
724
725
|
if (err) {
|
|
725
726
|
// Occasional 'Op already submitted' errors are expected to happen as
|
|
726
727
|
// part of normal operation, since inflight ops need to be resent after
|
package/lib/backend.js
CHANGED
|
@@ -229,7 +229,7 @@ Backend.prototype.submit = function(agent, index, id, op, options, originalCallb
|
|
|
229
229
|
|
|
230
230
|
var callback = function(error, ops) {
|
|
231
231
|
backend.emit('submitRequestEnd', error, request);
|
|
232
|
-
originalCallback(error, ops);
|
|
232
|
+
originalCallback(error, ops, request);
|
|
233
233
|
};
|
|
234
234
|
|
|
235
235
|
var err = ot.checkOp(op);
|
|
@@ -661,10 +661,32 @@ Backend.prototype.querySubscribe = function(agent, index, query, options, callba
|
|
|
661
661
|
'DB does not support subscribe'
|
|
662
662
|
));
|
|
663
663
|
}
|
|
664
|
-
|
|
665
|
-
|
|
664
|
+
|
|
665
|
+
var channels = request.channels;
|
|
666
|
+
|
|
667
|
+
if (request.channel) {
|
|
668
|
+
logger.warn(
|
|
669
|
+
'[DEPRECATED] "query" middleware\'s context.channel is deprecated, use context.channels instead. ' +
|
|
670
|
+
'Read more: https://share.github.io/sharedb/middleware/actions#query'
|
|
671
|
+
);
|
|
672
|
+
channels = [request.channel];
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
if (!channels || !channels.length) {
|
|
676
|
+
return callback(new ShareDBError(ERROR_CODE.ERR_QUERY_CHANNEL_MISSING, 'Required minimum one query channel.'));
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
var streams = [];
|
|
680
|
+
|
|
681
|
+
function destroyStreams() {
|
|
682
|
+
streams.forEach(function(stream) {
|
|
683
|
+
stream.destroy();
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
function createQueryEmitter() {
|
|
666
688
|
if (options.ids) {
|
|
667
|
-
var queryEmitter = new QueryEmitter(request,
|
|
689
|
+
var queryEmitter = new QueryEmitter(request, streams, options.ids);
|
|
668
690
|
backend.emit('timing', 'querySubscribe.reconnect', Date.now() - start, request);
|
|
669
691
|
callback(null, queryEmitter);
|
|
670
692
|
return;
|
|
@@ -672,14 +694,29 @@ Backend.prototype.querySubscribe = function(agent, index, query, options, callba
|
|
|
672
694
|
// Issue query on db to get our initial results
|
|
673
695
|
backend._query(agent, request, function(err, snapshots, extra) {
|
|
674
696
|
if (err) {
|
|
675
|
-
|
|
697
|
+
destroyStreams();
|
|
676
698
|
return callback(err);
|
|
677
699
|
}
|
|
678
700
|
var ids = pluckIds(snapshots);
|
|
679
|
-
var queryEmitter = new QueryEmitter(request,
|
|
701
|
+
var queryEmitter = new QueryEmitter(request, streams, ids, extra);
|
|
680
702
|
backend.emit('timing', 'querySubscribe.initial', Date.now() - start, request);
|
|
681
703
|
callback(null, queryEmitter, snapshots, extra);
|
|
682
704
|
});
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
channels.forEach(function(channel) {
|
|
708
|
+
backend.pubsub.subscribe(channel, function(err, stream) {
|
|
709
|
+
if (err) {
|
|
710
|
+
destroyStreams();
|
|
711
|
+
return callback(err);
|
|
712
|
+
}
|
|
713
|
+
streams.push(stream);
|
|
714
|
+
|
|
715
|
+
var subscribedToAllChannels = streams.length === channels.length;
|
|
716
|
+
if (subscribedToAllChannels) {
|
|
717
|
+
createQueryEmitter();
|
|
718
|
+
}
|
|
719
|
+
});
|
|
683
720
|
});
|
|
684
721
|
});
|
|
685
722
|
};
|
|
@@ -693,7 +730,7 @@ Backend.prototype._triggerQuery = function(agent, index, query, options, callbac
|
|
|
693
730
|
collection: collection,
|
|
694
731
|
projection: projection,
|
|
695
732
|
fields: fields,
|
|
696
|
-
|
|
733
|
+
channels: [this.getCollectionChannel(collection)],
|
|
697
734
|
query: query,
|
|
698
735
|
options: options,
|
|
699
736
|
db: null,
|
package/lib/client/doc.js
CHANGED
|
@@ -5,6 +5,7 @@ var types = require('../types');
|
|
|
5
5
|
var util = require('../util');
|
|
6
6
|
var clone = util.clone;
|
|
7
7
|
var deepEqual = require('fast-deep-equal');
|
|
8
|
+
var ACTIONS = require('../message-actions').ACTIONS;
|
|
8
9
|
|
|
9
10
|
var ERROR_CODE = ShareDBError.CODES;
|
|
10
11
|
|
|
@@ -960,6 +961,23 @@ Doc.prototype._opAcknowledged = function(message) {
|
|
|
960
961
|
return this.fetch();
|
|
961
962
|
}
|
|
962
963
|
|
|
964
|
+
if (message[ACTIONS.fixup]) {
|
|
965
|
+
for (var i = 0; i < message[ACTIONS.fixup].length; i++) {
|
|
966
|
+
var fixupOp = message[ACTIONS.fixup][i];
|
|
967
|
+
|
|
968
|
+
for (var j = 0; j < this.pendingOps.length; j++) {
|
|
969
|
+
var transformErr = transformX(this.pendingOps[i], fixupOp);
|
|
970
|
+
if (transformErr) return this._hardRollback(transformErr);
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
try {
|
|
974
|
+
this._otApply(fixupOp, false);
|
|
975
|
+
} catch (error) {
|
|
976
|
+
return this._hardRollback(error);
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
|
|
963
981
|
// The op was committed successfully. Increment the version number
|
|
964
982
|
this.version++;
|
|
965
983
|
|
package/lib/error.js
CHANGED
|
@@ -15,6 +15,8 @@ ShareDBError.prototype.name = 'ShareDBError';
|
|
|
15
15
|
ShareDBError.CODES = {
|
|
16
16
|
ERR_APPLY_OP_VERSION_DOES_NOT_MATCH_SNAPSHOT: 'ERR_APPLY_OP_VERSION_DOES_NOT_MATCH_SNAPSHOT',
|
|
17
17
|
ERR_APPLY_SNAPSHOT_NOT_PROVIDED: 'ERR_APPLY_SNAPSHOT_NOT_PROVIDED',
|
|
18
|
+
ERR_FIXUP_IS_ONLY_VALID_ON_APPLY: 'ERR_FIXUP_IS_ONLY_VALID_ON_APPLY',
|
|
19
|
+
ERR_CANNOT_FIXUP_DELETION: 'ERR_CANNOT_FIXUP_DELETION',
|
|
18
20
|
ERR_CLIENT_ID_BADLY_FORMED: 'ERR_CLIENT_ID_BADLY_FORMED',
|
|
19
21
|
ERR_CANNOT_PING_OFFLINE: 'ERR_CANNOT_PING_OFFLINE',
|
|
20
22
|
ERR_CONNECTION_SEQ_INTEGER_OVERFLOW: 'ERR_CONNECTION_SEQ_INTEGER_OVERFLOW',
|
|
@@ -45,6 +47,7 @@ ShareDBError.CODES = {
|
|
|
45
47
|
ERR_OT_OP_NOT_PROVIDED: 'ERR_OT_OP_NOT_PROVIDED',
|
|
46
48
|
ERR_PRESENCE_TRANSFORM_FAILED: 'ERR_PRESENCE_TRANSFORM_FAILED',
|
|
47
49
|
ERR_PROTOCOL_VERSION_NOT_SUPPORTED: 'ERR_PROTOCOL_VERSION_NOT_SUPPORTED',
|
|
50
|
+
ERR_QUERY_CHANNEL_MISSING: 'ERR_QUERY_CHANNEL_MISSING',
|
|
48
51
|
ERR_QUERY_EMITTER_LISTENER_NOT_ASSIGNED: 'ERR_QUERY_EMITTER_LISTENER_NOT_ASSIGNED',
|
|
49
52
|
/**
|
|
50
53
|
* A special error that a "readSnapshots" middleware implementation can use to indicate that it
|
|
@@ -67,6 +70,7 @@ ShareDBError.CODES = {
|
|
|
67
70
|
ERR_SNAPSHOT_READS_REJECTED: 'ERR_SNAPSHOT_READS_REJECTED',
|
|
68
71
|
ERR_SUBMIT_TRANSFORM_OPS_NOT_FOUND: 'ERR_SUBMIT_TRANSFORM_OPS_NOT_FOUND',
|
|
69
72
|
ERR_TYPE_CANNOT_BE_PROJECTED: 'ERR_TYPE_CANNOT_BE_PROJECTED',
|
|
73
|
+
ERR_TYPE_DOES_NOT_SUPPORT_COMPOSE: 'ERR_TYPE_DOES_NOT_SUPPORT_COMPOSE',
|
|
70
74
|
ERR_TYPE_DOES_NOT_SUPPORT_PRESENCE: 'ERR_TYPE_DOES_NOT_SUPPORT_PRESENCE',
|
|
71
75
|
ERR_UNKNOWN_ERROR: 'ERR_UNKNOWN_ERROR'
|
|
72
76
|
};
|
package/lib/message-actions.js
CHANGED
package/lib/query-emitter.js
CHANGED
|
@@ -5,7 +5,7 @@ var util = require('./util');
|
|
|
5
5
|
|
|
6
6
|
var ERROR_CODE = ShareDBError.CODES;
|
|
7
7
|
|
|
8
|
-
function QueryEmitter(request,
|
|
8
|
+
function QueryEmitter(request, streams, ids, extra) {
|
|
9
9
|
this.backend = request.backend;
|
|
10
10
|
this.agent = request.agent;
|
|
11
11
|
this.db = request.db;
|
|
@@ -15,7 +15,7 @@ function QueryEmitter(request, stream, ids, extra) {
|
|
|
15
15
|
this.fields = request.fields;
|
|
16
16
|
this.options = request.options;
|
|
17
17
|
this.snapshotProjection = request.snapshotProjection;
|
|
18
|
-
this.
|
|
18
|
+
this.streams = streams;
|
|
19
19
|
this.ids = ids;
|
|
20
20
|
this.extra = extra;
|
|
21
21
|
|
|
@@ -23,10 +23,12 @@ function QueryEmitter(request, stream, ids, extra) {
|
|
|
23
23
|
this.canPollDoc = this.db.canPollDoc(this.collection, this.query);
|
|
24
24
|
this.pollDebounce =
|
|
25
25
|
(typeof this.options.pollDebounce === 'number') ? this.options.pollDebounce :
|
|
26
|
-
(typeof this.db.pollDebounce === 'number') ? this.db.pollDebounce :
|
|
26
|
+
(typeof this.db.pollDebounce === 'number') ? this.db.pollDebounce :
|
|
27
|
+
streams.length > 1 ? 1000 : 0;
|
|
27
28
|
this.pollInterval =
|
|
28
29
|
(typeof this.options.pollInterval === 'number') ? this.options.pollInterval :
|
|
29
|
-
(typeof this.db.pollInterval === 'number') ? this.db.pollInterval :
|
|
30
|
+
(typeof this.db.pollInterval === 'number') ? this.db.pollInterval :
|
|
31
|
+
streams.length > 1 ? 1000 : 0;
|
|
30
32
|
|
|
31
33
|
this._polling = false;
|
|
32
34
|
this._pendingPoll = null;
|
|
@@ -41,15 +43,19 @@ QueryEmitter.prototype._open = function() {
|
|
|
41
43
|
this._defaultCallback = function(err) {
|
|
42
44
|
if (err) emitter.onError(err);
|
|
43
45
|
};
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
|
|
47
|
+
emitter.streams.forEach(function(stream) {
|
|
48
|
+
stream.on('data', function(data) {
|
|
49
|
+
if (data.error) {
|
|
50
|
+
return emitter.onError(data.error);
|
|
51
|
+
}
|
|
52
|
+
emitter._update(data);
|
|
53
|
+
});
|
|
54
|
+
stream.on('end', function() {
|
|
55
|
+
emitter.destroy();
|
|
56
|
+
});
|
|
52
57
|
});
|
|
58
|
+
|
|
53
59
|
// Make sure we start polling if pollInterval is being used
|
|
54
60
|
this._flushPoll();
|
|
55
61
|
};
|
|
@@ -57,7 +63,12 @@ QueryEmitter.prototype._open = function() {
|
|
|
57
63
|
QueryEmitter.prototype.destroy = function() {
|
|
58
64
|
clearTimeout(this._pollDebounceId);
|
|
59
65
|
clearTimeout(this._pollIntervalId);
|
|
60
|
-
|
|
66
|
+
|
|
67
|
+
var stream;
|
|
68
|
+
|
|
69
|
+
while (stream = this.streams.pop()) {
|
|
70
|
+
stream.destroy();
|
|
71
|
+
}
|
|
61
72
|
};
|
|
62
73
|
|
|
63
74
|
QueryEmitter.prototype._emitTiming = function(action, start) {
|
|
@@ -140,8 +151,8 @@ QueryEmitter.prototype._flushPoll = function() {
|
|
|
140
151
|
if (this._pendingPoll) {
|
|
141
152
|
this.queryPoll();
|
|
142
153
|
|
|
143
|
-
|
|
144
|
-
|
|
154
|
+
// If a pollInterval is specified, poll if the query doesn't get polled in
|
|
155
|
+
// the time of the interval
|
|
145
156
|
} else if (this.pollInterval) {
|
|
146
157
|
var emitter = this;
|
|
147
158
|
this._pollIntervalId = setTimeout(function() {
|
|
@@ -301,14 +312,14 @@ QueryEmitter.prototype.queryPollDoc = function(id, callback) {
|
|
|
301
312
|
// all messages are received and applied in order, so it is critical that none
|
|
302
313
|
// are dropped.
|
|
303
314
|
QueryEmitter.prototype.onError =
|
|
304
|
-
QueryEmitter.prototype.onDiff =
|
|
305
|
-
QueryEmitter.prototype.onExtra =
|
|
306
|
-
QueryEmitter.prototype.onOp = function() {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
};
|
|
315
|
+
QueryEmitter.prototype.onDiff =
|
|
316
|
+
QueryEmitter.prototype.onExtra =
|
|
317
|
+
QueryEmitter.prototype.onOp = function() {
|
|
318
|
+
throw new ShareDBError(
|
|
319
|
+
ERROR_CODE.ERR_QUERY_EMITTER_LISTENER_NOT_ASSIGNED,
|
|
320
|
+
'Required QueryEmitter listener not assigned'
|
|
321
|
+
);
|
|
322
|
+
};
|
|
312
323
|
|
|
313
324
|
function getInserted(diff) {
|
|
314
325
|
var inserted = [];
|
package/lib/submit-request.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
var ot = require('./ot');
|
|
2
2
|
var projections = require('./projections');
|
|
3
3
|
var ShareDBError = require('./error');
|
|
4
|
+
var types = require('./types');
|
|
4
5
|
|
|
5
6
|
var ERROR_CODE = ShareDBError.CODES;
|
|
6
7
|
|
|
@@ -40,9 +41,47 @@ function SubmitRequest(backend, agent, index, id, op, options) {
|
|
|
40
41
|
this.snapshot = null;
|
|
41
42
|
this.ops = [];
|
|
42
43
|
this.channels = null;
|
|
44
|
+
this._fixupOps = [];
|
|
43
45
|
}
|
|
44
46
|
module.exports = SubmitRequest;
|
|
45
47
|
|
|
48
|
+
SubmitRequest.prototype.$fixup = function(op) {
|
|
49
|
+
if (this.action !== this.backend.MIDDLEWARE_ACTIONS.apply) {
|
|
50
|
+
throw new ShareDBError(
|
|
51
|
+
ERROR_CODE.ERR_FIXUP_IS_ONLY_VALID_ON_APPLY,
|
|
52
|
+
'fixup can only be called during the apply middleware'
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (this.op.del) {
|
|
57
|
+
throw new ShareDBError(
|
|
58
|
+
ERROR_CODE.ERR_CANNOT_FIXUP_DELETION,
|
|
59
|
+
'fixup cannot be applied on deletion ops'
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
var typeId = this.op.create ? this.op.create.type : this.snapshot.type;
|
|
64
|
+
var type = types.map[typeId];
|
|
65
|
+
if (typeof type.compose !== 'function') {
|
|
66
|
+
throw new ShareDBError(
|
|
67
|
+
ERROR_CODE.ERR_TYPE_DOES_NOT_SUPPORT_COMPOSE,
|
|
68
|
+
typeId + ' does not support compose'
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (this.op.create) this.op.create.data = type.apply(this.op.create.data, op);
|
|
73
|
+
else this.op.op = type.compose(this.op.op, op);
|
|
74
|
+
|
|
75
|
+
var fixupOp = {
|
|
76
|
+
src: this.op.src,
|
|
77
|
+
seq: this.op.seq,
|
|
78
|
+
v: this.op.v,
|
|
79
|
+
op: op
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
this._fixupOps.push(fixupOp);
|
|
83
|
+
};
|
|
84
|
+
|
|
46
85
|
SubmitRequest.prototype.submit = function(callback) {
|
|
47
86
|
var request = this;
|
|
48
87
|
var backend = this.backend;
|
|
@@ -103,7 +142,7 @@ SubmitRequest.prototype.submit = function(callback) {
|
|
|
103
142
|
|
|
104
143
|
// Transform the op up to the current snapshot version, then apply
|
|
105
144
|
var from = op.v;
|
|
106
|
-
backend.db.getOpsToSnapshot(collection, id, from, snapshot,
|
|
145
|
+
backend.db.getOpsToSnapshot(collection, id, from, snapshot, {metadata: true}, function(err, ops) {
|
|
107
146
|
if (err) return callback(err);
|
|
108
147
|
|
|
109
148
|
if (ops.length !== snapshot.v - from) {
|
|
@@ -134,6 +173,8 @@ SubmitRequest.prototype.apply = function(callback) {
|
|
|
134
173
|
// Always set the channels before each attempt to apply. If the channels are
|
|
135
174
|
// modified in a middleware and we retry, we want to reset to a new array
|
|
136
175
|
this.channels = this.backend.getChannels(this.collection, this.id);
|
|
176
|
+
this._fixupOps = [];
|
|
177
|
+
delete this.op.m.fixup;
|
|
137
178
|
|
|
138
179
|
var request = this;
|
|
139
180
|
this.backend.trigger(this.backend.MIDDLEWARE_ACTIONS.apply, this.agent, this, function(err) {
|
|
@@ -152,6 +193,7 @@ SubmitRequest.prototype.commit = function(callback) {
|
|
|
152
193
|
var backend = this.backend;
|
|
153
194
|
backend.trigger(backend.MIDDLEWARE_ACTIONS.commit, this.agent, this, function(err) {
|
|
154
195
|
if (err) return callback(err);
|
|
196
|
+
if (request._fixupOps.length) request.op.m.fixup = request._fixupOps;
|
|
155
197
|
|
|
156
198
|
// Try committing the operation and snapshot to the database atomically
|
|
157
199
|
backend.db.commit(
|
|
@@ -204,6 +246,7 @@ SubmitRequest.prototype._transformOp = function(ops) {
|
|
|
204
246
|
// can happen in normal operation, such as a client resending an
|
|
205
247
|
// unacknowledged operation at reconnect. It's important we don't apply
|
|
206
248
|
// the same op twice
|
|
249
|
+
if (op.m.fixup) this._fixupOps = op.m.fixup;
|
|
207
250
|
return this.alreadySubmittedError();
|
|
208
251
|
}
|
|
209
252
|
|
|
@@ -213,6 +256,7 @@ SubmitRequest.prototype._transformOp = function(ops) {
|
|
|
213
256
|
|
|
214
257
|
var err = ot.transform(type, this.op, op);
|
|
215
258
|
if (err) return err;
|
|
259
|
+
delete op.m;
|
|
216
260
|
this.ops.push(op);
|
|
217
261
|
}
|
|
218
262
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sharedb",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.1",
|
|
4
4
|
"description": "JSON OT database backend",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"dependencies": {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"ot-json1": "^0.3.0",
|
|
23
23
|
"rich-text": "^4.1.0",
|
|
24
24
|
"sharedb-legacy": "npm:sharedb@=1.1.0",
|
|
25
|
-
"sinon": "^
|
|
25
|
+
"sinon": "^9.2.4"
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
|
28
28
|
"lib/",
|
|
@@ -38,7 +38,6 @@ describe('Presence', function() {
|
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
afterEach(function(done) {
|
|
41
|
-
sinon.restore();
|
|
42
41
|
connection1.close();
|
|
43
42
|
connection2.close();
|
|
44
43
|
backend.close(done);
|
|
@@ -147,30 +146,6 @@ describe('Presence', function() {
|
|
|
147
146
|
], errorHandler(done));
|
|
148
147
|
});
|
|
149
148
|
|
|
150
|
-
it('destroys old local presence but keeps new local presence when getting during destroy', function(done) {
|
|
151
|
-
presence2.create('presence-2');
|
|
152
|
-
var presence2a;
|
|
153
|
-
|
|
154
|
-
async.series([
|
|
155
|
-
presence2.subscribe.bind(presence2),
|
|
156
|
-
function(next) {
|
|
157
|
-
presence2.destroy(function() {
|
|
158
|
-
expect(presence2).to.equal(presence2a);
|
|
159
|
-
expect(Object.keys(presence2.localPresences)).to.eql(['presence-2a']);
|
|
160
|
-
done();
|
|
161
|
-
});
|
|
162
|
-
next();
|
|
163
|
-
},
|
|
164
|
-
function(next) {
|
|
165
|
-
presence2a = connection2.getPresence('test-channel');
|
|
166
|
-
presence2a.create('presence-2a');
|
|
167
|
-
presence2a.subscribe(function(error) {
|
|
168
|
-
next(error);
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
], errorHandler(done));
|
|
172
|
-
});
|
|
173
|
-
|
|
174
149
|
it('throws if trying to create local presence when wanting destroy', function(done) {
|
|
175
150
|
presence2.destroy(errorHandler(done));
|
|
176
151
|
expect(function() {
|