GettingADocumentTests.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. const sinon = require('sinon')
  2. const { expect } = require('chai')
  3. const MockWebApi = require('./helpers/MockWebApi')
  4. const DocUpdaterClient = require('./helpers/DocUpdaterClient')
  5. const DocUpdaterApp = require('./helpers/DocUpdaterApp')
  6. const { RequestFailedError } = require('@overleaf/fetch-utils')
  7. describe('Getting a document', function () {
  8. before(async function () {
  9. this.lines = ['one', 'two', 'three']
  10. this.version = 42
  11. await DocUpdaterApp.ensureRunning()
  12. })
  13. describe('when the document is not loaded', function () {
  14. before(async function () {
  15. this.project_id = DocUpdaterClient.randomId()
  16. this.doc_id = DocUpdaterClient.randomId()
  17. sinon.spy(MockWebApi, 'getDocument')
  18. MockWebApi.insertDoc(this.project_id, this.doc_id, {
  19. lines: this.lines,
  20. version: this.version,
  21. })
  22. this.returnedDoc = await DocUpdaterClient.getDoc(
  23. this.project_id,
  24. this.doc_id
  25. )
  26. })
  27. after(function () {
  28. MockWebApi.getDocument.restore()
  29. })
  30. it('should load the document from the web API', function () {
  31. MockWebApi.getDocument
  32. .calledWith(this.project_id, this.doc_id)
  33. .should.equal(true)
  34. })
  35. it('should return the document lines', function () {
  36. this.returnedDoc.lines.should.deep.equal(this.lines)
  37. })
  38. it('should return the document at its current version', function () {
  39. this.returnedDoc.version.should.equal(this.version)
  40. })
  41. })
  42. describe('when the document is already loaded', function () {
  43. before(async function () {
  44. this.project_id = DocUpdaterClient.randomId()
  45. this.doc_id = DocUpdaterClient.randomId()
  46. MockWebApi.insertDoc(this.project_id, this.doc_id, {
  47. lines: this.lines,
  48. version: this.version,
  49. })
  50. await DocUpdaterClient.preloadDoc(this.project_id, this.doc_id)
  51. sinon.spy(MockWebApi, 'getDocument')
  52. this.returnedDoc = await DocUpdaterClient.getDoc(
  53. this.project_id,
  54. this.doc_id
  55. )
  56. })
  57. after(function () {
  58. MockWebApi.getDocument.restore()
  59. })
  60. it('should not load the document from the web API', function () {
  61. MockWebApi.getDocument.called.should.equal(false)
  62. })
  63. it('should return the document lines', function () {
  64. this.returnedDoc.lines.should.deep.equal(this.lines)
  65. })
  66. })
  67. describe('when the request asks for some recent ops', function () {
  68. before(async function () {
  69. this.project_id = DocUpdaterClient.randomId()
  70. this.doc_id = DocUpdaterClient.randomId()
  71. MockWebApi.insertDoc(this.project_id, this.doc_id, {
  72. lines: (this.lines = ['one', 'two', 'three']),
  73. })
  74. this.updates = __range__(0, 199, true).map(v => ({
  75. doc_id: this.doc_id,
  76. op: [{ i: v.toString(), p: 0 }],
  77. v,
  78. }))
  79. await DocUpdaterClient.sendUpdates(
  80. this.project_id,
  81. this.doc_id,
  82. this.updates
  83. )
  84. sinon.spy(MockWebApi, 'getDocument')
  85. })
  86. after(function () {
  87. MockWebApi.getDocument.restore()
  88. })
  89. describe('when the ops are loaded', function () {
  90. before(async function () {
  91. this.returnedDoc = await DocUpdaterClient.getDocAndRecentOps(
  92. this.project_id,
  93. this.doc_id,
  94. 190
  95. )
  96. })
  97. it('should return the recent ops', function () {
  98. this.returnedDoc.ops.length.should.equal(10)
  99. for (const [i, update] of this.updates.slice(190, -1).entries()) {
  100. this.returnedDoc.ops[i].op.should.deep.equal(update.op)
  101. }
  102. })
  103. })
  104. describe('when the ops are not all loaded', function () {
  105. it('should return UnprocessableEntity', async function () {
  106. // We only track 100 ops
  107. await expect(
  108. DocUpdaterClient.getDocAndRecentOps(this.project_id, this.doc_id, 10)
  109. )
  110. .to.be.rejectedWith(RequestFailedError)
  111. .and.eventually.have.nested.property('response.status', 422)
  112. })
  113. })
  114. })
  115. describe('when the document does not exist', function () {
  116. it('should return 404', async function () {
  117. const projectId = DocUpdaterClient.randomId()
  118. const docId = DocUpdaterClient.randomId()
  119. await expect(DocUpdaterClient.getDoc(projectId, docId))
  120. .to.be.rejectedWith(RequestFailedError)
  121. .and.eventually.have.nested.property('response.status', 404)
  122. })
  123. })
  124. describe('when the web api returns an error', function () {
  125. before(function () {
  126. sinon.stub(MockWebApi, 'getDocument').rejects(new Error('oops'))
  127. })
  128. after(function () {
  129. MockWebApi.getDocument.restore()
  130. })
  131. it('should return 500', async function () {
  132. const projectId = DocUpdaterClient.randomId()
  133. const docId = DocUpdaterClient.randomId()
  134. await expect(DocUpdaterClient.getDoc(projectId, docId))
  135. .to.be.rejectedWith(RequestFailedError)
  136. .and.eventually.have.nested.property('response.status', 500)
  137. })
  138. })
  139. describe('when the web api http request takes a long time', function () {
  140. before(function (done) {
  141. this.timeout = 10000
  142. sinon.stub(MockWebApi, 'getDocument').returns(
  143. new Promise(resolve => {
  144. setTimeout(() => resolve(null), 30000)
  145. })
  146. )
  147. done()
  148. })
  149. after(function () {
  150. MockWebApi.getDocument.restore()
  151. })
  152. it('should return quickly(ish)', async function () {
  153. const projectId = DocUpdaterClient.randomId()
  154. const docId = DocUpdaterClient.randomId()
  155. const start = Date.now()
  156. await expect(DocUpdaterClient.getDoc(projectId, docId))
  157. .to.be.rejectedWith(RequestFailedError)
  158. .and.eventually.have.nested.property('response.status', 500)
  159. const delta = Date.now() - start
  160. expect(delta).to.be.below(20000)
  161. })
  162. })
  163. })
  164. function __range__(left, right, inclusive) {
  165. const range = []
  166. const ascending = left < right
  167. const end = !inclusive ? right : ascending ? right + 1 : right - 1
  168. for (let i = left; ascending ? i < end : i > end; ascending ? i++ : i--) {
  169. range.push(i)
  170. }
  171. return range
  172. }