2014-03-04 09:05:17 -05:00
|
|
|
sinon = require('sinon')
|
|
|
|
chai = require('chai')
|
|
|
|
should = chai.should()
|
|
|
|
expect = chai.expect
|
|
|
|
modulePath = "../../../../app/js/DiffManager.js"
|
|
|
|
SandboxedModule = require('sandboxed-module')
|
|
|
|
|
|
|
|
describe "DiffManager", ->
|
|
|
|
beforeEach ->
|
|
|
|
@DiffManager = SandboxedModule.require modulePath, requires:
|
2016-09-30 06:41:49 -04:00
|
|
|
"logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub(), warn: sinon.stub() }
|
2014-03-05 10:59:40 -05:00
|
|
|
"./UpdatesManager": @UpdatesManager = {}
|
2014-03-04 09:05:17 -05:00
|
|
|
"./DocumentUpdaterManager": @DocumentUpdaterManager = {}
|
|
|
|
"./DiffGenerator": @DiffGenerator = {}
|
|
|
|
@callback = sinon.stub()
|
|
|
|
@from = new Date()
|
|
|
|
@to = new Date(Date.now() + 10000)
|
|
|
|
@project_id = "mock-project-id"
|
|
|
|
@doc_id = "mock-doc-id"
|
|
|
|
|
|
|
|
describe "getLatestDocAndUpdates", ->
|
|
|
|
beforeEach ->
|
2014-03-10 12:03:03 -04:00
|
|
|
@content = "hello world"
|
2014-03-04 09:05:17 -05:00
|
|
|
@version = 42
|
|
|
|
@updates = [ "mock-update-1", "mock-update-2" ]
|
|
|
|
|
2014-03-10 12:03:03 -04:00
|
|
|
@DocumentUpdaterManager.getDocument = sinon.stub().callsArgWith(2, null, @content, @version)
|
2014-03-19 13:44:16 -04:00
|
|
|
@UpdatesManager.getDocUpdatesWithUserInfo = sinon.stub().callsArgWith(3, null, @updates)
|
2014-03-04 09:05:17 -05:00
|
|
|
|
2017-06-28 10:38:31 -04:00
|
|
|
describe "with a fromVersion", ->
|
|
|
|
beforeEach ->
|
|
|
|
@DiffManager.getLatestDocAndUpdates @project_id, @doc_id, @from, @callback
|
|
|
|
|
|
|
|
it "should get the latest version of the doc", ->
|
|
|
|
@DocumentUpdaterManager.getDocument
|
|
|
|
.calledWith(@project_id, @doc_id)
|
|
|
|
.should.equal true
|
|
|
|
|
|
|
|
it "should get the latest updates", ->
|
|
|
|
@UpdatesManager.getDocUpdatesWithUserInfo
|
|
|
|
.calledWith(@project_id, @doc_id, from: @from)
|
|
|
|
.should.equal true
|
|
|
|
|
|
|
|
it "should call the callback with the content, version and updates", ->
|
|
|
|
@callback.calledWith(null, @content, @version, @updates).should.equal true
|
2014-03-04 09:05:17 -05:00
|
|
|
|
2017-06-28 10:38:31 -04:00
|
|
|
describe "with no fromVersion", ->
|
|
|
|
beforeEach ->
|
|
|
|
@DiffManager.getLatestDocAndUpdates @project_id, @doc_id, null, @callback
|
|
|
|
|
|
|
|
it "should get the latest version of the doc", ->
|
|
|
|
@DocumentUpdaterManager.getDocument
|
|
|
|
.calledWith(@project_id, @doc_id)
|
|
|
|
.should.equal true
|
2014-03-04 09:05:17 -05:00
|
|
|
|
2017-06-28 10:38:31 -04:00
|
|
|
it "should not get the latest updates", ->
|
|
|
|
@UpdatesManager.getDocUpdatesWithUserInfo
|
|
|
|
.called.should.equal false
|
|
|
|
|
|
|
|
it "should call the callback with the content, version and blank updates", ->
|
|
|
|
@callback.calledWith(null, @content, @version, []).should.equal true
|
|
|
|
|
2014-03-04 09:05:17 -05:00
|
|
|
|
|
|
|
describe "getDiff", ->
|
|
|
|
beforeEach ->
|
2014-03-10 12:03:03 -04:00
|
|
|
@content = "hello world"
|
2014-03-05 11:31:38 -05:00
|
|
|
# Op versions are the version they were applied to, so doc is always one version
|
|
|
|
# ahead.s
|
|
|
|
@version = 43
|
2014-03-04 09:05:17 -05:00
|
|
|
@updates = [
|
2014-03-04 10:27:03 -05:00
|
|
|
{ op: "mock-4", v: 42, meta: { start_ts: new Date(@to.getTime() + 20)} }
|
|
|
|
{ op: "mock-3", v: 41, meta: { start_ts: new Date(@to.getTime() + 10)} }
|
|
|
|
{ op: "mock-2", v: 40, meta: { start_ts: new Date(@to.getTime() - 10)} }
|
|
|
|
{ op: "mock-1", v: 39, meta: { start_ts: new Date(@to.getTime() - 20)} }
|
2014-03-04 09:05:17 -05:00
|
|
|
]
|
2014-03-06 05:45:51 -05:00
|
|
|
@fromVersion = 39
|
|
|
|
@toVersion = 40
|
2014-03-04 10:27:03 -05:00
|
|
|
@diffed_updates = @updates.slice(2)
|
2014-03-04 09:05:17 -05:00
|
|
|
@rewound_content = "rewound-content"
|
|
|
|
@diff = [ u: "mock-diff" ]
|
|
|
|
|
|
|
|
describe "with matching versions", ->
|
|
|
|
beforeEach ->
|
2014-03-10 12:03:03 -04:00
|
|
|
@DiffManager.getDocumentBeforeVersion = sinon.stub().callsArgWith(3, null, @rewound_content, @updates)
|
2014-03-04 09:05:17 -05:00
|
|
|
@DiffGenerator.buildDiff = sinon.stub().returns(@diff)
|
2014-03-06 05:45:51 -05:00
|
|
|
@DiffManager.getDiff @project_id, @doc_id, @fromVersion, @toVersion, @callback
|
2014-03-04 09:05:17 -05:00
|
|
|
|
|
|
|
it "should get the latest doc and version with all recent updates", ->
|
2014-03-10 12:03:03 -04:00
|
|
|
@DiffManager.getDocumentBeforeVersion
|
|
|
|
.calledWith(@project_id, @doc_id, @fromVersion)
|
2014-03-04 09:05:17 -05:00
|
|
|
.should.equal true
|
|
|
|
|
|
|
|
it "should generate the diff", ->
|
|
|
|
@DiffGenerator.buildDiff
|
2014-03-10 12:03:03 -04:00
|
|
|
.calledWith(@rewound_content, @diffed_updates.slice().reverse())
|
2014-03-04 09:05:17 -05:00
|
|
|
.should.equal true
|
|
|
|
|
|
|
|
it "should call the callback with the diff", ->
|
|
|
|
@callback.calledWith(null, @diff).should.equal true
|
|
|
|
|
2014-03-10 12:03:03 -04:00
|
|
|
describe "when the updates are inconsistent", ->
|
|
|
|
beforeEach ->
|
2017-06-28 10:38:31 -04:00
|
|
|
@DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(3, null, @content, @version, @updates)
|
2014-03-10 12:03:03 -04:00
|
|
|
@DiffGenerator.buildDiff = sinon.stub().throws(@error = new Error("inconsistent!"))
|
|
|
|
@DiffManager.getDiff @project_id, @doc_id, @fromVersion, @toVersion, @callback
|
|
|
|
|
|
|
|
it "should call the callback with an error", ->
|
|
|
|
@callback
|
|
|
|
.calledWith(@error)
|
|
|
|
.should.equal true
|
|
|
|
|
|
|
|
describe "getDocumentBeforeVersion", ->
|
2016-09-30 06:36:47 -04:00
|
|
|
beforeEach ->
|
|
|
|
@DiffManager._tryGetDocumentBeforeVersion = sinon.stub()
|
|
|
|
@document = "mock-documents"
|
|
|
|
@rewound_updates = "mock-rewound-updates"
|
|
|
|
|
|
|
|
describe "succesfully", ->
|
|
|
|
beforeEach ->
|
|
|
|
@DiffManager._tryGetDocumentBeforeVersion.yields(null, @document, @rewound_updates)
|
|
|
|
@DiffManager.getDocumentBeforeVersion @project_id, @doc_id, @version, @callback
|
|
|
|
|
|
|
|
it "should call _tryGetDocumentBeforeVersion", ->
|
|
|
|
@DiffManager._tryGetDocumentBeforeVersion
|
|
|
|
.calledWith(@project_id, @doc_id, @version)
|
|
|
|
.should.equal true
|
|
|
|
|
|
|
|
it "should call the callback with the response", ->
|
|
|
|
@callback.calledWith(null, @document, @rewound_updates).should.equal true
|
|
|
|
|
|
|
|
describe "with a retry needed", ->
|
|
|
|
beforeEach ->
|
|
|
|
retried = false
|
|
|
|
@DiffManager._tryGetDocumentBeforeVersion = (project_id, doc_id, version, callback) =>
|
|
|
|
if !retried
|
|
|
|
retried = true
|
|
|
|
error = new Error()
|
|
|
|
error.retry = true
|
|
|
|
callback error
|
|
|
|
else
|
|
|
|
callback(null, @document, @rewound_updates)
|
|
|
|
sinon.spy @DiffManager, "_tryGetDocumentBeforeVersion"
|
|
|
|
@DiffManager.getDocumentBeforeVersion @project_id, @doc_id, @version, @callback
|
|
|
|
|
|
|
|
it "should call _tryGetDocumentBeforeVersion twice", ->
|
|
|
|
@DiffManager._tryGetDocumentBeforeVersion
|
|
|
|
.calledTwice
|
|
|
|
.should.equal true
|
|
|
|
|
|
|
|
it "should call the callback with the response", ->
|
|
|
|
@callback.calledWith(null, @document, @rewound_updates).should.equal true
|
|
|
|
|
|
|
|
describe "with a non-retriable error", ->
|
|
|
|
beforeEach ->
|
|
|
|
@error = new Error("oops")
|
|
|
|
@DiffManager._tryGetDocumentBeforeVersion.yields(@error)
|
|
|
|
@DiffManager.getDocumentBeforeVersion @project_id, @doc_id, @version, @callback
|
|
|
|
|
|
|
|
it "should call _tryGetDocumentBeforeVersion once", ->
|
|
|
|
@DiffManager._tryGetDocumentBeforeVersion
|
|
|
|
.calledOnce
|
|
|
|
.should.equal true
|
|
|
|
|
|
|
|
it "should call the callback with the error", ->
|
|
|
|
@callback.calledWith(@error).should.equal true
|
|
|
|
|
|
|
|
describe "when retry limit is matched", ->
|
|
|
|
beforeEach ->
|
|
|
|
@error = new Error("oops")
|
|
|
|
@error.retry = true
|
|
|
|
@DiffManager._tryGetDocumentBeforeVersion.yields(@error)
|
|
|
|
@DiffManager.getDocumentBeforeVersion @project_id, @doc_id, @version, @callback
|
|
|
|
|
|
|
|
it "should call _tryGetDocumentBeforeVersion three times (max retries)", ->
|
|
|
|
@DiffManager._tryGetDocumentBeforeVersion
|
|
|
|
.calledThrice
|
|
|
|
.should.equal true
|
|
|
|
|
|
|
|
it "should call the callback with the error", ->
|
|
|
|
@callback.calledWith(@error).should.equal true
|
|
|
|
|
|
|
|
describe "_tryGetDocumentBeforeVersion", ->
|
2014-03-10 12:03:03 -04:00
|
|
|
beforeEach ->
|
|
|
|
@content = "hello world"
|
|
|
|
# Op versions are the version they were applied to, so doc is always one version
|
|
|
|
# ahead.s
|
|
|
|
@version = 43
|
|
|
|
@updates = [
|
|
|
|
{ op: "mock-4", v: 42, meta: { start_ts: new Date(@to.getTime() + 20)} }
|
|
|
|
{ op: "mock-3", v: 41, meta: { start_ts: new Date(@to.getTime() + 10)} }
|
|
|
|
{ op: "mock-2", v: 40, meta: { start_ts: new Date(@to.getTime() - 10)} }
|
|
|
|
{ op: "mock-1", v: 39, meta: { start_ts: new Date(@to.getTime() - 20)} }
|
|
|
|
]
|
|
|
|
@fromVersion = 39
|
|
|
|
@rewound_content = "rewound-content"
|
|
|
|
@diff = [ u: "mock-diff" ]
|
|
|
|
|
|
|
|
describe "with matching versions", ->
|
|
|
|
beforeEach ->
|
2017-06-28 10:38:31 -04:00
|
|
|
@DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(3, null, @content, @version, @updates)
|
2015-12-04 08:57:15 -05:00
|
|
|
@DiffGenerator.rewindUpdates = sinon.spy (content, updates) =>
|
|
|
|
# the rewindUpdates method reverses the 'updates' array
|
|
|
|
updates.reverse()
|
|
|
|
return @rewound_content
|
|
|
|
@rewindUpdatesWithArgs = @DiffGenerator.rewindUpdates.withArgs(@content, @updates.slice().reverse())
|
2016-09-30 06:36:47 -04:00
|
|
|
@DiffManager._tryGetDocumentBeforeVersion @project_id, @doc_id, @fromVersion, @callback
|
2014-03-10 12:03:03 -04:00
|
|
|
|
|
|
|
it "should get the latest doc and version with all recent updates", ->
|
|
|
|
@DiffManager.getLatestDocAndUpdates
|
2017-06-28 10:38:31 -04:00
|
|
|
.calledWith(@project_id, @doc_id, @fromVersion)
|
2014-03-10 12:03:03 -04:00
|
|
|
.should.equal true
|
|
|
|
|
|
|
|
it "should rewind the diff", ->
|
2015-12-04 08:57:15 -05:00
|
|
|
sinon.assert.calledOnce(@rewindUpdatesWithArgs)
|
2014-03-10 12:03:03 -04:00
|
|
|
|
|
|
|
it "should call the callback with the rewound document and updates", ->
|
|
|
|
@callback.calledWith(null, @rewound_content, @updates).should.equal true
|
|
|
|
|
2014-03-04 09:05:17 -05:00
|
|
|
describe "with mismatching versions", ->
|
|
|
|
beforeEach ->
|
2014-03-05 11:31:38 -05:00
|
|
|
@version = 50
|
2014-03-04 10:27:03 -05:00
|
|
|
@updates = [ { op: "mock-1", v: 40 }, { op: "mock-1", v: 39 } ]
|
2017-06-28 10:38:31 -04:00
|
|
|
@DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(3, null, @content, @version, @updates)
|
2016-09-30 06:36:47 -04:00
|
|
|
@DiffManager._tryGetDocumentBeforeVersion @project_id, @doc_id, @fromVersion, @callback
|
2014-03-04 09:05:17 -05:00
|
|
|
|
2016-09-30 06:36:47 -04:00
|
|
|
it "should call the callback with an error with retry = true set", ->
|
2016-09-30 08:36:31 -04:00
|
|
|
@callback.calledOnce.should.equal true
|
2016-09-30 06:36:47 -04:00
|
|
|
error = @callback.args[0][0]
|
|
|
|
expect(error.retry).to.equal true
|
2014-03-04 09:05:17 -05:00
|
|
|
|
|
|
|
describe "when the updates are inconsistent", ->
|
|
|
|
beforeEach ->
|
2017-06-28 10:38:31 -04:00
|
|
|
@DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(3, null, @content, @version, @updates)
|
2014-03-04 09:05:17 -05:00
|
|
|
@DiffGenerator.rewindUpdates = sinon.stub().throws(@error = new Error("inconsistent!"))
|
2014-03-10 12:03:03 -04:00
|
|
|
@DiffManager.getDocumentBeforeVersion @project_id, @doc_id, @fromVersion, @callback
|
2014-03-04 09:05:17 -05:00
|
|
|
|
|
|
|
it "should call the callback with an error", ->
|
|
|
|
@callback
|
|
|
|
.calledWith(@error)
|
|
|
|
.should.equal true
|