ApplyingUpdatesToProjectStructureTests.coffee 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. sinon = require "sinon"
  2. chai = require("chai")
  3. chai.should()
  4. Settings = require('settings-sharelatex')
  5. rclient_history = require("redis-sharelatex").createClient(Settings.redis.history)
  6. ProjectHistoryKeys = Settings.redis.project_history.key_schema
  7. MockProjectHistoryApi = require "./helpers/MockProjectHistoryApi"
  8. MockWebApi = require "./helpers/MockWebApi"
  9. DocUpdaterClient = require "./helpers/DocUpdaterClient"
  10. DocUpdaterApp = require "./helpers/DocUpdaterApp"
  11. describe "Applying updates to a project's structure", ->
  12. before ->
  13. @user_id = 'user-id-123'
  14. @version = 1234
  15. describe "renaming a file", ->
  16. before (done) ->
  17. @project_id = DocUpdaterClient.randomId()
  18. @fileUpdate =
  19. id: DocUpdaterClient.randomId()
  20. pathname: '/file-path'
  21. newPathname: '/new-file-path'
  22. @fileUpdates = [ @fileUpdate ]
  23. DocUpdaterApp.ensureRunning (error) =>
  24. throw error if error?
  25. DocUpdaterClient.sendProjectUpdate @project_id, @user_id, [], @fileUpdates, @version, (error) ->
  26. throw error if error?
  27. setTimeout done, 200
  28. it "should push the applied file renames to the project history api", (done) ->
  29. rclient_history.lrange ProjectHistoryKeys.projectHistoryOps({@project_id}), 0, -1, (error, updates) =>
  30. throw error if error?
  31. update = JSON.parse(updates[0])
  32. update.file.should.equal @fileUpdate.id
  33. update.pathname.should.equal '/file-path'
  34. update.new_pathname.should.equal '/new-file-path'
  35. update.meta.user_id.should.equal @user_id
  36. update.meta.ts.should.be.a('string')
  37. update.version.should.equal "#{@version}.0"
  38. done()
  39. describe "renaming a document", ->
  40. before ->
  41. @docUpdate =
  42. id: DocUpdaterClient.randomId()
  43. pathname: '/doc-path'
  44. newPathname: '/new-doc-path'
  45. @docUpdates = [ @docUpdate ]
  46. describe "when the document is not loaded", ->
  47. before (done) ->
  48. @project_id = DocUpdaterClient.randomId()
  49. DocUpdaterClient.sendProjectUpdate @project_id, @user_id, @docUpdates, [], @version, (error) ->
  50. throw error if error?
  51. setTimeout done, 200
  52. it "should push the applied doc renames to the project history api", (done) ->
  53. rclient_history.lrange ProjectHistoryKeys.projectHistoryOps({@project_id}), 0, -1, (error, updates) =>
  54. throw error if error?
  55. update = JSON.parse(updates[0])
  56. update.doc.should.equal @docUpdate.id
  57. update.pathname.should.equal '/doc-path'
  58. update.new_pathname.should.equal '/new-doc-path'
  59. update.meta.user_id.should.equal @user_id
  60. update.meta.ts.should.be.a('string')
  61. update.version.should.equal "#{@version}.0"
  62. done()
  63. describe "when the document is loaded", ->
  64. before (done) ->
  65. @project_id = DocUpdaterClient.randomId()
  66. MockWebApi.insertDoc @project_id, @docUpdate.id, {}
  67. DocUpdaterClient.preloadDoc @project_id, @docUpdate.id, (error) =>
  68. throw error if error?
  69. sinon.spy MockWebApi, "getDocument"
  70. DocUpdaterClient.sendProjectUpdate @project_id, @user_id, @docUpdates, [], @version, (error) ->
  71. throw error if error?
  72. setTimeout done, 200
  73. after ->
  74. MockWebApi.getDocument.restore()
  75. it "should update the doc", (done) ->
  76. DocUpdaterClient.getDoc @project_id, @docUpdate.id, (error, res, doc) =>
  77. doc.pathname.should.equal @docUpdate.newPathname
  78. done()
  79. it "should push the applied doc renames to the project history api", (done) ->
  80. rclient_history.lrange ProjectHistoryKeys.projectHistoryOps({@project_id}), 0, -1, (error, updates) =>
  81. throw error if error?
  82. update = JSON.parse(updates[0])
  83. update.doc.should.equal @docUpdate.id
  84. update.pathname.should.equal '/doc-path'
  85. update.new_pathname.should.equal '/new-doc-path'
  86. update.meta.user_id.should.equal @user_id
  87. update.meta.ts.should.be.a('string')
  88. update.version.should.equal "#{@version}.0"
  89. done()
  90. describe "renaming multiple documents and files", ->
  91. before ->
  92. @docUpdate0 =
  93. id: DocUpdaterClient.randomId()
  94. pathname: '/doc-path0'
  95. newPathname: '/new-doc-path0'
  96. @docUpdate1 =
  97. id: DocUpdaterClient.randomId()
  98. pathname: '/doc-path1'
  99. newPathname: '/new-doc-path1'
  100. @docUpdates = [ @docUpdate0, @docUpdate1 ]
  101. @fileUpdate0 =
  102. id: DocUpdaterClient.randomId()
  103. pathname: '/file-path0'
  104. newPathname: '/new-file-path0'
  105. @fileUpdate1 =
  106. id: DocUpdaterClient.randomId()
  107. pathname: '/file-path1'
  108. newPathname: '/new-file-path1'
  109. @fileUpdates = [ @fileUpdate0, @fileUpdate1 ]
  110. describe "when the documents are not loaded", ->
  111. before (done) ->
  112. @project_id = DocUpdaterClient.randomId()
  113. DocUpdaterClient.sendProjectUpdate @project_id, @user_id, @docUpdates, @fileUpdates, @version, (error) ->
  114. throw error if error?
  115. setTimeout done, 200
  116. it "should push the applied doc renames to the project history api", (done) ->
  117. rclient_history.lrange ProjectHistoryKeys.projectHistoryOps({@project_id}), 0, -1, (error, updates) =>
  118. throw error if error?
  119. update = JSON.parse(updates[0])
  120. update.doc.should.equal @docUpdate0.id
  121. update.pathname.should.equal '/doc-path0'
  122. update.new_pathname.should.equal '/new-doc-path0'
  123. update.meta.user_id.should.equal @user_id
  124. update.meta.ts.should.be.a('string')
  125. update.version.should.equal "#{@version}.0"
  126. update = JSON.parse(updates[1])
  127. update.doc.should.equal @docUpdate1.id
  128. update.pathname.should.equal '/doc-path1'
  129. update.new_pathname.should.equal '/new-doc-path1'
  130. update.meta.user_id.should.equal @user_id
  131. update.meta.ts.should.be.a('string')
  132. update.version.should.equal "#{@version}.1"
  133. update = JSON.parse(updates[2])
  134. update.file.should.equal @fileUpdate0.id
  135. update.pathname.should.equal '/file-path0'
  136. update.new_pathname.should.equal '/new-file-path0'
  137. update.meta.user_id.should.equal @user_id
  138. update.meta.ts.should.be.a('string')
  139. update.version.should.equal "#{@version}.2"
  140. update = JSON.parse(updates[3])
  141. update.file.should.equal @fileUpdate1.id
  142. update.pathname.should.equal '/file-path1'
  143. update.new_pathname.should.equal '/new-file-path1'
  144. update.meta.user_id.should.equal @user_id
  145. update.meta.ts.should.be.a('string')
  146. update.version.should.equal "#{@version}.3"
  147. done()
  148. describe "adding a file", ->
  149. before (done) ->
  150. @project_id = DocUpdaterClient.randomId()
  151. @fileUpdate =
  152. id: DocUpdaterClient.randomId()
  153. pathname: '/file-path'
  154. url: 'filestore.example.com'
  155. @fileUpdates = [ @fileUpdate ]
  156. DocUpdaterClient.sendProjectUpdate @project_id, @user_id, [], @fileUpdates, @version, (error) ->
  157. throw error if error?
  158. setTimeout done, 200
  159. it "should push the file addition to the project history api", (done) ->
  160. rclient_history.lrange ProjectHistoryKeys.projectHistoryOps({@project_id}), 0, -1, (error, updates) =>
  161. throw error if error?
  162. update = JSON.parse(updates[0])
  163. update.file.should.equal @fileUpdate.id
  164. update.pathname.should.equal '/file-path'
  165. update.url.should.equal 'filestore.example.com'
  166. update.meta.user_id.should.equal @user_id
  167. update.meta.ts.should.be.a('string')
  168. update.version.should.equal "#{@version}.0"
  169. done()
  170. describe "adding a doc", ->
  171. before (done) ->
  172. @project_id = DocUpdaterClient.randomId()
  173. @docUpdate =
  174. id: DocUpdaterClient.randomId()
  175. pathname: '/file-path'
  176. docLines: 'a\nb'
  177. @docUpdates = [ @docUpdate ]
  178. DocUpdaterClient.sendProjectUpdate @project_id, @user_id, @docUpdates, [], @version, (error) ->
  179. throw error if error?
  180. setTimeout done, 200
  181. it "should push the doc addition to the project history api", (done) ->
  182. rclient_history.lrange ProjectHistoryKeys.projectHistoryOps({@project_id}), 0, -1, (error, updates) =>
  183. throw error if error?
  184. update = JSON.parse(updates[0])
  185. update.doc.should.equal @docUpdate.id
  186. update.pathname.should.equal '/file-path'
  187. update.docLines.should.equal 'a\nb'
  188. update.meta.user_id.should.equal @user_id
  189. update.meta.ts.should.be.a('string')
  190. update.version.should.equal "#{@version}.0"
  191. done()
  192. describe "with enough updates to flush to the history service", ->
  193. before (done) ->
  194. @project_id = DocUpdaterClient.randomId()
  195. @user_id = DocUpdaterClient.randomId()
  196. @version0 = 12345
  197. @version1 = @version0 + 1
  198. updates = []
  199. for v in [0..599] # Should flush after 500 ops
  200. updates.push
  201. id: DocUpdaterClient.randomId(),
  202. pathname: '/file-' + v
  203. docLines: 'a\nb'
  204. sinon.spy MockProjectHistoryApi, "flushProject"
  205. # Send updates in chunks to causes multiple flushes
  206. projectId = @project_id
  207. userId = @project_id
  208. DocUpdaterClient.sendProjectUpdate projectId, userId, updates.slice(0, 250), [], @version0, (error) ->
  209. throw error if error?
  210. DocUpdaterClient.sendProjectUpdate projectId, userId, updates.slice(250), [], @version1, (error) ->
  211. throw error if error?
  212. setTimeout done, 2000
  213. after ->
  214. MockProjectHistoryApi.flushProject.restore()
  215. it "should flush project history", ->
  216. MockProjectHistoryApi.flushProject.calledWith(@project_id).should.equal true
  217. describe "with too few updates to flush to the history service", ->
  218. before (done) ->
  219. @project_id = DocUpdaterClient.randomId()
  220. @user_id = DocUpdaterClient.randomId()
  221. @version0 = 12345
  222. @version1 = @version0 + 1
  223. updates = []
  224. for v in [0..42] # Should flush after 500 ops
  225. updates.push
  226. id: DocUpdaterClient.randomId(),
  227. pathname: '/file-' + v
  228. docLines: 'a\nb'
  229. sinon.spy MockProjectHistoryApi, "flushProject"
  230. # Send updates in chunks
  231. projectId = @project_id
  232. userId = @project_id
  233. DocUpdaterClient.sendProjectUpdate projectId, userId, updates.slice(0, 10), [], @version0, (error) ->
  234. throw error if error?
  235. DocUpdaterClient.sendProjectUpdate projectId, userId, updates.slice(10), [], @version1, (error) ->
  236. throw error if error?
  237. setTimeout done, 2000
  238. after ->
  239. MockProjectHistoryApi.flushProject.restore()
  240. it "should not flush project history", ->
  241. MockProjectHistoryApi.flushProject.calledWith(@project_id).should.equal false