|
|
@@ -0,0 +1,320 @@
|
|
|
+sinon = require "sinon"
|
|
|
+chai = require("chai")
|
|
|
+chai.should()
|
|
|
+expect = chai.expect
|
|
|
+async = require "async"
|
|
|
+Settings = require('settings-sharelatex')
|
|
|
+rclient_old = require("redis-sharelatex").createClient(Settings.redis.project_history)
|
|
|
+rclient_new = require("redis-sharelatex").createClient(Settings.redis.new_project_history)
|
|
|
+rclient_du = require("redis-sharelatex").createClient(Settings.redis.documentupdater)
|
|
|
+Keys = Settings.redis.documentupdater.key_schema
|
|
|
+HistoryKeys = Settings.redis.history.key_schema
|
|
|
+ProjectHistoryKeys = Settings.redis.project_history.key_schema
|
|
|
+NewProjectHistoryKeys = Settings.redis.new_project_history.key_schema
|
|
|
+
|
|
|
+MockTrackChangesApi = require "./helpers/MockTrackChangesApi"
|
|
|
+MockWebApi = require "./helpers/MockWebApi"
|
|
|
+DocUpdaterClient = require "./helpers/DocUpdaterClient"
|
|
|
+DocUpdaterApp = require "./helpers/DocUpdaterApp"
|
|
|
+
|
|
|
+describe "RedisMigrationManager", ->
|
|
|
+ before (done) ->
|
|
|
+ @lines = ["one", "two", "three"]
|
|
|
+ @version = 42
|
|
|
+ @update =
|
|
|
+ doc: @doc_id
|
|
|
+ op: [{
|
|
|
+ i: "one and a half\n"
|
|
|
+ p: 4
|
|
|
+ }]
|
|
|
+ v: @version
|
|
|
+ DocUpdaterApp.ensureRunning(done)
|
|
|
+
|
|
|
+ describe "when the migration phase is 'prepare' (default)", ->
|
|
|
+
|
|
|
+ describe "when there is no migration flag", ->
|
|
|
+ before (done) ->
|
|
|
+ [@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
|
|
+
|
|
|
+ MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
|
|
|
+ DocUpdaterClient.preloadDoc @project_id, @doc_id, (error) =>
|
|
|
+ throw error if error?
|
|
|
+ sinon.spy MockWebApi, "getDocument"
|
|
|
+ DocUpdaterClient.sendUpdate @project_id, @doc_id, @update, (error) ->
|
|
|
+ throw error if error?
|
|
|
+ setTimeout done, 200
|
|
|
+ return null
|
|
|
+
|
|
|
+ after ->
|
|
|
+ MockWebApi.getDocument.restore()
|
|
|
+
|
|
|
+ it "should push the applied updates to old redis", (done) ->
|
|
|
+ rclient_old.lrange ProjectHistoryKeys.projectHistoryOps({@project_id}), 0, -1, (error, updates) =>
|
|
|
+ JSON.parse(updates[0]).op.should.deep.equal @update.op
|
|
|
+ done()
|
|
|
+ return null
|
|
|
+
|
|
|
+ it "should not push the applied updates to the new redis", (done) ->
|
|
|
+ rclient_new.exists ProjectHistoryKeys.projectHistoryOps({@project_id}), (error, result) =>
|
|
|
+ result.should.equal 0
|
|
|
+ done()
|
|
|
+ return null
|
|
|
+
|
|
|
+ it "should not set the migration flag for the project", (done) ->
|
|
|
+ rclient_new.exists NewProjectHistoryKeys.projectHistoryMigrationKey({@project_id}), (error, result) =>
|
|
|
+ result.should.equal 0
|
|
|
+ done()
|
|
|
+ return null
|
|
|
+
|
|
|
+ describe "when the migration flag is set for the project", ->
|
|
|
+ before (done) ->
|
|
|
+ [@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
|
|
+
|
|
|
+ rclient_new.set NewProjectHistoryKeys.projectHistoryMigrationKey({@project_id}), '1', (error) =>
|
|
|
+ throw error if error?
|
|
|
+ MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
|
|
|
+ DocUpdaterClient.preloadDoc @project_id, @doc_id, (error) =>
|
|
|
+ throw error if error?
|
|
|
+ sinon.spy MockWebApi, "getDocument"
|
|
|
+ DocUpdaterClient.sendUpdate @project_id, @doc_id, @update, (error) ->
|
|
|
+ throw error if error?
|
|
|
+ setTimeout done, 200
|
|
|
+ return null
|
|
|
+
|
|
|
+ after (done) ->
|
|
|
+ MockWebApi.getDocument.restore()
|
|
|
+ rclient_new.del NewProjectHistoryKeys.projectHistoryMigrationKey({@project_id}), done
|
|
|
+ return null
|
|
|
+
|
|
|
+ it "should push the applied updates to the new redis", (done) ->
|
|
|
+ rclient_new.lrange ProjectHistoryKeys.projectHistoryOps({@project_id}), 0, -1, (error, updates) =>
|
|
|
+ JSON.parse(updates[0]).op.should.deep.equal @update.op
|
|
|
+ done()
|
|
|
+ return null
|
|
|
+
|
|
|
+ it "should not push the applied updates to the old redis", (done) ->
|
|
|
+ rclient_old.exists ProjectHistoryKeys.projectHistoryOps({@project_id}), (error, result) =>
|
|
|
+ result.should.equal 0
|
|
|
+ done()
|
|
|
+ return null
|
|
|
+
|
|
|
+ it "should keep the migration flag for the project", (done) ->
|
|
|
+ rclient_new.exists NewProjectHistoryKeys.projectHistoryMigrationKey({@project_id}), (error, result) =>
|
|
|
+ result.should.equal 1
|
|
|
+ done()
|
|
|
+ return null
|
|
|
+
|
|
|
+ describe "when the migration phase is 'switch'", ->
|
|
|
+ before ->
|
|
|
+ Settings.redis.new_project_history.migration_phase = 'switch'
|
|
|
+
|
|
|
+ describe "when the old queue is empty", ->
|
|
|
+ before (done) ->
|
|
|
+ [@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
|
|
+
|
|
|
+ MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
|
|
|
+ DocUpdaterClient.preloadDoc @project_id, @doc_id, (error) =>
|
|
|
+ throw error if error?
|
|
|
+ sinon.spy MockWebApi, "getDocument"
|
|
|
+ DocUpdaterClient.sendUpdate @project_id, @doc_id, @update, (error) ->
|
|
|
+ throw error if error?
|
|
|
+ setTimeout done, 200
|
|
|
+ return null
|
|
|
+
|
|
|
+ after ->
|
|
|
+ MockWebApi.getDocument.restore()
|
|
|
+
|
|
|
+ it "should push the applied updates to the new redis", (done) ->
|
|
|
+ rclient_new.lrange ProjectHistoryKeys.projectHistoryOps({@project_id}), 0, -1, (error, updates) =>
|
|
|
+ JSON.parse(updates[0]).op.should.deep.equal @update.op
|
|
|
+ done()
|
|
|
+ return null
|
|
|
+
|
|
|
+ it "should not push the applied updates to the old redis", (done) ->
|
|
|
+ rclient_old.exists ProjectHistoryKeys.projectHistoryOps({@project_id}), (error, result) =>
|
|
|
+ result.should.equal 0
|
|
|
+ done()
|
|
|
+ return null
|
|
|
+
|
|
|
+ it "should set the migration flag for the project", (done) ->
|
|
|
+ rclient_new.get NewProjectHistoryKeys.projectHistoryMigrationKey({@project_id}), (error, result) =>
|
|
|
+ result.should.equal "NEW"
|
|
|
+ done()
|
|
|
+ return null
|
|
|
+
|
|
|
+ describe "when the old queue is not empty", ->
|
|
|
+ before (done) ->
|
|
|
+ [@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
|
|
+
|
|
|
+ MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
|
|
|
+ DocUpdaterClient.preloadDoc @project_id, @doc_id, (error) =>
|
|
|
+ throw error if error?
|
|
|
+ sinon.spy MockWebApi, "getDocument"
|
|
|
+ rclient_old.rpush ProjectHistoryKeys.projectHistoryOps({@project_id}), JSON.stringify({op: "dummy-op"}), (error) =>
|
|
|
+ throw error if error?
|
|
|
+ DocUpdaterClient.sendUpdate @project_id, @doc_id, @update, (error) ->
|
|
|
+ throw error if error?
|
|
|
+ setTimeout done, 200
|
|
|
+ return null
|
|
|
+
|
|
|
+ after ->
|
|
|
+ MockWebApi.getDocument.restore()
|
|
|
+
|
|
|
+ it "should push the applied updates to the old redis", (done) ->
|
|
|
+ rclient_old.lrange ProjectHistoryKeys.projectHistoryOps({@project_id}), 0, -1, (error, updates) =>
|
|
|
+ JSON.parse(updates[0]).op.should.deep.equal "dummy-op"
|
|
|
+ JSON.parse(updates[1]).op.should.deep.equal @update.op
|
|
|
+ done()
|
|
|
+ return null
|
|
|
+
|
|
|
+ it "should not push the applied updates to the new redis", (done) ->
|
|
|
+ rclient_new.exists ProjectHistoryKeys.projectHistoryOps({@project_id}), (error, result) =>
|
|
|
+ result.should.equal 0
|
|
|
+ done()
|
|
|
+ return null
|
|
|
+
|
|
|
+ it "should not set the migration flag for the project", (done) ->
|
|
|
+ rclient_new.exists NewProjectHistoryKeys.projectHistoryMigrationKey({@project_id}), (error, result) =>
|
|
|
+ result.should.equal 0
|
|
|
+ done()
|
|
|
+ return null
|
|
|
+
|
|
|
+ describe "when the migration flag is set for the project", ->
|
|
|
+ before (done) ->
|
|
|
+ [@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
|
|
+
|
|
|
+ rclient_new.set NewProjectHistoryKeys.projectHistoryMigrationKey({@project_id}), '1', (error) =>
|
|
|
+ throw error if error?
|
|
|
+ MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
|
|
|
+ DocUpdaterClient.preloadDoc @project_id, @doc_id, (error) =>
|
|
|
+ throw error if error?
|
|
|
+ sinon.spy MockWebApi, "getDocument"
|
|
|
+ DocUpdaterClient.sendUpdate @project_id, @doc_id, @update, (error) ->
|
|
|
+ throw error if error?
|
|
|
+ setTimeout done, 200
|
|
|
+ return null
|
|
|
+
|
|
|
+ after (done) ->
|
|
|
+ MockWebApi.getDocument.restore()
|
|
|
+ rclient_new.del NewProjectHistoryKeys.projectHistoryMigrationKey({@project_id}), done
|
|
|
+ return null
|
|
|
+
|
|
|
+ it "should push the applied updates to the new redis", (done) ->
|
|
|
+ rclient_new.lrange ProjectHistoryKeys.projectHistoryOps({@project_id}), 0, -1, (error, updates) =>
|
|
|
+ JSON.parse(updates[0]).op.should.deep.equal @update.op
|
|
|
+ done()
|
|
|
+ return null
|
|
|
+
|
|
|
+ it "should not push the applied updates to the old redis", (done) ->
|
|
|
+ rclient_old.exists ProjectHistoryKeys.projectHistoryOps({@project_id}), (error, result) =>
|
|
|
+ result.should.equal 0
|
|
|
+ done()
|
|
|
+ return null
|
|
|
+
|
|
|
+ it "should keep the migration flag for the project", (done) ->
|
|
|
+ rclient_new.exists NewProjectHistoryKeys.projectHistoryMigrationKey({@project_id}), (error, result) =>
|
|
|
+ result.should.equal 1
|
|
|
+ done()
|
|
|
+ return null
|
|
|
+
|
|
|
+ describe "when the migration phase is 'rollback'", ->
|
|
|
+ before ->
|
|
|
+ Settings.redis.new_project_history.migration_phase = 'rollback'
|
|
|
+
|
|
|
+ describe "when the old queue is empty", ->
|
|
|
+ before (done) ->
|
|
|
+ [@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
|
|
+
|
|
|
+ MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
|
|
|
+ DocUpdaterClient.preloadDoc @project_id, @doc_id, (error) =>
|
|
|
+ throw error if error?
|
|
|
+ sinon.spy MockWebApi, "getDocument"
|
|
|
+ DocUpdaterClient.sendUpdate @project_id, @doc_id, @update, (error) ->
|
|
|
+ throw error if error?
|
|
|
+ setTimeout done, 200
|
|
|
+ return null
|
|
|
+
|
|
|
+ after ->
|
|
|
+ MockWebApi.getDocument.restore()
|
|
|
+
|
|
|
+ it "should push the applied updates to the old redis", (done) ->
|
|
|
+ rclient_old.lrange ProjectHistoryKeys.projectHistoryOps({@project_id}), 0, -1, (error, updates) =>
|
|
|
+ JSON.parse(updates[0]).op.should.deep.equal @update.op
|
|
|
+ done()
|
|
|
+ return null
|
|
|
+
|
|
|
+ it "should not push the applied updates to the new redis", (done) ->
|
|
|
+ rclient_new.exists ProjectHistoryKeys.projectHistoryOps({@project_id}), (error, result) =>
|
|
|
+ result.should.equal 0
|
|
|
+ done()
|
|
|
+ return null
|
|
|
+
|
|
|
+ describe "when the new queue is not empty", ->
|
|
|
+ before (done) ->
|
|
|
+ [@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
|
|
+
|
|
|
+ MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
|
|
|
+ DocUpdaterClient.preloadDoc @project_id, @doc_id, (error) =>
|
|
|
+ throw error if error?
|
|
|
+ sinon.spy MockWebApi, "getDocument"
|
|
|
+ rclient_new.rpush ProjectHistoryKeys.projectHistoryOps({@project_id}), JSON.stringify({op: "dummy-op"}), (error) =>
|
|
|
+ throw error if error?
|
|
|
+ DocUpdaterClient.sendUpdate @project_id, @doc_id, @update, (error) ->
|
|
|
+ throw error if error?
|
|
|
+ setTimeout done, 200
|
|
|
+ return null
|
|
|
+
|
|
|
+ after ->
|
|
|
+ MockWebApi.getDocument.restore()
|
|
|
+
|
|
|
+ it "should push the applied updates to the old redis", (done) ->
|
|
|
+ rclient_old.lrange ProjectHistoryKeys.projectHistoryOps({@project_id}), 0, -1, (error, updates) =>
|
|
|
+ JSON.parse(updates[0]).op.should.deep.equal @update.op
|
|
|
+ done()
|
|
|
+ return null
|
|
|
+
|
|
|
+ it "should not push the applied updates to the new redis", (done) ->
|
|
|
+ rclient_new.lrange ProjectHistoryKeys.projectHistoryOps({@project_id}), 0, -1, (error, updates) =>
|
|
|
+ JSON.parse(updates[0]).op.should.deep.equal "dummy-op"
|
|
|
+ updates.length.should.equal 1
|
|
|
+ done()
|
|
|
+ return null
|
|
|
+
|
|
|
+ describe "when the migration flag is set for the project", ->
|
|
|
+ before (done) ->
|
|
|
+ [@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
|
|
+
|
|
|
+ rclient_new.set NewProjectHistoryKeys.projectHistoryMigrationKey({@project_id}), '1', (error) =>
|
|
|
+ throw error if error?
|
|
|
+ MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
|
|
|
+ DocUpdaterClient.preloadDoc @project_id, @doc_id, (error) =>
|
|
|
+ throw error if error?
|
|
|
+ sinon.spy MockWebApi, "getDocument"
|
|
|
+ DocUpdaterClient.sendUpdate @project_id, @doc_id, @update, (error) ->
|
|
|
+ throw error if error?
|
|
|
+ setTimeout done, 200
|
|
|
+ return null
|
|
|
+
|
|
|
+ after (done) ->
|
|
|
+ MockWebApi.getDocument.restore()
|
|
|
+ rclient_new.del NewProjectHistoryKeys.projectHistoryMigrationKey({@project_id}), done
|
|
|
+ return null
|
|
|
+
|
|
|
+ it "should push the applied updates to the old redis", (done) ->
|
|
|
+ rclient_old.lrange ProjectHistoryKeys.projectHistoryOps({@project_id}), 0, -1, (error, updates) =>
|
|
|
+ JSON.parse(updates[0]).op.should.deep.equal @update.op
|
|
|
+ done()
|
|
|
+ return null
|
|
|
+
|
|
|
+ it "should not push the applied updates to the new redis", (done) ->
|
|
|
+ rclient_new.exists ProjectHistoryKeys.projectHistoryOps({@project_id}), (error, result) =>
|
|
|
+ result.should.equal 0
|
|
|
+ done()
|
|
|
+ return null
|
|
|
+
|
|
|
+ it "should delete the migration flag for the project", (done) ->
|
|
|
+ rclient_new.exists NewProjectHistoryKeys.projectHistoryMigrationKey({@project_id}), (error, result) =>
|
|
|
+ result.should.equal 0
|
|
|
+ done()
|
|
|
+ return null
|
|
|
+
|