overleaf/services/document-updater/test/acceptance/coffee/RedisMigrationManagerTests.coffee
2020-01-06 16:50:06 +00:00

320 lines
12 KiB
CoffeeScript

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