|
@@ -16,13 +16,16 @@ const DocUpdaterClient = require('./helpers/DocUpdaterClient')
|
|
|
const DocUpdaterApp = require('./helpers/DocUpdaterApp')
|
|
const DocUpdaterApp = require('./helpers/DocUpdaterApp')
|
|
|
|
|
|
|
|
describe('Applying updates to a doc', function () {
|
|
describe('Applying updates to a doc', function () {
|
|
|
- before(function (done) {
|
|
|
|
|
|
|
+ beforeEach(function (done) {
|
|
|
|
|
+ sinon.spy(MockWebApi, 'getDocument')
|
|
|
this.lines = ['one', 'two', 'three']
|
|
this.lines = ['one', 'two', 'three']
|
|
|
this.version = 42
|
|
this.version = 42
|
|
|
this.op = {
|
|
this.op = {
|
|
|
i: 'one and a half\n',
|
|
i: 'one and a half\n',
|
|
|
p: 4,
|
|
p: 4,
|
|
|
}
|
|
}
|
|
|
|
|
+ this.project_id = DocUpdaterClient.randomId()
|
|
|
|
|
+ this.doc_id = DocUpdaterClient.randomId()
|
|
|
this.update = {
|
|
this.update = {
|
|
|
doc: this.doc_id,
|
|
doc: this.doc_id,
|
|
|
op: [this.op],
|
|
op: [this.op],
|
|
@@ -31,12 +34,12 @@ describe('Applying updates to a doc', function () {
|
|
|
this.result = ['one', 'one and a half', 'two', 'three']
|
|
this.result = ['one', 'one and a half', 'two', 'three']
|
|
|
DocUpdaterApp.ensureRunning(done)
|
|
DocUpdaterApp.ensureRunning(done)
|
|
|
})
|
|
})
|
|
|
|
|
+ afterEach(function () {
|
|
|
|
|
+ sinon.restore()
|
|
|
|
|
+ })
|
|
|
|
|
|
|
|
describe('when the document is not loaded', function () {
|
|
describe('when the document is not loaded', function () {
|
|
|
- before(function (done) {
|
|
|
|
|
- this.project_id = DocUpdaterClient.randomId()
|
|
|
|
|
- this.doc_id = DocUpdaterClient.randomId()
|
|
|
|
|
- sinon.spy(MockWebApi, 'getDocument')
|
|
|
|
|
|
|
+ beforeEach(function (done) {
|
|
|
this.startTime = Date.now()
|
|
this.startTime = Date.now()
|
|
|
MockWebApi.insertDoc(this.project_id, this.doc_id, {
|
|
MockWebApi.insertDoc(this.project_id, this.doc_id, {
|
|
|
lines: this.lines,
|
|
lines: this.lines,
|
|
@@ -50,15 +53,25 @@ describe('Applying updates to a doc', function () {
|
|
|
if (error != null) {
|
|
if (error != null) {
|
|
|
throw error
|
|
throw error
|
|
|
}
|
|
}
|
|
|
- setTimeout(done, 200)
|
|
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ rclientProjectHistory.get(
|
|
|
|
|
+ ProjectHistoryKeys.projectHistoryFirstOpTimestamp({
|
|
|
|
|
+ project_id: this.project_id,
|
|
|
|
|
+ }),
|
|
|
|
|
+ (error, result) => {
|
|
|
|
|
+ if (error != null) {
|
|
|
|
|
+ throw error
|
|
|
|
|
+ }
|
|
|
|
|
+ result = parseInt(result, 10)
|
|
|
|
|
+ this.firstOpTimestamp = result
|
|
|
|
|
+ done()
|
|
|
|
|
+ }
|
|
|
|
|
+ )
|
|
|
|
|
+ }, 200)
|
|
|
}
|
|
}
|
|
|
)
|
|
)
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- after(function () {
|
|
|
|
|
- MockWebApi.getDocument.restore()
|
|
|
|
|
- })
|
|
|
|
|
-
|
|
|
|
|
it('should load the document from the web API', function () {
|
|
it('should load the document from the web API', function () {
|
|
|
MockWebApi.getDocument
|
|
MockWebApi.getDocument
|
|
|
.calledWith(this.project_id, this.doc_id)
|
|
.calledWith(this.project_id, this.doc_id)
|
|
@@ -92,21 +105,8 @@ describe('Applying updates to a doc', function () {
|
|
|
)
|
|
)
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('should set the first op timestamp', function (done) {
|
|
|
|
|
- rclientProjectHistory.get(
|
|
|
|
|
- ProjectHistoryKeys.projectHistoryFirstOpTimestamp({
|
|
|
|
|
- project_id: this.project_id,
|
|
|
|
|
- }),
|
|
|
|
|
- (error, result) => {
|
|
|
|
|
- if (error != null) {
|
|
|
|
|
- throw error
|
|
|
|
|
- }
|
|
|
|
|
- result = parseInt(result, 10)
|
|
|
|
|
- result.should.be.within(this.startTime, Date.now())
|
|
|
|
|
- this.firstOpTimestamp = result
|
|
|
|
|
- done()
|
|
|
|
|
- }
|
|
|
|
|
- )
|
|
|
|
|
|
|
+ it('should set the first op timestamp', function () {
|
|
|
|
|
+ this.firstOpTimestamp.should.be.within(this.startTime, Date.now())
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
it('should yield last updated time', function (done) {
|
|
it('should yield last updated time', function (done) {
|
|
@@ -138,7 +138,7 @@ describe('Applying updates to a doc', function () {
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
describe('when sending another update', function () {
|
|
describe('when sending another update', function () {
|
|
|
- before(function (done) {
|
|
|
|
|
|
|
+ beforeEach(function (done) {
|
|
|
this.timeout(10000)
|
|
this.timeout(10000)
|
|
|
this.second_update = Object.assign({}, this.update)
|
|
this.second_update = Object.assign({}, this.update)
|
|
|
this.second_update.v = this.version + 1
|
|
this.second_update.v = this.version + 1
|
|
@@ -207,13 +207,85 @@ describe('Applying updates to a doc', function () {
|
|
|
)
|
|
)
|
|
|
})
|
|
})
|
|
|
})
|
|
})
|
|
|
|
|
+
|
|
|
|
|
+ describe('when another client is sending a concurrent update', function () {
|
|
|
|
|
+ beforeEach(function (done) {
|
|
|
|
|
+ this.timeout(10000)
|
|
|
|
|
+ this.otherUpdate = {
|
|
|
|
|
+ doc: this.doc_id,
|
|
|
|
|
+ op: [{ p: 8, i: 'two and a half\n' }],
|
|
|
|
|
+ v: this.version,
|
|
|
|
|
+ meta: { source: 'other-random-publicId' },
|
|
|
|
|
+ }
|
|
|
|
|
+ this.secondStartTime = Date.now()
|
|
|
|
|
+ DocUpdaterClient.sendUpdate(
|
|
|
|
|
+ this.project_id,
|
|
|
|
|
+ this.doc_id,
|
|
|
|
|
+ this.otherUpdate,
|
|
|
|
|
+ error => {
|
|
|
|
|
+ if (error != null) {
|
|
|
|
|
+ throw error
|
|
|
|
|
+ }
|
|
|
|
|
+ setTimeout(done, 200)
|
|
|
|
|
+ }
|
|
|
|
|
+ )
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ it('should update the doc', function (done) {
|
|
|
|
|
+ DocUpdaterClient.getDoc(
|
|
|
|
|
+ this.project_id,
|
|
|
|
|
+ this.doc_id,
|
|
|
|
|
+ (error, res, doc) => {
|
|
|
|
|
+ if (error) done(error)
|
|
|
|
|
+ doc.lines.should.deep.equal([
|
|
|
|
|
+ 'one',
|
|
|
|
|
+ 'one and a half',
|
|
|
|
|
+ 'two',
|
|
|
|
|
+ 'two and a half',
|
|
|
|
|
+ 'three',
|
|
|
|
|
+ ])
|
|
|
|
|
+ done()
|
|
|
|
|
+ }
|
|
|
|
|
+ )
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ it('should not change the first op timestamp', function (done) {
|
|
|
|
|
+ rclientProjectHistory.get(
|
|
|
|
|
+ ProjectHistoryKeys.projectHistoryFirstOpTimestamp({
|
|
|
|
|
+ project_id: this.project_id,
|
|
|
|
|
+ }),
|
|
|
|
|
+ (error, result) => {
|
|
|
|
|
+ if (error != null) {
|
|
|
|
|
+ throw error
|
|
|
|
|
+ }
|
|
|
|
|
+ result = parseInt(result, 10)
|
|
|
|
|
+ result.should.equal(this.firstOpTimestamp)
|
|
|
|
|
+ done()
|
|
|
|
|
+ }
|
|
|
|
|
+ )
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ it('should yield last updated time', function (done) {
|
|
|
|
|
+ DocUpdaterClient.getProjectLastUpdatedAt(
|
|
|
|
|
+ this.project_id,
|
|
|
|
|
+ (error, res, body) => {
|
|
|
|
|
+ if (error != null) {
|
|
|
|
|
+ throw error
|
|
|
|
|
+ }
|
|
|
|
|
+ res.statusCode.should.equal(200)
|
|
|
|
|
+ body.lastUpdatedAt.should.be.within(
|
|
|
|
|
+ this.secondStartTime,
|
|
|
|
|
+ Date.now()
|
|
|
|
|
+ )
|
|
|
|
|
+ done()
|
|
|
|
|
+ }
|
|
|
|
|
+ )
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
describe('when the document is loaded', function () {
|
|
describe('when the document is loaded', function () {
|
|
|
- before(function (done) {
|
|
|
|
|
- this.project_id = DocUpdaterClient.randomId()
|
|
|
|
|
- this.doc_id = DocUpdaterClient.randomId()
|
|
|
|
|
-
|
|
|
|
|
|
|
+ beforeEach(function (done) {
|
|
|
MockWebApi.insertDoc(this.project_id, this.doc_id, {
|
|
MockWebApi.insertDoc(this.project_id, this.doc_id, {
|
|
|
lines: this.lines,
|
|
lines: this.lines,
|
|
|
version: this.version,
|
|
version: this.version,
|
|
@@ -222,7 +294,7 @@ describe('Applying updates to a doc', function () {
|
|
|
if (error != null) {
|
|
if (error != null) {
|
|
|
throw error
|
|
throw error
|
|
|
}
|
|
}
|
|
|
- sinon.spy(MockWebApi, 'getDocument')
|
|
|
|
|
|
|
+ sinon.resetHistory()
|
|
|
DocUpdaterClient.sendUpdate(
|
|
DocUpdaterClient.sendUpdate(
|
|
|
this.project_id,
|
|
this.project_id,
|
|
|
this.doc_id,
|
|
this.doc_id,
|
|
@@ -237,10 +309,6 @@ describe('Applying updates to a doc', function () {
|
|
|
})
|
|
})
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- after(function () {
|
|
|
|
|
- MockWebApi.getDocument.restore()
|
|
|
|
|
- })
|
|
|
|
|
-
|
|
|
|
|
it('should not need to call the web api', function () {
|
|
it('should not need to call the web api', function () {
|
|
|
MockWebApi.getDocument.called.should.equal(false)
|
|
MockWebApi.getDocument.called.should.equal(false)
|
|
|
})
|
|
})
|
|
@@ -272,10 +340,7 @@ describe('Applying updates to a doc', function () {
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
describe('when the document is loaded and is using project-history only', function () {
|
|
describe('when the document is loaded and is using project-history only', function () {
|
|
|
- before(function (done) {
|
|
|
|
|
- this.project_id = DocUpdaterClient.randomId()
|
|
|
|
|
- this.doc_id = DocUpdaterClient.randomId()
|
|
|
|
|
-
|
|
|
|
|
|
|
+ beforeEach(function (done) {
|
|
|
MockWebApi.insertDoc(this.project_id, this.doc_id, {
|
|
MockWebApi.insertDoc(this.project_id, this.doc_id, {
|
|
|
lines: this.lines,
|
|
lines: this.lines,
|
|
|
version: this.version,
|
|
version: this.version,
|
|
@@ -284,7 +349,7 @@ describe('Applying updates to a doc', function () {
|
|
|
if (error != null) {
|
|
if (error != null) {
|
|
|
throw error
|
|
throw error
|
|
|
}
|
|
}
|
|
|
- sinon.spy(MockWebApi, 'getDocument')
|
|
|
|
|
|
|
+ sinon.resetHistory()
|
|
|
DocUpdaterClient.sendUpdate(
|
|
DocUpdaterClient.sendUpdate(
|
|
|
this.project_id,
|
|
this.project_id,
|
|
|
this.doc_id,
|
|
this.doc_id,
|
|
@@ -299,10 +364,6 @@ describe('Applying updates to a doc', function () {
|
|
|
})
|
|
})
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- after(function () {
|
|
|
|
|
- MockWebApi.getDocument.restore()
|
|
|
|
|
- })
|
|
|
|
|
-
|
|
|
|
|
it('should update the doc', function (done) {
|
|
it('should update the doc', function (done) {
|
|
|
DocUpdaterClient.getDoc(
|
|
DocUpdaterClient.getDoc(
|
|
|
this.project_id,
|
|
this.project_id,
|
|
@@ -331,9 +392,7 @@ describe('Applying updates to a doc', function () {
|
|
|
|
|
|
|
|
describe('when the document has been deleted', function () {
|
|
describe('when the document has been deleted', function () {
|
|
|
describe('when the ops come in a single linear order', function () {
|
|
describe('when the ops come in a single linear order', function () {
|
|
|
- before(function (done) {
|
|
|
|
|
- this.project_id = DocUpdaterClient.randomId()
|
|
|
|
|
- this.doc_id = DocUpdaterClient.randomId()
|
|
|
|
|
|
|
+ beforeEach(function (done) {
|
|
|
const lines = ['', '', '']
|
|
const lines = ['', '', '']
|
|
|
MockWebApi.insertDoc(this.project_id, this.doc_id, {
|
|
MockWebApi.insertDoc(this.project_id, this.doc_id, {
|
|
|
lines,
|
|
lines,
|
|
@@ -353,54 +412,49 @@ describe('Applying updates to a doc', function () {
|
|
|
{ doc_id: this.doc_id, v: 10, op: [{ i: 'd', p: 10 }] },
|
|
{ doc_id: this.doc_id, v: 10, op: [{ i: 'd', p: 10 }] },
|
|
|
]
|
|
]
|
|
|
this.my_result = ['hello world', '', '']
|
|
this.my_result = ['hello world', '', '']
|
|
|
- done()
|
|
|
|
|
- })
|
|
|
|
|
-
|
|
|
|
|
- it('should be able to continue applying updates when the project has been deleted', function (done) {
|
|
|
|
|
- let update
|
|
|
|
|
const actions = []
|
|
const actions = []
|
|
|
- for (update of this.updates.slice(0, 6)) {
|
|
|
|
|
- ;(update => {
|
|
|
|
|
- actions.push(callback =>
|
|
|
|
|
- DocUpdaterClient.sendUpdate(
|
|
|
|
|
- this.project_id,
|
|
|
|
|
- this.doc_id,
|
|
|
|
|
- update,
|
|
|
|
|
- callback
|
|
|
|
|
- )
|
|
|
|
|
|
|
+ for (const update of this.updates.slice(0, 6)) {
|
|
|
|
|
+ actions.push(callback =>
|
|
|
|
|
+ DocUpdaterClient.sendUpdate(
|
|
|
|
|
+ this.project_id,
|
|
|
|
|
+ this.doc_id,
|
|
|
|
|
+ update,
|
|
|
|
|
+ callback
|
|
|
)
|
|
)
|
|
|
- })(update)
|
|
|
|
|
|
|
+ )
|
|
|
}
|
|
}
|
|
|
actions.push(callback =>
|
|
actions.push(callback =>
|
|
|
DocUpdaterClient.deleteDoc(this.project_id, this.doc_id, callback)
|
|
DocUpdaterClient.deleteDoc(this.project_id, this.doc_id, callback)
|
|
|
)
|
|
)
|
|
|
- for (update of this.updates.slice(6)) {
|
|
|
|
|
- ;(update => {
|
|
|
|
|
- actions.push(callback =>
|
|
|
|
|
- DocUpdaterClient.sendUpdate(
|
|
|
|
|
- this.project_id,
|
|
|
|
|
- this.doc_id,
|
|
|
|
|
- update,
|
|
|
|
|
- callback
|
|
|
|
|
- )
|
|
|
|
|
|
|
+ for (const update of this.updates.slice(6)) {
|
|
|
|
|
+ actions.push(callback =>
|
|
|
|
|
+ DocUpdaterClient.sendUpdate(
|
|
|
|
|
+ this.project_id,
|
|
|
|
|
+ this.doc_id,
|
|
|
|
|
+ update,
|
|
|
|
|
+ callback
|
|
|
)
|
|
)
|
|
|
- })(update)
|
|
|
|
|
|
|
+ )
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- async.series(actions, error => {
|
|
|
|
|
- if (error != null) {
|
|
|
|
|
- throw error
|
|
|
|
|
|
|
+ // process updates
|
|
|
|
|
+ actions.push(cb =>
|
|
|
|
|
+ DocUpdaterClient.getDoc(this.project_id, this.doc_id, cb)
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ async.series(actions, done)
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ it('should be able to continue applying updates when the project has been deleted', function (done) {
|
|
|
|
|
+ DocUpdaterClient.getDoc(
|
|
|
|
|
+ this.project_id,
|
|
|
|
|
+ this.doc_id,
|
|
|
|
|
+ (error, res, doc) => {
|
|
|
|
|
+ if (error) return done(error)
|
|
|
|
|
+ doc.lines.should.deep.equal(this.my_result)
|
|
|
|
|
+ done()
|
|
|
}
|
|
}
|
|
|
- DocUpdaterClient.getDoc(
|
|
|
|
|
- this.project_id,
|
|
|
|
|
- this.doc_id,
|
|
|
|
|
- (error, res, doc) => {
|
|
|
|
|
- if (error) return done(error)
|
|
|
|
|
- doc.lines.should.deep.equal(this.my_result)
|
|
|
|
|
- done()
|
|
|
|
|
- }
|
|
|
|
|
- )
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ )
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
it('should store the doc ops in the correct order', function (done) {
|
|
it('should store the doc ops in the correct order', function (done) {
|
|
@@ -422,9 +476,7 @@ describe('Applying updates to a doc', function () {
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
describe('when older ops come in after the delete', function () {
|
|
describe('when older ops come in after the delete', function () {
|
|
|
- before(function (done) {
|
|
|
|
|
- this.project_id = DocUpdaterClient.randomId()
|
|
|
|
|
- this.doc_id = DocUpdaterClient.randomId()
|
|
|
|
|
|
|
+ beforeEach(function (done) {
|
|
|
const lines = ['', '', '']
|
|
const lines = ['', '', '']
|
|
|
MockWebApi.insertDoc(this.project_id, this.doc_id, {
|
|
MockWebApi.insertDoc(this.project_id, this.doc_id, {
|
|
|
lines,
|
|
lines,
|
|
@@ -492,11 +544,9 @@ describe('Applying updates to a doc', function () {
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
describe('with a broken update', function () {
|
|
describe('with a broken update', function () {
|
|
|
- before(function (done) {
|
|
|
|
|
- this.project_id = DocUpdaterClient.randomId()
|
|
|
|
|
- this.doc_id = DocUpdaterClient.randomId()
|
|
|
|
|
|
|
+ beforeEach(function (done) {
|
|
|
this.broken_update = {
|
|
this.broken_update = {
|
|
|
- doc_id: this.doc_id,
|
|
|
|
|
|
|
+ doc: this.doc_id,
|
|
|
v: this.version,
|
|
v: this.version,
|
|
|
op: [{ d: 'not the correct content', p: 0 }],
|
|
op: [{ d: 'not the correct content', p: 0 }],
|
|
|
}
|
|
}
|
|
@@ -547,9 +597,7 @@ describe('Applying updates to a doc', function () {
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
describe('when there is no version in Mongo', function () {
|
|
describe('when there is no version in Mongo', function () {
|
|
|
- before(function (done) {
|
|
|
|
|
- this.project_id = DocUpdaterClient.randomId()
|
|
|
|
|
- this.doc_id = DocUpdaterClient.randomId()
|
|
|
|
|
|
|
+ beforeEach(function (done) {
|
|
|
MockWebApi.insertDoc(this.project_id, this.doc_id, {
|
|
MockWebApi.insertDoc(this.project_id, this.doc_id, {
|
|
|
lines: this.lines,
|
|
lines: this.lines,
|
|
|
})
|
|
})
|
|
@@ -586,9 +634,7 @@ describe('Applying updates to a doc', function () {
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
describe('when the sending duplicate ops', function () {
|
|
describe('when the sending duplicate ops', function () {
|
|
|
- before(function (done) {
|
|
|
|
|
- this.project_id = DocUpdaterClient.randomId()
|
|
|
|
|
- this.doc_id = DocUpdaterClient.randomId()
|
|
|
|
|
|
|
+ beforeEach(function (done) {
|
|
|
MockWebApi.insertDoc(this.project_id, this.doc_id, {
|
|
MockWebApi.insertDoc(this.project_id, this.doc_id, {
|
|
|
lines: this.lines,
|
|
lines: this.lines,
|
|
|
version: this.version,
|
|
version: this.version,
|
|
@@ -671,11 +717,9 @@ describe('Applying updates to a doc', function () {
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
describe('when sending updates for a non-existing doc id', function () {
|
|
describe('when sending updates for a non-existing doc id', function () {
|
|
|
- before(function (done) {
|
|
|
|
|
- this.project_id = DocUpdaterClient.randomId()
|
|
|
|
|
- this.doc_id = DocUpdaterClient.randomId()
|
|
|
|
|
|
|
+ beforeEach(function (done) {
|
|
|
this.non_existing = {
|
|
this.non_existing = {
|
|
|
- doc_id: this.doc_id,
|
|
|
|
|
|
|
+ doc: this.doc_id,
|
|
|
v: this.version,
|
|
v: this.version,
|
|
|
op: [{ d: 'content', p: 0 }],
|
|
op: [{ d: 'content', p: 0 }],
|
|
|
}
|
|
}
|