Skip to content

Commit 8ba276c

Browse files
authored
Merge pull request #651 from share/milestone-mutation
🐛 Avoid mutating milestone snapshots when building from ops
2 parents a5c19b9 + 1683efa commit 8ba276c

File tree

2 files changed

+35
-1
lines changed

2 files changed

+35
-1
lines changed

lib/backend.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -909,7 +909,7 @@ Backend.prototype._fetchSnapshotByTimestamp = function(collection, id, timestamp
909909
};
910910

911911
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);
913913
var error = ot.applyOps(snapshot, ops, {_normalizeLegacyJson0Ops: true});
914914
callback(error, snapshot);
915915
};

test/client/snapshot-version-request.js

+34
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ var sinon = require('sinon');
66
var async = require('async');
77
var json0v2 = require('ot-json0-v2').type;
88
var types = require('../../lib/types');
9+
var clone = require('../../lib/util').clone;
910

1011
describe('SnapshotVersionRequest', function() {
1112
var backend;
@@ -440,6 +441,39 @@ describe('SnapshotVersionRequest', function() {
440441
], done);
441442
});
442443

444+
it('does not mutate the milestone snapshot', function(done) {
445+
var connection = backendWithMilestones.connect();
446+
var doc = connection.get('books', 'mocking-bird');
447+
var milestoneDb = backendWithMilestones.milestoneDb;
448+
var milestone;
449+
450+
async.waterfall([
451+
doc.create.bind(doc, {title: 'To Kill a Mocking Bird'}),
452+
doc.submitOp.bind(doc, {p: ['author'], oi: 'Harper Lea'}),
453+
doc.submitOp.bind(doc, {p: ['author'], od: 'Harper Lea', oi: 'Harper Lee'}),
454+
milestoneDb.getMilestoneSnapshot.bind(milestoneDb, 'books', 'mocking-bird', 3),
455+
function(snapshot, next) {
456+
milestone = clone(snapshot);
457+
next();
458+
},
459+
function(next) {
460+
// Internally, this calls ot.applyOps(), which mutates the snapshot it's passed.
461+
// This call shouldn't cause the in-memory milestone snapshot to be mutated.
462+
connection.fetchSnapshot('books', 'mocking-bird', 3, next);
463+
},
464+
function(snapshot, next) {
465+
expect(snapshot.v).to.equal(3);
466+
expect(snapshot.data).to.eql({title: 'To Kill a Mocking Bird', author: 'Harper Lee'});
467+
next();
468+
},
469+
milestoneDb.getMilestoneSnapshot.bind(milestoneDb, 'books', 'mocking-bird', 3),
470+
function(snapshot, next) {
471+
expect(snapshot).to.eql(milestone);
472+
next();
473+
}
474+
], done);
475+
});
476+
443477
describe('with version null', function() {
444478
it('fetches a latest version', function(done) {
445479
var doc = backendWithMilestones.connect().get('books', 'mocking-bird');

0 commit comments

Comments
 (0)