sharedb 4.1.4 → 5.0.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/backend.js CHANGED
@@ -909,7 +909,7 @@ Backend.prototype._fetchSnapshotByTimestamp = function(collection, id, timestamp
909
909
  };
910
910
 
911
911
  Backend.prototype._buildSnapshotFromOps = function(id, startingSnapshot, ops, callback) {
912
- var snapshot = startingSnapshot || new Snapshot(id, 0, null, undefined, null);
912
+ var snapshot = util.clone(startingSnapshot) || new Snapshot(id, 0, null, undefined, null);
913
913
  var error = ot.applyOps(snapshot, ops, {_normalizeLegacyJson0Ops: true});
914
914
  callback(error, snapshot);
915
915
  };
package/lib/db/memory.js CHANGED
@@ -84,10 +84,12 @@ MemoryDB.prototype.getOps = function(collection, id, from, to, options, callback
84
84
  if (typeof callback !== 'function') throw new Error('Callback required');
85
85
  util.nextTick(function() {
86
86
  var opLog = db._getOpLogSync(collection, id);
87
- if (to == null) {
88
- to = opLog.length;
87
+ if (!from) from = 0;
88
+ if (to == null) to = opLog.length;
89
+ var ops = clone(opLog.slice(from, to).filter(Boolean));
90
+ if (ops.length < to - from) {
91
+ return callback(new Error('Missing ops'));
89
92
  }
90
- var ops = clone(opLog.slice(from, to));
91
93
  if (!includeMetadata) {
92
94
  for (var i = 0; i < ops.length; i++) {
93
95
  delete ops[i].m;
@@ -97,6 +99,18 @@ MemoryDB.prototype.getOps = function(collection, id, from, to, options, callback
97
99
  });
98
100
  };
99
101
 
102
+ MemoryDB.prototype.deleteOps = function(collection, id, from, to, options, callback) {
103
+ if (typeof callback !== 'function') throw new Error('Callback required');
104
+ var db = this;
105
+ util.nextTick(function() {
106
+ var opLog = db._getOpLogSync(collection, id);
107
+ if (!from) from = 0;
108
+ if (to == null) to = opLog.length;
109
+ for (var i = from; i < to; i++) opLog[i] = null;
110
+ callback(null);
111
+ });
112
+ };
113
+
100
114
  // The memory database query function returns all documents in a collection
101
115
  // regardless of query by default
102
116
  MemoryDB.prototype.query = function(collection, query, fields, options, callback) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sharedb",
3
- "version": "4.1.4",
3
+ "version": "5.0.0",
4
4
  "description": "JSON OT database backend",
5
5
  "main": "lib/index.js",
6
6
  "dependencies": {
@@ -6,6 +6,7 @@ var sinon = require('sinon');
6
6
  var async = require('async');
7
7
  var json0v2 = require('ot-json0-v2').type;
8
8
  var types = require('../../lib/types');
9
+ var clone = require('../../lib/util').clone;
9
10
 
10
11
  describe('SnapshotVersionRequest', function() {
11
12
  var backend;
@@ -144,7 +145,18 @@ describe('SnapshotVersionRequest', function() {
144
145
 
145
146
  it('errors if asking for a version that does not exist', function(done) {
146
147
  backend.connect().fetchSnapshot('books', 'don-quixote', 4, function(error, snapshot) {
147
- expect(error.code).to.equal('ERR_OP_VERSION_NEWER_THAN_CURRENT_SNAPSHOT');
148
+ expect(error).to.be.ok;
149
+ expect(snapshot).to.equal(undefined);
150
+ done();
151
+ });
152
+ });
153
+
154
+ it('errors if asking for a version that does not exist if the DB adapter does not error', function(done) {
155
+ sinon.stub(MemoryDb.prototype, 'getOps').callsFake(function(collection, id, from, to, options, callback) {
156
+ callback(null, []);
157
+ });
158
+ backend.connect().fetchSnapshot('books', 'don-quixote', 4, function(error, snapshot) {
159
+ expect(error).to.be.ok;
148
160
  expect(snapshot).to.equal(undefined);
149
161
  done();
150
162
  });
@@ -440,6 +452,39 @@ describe('SnapshotVersionRequest', function() {
440
452
  ], done);
441
453
  });
442
454
 
455
+ it('does not mutate the milestone snapshot', function(done) {
456
+ var connection = backendWithMilestones.connect();
457
+ var doc = connection.get('books', 'mocking-bird');
458
+ var milestoneDb = backendWithMilestones.milestoneDb;
459
+ var milestone;
460
+
461
+ async.waterfall([
462
+ doc.create.bind(doc, {title: 'To Kill a Mocking Bird'}),
463
+ doc.submitOp.bind(doc, {p: ['author'], oi: 'Harper Lea'}),
464
+ doc.submitOp.bind(doc, {p: ['author'], od: 'Harper Lea', oi: 'Harper Lee'}),
465
+ milestoneDb.getMilestoneSnapshot.bind(milestoneDb, 'books', 'mocking-bird', 3),
466
+ function(snapshot, next) {
467
+ milestone = clone(snapshot);
468
+ next();
469
+ },
470
+ function(next) {
471
+ // Internally, this calls ot.applyOps(), which mutates the snapshot it's passed.
472
+ // This call shouldn't cause the in-memory milestone snapshot to be mutated.
473
+ connection.fetchSnapshot('books', 'mocking-bird', 3, next);
474
+ },
475
+ function(snapshot, next) {
476
+ expect(snapshot.v).to.equal(3);
477
+ expect(snapshot.data).to.eql({title: 'To Kill a Mocking Bird', author: 'Harper Lee'});
478
+ next();
479
+ },
480
+ milestoneDb.getMilestoneSnapshot.bind(milestoneDb, 'books', 'mocking-bird', 3),
481
+ function(snapshot, next) {
482
+ expect(snapshot).to.eql(milestone);
483
+ next();
484
+ }
485
+ ], done);
486
+ });
487
+
443
488
  describe('with version null', function() {
444
489
  it('fetches a latest version', function(done) {
445
490
  var doc = backendWithMilestones.connect().get('books', 'mocking-bird');
package/test/db-memory.js CHANGED
@@ -1,6 +1,8 @@
1
1
  var expect = require('chai').expect;
2
+ var Backend = require('../lib/backend');
2
3
  var DB = require('../lib/db');
3
4
  var BasicQueryableMemoryDB = require('./BasicQueryableMemoryDB');
5
+ var async = require('async');
4
6
 
5
7
  describe('DB base class', function() {
6
8
  it('can call db.close() without callback', function() {
@@ -54,17 +56,83 @@ describe('DB base class', function() {
54
56
  });
55
57
  });
56
58
 
57
- // Run all the DB-based tests against the BasicQueryableMemoryDB.
58
- require('./db')({
59
- create: function(options, callback) {
60
- if (typeof options === 'function') {
61
- callback = options;
62
- options = null;
59
+ describe('MemoryDB', function() {
60
+ // Run all the DB-based tests against the BasicQueryableMemoryDB.
61
+ require('./db')({
62
+ create: function(options, callback) {
63
+ if (typeof options === 'function') {
64
+ callback = options;
65
+ options = null;
66
+ }
67
+ var db = new BasicQueryableMemoryDB(options);
68
+ callback(null, db);
69
+ },
70
+ getQuery: function(options) {
71
+ return {filter: options.query, sort: options.sort};
63
72
  }
64
- var db = new BasicQueryableMemoryDB(options);
65
- callback(null, db);
66
- },
67
- getQuery: function(options) {
68
- return {filter: options.query, sort: options.sort};
69
- }
73
+ });
74
+
75
+ describe('deleteOps', function() {
76
+ describe('with some ops', function() {
77
+ var backend;
78
+ var db;
79
+ var connection;
80
+ var doc;
81
+
82
+ beforeEach(function(done) {
83
+ backend = new Backend();
84
+ db = backend.db;
85
+ connection = backend.connect();
86
+ doc = connection.get('dogs', 'fido');
87
+
88
+ async.waterfall([
89
+ doc.create.bind(doc, {name: 'Fido'}),
90
+ doc.submitOp.bind(doc, [{p: ['tricks'], oi: ['fetch']}]),
91
+ db.getOps.bind(db, 'dogs', 'fido', null, null, null),
92
+ function(ops, next) {
93
+ expect(ops).to.have.length(2);
94
+ next();
95
+ }
96
+ ], done);
97
+ });
98
+
99
+ it('deletes all ops', function(done) {
100
+ async.waterfall([
101
+ db.deleteOps.bind(db, 'dogs', 'fido', null, null, null),
102
+ function(next) {
103
+ db.getOps('dogs', 'fido', null, null, null, function(error) {
104
+ expect(error.message).to.equal('Missing ops');
105
+ next();
106
+ });
107
+ }
108
+ ], done);
109
+ });
110
+
111
+ it('deletes some ops', function(done) {
112
+ async.waterfall([
113
+ db.deleteOps.bind(db, 'dogs', 'fido', 0, 1, null),
114
+ db.getOps.bind(db, 'dogs', 'fido', 1, 2, null),
115
+ function(ops, next) {
116
+ expect(ops).to.have.length(1);
117
+ expect(ops[0].op).to.eql([{p: ['tricks'], oi: ['fetch']}]);
118
+ db.getOps('dogs', 'fido', 0, 1, null, function(error) {
119
+ expect(error.message).to.equal('Missing ops');
120
+ next();
121
+ });
122
+ }
123
+ ], done);
124
+ });
125
+
126
+ it('submits more ops after deleting ops', function(done) {
127
+ async.series([
128
+ db.deleteOps.bind(db, 'dogs', 'fido', null, null, null),
129
+ doc.submitOp.bind(doc, [{p: ['tricks', 1], li: 'sit'}]),
130
+ function(next) {
131
+ expect(doc.data.tricks).to.eql(['fetch', 'sit']);
132
+ next();
133
+ }
134
+ ], done);
135
+ });
136
+ });
137
+ });
70
138
  });