Add in acceptance tests for getting a diff

This commit is contained in:
James Allen 2014-03-04 15:27:03 +00:00
parent 8b71d222d4
commit e4d8cc7a11
10 changed files with 200 additions and 34 deletions

View file

@ -12,6 +12,8 @@ app.use express.logger()
app.post "/doc/:doc_id/flush", HttpController.flushUpdatesWithLock
app.get "/project/:project_id/doc/:doc_id/diff", HttpController.getDiff
app.get "/status", (req, res, next) ->
res.send "track-changes is alive"

View file

@ -2,6 +2,7 @@ HistoryManager = require "./HistoryManager"
DocumentUpdaterManager = require "./DocumentUpdaterManager"
MongoManager = require "./MongoManager"
DiffGenerator = require "./DiffGenerator"
logger = require "logger-sharelatex"
module.exports = DiffManager =
getLatestDocAndUpdates: (project_id, doc_id, fromDate, toDate, callback = (error, lines, version, updates) ->) ->
@ -14,20 +15,26 @@ module.exports = DiffManager =
callback(null, lines, version, updates)
getDiff: (project_id, doc_id, fromDate, toDate, callback = (error, diff) ->) ->
logger.log project_id: project_id, doc_id: doc_id, from: fromDate, to: toDate, "getting diff"
DiffManager.getLatestDocAndUpdates project_id, doc_id, fromDate, null, (error, lines, version, updates) ->
return callback(error) if error?
lastUpdate = updates[updates.length - 1]
logger.log lines: lines, version: version, updates: updates, "got doc and updates"
lastUpdate = updates[0]
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
for update in updates.reverse()
if update.meta.start_ts <= toDate
updatesToApply.push update
logger.log project_id: project_id, doc_id: doc_id, updatesToApply: updatesToApply, "got updates to apply"
try
startingContent = DiffGenerator.rewindUpdates lines.join("\n"), updates
logger.log project_id: project_id, doc_id: doc_id, startingContent: startingContent, "rewound doc"
diff = DiffGenerator.buildDiff startingContent, updatesToApply
catch e
return callback(e)

View file

@ -1,4 +1,5 @@
HistoryManager = require "./HistoryManager"
DiffManager = require "./DiffManager"
logger = require "logger-sharelatex"
module.exports = HttpController =
@ -7,5 +8,23 @@ module.exports = HttpController =
logger.log doc_id: doc_id, "compressing doc history"
HistoryManager.processUncompressedUpdatesWithLock doc_id, (error) ->
return next(error) if error?
logger.log "done http request"
res.send 204
getDiff: (req, res, next = (error) ->) ->
doc_id = req.params.doc_id
project_id = req.params.project_id
if req.query.from?
from = parseInt(req.query.from, 10)
else
from = null
if req.query.to?
to = parseInt(req.query.to, 10)
else
to = null
logger.log project_id, doc_id: doc_id, from: from, to: to, "getting diff"
DiffManager.getDiff project_id, doc_id, from, to, (error, diff) ->
return next(error) if error?
res.send JSON.stringify(diff: diff)

View file

@ -5,3 +5,6 @@ module.exports =
trackchanges:
port: 3015
host: "localhost"
apis:
documentupdater:
url: "http://localhost:3003"

View file

@ -3,31 +3,19 @@ chai = require("chai")
chai.should()
expect = chai.expect
mongojs = require "../../../app/js/mongojs"
db = mongojs.db
ObjectId = mongojs.ObjectId
Settings = require "settings-sharelatex"
request = require "request"
rclient = require("redis").createClient() # Only works locally for now
flushAndGetCompressedUpdates = (doc_id, callback = (error, updates) ->) ->
request.post {
url: "http://localhost:3015/doc/#{doc_id}/flush"
}, (error, response, body) =>
response.statusCode.should.equal 204
db.docHistory
.find(doc_id: ObjectId(doc_id))
.sort("meta.end_ts": 1)
.toArray callback
pushRawUpdates = (doc_id, updates, callback = (error) ->) ->
rclient.rpush "UncompressedHistoryOps:#{doc_id}", (JSON.stringify(u) for u in updates)..., callback
TrackChangesClient = require "./helpers/TrackChangesClient"
describe "Appending doc ops to the history", ->
describe "when the history does not exist yet", ->
before (done) ->
@doc_id = ObjectId().toString()
@user_id = ObjectId().toString()
pushRawUpdates @doc_id, [{
TrackChangesClient.pushRawUpdates @doc_id, [{
op: [{ i: "f", p: 3 }]
meta: { ts: Date.now(), user_id: @user_id }
v: 3
@ -41,7 +29,7 @@ describe "Appending doc ops to the history", ->
v: 5
}], (error) =>
throw error if error?
flushAndGetCompressedUpdates @doc_id, (error, @updates) =>
TrackChangesClient.flushAndGetCompressedUpdates @doc_id, (error, @updates) =>
throw error if error?
done()
@ -57,7 +45,7 @@ describe "Appending doc ops to the history", ->
beforeEach (done) ->
@doc_id = ObjectId().toString()
@user_id = ObjectId().toString()
pushRawUpdates @doc_id, [{
TrackChangesClient.pushRawUpdates @doc_id, [{
op: [{ i: "f", p: 3 }]
meta: { ts: Date.now(), user_id: @user_id }
v: 3
@ -71,13 +59,13 @@ describe "Appending doc ops to the history", ->
v: 5
}], (error) =>
throw error if error?
flushAndGetCompressedUpdates @doc_id, (error, updates) =>
TrackChangesClient.flushAndGetCompressedUpdates @doc_id, (error, updates) =>
throw error if error?
done()
describe "when the updates are recent and from the same user", ->
beforeEach (done) ->
pushRawUpdates @doc_id, [{
TrackChangesClient.pushRawUpdates @doc_id, [{
op: [{ i: "b", p: 6 }]
meta: { ts: Date.now(), user_id: @user_id }
v: 6
@ -91,7 +79,7 @@ describe "Appending doc ops to the history", ->
v: 8
}], (error) =>
throw error if error?
flushAndGetCompressedUpdates @doc_id, (error, @updates) =>
TrackChangesClient.flushAndGetCompressedUpdates @doc_id, (error, @updates) =>
throw error if error?
done()
@ -107,7 +95,7 @@ describe "Appending doc ops to the history", ->
describe "when the updates are far apart", ->
beforeEach (done) ->
oneDay = 24 * 60 * 60 * 1000
pushRawUpdates @doc_id, [{
TrackChangesClient.pushRawUpdates @doc_id, [{
op: [{ i: "b", p: 6 }]
meta: { ts: Date.now() + oneDay, user_id: @user_id }
v: 6
@ -121,7 +109,7 @@ describe "Appending doc ops to the history", ->
v: 8
}], (error) =>
throw error if error?
flushAndGetCompressedUpdates @doc_id, (error, @updates) =>
TrackChangesClient.flushAndGetCompressedUpdates @doc_id, (error, @updates) =>
throw error if error?
done()
@ -147,9 +135,9 @@ describe "Appending doc ops to the history", ->
}
@expectedOp.i = "a" + @expectedOp.i
pushRawUpdates @doc_id, updates, (error) =>
TrackChangesClient.pushRawUpdates @doc_id, updates, (error) =>
throw error if error?
flushAndGetCompressedUpdates @doc_id, (error, @updates) =>
TrackChangesClient.flushAndGetCompressedUpdates @doc_id, (error, @updates) =>
throw error if error?
done()

View file

@ -0,0 +1,70 @@
sinon = require "sinon"
chai = require("chai")
chai.should()
expect = chai.expect
mongojs = require "../../../app/js/mongojs"
db = mongojs.db
ObjectId = mongojs.ObjectId
Settings = require "settings-sharelatex"
TrackChangesClient = require "./helpers/TrackChangesClient"
MockDocUpdaterApi = require "./helpers/MockDocUpdaterApi"
describe "Getting a diff", ->
before (done) ->
sinon.spy MockDocUpdaterApi, "getDoc"
@now = Date.now()
@from = @now - 100000000
@to = @now
@user_id = ObjectId().toString()
@doc_id = ObjectId().toString()
@project_id = ObjectId().toString()
twoMinutes = 2 * 60 * 1000
@updates = [{
op: [{ i: "one ", p: 0 }]
meta: { ts: @from - twoMinutes, user_id: @user_id }
v: 3
}, {
op: [{ i: "two ", p: 4 }]
meta: { ts: @from + twoMinutes, user_id: @user_id }
v: 4
}, {
op: [{ i: "three ", p: 8 }]
meta: { ts: @to - twoMinutes, user_id: @user_id }
v: 5
}, {
op: [{ i: "four", p: 14 }]
meta: { ts: @to + twoMinutes, user_id: @user_id }
v: 6
}]
@lines = ["one two three four"]
@expected_diff = [
{ u: "one " }
{ i: "two ", meta: { start_ts: @from + twoMinutes, end_ts: @from + twoMinutes, user_id: @user_id } }
{ i: "three ", meta: { start_ts: @to - twoMinutes, end_ts: @to - twoMinutes, user_id: @user_id } }
]
MockDocUpdaterApi.docs[@doc_id] =
lines: @lines
version: 6
TrackChangesClient.pushRawUpdates @doc_id, @updates, (error) =>
throw error if error?
TrackChangesClient.getDiff @project_id, @doc_id, @from, @to, (error, diff) =>
throw error if error?
@diff = diff.diff
done()
after () ->
MockDocUpdaterApi.getDoc.restore()
it "should return the diff", ->
expect(@diff).to.deep.equal @expected_diff
it "should get the doc from the doc updater", ->
MockDocUpdaterApi.getDoc
.calledWith(@project_id, @doc_id)
.should.equal true

View file

@ -0,0 +1,24 @@
express = require("express")
app = express()
module.exports = MockDocUpdaterApi =
docs: {}
getDoc: (project_id, doc_id, callback = (error) ->) ->
callback null, @docs[doc_id]
run: () ->
app.get "/project/:project_id/doc/:doc_id", (req, res, next) =>
@getDoc req.params.project_id, req.params.doc_id, (error, doc) ->
if error?
res.send 500
if !doc?
res.send 404
else
res.send JSON.stringify doc
app.listen 3003, (error) ->
throw error if error?
MockDocUpdaterApi.run()

View file

@ -0,0 +1,24 @@
request = require "request"
rclient = require("redis").createClient() # Only works locally for now
{db, ObjectId} = require "../../../../app/js/mongojs"
module.exports = TrackChangesClient =
flushAndGetCompressedUpdates: (doc_id, callback = (error, updates) ->) ->
request.post {
url: "http://localhost:3015/doc/#{doc_id}/flush"
}, (error, response, body) =>
response.statusCode.should.equal 204
db.docHistory
.find(doc_id: ObjectId(doc_id))
.sort("meta.end_ts": 1)
.toArray callback
pushRawUpdates: (doc_id, updates, callback = (error) ->) ->
rclient.rpush "UncompressedHistoryOps:#{doc_id}", (JSON.stringify(u) for u in updates)..., callback
getDiff: (project_id, doc_id, from, to, callback = (error, diff) ->) ->
request.get {
url: "http://localhost:3015/project/#{project_id}/doc/#{doc_id}/diff?from=#{from}&to=#{to}"
}, (error, response, body) =>
response.statusCode.should.equal 200
callback null, JSON.parse(body)

View file

@ -53,10 +53,12 @@ describe "DiffManager", ->
@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)} }
{ 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)} }
]
@diffed_updates = @updates.slice(0,1)
@diffed_updates = @updates.slice(2)
@rewound_content = "rewound-content"
@diff = [ u: "mock-diff" ]
@ -79,7 +81,7 @@ describe "DiffManager", ->
it "should generate the diff", ->
@DiffGenerator.buildDiff
.calledWith(@rewound_content, @diffed_updates)
.calledWith(@rewound_content, @diffed_updates.reverse())
.should.equal true
it "should call the callback with the diff", ->
@ -88,7 +90,7 @@ describe "DiffManager", ->
describe "with mismatching versions", ->
beforeEach ->
@version = 42
@updates = [ { op: "mock-1", v: 39 }, { op: "mock-1", v: 40 } ]
@updates = [ { op: "mock-1", v: 40 }, { op: "mock-1", v: 39 } ]
@DiffManager.getLatestDocAndUpdates = sinon.stub().callsArgWith(4, null, @lines, @version, @updates)
@DiffManager.getDiff @project_id, @doc_id, @from, @to, @callback

View file

@ -10,7 +10,9 @@ describe "HttpController", ->
@HttpController = SandboxedModule.require modulePath, requires:
"logger-sharelatex": { log: sinon.stub() }
"./HistoryManager": @HistoryManager = {}
"./DiffManager": @DiffManager = {}
@doc_id = "doc-id-123"
@project_id = "project-id-123"
@version = 42
@next = sinon.stub()
@ -30,4 +32,29 @@ describe "HttpController", ->
.should.equal true
it "should return a success code", ->
@res.send.calledWith(204).should.equal true
@res.send.calledWith(204).should.equal true
describe "getDiff", ->
beforeEach ->
@from = Date.now() - 10000
@to = Date.now()
@req =
params:
doc_id: @doc_id
project_id: @project_id
query:
from: @from.toString()
to: @to.toString()
@res =
send: sinon.stub()
@diff = [ u: "mock-diff" ]
@DiffManager.getDiff = sinon.stub().callsArgWith(4, null, @diff)
@HttpController.getDiff @req, @res, @next
it "should get the diff", ->
@DiffManager.getDiff
.calledWith(@project_id, @doc_id, parseInt(@from, 10), parseInt(@to, 10))
.should.equal true
it "should return the diff", ->
@res.send.calledWith(JSON.stringify(diff: @diff)).should.equal true