Pārlūkot izejas kodu

Merge branch 'master' into ja-track-changes

Conflicts:
	app/coffee/PersistenceManager.coffee
James Allen 10 gadi atpakaļ
vecāks
revīzija
d36f1a86f1
22 mainītis faili ar 329 papildinājumiem un 685 dzēšanām
  1. 9 9
      services/document-updater/Gruntfile.coffee
  2. 0 8
      services/document-updater/app.coffee
  3. 0 26
      services/document-updater/app/coffee/MongoHealthCheck.coffee
  4. 7 40
      services/document-updater/app/coffee/PersistenceManager.coffee
  5. 0 6
      services/document-updater/app/coffee/mongojs.coffee
  6. 2 3
      services/document-updater/package.json
  7. 79 100
      services/document-updater/test/acceptance/coffee/ApplyingUpdatesToADocTests.coffee
  8. 16 32
      services/document-updater/test/acceptance/coffee/DeletingADocumentTests.coffee
  9. 3 3
      services/document-updater/test/acceptance/coffee/DeletingAProjectTests.coffee
  10. 3 3
      services/document-updater/test/acceptance/coffee/FlushingAProjectTests.coffee
  11. 17 39
      services/document-updater/test/acceptance/coffee/FlushingDocsTests.coffee
  12. 7 17
      services/document-updater/test/acceptance/coffee/GettingADocumentTests.coffee
  13. 19 29
      services/document-updater/test/acceptance/coffee/SettingADocumentTests.coffee
  14. 7 4
      services/document-updater/test/acceptance/coffee/helpers/MockWebApi.coffee
  15. 9 7
      services/document-updater/test/acceptance/scripts/full-test.sh
  16. 0 52
      services/document-updater/test/unit/coffee/MongoHealthCheckTests/MongoHealthCheckTests.coffee
  17. 0 87
      services/document-updater/test/unit/coffee/PersistenceManager/getDocFromWebTests.coffee
  18. 81 23
      services/document-updater/test/unit/coffee/PersistenceManager/getDocTests.coffee
  19. 0 47
      services/document-updater/test/unit/coffee/PersistenceManager/getDocVersionInMongoTests.coffee
  20. 0 88
      services/document-updater/test/unit/coffee/PersistenceManager/setDocInWebTests.coffee
  21. 70 18
      services/document-updater/test/unit/coffee/PersistenceManager/setDocTests.coffee
  22. 0 44
      services/document-updater/test/unit/coffee/PersistenceManager/setDocVersionInMongo.coffee

+ 9 - 9
services/document-updater/Gruntfile.coffee

@@ -1,13 +1,4 @@
 module.exports = (grunt) ->
-	grunt.loadNpmTasks 'grunt-contrib-coffee'
-	grunt.loadNpmTasks 'grunt-contrib-clean'
-	grunt.loadNpmTasks 'grunt-mocha-test'
-	grunt.loadNpmTasks 'grunt-available-tasks'
-	grunt.loadNpmTasks 'grunt-execute'
-	grunt.loadNpmTasks 'grunt-bunyan'
-	grunt.loadNpmTasks 'grunt-forever'
-	grunt.loadNpmTasks 'grunt-shell'
-
 	grunt.initConfig
 		forever:
 			app:
@@ -107,6 +98,15 @@ module.exports = (grunt) ->
 		            		"help"
 		            	]
 
+	grunt.loadNpmTasks 'grunt-contrib-coffee'
+	grunt.loadNpmTasks 'grunt-contrib-clean'
+	grunt.loadNpmTasks 'grunt-mocha-test'
+	grunt.loadNpmTasks 'grunt-available-tasks'
+	grunt.loadNpmTasks 'grunt-execute'
+	grunt.loadNpmTasks 'grunt-bunyan'
+	grunt.loadNpmTasks 'grunt-forever'
+	grunt.loadNpmTasks 'grunt-shell'
+
 	grunt.registerTask 'help', 'Display this help list', 'availabletasks'
 
 	grunt.registerTask 'compile:server', 'Compile the server side coffee script', ['clean:app', 'coffee:app', 'coffee:app_dir']

+ 0 - 8
services/document-updater/app.coffee

@@ -8,7 +8,6 @@ DispatchManager = require('./app/js/DispatchManager')
 Keys = require('./app/js/RedisKeyBuilder')
 Errors = require "./app/js/Errors"
 HttpController = require "./app/js/HttpController"
-MongoHealthCheck = require('./app/js/MongoHealthCheck')
 
 redis = require("redis-sharelatex")
 rclient = redis.createClient(Settings.redis.web)
@@ -60,13 +59,6 @@ app.get '/status', (req, res)->
 	else
 		res.send('document updater is alive')
 
-app.get '/health_check/mongo', (req, res, next) ->
-	MongoHealthCheck.isAlive (error) ->
-		if error?
-			res.send 500, error.message
-		else
-			res.send 200
-
 redisCheck = require("redis-sharelatex").activeHealthCheckRedis(Settings.redis.web)
 app.get "/health_check/redis", (req, res, next)->
 	if redisCheck.isAlive()

+ 0 - 26
services/document-updater/app/coffee/MongoHealthCheck.coffee

@@ -1,26 +0,0 @@
-Settings = require "settings-sharelatex"
-PersistenceManager = require "./PersistenceManager"
-
-module.exports = MongoHealthCheck =
-	isAlive: (_callback = (error) ->) ->
-		# We've seen very occasionally the doc-updater losing its connection to Mongo.
-		# E.g. https://sharelatex.hackpad.com/29th-Aug-2015-0650-0740-fHlw8RL8zuN
-		# It seems that the mongo callbacks never returned.
-		# Mongo is only called in the persistence manager, so we do a read-only
-		# test call, check that it's working, and returns in a reasonable time.
-		callback = (args...) ->
-			_callback(args...)
-			_callback = () ->
-
-		doc_id = Settings.smokeTest?.doc_id
-		if !doc_id?
-			return callback(new Error("No test doc_id configured"))
-
-		PersistenceManager.getDocVersionInMongo doc_id, (error, version) ->
-			return callback(error) if error?
-			callback(null)
-
-		timeout = Settings.smokeTest?.timeout or 10000
-		setTimeout () ->
-			callback(new Error("Mongo did not return in #{timeout}ms"))
-		, timeout

+ 7 - 40
services/document-updater/app/coffee/PersistenceManager.coffee

@@ -2,7 +2,6 @@ request = require "request"
 Settings = require "settings-sharelatex"
 Errors = require "./Errors"
 Metrics = require "./Metrics"
-{db, ObjectId} = require("./mongojs")
 logger = require "logger-sharelatex"
 
 # We have to be quick with HTTP calls because we're holding a lock that
@@ -11,21 +10,7 @@ logger = require "logger-sharelatex"
 MAX_HTTP_REQUEST_LENGTH = 5000 # 5 seconds
 
 module.exports = PersistenceManager =
-	getDoc: (project_id, doc_id, callback = (error, lines, version) ->) ->
-		PersistenceManager.getDocFromWeb project_id, doc_id, (error, lines, track_changes, track_changes_entries) ->
-			return callback(error) if error?
-			PersistenceManager.getDocVersionInMongo doc_id, (error, version) ->
-				return callback(error) if error?
-				callback null, lines, version, track_changes, track_changes_entries
-
-	setDoc: (project_id, doc_id, lines, version, track_changes, track_changes_entries, callback = (error) ->) ->
-		PersistenceManager.setDocInWeb project_id, doc_id, lines, track_changes, track_changes_entries, (error) ->
-			return callback(error) if error?
-			PersistenceManager.setDocVersionInMongo doc_id, version, (error) ->
-				return callback(error) if error?
-				callback()
-
-	getDocFromWeb: (project_id, doc_id, _callback = (error, lines) ->) ->
+	getDoc: (project_id, doc_id, _callback = (error, lines, version, track_changes, track_changes_entries) ->) ->
 		timer = new Metrics.Timer("persistenceManager.getDoc")
 		callback = (args...) ->
 			timer.done()
@@ -50,13 +35,17 @@ module.exports = PersistenceManager =
 					body = JSON.parse body
 				catch e
 					return callback(e)
+				if !body.lines?
+					return callback(new Error("web API response had no doc lines"))
+				if !body.version? or not body.version instanceof Number
+					return callback(new Error("web API response had no valid doc version"))
 				return callback null, body.lines, body.track_changes, body.track_changes_entries
 			else if res.statusCode == 404
 				return callback(new Errors.NotFoundError("doc not not found: #{url}"))
 			else
 				return callback(new Error("error accessing web API: #{url} #{res.statusCode}"))
 
-	setDocInWeb: (project_id, doc_id, lines, track_changes, track_changes_entries, _callback = (error) ->) ->
+	setDoc: (project_id, doc_id, lines, version, track_changes, track_changes_entries, _callback = (error) ->) ->
 		timer = new Metrics.Timer("persistenceManager.setDoc")
 		callback = (args...) ->
 			timer.done()
@@ -70,6 +59,7 @@ module.exports = PersistenceManager =
 				lines: lines
 				track_changes: track_changes
 				track_changes_entries: track_changes_entries
+				version: version
 			headers:
 				"content-type": "application/json"
 			auth:
@@ -86,27 +76,4 @@ module.exports = PersistenceManager =
 				return callback(new Errors.NotFoundError("doc not not found: #{url}"))
 			else
 				return callback(new Error("error accessing web API: #{url} #{res.statusCode}"))
-		
-	getDocVersionInMongo: (doc_id, callback = (error, version) ->) ->
-		db.docOps.find {
-			doc_id: ObjectId(doc_id)
-		}, {
-			version: 1
-		}, (error, docs) ->
-			return callback(error) if error?
-			if docs.length < 1 or !docs[0].version?
-				return callback null, 0
-			else
-				return callback null, docs[0].version
-
-	setDocVersionInMongo: (doc_id, version, callback = (error) ->) ->
-		db.docOps.update {
-			doc_id: ObjectId(doc_id)
-		}, {
-			$set: version: version
-		}, {
-			upsert: true
-		}, callback
-
 
-			

+ 0 - 6
services/document-updater/app/coffee/mongojs.coffee

@@ -1,6 +0,0 @@
-Settings = require "settings-sharelatex"
-mongojs = require "mongojs"
-db = mongojs.connect(Settings.mongo.url, ["docOps"])
-module.exports =
-	db: db
-	ObjectId: mongojs.ObjectId

+ 2 - 3
services/document-updater/package.json

@@ -11,10 +11,9 @@
     "coffee-script": "1.4.0",
     "express": "3.3.4",
     "ioredis": "^2.2.0",
-    "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.0.0",
+    "logger-sharelatex": "git+https://github.com/sharelatex/logger-sharelatex.git#v1.5.1",
     "lynx": "0.0.11",
     "metrics-sharelatex": "git+https://github.com/sharelatex/metrics-sharelatex.git#v1.5.0",
-    "mongojs": "0.9.11",
     "redis-sharelatex": "0.0.9",
     "request": "2.25.0",
     "sandboxed-module": "~0.2.0",
@@ -33,7 +32,7 @@
     "grunt-contrib-clean": "~0.5.0",
     "grunt-contrib-coffee": "~0.10.0",
     "grunt-execute": "~0.1.5",
-    "grunt-forever": "0.4.1",
+    "grunt-forever": "^0.4.7",
     "grunt-mocha-test": "~0.9.0",
     "grunt-shell": "^1.3.0"
   }

+ 79 - 100
services/document-updater/test/acceptance/coffee/ApplyingUpdatesToADocTests.coffee

@@ -3,8 +3,8 @@ chai = require("chai")
 chai.should()
 expect = chai.expect
 async = require "async"
-rclient = require("redis").createClient()
-{db, ObjectId} = require "../../../app/js/mongojs"
+Settings = require('settings-sharelatex')
+rclient = require("redis-sharelatex").createClient(Settings.redis.web)
 
 MockTrackChangesApi = require "./helpers/MockTrackChangesApi"
 MockWebApi = require "./helpers/MockWebApi"
@@ -28,15 +28,10 @@ describe "Applying updates to a doc", ->
 			[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
 			sinon.spy MockWebApi, "getDocument"
 
-			MockWebApi.insertDoc @project_id, @doc_id, lines: @lines
-			db.docOps.insert {
-				doc_id: ObjectId(@doc_id)
-				version: @version
-			}, (error) =>
+			MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
+			DocUpdaterClient.sendUpdate @project_id, @doc_id, @update, (error) ->
 				throw error if error?
-				DocUpdaterClient.sendUpdate @project_id, @doc_id, @update, (error) ->
-					throw error if error?
-					setTimeout done, 200
+				setTimeout done, 200
 
 		after ->
 			MockWebApi.getDocument.restore()
@@ -65,15 +60,13 @@ describe "Applying updates to a doc", ->
 		before (done) ->
 			[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
 
-			MockWebApi.insertDoc @project_id, @doc_id, lines: @lines
-			db.docOps.insert doc_id: ObjectId(@doc_id), version: @version, (error) =>
+			MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
+			DocUpdaterClient.preloadDoc @project_id, @doc_id, (error) =>
 				throw error if error?
-				DocUpdaterClient.preloadDoc @project_id, @doc_id, (error) =>
+				sinon.spy MockWebApi, "getDocument"
+				DocUpdaterClient.sendUpdate @project_id, @doc_id, @update, (error) ->
 					throw error if error?
-					sinon.spy MockWebApi, "getDocument"
-					DocUpdaterClient.sendUpdate @project_id, @doc_id, @update, (error) ->
-						throw error if error?
-						setTimeout done, 200
+					setTimeout done, 200
 
 		after ->
 			MockWebApi.getDocument.restore()
@@ -98,24 +91,22 @@ describe "Applying updates to a doc", ->
 			before (done) ->
 				[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
 				lines = ["", "", ""]
-				MockWebApi.insertDoc @project_id, @doc_id, lines: lines
-				db.docOps.insert doc_id: ObjectId(@doc_id), version: 0, (error) =>
-					throw error if error?
-					@updates = [
-						{ doc_id: @doc_id, v: 0,  op: [i: "h", p: 0 ] }
-						{ doc_id: @doc_id, v: 1,  op: [i: "e", p: 1 ] }
-						{ doc_id: @doc_id, v: 2,  op: [i: "l", p: 2 ] }
-						{ doc_id: @doc_id, v: 3,  op: [i: "l", p: 3 ] }
-						{ doc_id: @doc_id, v: 4,  op: [i: "o", p: 4 ] }
-						{ doc_id: @doc_id, v: 5,  op: [i: " ", p: 5 ] }
-						{ doc_id: @doc_id, v: 6,  op: [i: "w", p: 6 ] }
-						{ doc_id: @doc_id, v: 7,  op: [i: "o", p: 7 ] }
-						{ doc_id: @doc_id, v: 8,  op: [i: "r", p: 8 ] }
-						{ doc_id: @doc_id, v: 9,  op: [i: "l", p: 9 ] }
-						{ doc_id: @doc_id, v: 10, op: [i: "d", p: 10] }
-					]
-					@my_result = ["hello world", "", ""]
-					done()
+				MockWebApi.insertDoc @project_id, @doc_id, {lines: lines, version: 0}
+				@updates = [
+					{ doc_id: @doc_id, v: 0,  op: [i: "h", p: 0 ] }
+					{ doc_id: @doc_id, v: 1,  op: [i: "e", p: 1 ] }
+					{ doc_id: @doc_id, v: 2,  op: [i: "l", p: 2 ] }
+					{ doc_id: @doc_id, v: 3,  op: [i: "l", p: 3 ] }
+					{ doc_id: @doc_id, v: 4,  op: [i: "o", p: 4 ] }
+					{ doc_id: @doc_id, v: 5,  op: [i: " ", p: 5 ] }
+					{ doc_id: @doc_id, v: 6,  op: [i: "w", p: 6 ] }
+					{ doc_id: @doc_id, v: 7,  op: [i: "o", p: 7 ] }
+					{ doc_id: @doc_id, v: 8,  op: [i: "r", p: 8 ] }
+					{ doc_id: @doc_id, v: 9,  op: [i: "l", p: 9 ] }
+					{ doc_id: @doc_id, v: 10, op: [i: "d", p: 10] }
+				]
+				@my_result = ["hello world", "", ""]
+				done()
 
 			it "should be able to continue applying updates when the project has been deleted", (done) ->
 				actions = []
@@ -154,21 +145,17 @@ describe "Applying updates to a doc", ->
 			before (done) ->
 				[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
 				lines = ["", "", ""]
-				MockWebApi.insertDoc @project_id, @doc_id, lines: lines
-				db.docOps.insert doc_id: ObjectId(@doc_id), version: 0, (error) =>
-					throw error if error?
-
-					@updates = [
-						{ doc_id: @doc_id, v: 0, op: [i: "h", p: 0 ] }
-						{ doc_id: @doc_id, v: 1, op: [i: "e", p: 1 ] }
-						{ doc_id: @doc_id, v: 2, op: [i: "l", p: 2 ] }
-						{ doc_id: @doc_id, v: 3, op: [i: "l", p: 3 ] }
-						{ doc_id: @doc_id, v: 4, op: [i: "o", p: 4 ] }
-						{ doc_id: @doc_id, v: 0, op: [i: "world", p: 1 ] }
-					]
-					@my_result = ["hello", "world", ""]
-
-					done()
+				MockWebApi.insertDoc @project_id, @doc_id, {lines: lines, version: 0}
+				@updates = [
+					{ doc_id: @doc_id, v: 0, op: [i: "h", p: 0 ] }
+					{ doc_id: @doc_id, v: 1, op: [i: "e", p: 1 ] }
+					{ doc_id: @doc_id, v: 2, op: [i: "l", p: 2 ] }
+					{ doc_id: @doc_id, v: 3, op: [i: "l", p: 3 ] }
+					{ doc_id: @doc_id, v: 4, op: [i: "o", p: 4 ] }
+					{ doc_id: @doc_id, v: 0, op: [i: "world", p: 1 ] }
+				]
+				@my_result = ["hello", "world", ""]
+				done()
 
 			it "should be able to continue applying updates when the project has been deleted", (done) ->
 				actions = []
@@ -189,12 +176,10 @@ describe "Applying updates to a doc", ->
 	describe "with a broken update", ->
 		before (done) ->
 			[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
-			MockWebApi.insertDoc @project_id, @doc_id, lines: @lines
-			db.docOps.insert doc_id: ObjectId(@doc_id), version: @version, (error) =>
+			MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
+			DocUpdaterClient.sendUpdate @project_id, @doc_id, @undefined, (error) ->
 				throw error if error?
-				DocUpdaterClient.sendUpdate @project_id, @doc_id, @undefined, (error) ->
-					throw error if error?
-					setTimeout done, 200
+				setTimeout done, 200
 
 		it "should not update the doc", (done) ->
 			DocUpdaterClient.getDoc @project_id, @doc_id, (error, res, doc) =>
@@ -213,19 +198,17 @@ describe "Applying updates to a doc", ->
 
 			sinon.spy MockTrackChangesApi, "flushDoc"
 
-			MockWebApi.insertDoc @project_id, @doc_id, lines: @lines
-			db.docOps.insert doc_id: ObjectId(@doc_id), version: 0, (error) =>
+			MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: 0}
+
+			# Send updates in chunks to causes multiple flushes
+			actions = []
+			for i in [0..9]
+				do (i) =>
+					actions.push (cb) =>
+						DocUpdaterClient.sendUpdates @project_id, @doc_id, updates.slice(i*10, (i+1)*10), cb
+			async.series actions, (error) =>
 				throw error if error?
-				
-				# Send updates in chunks to causes multiple flushes
-				actions = []
-				for i in [0..9]
-					do (i) =>
-						actions.push (cb) =>
-							DocUpdaterClient.sendUpdates @project_id, @doc_id, updates.slice(i*10, (i+1)*10), cb
-				async.series actions, (error) =>
-					throw error if error?
-					setTimeout done, 2000
+				setTimeout done, 2000
 
 		after ->
 			MockTrackChangesApi.flushDoc.restore()
@@ -256,41 +239,37 @@ describe "Applying updates to a doc", ->
 	describe "when the sending duplicate ops", ->
 		before (done) ->
 			[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
-			MockWebApi.insertDoc @project_id, @doc_id, lines: @lines
-			db.docOps.insert {
-				doc_id: ObjectId(@doc_id)
-				version: @version
+			MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
+	
+			DocUpdaterClient.subscribeToAppliedOps @messageCallback = sinon.stub()
+
+			# One user delete 'one', the next turns it into 'once'. The second becomes a NOP.
+			DocUpdaterClient.sendUpdate @project_id, @doc_id, {
+				doc: @doc_id
+				op: [{
+					i: "one and a half\n"
+					p: 4
+				}]
+				v: @version
+				meta:
+					source: "ikHceq3yfAdQYzBo4-xZ"
 			}, (error) =>
 				throw error if error?
-				# One user delete 'one', the next turns it into 'once'. The second becomes a NOP.
-				DocUpdaterClient.sendUpdate @project_id, @doc_id, {
-					doc: @doc_id
-					op: [{
-						i: "one and a half\n"
-						p: 4
-					}]
-					v: @version
-					meta:
-						source: "ikHceq3yfAdQYzBo4-xZ"
-				}, (error) =>
-					throw error if error?
-					setTimeout () =>
-						DocUpdaterClient.sendUpdate @project_id, @doc_id, {
-							doc: @doc_id
-							op: [{
-								i: "one and a half\n"
-								p: 4
-							}]
-							v: @version
-							dupIfSource: ["ikHceq3yfAdQYzBo4-xZ"]
-							meta:
-								source: "ikHceq3yfAdQYzBo4-xZ"
-						}, (error) =>
-							throw error if error?
-							setTimeout done, 200
-					, 200
-			
-			DocUpdaterClient.subscribeToAppliedOps @messageCallback = sinon.stub()
+				setTimeout () =>
+					DocUpdaterClient.sendUpdate @project_id, @doc_id, {
+						doc: @doc_id
+						op: [{
+							i: "one and a half\n"
+							p: 4
+						}]
+						v: @version
+						dupIfSource: ["ikHceq3yfAdQYzBo4-xZ"]
+						meta:
+							source: "ikHceq3yfAdQYzBo4-xZ"
+					}, (error) =>
+						throw error if error?
+						setTimeout done, 200
+				, 200
 
 		it "should update the doc", (done) ->
 			DocUpdaterClient.getDoc @project_id, @doc_id, (error, res, doc) =>

+ 16 - 32
services/document-updater/test/acceptance/coffee/DeletingADocumentTests.coffee

@@ -1,7 +1,6 @@
 sinon = require "sinon"
 chai = require("chai")
 chai.should()
-{db, ObjectId} = require "../../../app/js/mongojs"
 
 MockTrackChangesApi = require "./helpers/MockTrackChangesApi"
 MockWebApi = require "./helpers/MockWebApi"
@@ -28,47 +27,32 @@ describe "Deleting a document", ->
 	describe "when the updated doc exists in the doc updater", ->
 		before (done) ->
 			[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
-			sinon.spy MockWebApi, "setDocumentLines"
+			sinon.spy MockWebApi, "setDocument"
 			sinon.spy MockWebApi, "getDocument"
 
-			MockWebApi.insertDoc @project_id, @doc_id, lines: @lines
-			db.docOps.insert {
-				doc_id: ObjectId(@doc_id)
-				version: @version
-			}, (error) =>
+			MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
+			DocUpdaterClient.preloadDoc @project_id, @doc_id, (error) =>
 				throw error if error?
-				DocUpdaterClient.preloadDoc @project_id, @doc_id, (error) =>
+				DocUpdaterClient.sendUpdate @project_id, @doc_id, @update, (error) =>
 					throw error if error?
-					DocUpdaterClient.sendUpdate @project_id, @doc_id, @update, (error) =>
-						throw error if error?
-						setTimeout () =>
-							DocUpdaterClient.deleteDoc @project_id, @doc_id, (error, res, body) =>
-								@statusCode = res.statusCode
-								setTimeout done, 200
-						, 200
+					setTimeout () =>
+						DocUpdaterClient.deleteDoc @project_id, @doc_id, (error, res, body) =>
+							@statusCode = res.statusCode
+							setTimeout done, 200
+					, 200
 
 		after ->
-			MockWebApi.setDocumentLines.restore()
+			MockWebApi.setDocument.restore()
 			MockWebApi.getDocument.restore()
 
 		it "should return a 204 status code", ->
 			@statusCode.should.equal 204
 
-		it "should send the updated document to the web api", ->
-			MockWebApi.setDocumentLines
-				.calledWith(@project_id, @doc_id, @result)
+		it "should send the updated document and version to the web api", ->
+			MockWebApi.setDocument
+				.calledWith(@project_id, @doc_id, @result, @version + 1)
 				.should.equal true
 
-		it "should write the version to mongo", (done) ->
-			db.docOps.find {
-				doc_id: ObjectId(@doc_id)
-			}, {
-				version: 1
-			}, (error, docs) =>
-				doc = docs[0]
-				doc.version.should.equal @version + 1
-				done()
-
 		it "should need to reload the doc if read again", (done) ->
 			MockWebApi.getDocument.called.should.equal.false
 			DocUpdaterClient.getDoc @project_id, @doc_id, (error, res, doc) =>
@@ -86,21 +70,21 @@ describe "Deleting a document", ->
 			MockWebApi.insertDoc @project_id, @doc_id, {
 				lines: @lines
 			}
-			sinon.spy MockWebApi, "setDocumentLines"
+			sinon.spy MockWebApi, "setDocument"
 			sinon.spy MockWebApi, "getDocument"
 			DocUpdaterClient.deleteDoc @project_id, @doc_id, (error, res, body) =>
 				@statusCode = res.statusCode
 				setTimeout done, 200
 
 		after ->
-			MockWebApi.setDocumentLines.restore()
+			MockWebApi.setDocument.restore()
 			MockWebApi.getDocument.restore()
 
 		it "should return a 204 status code", ->
 			@statusCode.should.equal 204
 
 		it "should not need to send the updated document to the web api", ->
-			MockWebApi.setDocumentLines.called.should.equal false
+			MockWebApi.setDocument.called.should.equal false
 
 		it "should need to reload the doc if read again", (done) ->
 			MockWebApi.getDocument.called.should.equal.false

+ 3 - 3
services/document-updater/test/acceptance/coffee/DeletingAProjectTests.coffee

@@ -46,7 +46,7 @@ describe "Deleting a project", ->
 
 	describe "with documents which have been updated", ->
 		before (done) ->
-			sinon.spy MockWebApi, "setDocumentLines"
+			sinon.spy MockWebApi, "setDocument"
 			async.series @docs.map((doc) =>
 				(callback) =>
 					DocUpdaterClient.preloadDoc @project_id, doc.id, (error) =>
@@ -62,14 +62,14 @@ describe "Deleting a project", ->
 				, 200
 
 		after ->
-			MockWebApi.setDocumentLines.restore()
+			MockWebApi.setDocument.restore()
 
 		it "should return a 204 status code", ->
 			@statusCode.should.equal 204
 
 		it "should send each document to the web api", ->
 			for doc in @docs
-				MockWebApi.setDocumentLines
+				MockWebApi.setDocument
 					.calledWith(@project_id, doc.id, doc.updatedLines)
 					.should.equal true
 

+ 3 - 3
services/document-updater/test/acceptance/coffee/FlushingAProjectTests.coffee

@@ -40,7 +40,7 @@ describe "Flushing a project", ->
 
 	describe "with documents which have been updated", ->
 		before (done) ->
-			sinon.spy MockWebApi, "setDocumentLines"
+			sinon.spy MockWebApi, "setDocument"
 
 			async.series @docs.map((doc) =>
 				(callback) =>
@@ -57,14 +57,14 @@ describe "Flushing a project", ->
 				, 200
 
 		after ->
-			MockWebApi.setDocumentLines.restore()
+			MockWebApi.setDocument.restore()
 
 		it "should return a 204 status code", ->
 			@statusCode.should.equal 204
 
 		it "should send each document to the web api", ->
 			for doc in @docs
-				MockWebApi.setDocumentLines
+				MockWebApi.setDocument
 					.calledWith(@project_id, doc.id, doc.updatedLines)
 					.should.equal true
 

+ 17 - 39
services/document-updater/test/acceptance/coffee/FlushingDocsTests.coffee

@@ -3,7 +3,6 @@ chai = require("chai")
 chai.should()
 expect = chai.expect
 async = require "async"
-{db, ObjectId} = require "../../../app/js/mongojs"
 
 MockWebApi = require "./helpers/MockWebApi"
 DocUpdaterClient = require "./helpers/DocUpdaterClient"
@@ -24,53 +23,37 @@ describe "Flushing a doc to Mongo", ->
 	describe "when the updated doc exists in the doc updater", ->
 		before (done) ->
 			[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
-			sinon.spy MockWebApi, "setDocumentLines"
+			sinon.spy MockWebApi, "setDocument"
 
-			MockWebApi.insertDoc @project_id, @doc_id, lines: @lines
-			db.docOps.insert {
-				doc_id: ObjectId(@doc_id)
-				version: @version
-			}, (error) =>
+			MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
+			DocUpdaterClient.sendUpdates @project_id, @doc_id, [@update], (error) =>
 				throw error if error?
-				DocUpdaterClient.sendUpdates @project_id, @doc_id, [@update], (error) =>
-					throw error if error?
-					setTimeout () =>
-						DocUpdaterClient.flushDoc @project_id, @doc_id, done
-					, 200
+				setTimeout () =>
+					DocUpdaterClient.flushDoc @project_id, @doc_id, done
+				, 200
 
 		after ->
-			MockWebApi.setDocumentLines.restore()
+			MockWebApi.setDocument.restore()
 
-		it "should flush the updated doc lines to the web api", ->
-			MockWebApi.setDocumentLines
-				.calledWith(@project_id, @doc_id, @result)
+		it "should flush the updated doc lines and version to the web api", ->
+			MockWebApi.setDocument
+				.calledWith(@project_id, @doc_id, @result, @version + 1)
 				.should.equal true
 
-		it "should store the updated doc version into mongo", (done) ->
-			db.docOps.find {
-				doc_id: ObjectId(@doc_id)
-			}, {
-				version: 1
-			}, (error, docs) =>
-				doc = docs[0]
-				doc.version.should.equal @version + 1
-				done()
-
-
 	describe "when the doc does not exist in the doc updater", ->
 		before (done) ->
 			[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
 			MockWebApi.insertDoc @project_id, @doc_id, {
 				lines: @lines
 			}
-			sinon.spy MockWebApi, "setDocumentLines"
+			sinon.spy MockWebApi, "setDocument"
 			DocUpdaterClient.flushDoc @project_id, @doc_id, done
 
 		after ->
-			MockWebApi.setDocumentLines.restore()
+			MockWebApi.setDocument.restore()
 
 		it "should not flush the doc to the web api", ->
-			MockWebApi.setDocumentLines.called.should.equal false
+			MockWebApi.setDocument.called.should.equal false
 
 	describe "when the web api http request takes a long time", ->
 		before (done) ->
@@ -78,19 +61,14 @@ describe "Flushing a doc to Mongo", ->
 			@timeout = 10000
 			MockWebApi.insertDoc @project_id, @doc_id, {
 				lines: @lines
+				version: @version
 			}
-			sinon.stub MockWebApi, "setDocumentLines", (project_id, doc_id, lines, callback = (error) ->) ->
+			sinon.stub MockWebApi, "setDocument", (project_id, doc_id, lines, version, callback = (error) ->) ->
 				setTimeout callback, 30000
-
-			db.docOps.insert {
-				doc_id: ObjectId(@doc_id)
-				version: @version
-			}, (error) =>
-				throw error if error?
-				DocUpdaterClient.preloadDoc @project_id, @doc_id, done
+			DocUpdaterClient.preloadDoc @project_id, @doc_id, done
 
 		after ->
-			MockWebApi.setDocumentLines.restore()
+			MockWebApi.setDocument.restore()
 		
 		it "should return quickly(ish)", (done) ->
 			start = Date.now()

+ 7 - 17
services/document-updater/test/acceptance/coffee/GettingADocumentTests.coffee

@@ -2,7 +2,6 @@ sinon = require "sinon"
 chai = require("chai")
 chai.should()
 expect = chai.expect
-{db, ObjectId} = require "../../../app/js/mongojs"
 
 MockWebApi = require "./helpers/MockWebApi"
 DocUpdaterClient = require "./helpers/DocUpdaterClient"
@@ -18,13 +17,9 @@ describe "Getting a document", ->
 			[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
 			sinon.spy MockWebApi, "getDocument"
 
-			MockWebApi.insertDoc @project_id, @doc_id, lines: @lines
-			db.docOps.insert {
-				doc_id: ObjectId(@doc_id)
-				version: @version
-			}, (error) =>
-				throw error if error?
-				DocUpdaterClient.getDoc @project_id, @doc_id, (error, res, @returnedDoc) => done()
+			MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
+
+			DocUpdaterClient.getDoc @project_id, @doc_id, (error, res, @returnedDoc) => done()
 
 		after ->
 			MockWebApi.getDocument.restore()
@@ -44,16 +39,11 @@ describe "Getting a document", ->
 		before (done) ->
 			[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
 			
-			MockWebApi.insertDoc @project_id, @doc_id, lines: @lines
-			db.docOps.insert {
-				doc_id: ObjectId(@doc_id)
-				version: @version
-			}, (error) =>
+			MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
+			DocUpdaterClient.preloadDoc @project_id, @doc_id, (error) =>
 				throw error if error?
-				DocUpdaterClient.preloadDoc @project_id, @doc_id, (error) =>
-					throw error if error?
-					sinon.spy MockWebApi, "getDocument"
-					DocUpdaterClient.getDoc @project_id, @doc_id, (error, res, @returnedDoc) =>	done()
+				sinon.spy MockWebApi, "getDocument"
+				DocUpdaterClient.getDoc @project_id, @doc_id, (error, res, @returnedDoc) =>	done()
 
 		after ->
 			MockWebApi.getDocument.restore()

+ 19 - 29
services/document-updater/test/acceptance/coffee/SettingADocumentTests.coffee

@@ -2,8 +2,8 @@ sinon = require "sinon"
 chai = require("chai")
 chai.should()
 expect = require("chai").expect
-{db, ObjectId} = require "../../../app/js/mongojs"
-rclient = require("redis").createClient()
+Settings = require('settings-sharelatex')
+rclient = require("redis-sharelatex").createClient(Settings.redis.web)
 
 MockTrackChangesApi = require "./helpers/MockTrackChangesApi"
 MockWebApi = require "./helpers/MockWebApi"
@@ -26,36 +26,31 @@ describe "Setting a document", ->
 		@user_id = "user-id-123"
 		
 		sinon.spy MockTrackChangesApi, "flushDoc"
-		sinon.spy MockWebApi, "setDocumentLines"
+		sinon.spy MockWebApi, "setDocument"
 	
 	after ->
-		MockWebApi.setDocumentLines.restore()
+		MockWebApi.setDocument.restore()
 		MockTrackChangesApi.flushDoc.restore()
 				
 	describe "when the updated doc exists in the doc updater", ->
 		before (done) ->
 			[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
-			MockWebApi.insertDoc @project_id, @doc_id, lines: @lines
-			db.docOps.insert {
-				doc_id: ObjectId(@doc_id)
-				version: @version
-			}, (error) =>
+			MockWebApi.insertDoc @project_id, @doc_id, lines: @lines, version: @version
+			DocUpdaterClient.preloadDoc @project_id, @doc_id, (error) =>
 				throw error if error?
-				DocUpdaterClient.preloadDoc @project_id, @doc_id, (error) =>
+				DocUpdaterClient.sendUpdate @project_id, @doc_id, @update, (error) =>
 					throw error if error?
-					DocUpdaterClient.sendUpdate @project_id, @doc_id, @update, (error) =>
-						throw error if error?
-						setTimeout () =>
-							DocUpdaterClient.setDocLines @project_id, @doc_id, @newLines, @source, @user_id, (error, res, body) =>
-								@statusCode = res.statusCode
-								done()
-						, 200
+					setTimeout () =>
+						DocUpdaterClient.setDocLines @project_id, @doc_id, @newLines, @source, @user_id, (error, res, body) =>
+							@statusCode = res.statusCode
+							done()
+					, 200
 
 		it "should return a 204 status code", ->
 			@statusCode.should.equal 204
 
-		it "should send the updated doc lines to the web api", ->
-			MockWebApi.setDocumentLines
+		it "should send the updated doc lines and version to the web api", ->
+			MockWebApi.setDocument
 				.calledWith(@project_id, @doc_id, @newLines)
 				.should.equal true
 
@@ -78,21 +73,16 @@ describe "Setting a document", ->
 	describe "when the updated doc does not exist in the doc updater", ->
 		before (done) ->
 			[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
-			MockWebApi.insertDoc @project_id, @doc_id, lines: @lines
-			db.docOps.insert {
-				doc_id: ObjectId(@doc_id)
-				version: @version
-			}, (error) =>
-				throw error if error?
-				DocUpdaterClient.setDocLines @project_id, @doc_id, @newLines, @source, @user_id, (error, res, body) =>
-					@statusCode = res.statusCode
-					setTimeout done, 200
+			MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
+			DocUpdaterClient.setDocLines @project_id, @doc_id, @newLines, @source, @user_id, (error, res, body) =>
+				@statusCode = res.statusCode
+				setTimeout done, 200
 		
 		it "should return a 204 status code", ->
 			@statusCode.should.equal 204
 
 		it "should send the updated doc lines to the web api", ->
-			MockWebApi.setDocumentLines
+			MockWebApi.setDocument
 				.calledWith(@project_id, @doc_id, @newLines)
 				.should.equal true
 		

+ 7 - 4
services/document-updater/test/acceptance/coffee/helpers/MockWebApi.coffee

@@ -7,11 +7,14 @@ module.exports = MockWebApi =
 	clearDocs: () -> @docs = {}
 
 	insertDoc: (project_id, doc_id, doc) ->
+		doc.version ?= 0
+		doc.lines ?= []
 		@docs["#{project_id}:#{doc_id}"] = doc
 
-	setDocumentLines: (project_id, doc_id, lines, callback = (error) ->) ->
-		@docs["#{project_id}:#{doc_id}"] ||= {}
-		@docs["#{project_id}:#{doc_id}"].lines = lines
+	setDocument: (project_id, doc_id, lines, version, callback = (error) ->) ->
+		doc = @docs["#{project_id}:#{doc_id}"] ||= {}
+		doc.lines = lines
+		doc.version = version
 		callback null
 
 	getDocument: (project_id, doc_id, callback = (error, doc) ->) ->
@@ -28,7 +31,7 @@ module.exports = MockWebApi =
 					res.send 404
 
 		app.post "/project/:project_id/doc/:doc_id", express.bodyParser(), (req, res, next) =>
-			MockWebApi.setDocumentLines req.params.project_id, req.params.doc_id, req.body.lines, (error) ->
+			MockWebApi.setDocument req.params.project_id, req.params.doc_id, req.body.lines, req.body.version, (error) ->
 				if error?
 					res.send 500
 				else

+ 9 - 7
services/document-updater/test/acceptance/scripts/full-test.sh

@@ -1,20 +1,22 @@
 #! /usr/bin/env bash
 
+npm rebuild
+
 echo ">> Starting server..."
 
-grunt execute:app >> /dev/null &
-_pid="$!"
+grunt --no-color forever:app:start
 
-echo ">> Server started with pid: $_pid"
+echo ">> Server started"
 
-sleep 20
+sleep 5
 
 echo ">> Running acceptance tests..."
-grunt mochaTest:acceptance
+grunt --no-color test:acceptance
 _test_exit_code=$?
 
-echo ">> Killing server (pid: $_pid)"
-kill -1 "$_pid"
+echo ">> Killing server"
+
+grunt --no-color forever:app:stop
 
 echo ">> Done"
 

+ 0 - 52
services/document-updater/test/unit/coffee/MongoHealthCheckTests/MongoHealthCheckTests.coffee

@@ -1,52 +0,0 @@
-SandboxedModule = require('sandboxed-module')
-sinon = require('sinon')
-require('chai').should()
-modulePath = require('path').join __dirname, '../../../../app/js/MongoHealthCheck'
-
-describe "MongoHealthCheck", ->
-	beforeEach ->
-		@MongoHealthCheck = SandboxedModule.require modulePath, requires:
-			"settings-sharelatex": @Settings = {}
-			"./PersistenceManager": @PersistenceManager = {}
-		@doc_id = "mock-doc-id"
-		@callback = sinon.stub()
-
-	describe "isAlive", ->
-		describe "with no configured doc_id", ->
-			beforeEach ->
-				@MongoHealthCheck.isAlive @callback
-			
-			it "should call the call the callback with an error", ->
-				@callback.calledOnce.should.equal true
-				error = @callback.args[0][0]
-				error.message.should.equal "No test doc_id configured"
-			
-		describe "when mongo returns within the timeout", ->
-			beforeEach ->
-				@Settings.smokeTest =
-					doc_id: @doc_id
-				@PersistenceManager.getDocVersionInMongo = sinon.stub().callsArg(1)
-				@MongoHealthCheck.isAlive @callback
-			
-			it "should call PersistenceManager.getDocVersionInMongo", ->
-				@PersistenceManager.getDocVersionInMongo
-					.calledWith(@doc_id)
-					.should.equal true
-			
-			it "should call the call the callback without an error", ->
-				@callback.calledOnce.should.equal true
-				@callback.calledWith(null).should.equal true
-			
-		describe "when mongo does not return within the timeout", ->
-			beforeEach (done) ->
-				@Settings.smokeTest =
-					doc_id: @doc_id
-					timeout: 50
-				@PersistenceManager.getDocVersionInMongo = (doc_id, callback) ->
-					setTimeout callback, 100
-				@MongoHealthCheck.isAlive (@error) =>
-					done()
-			
-			it "should call the call the callback with an error", ->
-				@error.message.should.equal "Mongo did not return in 50ms"
-			

+ 0 - 87
services/document-updater/test/unit/coffee/PersistenceManager/getDocFromWebTests.coffee

@@ -1,87 +0,0 @@
-sinon = require('sinon')
-chai = require('chai')
-should = chai.should()
-modulePath = "../../../../app/js/PersistenceManager.js"
-SandboxedModule = require('sandboxed-module')
-Errors = require "../../../../app/js/Errors"
-
-describe "PersistenceManager.getDocFromWeb", ->
-	beforeEach ->
-		@PersistenceManager = SandboxedModule.require modulePath, requires:
-			"request": @request = sinon.stub()
-			"settings-sharelatex": @Settings = {}
-			"./Metrics": @Metrics =
-				Timer: class Timer
-					done: sinon.stub()
-			"logger-sharelatex": @logger = {log: sinon.stub(), err: sinon.stub()}
-		@project_id = "project-id-123"
-		@doc_id = "doc-id-123"
-		@lines = ["one", "two", "three"]
-		@callback = sinon.stub()
-		@Settings.apis =
-			web:
-				url: @url = "www.example.com"
-				user: @user = "sharelatex"
-				pass: @pass = "password"
-
-	describe "with a successful response from the web api", ->
-		beforeEach ->
-			@request.callsArgWith(1, null, {statusCode: 200}, JSON.stringify(lines: @lines))
-			@PersistenceManager.getDocFromWeb(@project_id, @doc_id, @callback)
-
-		it "should call the web api", ->
-			@request
-				.calledWith({
-					url: "#{@url}/project/#{@project_id}/doc/#{@doc_id}"
-					method: "GET"
-					headers:
-						"accept": "application/json"
-					auth:
-						user: @user
-						pass: @pass
-						sendImmediately: true
-					jar: false
-					timeout: 5000
-				})
-				.should.equal true
-
-		it "should call the callback with the doc lines", ->
-			@callback.calledWith(null, @lines).should.equal true
-
-		it "should time the execution", ->
-			@Metrics.Timer::done.called.should.equal true
-
-	describe "when request returns an error", ->
-		beforeEach ->
-			@request.callsArgWith(1, @error = new Error("oops"), null, null)
-			@PersistenceManager.getDocFromWeb(@project_id, @doc_id, @callback)
-
-		it "should return the error", ->
-			@callback.calledWith(@error).should.equal true
-
-		it "should time the execution", ->
-			@Metrics.Timer::done.called.should.equal true
-
-	describe "when the request returns 404", ->
-		beforeEach ->
-			@request.callsArgWith(1, null, {statusCode: 404}, "")
-			@PersistenceManager.getDocFromWeb(@project_id, @doc_id, @callback)
-			
-		it "should return a NotFoundError", ->
-			@callback.calledWith(new Errors.NotFoundError("not found")).should.equal true
-
-		it "should time the execution", ->
-			@Metrics.Timer::done.called.should.equal true
-
-	describe "when the request returns an error status code", ->
-		beforeEach ->
-			@request.callsArgWith(1, null, {statusCode: 500}, "")
-			@PersistenceManager.getDocFromWeb(@project_id, @doc_id, @callback)
-			
-		it "should return an error", ->
-			@callback.calledWith(new Error("web api error")).should.equal true
-
-		it "should time the execution", ->
-			@Metrics.Timer::done.called.should.equal true
-		
-		

+ 81 - 23
services/document-updater/test/unit/coffee/PersistenceManager/getDocTests.coffee

@@ -3,7 +3,7 @@ chai = require('chai')
 should = chai.should()
 modulePath = "../../../../app/js/PersistenceManager.js"
 SandboxedModule = require('sandboxed-module')
-{ObjectId} = require("mongojs")
+Errors = require "../../../../app/js/Errors"
 
 describe "PersistenceManager.getDoc", ->
 	beforeEach ->
@@ -13,34 +13,92 @@ describe "PersistenceManager.getDoc", ->
 			"./Metrics": @Metrics =
 				Timer: class Timer
 					done: sinon.stub()
-			"logger-sharelatex": @logger = {warn: sinon.stub()}
-			"./mongojs":
-				db: @db = { docOps: {} }
-				ObjectId: ObjectId
-
-		@project_id = ObjectId().toString()
-		@doc_id = ObjectId().toString()
-		@callback = sinon.stub()
-		@lines = ["mock", "doc", "lines"]
+			"logger-sharelatex": @logger = {log: sinon.stub(), err: sinon.stub()}
+		@project_id = "project-id-123"
+		@doc_id = "doc-id-123"
+		@lines = ["one", "two", "three"]
 		@version = 42
+		@callback = sinon.stub()
+		@Settings.apis =
+			web:
+				url: @url = "www.example.com"
+				user: @user = "sharelatex"
+				pass: @pass = "password"
 
-	describe "successfully", ->
+	describe "with a successful response from the web api", ->
 		beforeEach ->
-			@PersistenceManager.getDocFromWeb = sinon.stub().callsArgWith(2, null, @lines)
-			@PersistenceManager.getDocVersionInMongo = sinon.stub().callsArgWith(1, null, @version)
-			@PersistenceManager.getDoc @project_id, @doc_id, @callback
+			@request.callsArgWith(1, null, {statusCode: 200}, JSON.stringify(lines: @lines, version: @version))
+			@PersistenceManager.getDoc(@project_id, @doc_id, @callback)
 
-		it "should look up the doc in the web api", ->
-			@PersistenceManager.getDocFromWeb
-				.calledWith(@project_id, @doc_id)
+		it "should call the web api", ->
+			@request
+				.calledWith({
+					url: "#{@url}/project/#{@project_id}/doc/#{@doc_id}"
+					method: "GET"
+					headers:
+						"accept": "application/json"
+					auth:
+						user: @user
+						pass: @pass
+						sendImmediately: true
+					jar: false
+					timeout: 5000
+				})
 				.should.equal true
 
-		it "should look up the version in Mongo", ->
-			@PersistenceManager.getDocVersionInMongo
-				.calledWith(@doc_id)
-				.should.equal true
-
-		it "should call the callback with the lines and version", ->
+		it "should call the callback with the doc lines and version", ->
 			@callback.calledWith(null, @lines, @version).should.equal true
 
+		it "should time the execution", ->
+			@Metrics.Timer::done.called.should.equal true
+
+	describe "when request returns an error", ->
+		beforeEach ->
+			@request.callsArgWith(1, @error = new Error("oops"), null, null)
+			@PersistenceManager.getDoc(@project_id, @doc_id, @callback)
+
+		it "should return the error", ->
+			@callback.calledWith(@error).should.equal true
+
+		it "should time the execution", ->
+			@Metrics.Timer::done.called.should.equal true
+
+	describe "when the request returns 404", ->
+		beforeEach ->
+			@request.callsArgWith(1, null, {statusCode: 404}, "")
+			@PersistenceManager.getDoc(@project_id, @doc_id, @callback)
+			
+		it "should return a NotFoundError", ->
+			@callback.calledWith(new Errors.NotFoundError("not found")).should.equal true
+
+		it "should time the execution", ->
+			@Metrics.Timer::done.called.should.equal true
+
+	describe "when the request returns an error status code", ->
+		beforeEach ->
+			@request.callsArgWith(1, null, {statusCode: 500}, "")
+			@PersistenceManager.getDoc(@project_id, @doc_id, @callback)
+			
+		it "should return an error", ->
+			@callback.calledWith(new Error("web api error")).should.equal true
+
+		it "should time the execution", ->
+			@Metrics.Timer::done.called.should.equal true
+
+	describe "when request returns an doc without lines", ->
+		beforeEach ->
+			@request.callsArgWith(1, null, {statusCode: 200}, JSON.stringify(version: @version))
+			@PersistenceManager.getDoc(@project_id, @doc_id, @callback)
+
+		it "should return and error", ->
+			@callback.calledWith(new Error("web API response had no doc lines")).should.equal true
+
+	describe "when request returns an doc without a version", ->
+		beforeEach ->
+			@request.callsArgWith(1, null, {statusCode: 200}, JSON.stringify(lines: @lines))
+			@PersistenceManager.getDoc(@project_id, @doc_id, @callback)
+
+		it "should return and error", ->
+			@callback.calledWith(new Error("web API response had no valid doc version")).should.equal true
 
+		

+ 0 - 47
services/document-updater/test/unit/coffee/PersistenceManager/getDocVersionInMongoTests.coffee

@@ -1,47 +0,0 @@
-sinon = require('sinon')
-chai = require('chai')
-should = chai.should()
-modulePath = "../../../../app/js/PersistenceManager.js"
-SandboxedModule = require('sandboxed-module')
-Errors = require "../../../../app/js/Errors"
-{ObjectId} = require("mongojs")
-
-describe "PersistenceManager.getDocVersionInMongo", ->
-	beforeEach ->
-		@PersistenceManager = SandboxedModule.require modulePath, requires:
-			"request": @request = sinon.stub()
-			"settings-sharelatex": @Settings = {}
-			"./Metrics": @Metrics =
-				Timer: class Timer
-					done: sinon.stub()
-			"./mongojs":
-				db: @db = { docOps: {} }
-				ObjectId: ObjectId
-			"logger-sharelatex": @logger = {log: sinon.stub(), err: sinon.stub()}
-
-		@doc_id = ObjectId().toString()
-		@callback = sinon.stub()
-
-	describe "getDocVersionInMongo", ->
-		describe "when the doc exists", ->
-			beforeEach ->
-				@doc =
-					version: @version = 42
-				@db.docOps.find = sinon.stub().callsArgWith(2, null, [@doc])
-				@PersistenceManager.getDocVersionInMongo @doc_id, @callback
-
-			it "should look for the doc in the database", ->
-				@db.docOps.find
-					.calledWith({ doc_id: ObjectId(@doc_id) }, {version: 1})
-					.should.equal true
-
-			it "should call the callback with the version", ->
-				@callback.calledWith(null, @version).should.equal true
-
-		describe "when the doc doesn't exist", ->
-			beforeEach ->
-				@db.docOps.find = sinon.stub().callsArgWith(2, null, [])
-				@PersistenceManager.getDocVersionInMongo @doc_id, @callback
-
-			it "should call the callback with 0", ->
-				@callback.calledWith(null, 0).should.equal true

+ 0 - 88
services/document-updater/test/unit/coffee/PersistenceManager/setDocInWebTests.coffee

@@ -1,88 +0,0 @@
-sinon = require('sinon')
-chai = require('chai')
-should = chai.should()
-modulePath = "../../../../app/js/PersistenceManager.js"
-SandboxedModule = require('sandboxed-module')
-Errors = require "../../../../app/js/Errors"
-
-describe "PersistenceManager.setDocInWeb", ->
-	beforeEach ->
-		@PersistenceManager = SandboxedModule.require modulePath, requires:
-			"request": @request = sinon.stub()
-			"settings-sharelatex": @Settings = {}
-			"./Metrics": @Metrics =
-				Timer: class Timer
-					done: sinon.stub()
-			"logger-sharelatex": @logger = {log: sinon.stub(), err: sinon.stub()}
-		@project_id = "project-id-123"
-		@doc_id = "doc-id-123"
-		@lines = ["one", "two", "three"]
-		@callback = sinon.stub()
-		@Settings.apis =
-			web:
-				url: @url = "www.example.com"
-				user: @user = "sharelatex"
-				pass: @pass = "password"
-
-	describe "with a successful response from the web api", ->
-		beforeEach ->
-			@request.callsArgWith(1, null, {statusCode: 200}, JSON.stringify(lines: @lines, version: @version))
-			@PersistenceManager.setDocInWeb(@project_id, @doc_id, @lines, @callback)
-
-		it "should call the web api", ->
-			@request
-				.calledWith({
-					url: "#{@url}/project/#{@project_id}/doc/#{@doc_id}"
-					body: JSON.stringify
-						lines: @lines
-					method: "POST"
-					headers:
-						"content-type": "application/json"
-					auth:
-						user: @user
-						pass: @pass
-						sendImmediately: true
-					jar: false
-					timeout: 5000
-				})
-				.should.equal true
-
-		it "should call the callback without error", ->
-			@callback.calledWith(null).should.equal true
-
-		it "should time the execution", ->
-			@Metrics.Timer::done.called.should.equal true
-
-	describe "when request returns an error", ->
-		beforeEach ->
-			@request.callsArgWith(1, @error = new Error("oops"), null, null)
-			@PersistenceManager.setDocInWeb(@project_id, @doc_id, @lines, @callback)
-
-		it "should return the error", ->
-			@callback.calledWith(@error).should.equal true
-
-		it "should time the execution", ->
-			@Metrics.Timer::done.called.should.equal true
-
-	describe "when the request returns 404", ->
-		beforeEach ->
-			@request.callsArgWith(1, null, {statusCode: 404}, "")
-			@PersistenceManager.setDocInWeb(@project_id, @doc_id, @lines, @callback)
-			
-		it "should return a NotFoundError", ->
-			@callback.calledWith(new Errors.NotFoundError("not found")).should.equal true
-
-		it "should time the execution", ->
-			@Metrics.Timer::done.called.should.equal true
-
-	describe "when the request returns an error status code", ->
-		beforeEach ->
-			@request.callsArgWith(1, null, {statusCode: 500}, "")
-			@PersistenceManager.setDocInWeb(@project_id, @doc_id, @lines, @callback)
-			
-		it "should return an error", ->
-			@callback.calledWith(new Error("web api error")).should.equal true
-
-		it "should time the execution", ->
-			@Metrics.Timer::done.called.should.equal true
-

+ 70 - 18
services/document-updater/test/unit/coffee/PersistenceManager/setDocTests.coffee

@@ -3,6 +3,7 @@ chai = require('chai')
 should = chai.should()
 modulePath = "../../../../app/js/PersistenceManager.js"
 SandboxedModule = require('sandboxed-module')
+Errors = require "../../../../app/js/Errors"
 
 describe "PersistenceManager.setDoc", ->
 	beforeEach ->
@@ -12,27 +13,78 @@ describe "PersistenceManager.setDoc", ->
 			"./Metrics": @Metrics =
 				Timer: class Timer
 					done: sinon.stub()
-			"logger-sharelatex": @logger = {warn: sinon.stub()}
-
-		@project_id = "mock-project-id"
-		@doc_id = "mock-doc-id"
+			"logger-sharelatex": @logger = {log: sinon.stub(), err: sinon.stub()}
+		@project_id = "project-id-123"
+		@doc_id = "doc-id-123"
+		@lines = ["one", "two", "three"]
+		@version = 42
 		@callback = sinon.stub()
-		@lines = ["mock", "doc", "lines"]
+		@Settings.apis =
+			web:
+				url: @url = "www.example.com"
+				user: @user = "sharelatex"
+				pass: @pass = "password"
+
+	describe "with a successful response from the web api", ->
+		beforeEach ->
+			@request.callsArgWith(1, null, {statusCode: 200}, JSON.stringify(lines: @lines, version: @version))
+			@PersistenceManager.setDoc(@project_id, @doc_id, @lines, @version, @callback)
+
+		it "should call the web api", ->
+			@request
+				.calledWith({
+					url: "#{@url}/project/#{@project_id}/doc/#{@doc_id}"
+					body: JSON.stringify
+						lines: @lines
+						version: @version
+					method: "POST"
+					headers:
+						"content-type": "application/json"
+					auth:
+						user: @user
+						pass: @pass
+						sendImmediately: true
+					jar: false
+					timeout: 5000
+				})
+				.should.equal true
+
+		it "should call the callback without error", ->
+			@callback.calledWith(null).should.equal true
+
+		it "should time the execution", ->
+			@Metrics.Timer::done.called.should.equal true
+
+	describe "when request returns an error", ->
+		beforeEach ->
+			@request.callsArgWith(1, @error = new Error("oops"), null, null)
+			@PersistenceManager.setDoc(@project_id, @doc_id, @lines, @version, @callback)
+
+		it "should return the error", ->
+			@callback.calledWith(@error).should.equal true
+
+		it "should time the execution", ->
+			@Metrics.Timer::done.called.should.equal true
 
-		@PersistenceManager.setDocInWeb = sinon.stub().callsArg(3)
-		@PersistenceManager.setDocVersionInMongo = sinon.stub().callsArg(2)
+	describe "when the request returns 404", ->
+		beforeEach ->
+			@request.callsArgWith(1, null, {statusCode: 404}, "")
+			@PersistenceManager.setDoc(@project_id, @doc_id, @lines, @version, @callback)
+			
+		it "should return a NotFoundError", ->
+			@callback.calledWith(new Errors.NotFoundError("not found")).should.equal true
 
-		@PersistenceManager.setDoc @project_id, @doc_id, @lines, @version, @callback
+		it "should time the execution", ->
+			@Metrics.Timer::done.called.should.equal true
 
-	it "should set the doc in the web api", ->
-		@PersistenceManager.setDocInWeb
-			.calledWith(@project_id, @doc_id, @lines)
-			.should.equal true
+	describe "when the request returns an error status code", ->
+		beforeEach ->
+			@request.callsArgWith(1, null, {statusCode: 500}, "")
+			@PersistenceManager.setDoc(@project_id, @doc_id, @lines, @version, @callback)
+			
+		it "should return an error", ->
+			@callback.calledWith(new Error("web api error")).should.equal true
 
-	it "should set the doc version in mongo", ->
-		@PersistenceManager.setDocVersionInMongo
-			.calledWith(@doc_id, @version)
-			.should.equal true
+		it "should time the execution", ->
+			@Metrics.Timer::done.called.should.equal true
 
-	it "should call the callback", ->
-		@callback.called.should.equal true

+ 0 - 44
services/document-updater/test/unit/coffee/PersistenceManager/setDocVersionInMongo.coffee

@@ -1,44 +0,0 @@
-sinon = require('sinon')
-chai = require('chai')
-should = chai.should()
-modulePath = "../../../../app/js/PersistenceManager.js"
-SandboxedModule = require('sandboxed-module')
-Errors = require "../../../../app/js/Errors"
-{ObjectId} = require("mongojs")
-
-describe "PersistenceManager.getDocVersionInMongo", ->
-	beforeEach ->
-		@PersistenceManager = SandboxedModule.require modulePath, requires:
-			"request": @request = sinon.stub()
-			"settings-sharelatex": @Settings = {}
-			"./Metrics": @Metrics =
-				Timer: class Timer
-					done: sinon.stub()
-			"./mongojs":
-				db: @db = { docOps: {} }
-				ObjectId: ObjectId
-			"logger-sharelatex": @logger = {log: sinon.stub(), err: sinon.stub()}
-
-		@doc_id = ObjectId().toString()
-		@callback = sinon.stub()
-
-	describe "setDocVersionInMongo", ->
-		beforeEach ->
-			@version = 42
-			@db.docOps.update = sinon.stub().callsArg(3)
-			@PersistenceManager.setDocVersionInMongo @doc_id, @version, @callback
-
-		it "should update the doc version", ->
-			@db.docOps.update
-				.calledWith({
-					doc_id: ObjectId(@doc_id)
-				}, {
-					$set:
-						version: @version
-				}, {
-					upsert: true 
-				})
-				.should.equal true
-
-		it "should call the callback", ->
-			@callback.called.should.equal true