James Allen 12 лет назад
Родитель
Сommit
8b71d222d4

+ 34 - 7
services/track-changes/app/coffee/DiffManager.coffee

@@ -1,8 +1,35 @@
+HistoryManager = require "./HistoryManager"
+DocumentUpdaterManager = require "./DocumentUpdaterManager"
+MongoManager = require "./MongoManager"
+DiffGenerator = require "./DiffGenerator"
+
 module.exports = DiffManager =
 module.exports = DiffManager =
-	getDiff: (doc_id, fromDate, toDate, callback = (error, diff) ->) ->
-		# Flush diff
-		# Get doc content and version
-		# Get updates from Mongo
-		# Check version matches
-		# Build diff
-		# Return diff
+	getLatestDocAndUpdates: (project_id, doc_id, fromDate, toDate, callback = (error, lines, version, updates) ->) ->
+		HistoryManager.processUncompressedUpdatesWithLock doc_id, (error) ->
+			return callback(error) if error?
+			DocumentUpdaterManager.getDocument project_id, doc_id, (error, lines, version) ->
+				return callback(error) if error?
+				MongoManager.getUpdatesBetweenDates doc_id, fromDate, toDate, (error, updates) ->
+					return callback(error) if error?
+					callback(null, lines, version, updates)
+	
+	getDiff: (project_id, doc_id, fromDate, toDate, callback = (error, diff) ->) ->
+		DiffManager.getLatestDocAndUpdates project_id, doc_id, fromDate, null, (error, lines, version, updates) ->
+			return callback(error) if error?
+			lastUpdate = updates[updates.length - 1]
+			if lastUpdate? and lastUpdate.v != version
+				return callback new Error("latest update version, #{lastUpdate.v}, does not match doc version, #{version}")
+			
+
+			updatesToApply = []
+			for update in updates
+				if update.meta.end_ts <= toDate
+					updatesToApply.push update
+
+			try
+				startingContent = DiffGenerator.rewindUpdates lines.join("\n"), updates
+				diff = DiffGenerator.buildDiff startingContent, updatesToApply
+			catch e
+				return callback(e)
+			
+			callback(null, diff)

+ 7 - 5
services/track-changes/app/coffee/MongoManager.coffee

@@ -40,12 +40,14 @@ module.exports = MongoManager =
 		}, callback
 		}, callback
 
 
 	getUpdatesBetweenDates:(doc_id, fromDate, toDate, callback = (error, updates) ->) ->
 	getUpdatesBetweenDates:(doc_id, fromDate, toDate, callback = (error, updates) ->) ->
+		query = 
+			doc_id: ObjectId(doc_id.toString())
+		if fromDate?
+			query["meta.start_ts"] = { $gte: fromDate }
+		if toDate?
+			query["meta.end_ts"] = { $lte: toDate }
 		db.docHistory
 		db.docHistory
-			.find({
-				doc_id: ObjectId(doc_id.toString())
-				"meta.start_ts" : { $gte: fromDate }
-				"meta.end_ts"   : { $lte: toDate }
-			})
+			.find( query )
 			.sort( "meta.end_ts": -1 )
 			.sort( "meta.end_ts": -1 )
 			.toArray callback
 			.toArray callback
 
 

+ 110 - 0
services/track-changes/test/unit/coffee/DiffManager/DiffManagerTests.coffee

@@ -0,0 +1,110 @@
+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:
+			"logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() }
+			"./HistoryManager": @HistoryManager = {}
+			"./DocumentUpdaterManager": @DocumentUpdaterManager = {}
+			"./MongoManager": @MongoManager = {}
+			"./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 ->
+			@lines = [ "hello", "world" ]
+			@version = 42
+			@updates = [ "mock-update-1", "mock-update-2" ]
+
+			@HistoryManager.processUncompressedUpdatesWithLock = sinon.stub().callsArg(1)
+			@DocumentUpdaterManager.getDocument = sinon.stub().callsArgWith(2, null, @lines, @version)
+			@MongoManager.getUpdatesBetweenDates = sinon.stub().callsArgWith(3, null, @updates)
+			@DiffManager.getLatestDocAndUpdates @project_id, @doc_id, @from, @to, @callback
+
+		it "should ensure the latest updates have been compressed", ->
+			@HistoryManager.processUncompressedUpdatesWithLock
+				.calledWith(@doc_id)
+				.should.equal true
+
+		it "should get the latest version of the doc", ->
+			@DocumentUpdaterManager.getDocument
+				.calledWith(@project_id, @doc_id)
+				.should.equal true
+
+		it "should get the requested updates from Mongo", ->
+			@MongoManager.getUpdatesBetweenDates
+				.calledWith(@doc_id, @from, @to)
+				.should.equal true
+
+		it "should call the callback with the lines, version and updates", ->
+			@callback.calledWith(null, @lines, @version, @updates).should.equal true
+
+	describe "getDiff", ->
+		beforeEach ->
+			@lines = [ "hello", "world" ]
+			@version = 42
+			@updates = [
+				{ op: "mock-1", v: 41, meta: { end_ts: new Date(@to.getTime() - 10)} }
+				{ op: "mock-2", v: 42, meta: { end_ts: new Date(@to.getTime() + 10)} }
+			]
+			@diffed_updates = @updates.slice(0,1)
+			@rewound_content = "rewound-content"
+			@diff = [ u: "mock-diff" ]
+			
+		describe "with matching versions", ->
+			beforeEach ->
+				@DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(4, null, @lines, @version, @updates)
+				@DiffGenerator.rewindUpdates = sinon.stub().returns(@rewound_content)
+				@DiffGenerator.buildDiff = sinon.stub().returns(@diff)
+				@DiffManager.getDiff @project_id, @doc_id, @from, @to, @callback
+
+			it "should get the latest doc and version with all recent updates", ->
+				@DiffManager.getLatestDocAndUpdates
+					.calledWith(@project_id, @doc_id, @from, null)
+					.should.equal true
+
+			it "should rewind the diff", ->
+				@DiffGenerator.rewindUpdates
+					.calledWith(@lines.join("\n"), @updates)
+					.should.equal true
+
+			it "should generate the diff", ->
+				@DiffGenerator.buildDiff
+					.calledWith(@rewound_content, @diffed_updates)
+					.should.equal true
+
+			it "should call the callback with the diff", ->
+				@callback.calledWith(null, @diff).should.equal true
+
+		describe "with mismatching versions", ->
+			beforeEach ->
+				@version = 42
+				@updates = [ { op: "mock-1", v: 39 }, { op: "mock-1", v: 40 } ]
+				@DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(4, null, @lines, @version, @updates)
+				@DiffManager.getDiff @project_id, @doc_id, @from, @to, @callback
+
+			it "should call the callback with an error", ->
+				@callback
+					.calledWith(new Error("latest update version, 40, does not match doc version, 42"))
+					.should.equal true
+
+		describe "when the updates are inconsistent", ->
+			beforeEach ->
+				@DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(4, null, @lines, @version, @updates)
+				@DiffGenerator.rewindUpdates = sinon.stub().throws(@error = new Error("inconsistent!"))
+				@DiffManager.getDiff @project_id, @doc_id, @from, @to, @callback
+
+			it "should call the callback with an error", ->
+				@callback
+					.calledWith(@error)
+					.should.equal true
+

+ 33 - 15
services/track-changes/test/unit/coffee/MongoManager/MongoManagerTests.coffee

@@ -143,21 +143,39 @@ describe "MongoManager", ->
 			@from = new Date(Date.now())
 			@from = new Date(Date.now())
 			@to = new Date(Date.now() + 100000)
 			@to = new Date(Date.now() + 100000)
 
 
-			@MongoManager.getUpdatesBetweenDates @doc_id, @from, @to, @callback
+		describe "with a toDate", ->
+			beforeEach ->
+				@MongoManager.getUpdatesBetweenDates @doc_id, @from, @to, @callback
+
+			it "should find the all updates between the to and from date", ->
+				@db.docHistory.find
+					.calledWith({
+						doc_id: ObjectId(@doc_id)
+						"meta.start_ts": { $gte: @from }
+						"meta.end_ts": { $lte: @to }
+					})
+					.should.equal true
 
 
-		it "should find the updates for the doc", ->
-			@db.docHistory.find
-				.calledWith({
-					doc_id: ObjectId(@doc_id)
-					"meta.start_ts": { $gte: @from }
-					"meta.end_ts": { $lte: @to }
-				})
-				.should.equal true
+			it "should sort in descending timestamp order", ->
+				@db.docHistory.sort
+					.calledWith("meta.end_ts": -1)
+					.should.equal true
 
 
-		it "should sort in descending timestamp order", ->
-			@db.docHistory.sort
-				.calledWith("meta.end_ts": -1)
-				.should.equal true
+			it "should call the call back with the updates", ->
+				@callback.calledWith(null, @updates).should.equal true
+
+		describe "without a todo date", ->
+			beforeEach ->
+				@MongoManager.getUpdatesBetweenDates @doc_id, @from, null, @callback
+
+			it "should find the all updates after the from date", ->
+				@db.docHistory.find
+					.calledWith({
+						doc_id: ObjectId(@doc_id)
+						"meta.start_ts": { $gte: @from }
+					})
+					.should.equal true
+
+			it "should call the call back with the updates", ->
+				@callback.calledWith(null, @updates).should.equal true
 
 
-		it "should call the call back with the updates", ->
-			@callback.calledWith(null, @updates).should.equal true