From 9d9031ba71686a8e1ca2fb48501730bde63736d2 Mon Sep 17 00:00:00 2001 From: antoinelyset Date: Tue, 16 Mar 2021 11:34:17 +0100 Subject: [PATCH] Add preventInvertInRollback option --- lib/client/doc.js | 5 +++- test/client/doc.js | 69 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/lib/client/doc.js b/lib/client/doc.js index e37023b2c..0aad2e84b 100644 --- a/lib/client/doc.js +++ b/lib/client/doc.js @@ -104,6 +104,9 @@ function Doc(connection, collection, id) { // specifc op and toggled off afterward this.preventCompose = false; + // Disable the default behavior of inverting ops in rollback + this.preventInvertInRollback = false; + // If set to true, the source will be submitted over the connection. This // will also have the side-effect of only composing ops whose sources are // equal @@ -947,7 +950,7 @@ Doc.prototype._rollback = function(err) { // working state, then call back var op = this.inflightOp; - if ('op' in op && op.type.invert) { + if ('op' in op && !this.preventInvertInRollback && op.type.invert) { op.op = op.type.invert(op.op); // Transform the undo operation by any pending ops. diff --git a/test/client/doc.js b/test/client/doc.js index 84af1fb68..03c792715 100644 --- a/test/client/doc.js +++ b/test/client/doc.js @@ -474,5 +474,74 @@ describe('Doc', function() { } ], done); }); + + describe('with an op collision', function() { + function testScenario(preventInvertInRollback, done) { + var doc1 = this.backend.connect().get('dogs', 'snoopy'); + var doc2 = this.backend.connect().get('dogs', 'snoopy'); + doc2.preventInvertInRollback = preventInvertInRollback; + + var pauseSubmit = false; + var fireSubmit; + var invertHaveBeenCalled = false; + + this.backend.use('submit', function(request, callback) { + if (pauseSubmit) { + fireSubmit = function() { + pauseSubmit = false; + callback(); + }; + } else { + fireSubmit = null; + callback(); + } + }); + async.series([ + doc1.create.bind(doc1, {colours: ['white']}), + doc1.whenNothingPending.bind(doc1), + doc2.fetch.bind(doc2), + doc2.whenNothingPending.bind(doc2), + function(next) { + // Wrap invert to test that it has been called + var originalInvert = doc2.type.invert; + doc2.type.invert = function(op) { + invertHaveBeenCalled = true; + return originalInvert(op); + }; + next(); + }, + doc1.submitOp.bind(doc1, {p: ['colours'], oi: 'white,black'}), + function(next) { + pauseSubmit = true; + doc2.submitOp({p: ['colours', '0'], li: 'black'}, function(error) { + expect(error.message).to.equal('Referenced element not a list'); + next(); + }); + + doc2.fetch(function(error) { + if (error) return next(error); + fireSubmit(); + }); + }, + function(next) { + expect(invertHaveBeenCalled).to.eql(!preventInvertInRollback); + expect(doc2.data).to.eql(doc1.data); + next(); + } + ], done); + } + + it('calls doc type invert by default', function(done) { + var preventInvertInRollback = false; + var testScenarioBound = testScenario.bind(this); + testScenarioBound(preventInvertInRollback, done); + }); + + it('does not call doc type invert if preventInvertInRollback is `true`', function(done) { + var preventInvertInRollback = true; + var testScenarioBound = testScenario.bind(this); + testScenarioBound(preventInvertInRollback, done); + }); + }); }); });