RangesManagerTests.coffee 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. sinon = require('sinon')
  2. chai = require('chai')
  3. should = chai.should()
  4. expect = chai.expect
  5. modulePath = "../../../../app/js/RangesManager.js"
  6. SandboxedModule = require('sandboxed-module')
  7. describe "RangesManager", ->
  8. beforeEach ->
  9. @RangesManager = SandboxedModule.require modulePath,
  10. requires:
  11. "logger-sharelatex": @logger = { error: sinon.stub(), log: sinon.stub(), warn: sinon.stub() }
  12. @doc_id = "doc-id-123"
  13. @project_id = "project-id-123"
  14. @user_id = "user-id-123"
  15. @callback = sinon.stub()
  16. describe "applyUpdate", ->
  17. beforeEach ->
  18. @updates = [{
  19. meta:
  20. user_id: @user_id
  21. op: [{
  22. i: "two "
  23. p: 4
  24. }]
  25. }]
  26. @entries = {
  27. comments: [{
  28. op:
  29. c: "three "
  30. p: 4
  31. metadata:
  32. user_id: @user_id
  33. }]
  34. changes: [{
  35. op:
  36. i: "five"
  37. p: 15
  38. metadata:
  39. user_id: @user_id
  40. }]
  41. }
  42. @newDocLines = ["one two three four five"] # old is "one three four five"
  43. describe "successfully", ->
  44. beforeEach ->
  45. @RangesManager.applyUpdate @project_id, @doc_id, @entries, @updates, @newDocLines, @callback
  46. it "should return the modified the comments and changes", ->
  47. @callback.called.should.equal true
  48. [error, entries, ranges_were_collapsed] = @callback.args[0]
  49. expect(error).to.be.null
  50. expect(ranges_were_collapsed).to.equal false
  51. entries.comments[0].op.should.deep.equal {
  52. c: "three "
  53. p: 8
  54. }
  55. entries.changes[0].op.should.deep.equal {
  56. i: "five"
  57. p: 19
  58. }
  59. describe "with empty comments", ->
  60. beforeEach ->
  61. @entries.comments = []
  62. @RangesManager.applyUpdate @project_id, @doc_id, @entries, @updates, @newDocLines, @callback
  63. it "should return an object with no comments", ->
  64. # Save space in redis and don't store just {}
  65. @callback.called.should.equal true
  66. [error, entries] = @callback.args[0]
  67. expect(error).to.be.null
  68. expect(entries.comments).to.be.undefined
  69. describe "with empty changes", ->
  70. beforeEach ->
  71. @entries.changes = []
  72. @RangesManager.applyUpdate @project_id, @doc_id, @entries, @updates, @newDocLines, @callback
  73. it "should return an object with no changes", ->
  74. # Save space in redis and don't store just {}
  75. @callback.called.should.equal true
  76. [error, entries] = @callback.args[0]
  77. expect(error).to.be.null
  78. expect(entries.changes).to.be.undefined
  79. describe "with too many comments", ->
  80. beforeEach ->
  81. @RangesManager.MAX_COMMENTS = 2
  82. @updates = [{
  83. meta:
  84. user_id: @user_id
  85. op: [{
  86. c: "one"
  87. p: 0
  88. t: "thread-id-1"
  89. }]
  90. }]
  91. @entries = {
  92. comments: [{
  93. op:
  94. c: "three "
  95. p: 4
  96. t: "thread-id-2"
  97. metadata:
  98. user_id: @user_id
  99. }, {
  100. op:
  101. c: "four "
  102. p: 10
  103. t: "thread-id-3"
  104. metadata:
  105. user_id: @user_id
  106. }]
  107. changes: []
  108. }
  109. @RangesManager.applyUpdate @project_id, @doc_id, @entries, @updates, @newDocLines, @callback
  110. it "should return an error", ->
  111. @callback.called.should.equal true
  112. [error, entries] = @callback.args[0]
  113. expect(error).to.not.be.null
  114. expect(error.message).to.equal("too many comments or tracked changes")
  115. describe "with too many changes", ->
  116. beforeEach ->
  117. @RangesManager.MAX_CHANGES = 2
  118. @updates = [{
  119. meta:
  120. user_id: @user_id
  121. tc: "track-changes-id-yes"
  122. op: [{
  123. i: "one "
  124. p: 0
  125. }]
  126. }]
  127. @entries = {
  128. changes: [{
  129. op:
  130. i: "three"
  131. p: 4
  132. metadata:
  133. user_id: @user_id
  134. }, {
  135. op:
  136. i: "four"
  137. p: 10
  138. metadata:
  139. user_id: @user_id
  140. }]
  141. comments: []
  142. }
  143. @newDocLines = ["one two three four"]
  144. @RangesManager.applyUpdate @project_id, @doc_id, @entries, @updates, @newDocLines, @callback
  145. it "should return an error", ->
  146. # Save space in redis and don't store just {}
  147. @callback.called.should.equal true
  148. [error, entries] = @callback.args[0]
  149. expect(error).to.not.be.null
  150. expect(error.message).to.equal("too many comments or tracked changes")
  151. describe "inconsistent changes", ->
  152. beforeEach ->
  153. @updates = [{
  154. meta:
  155. user_id: @user_id
  156. op: [{
  157. c: "doesn't match"
  158. p: 0
  159. }]
  160. }]
  161. @RangesManager.applyUpdate @project_id, @doc_id, @entries, @updates, @newDocLines, @callback
  162. it "should return an error", ->
  163. # Save space in redis and don't store just {}
  164. @callback.called.should.equal true
  165. [error, entries] = @callback.args[0]
  166. expect(error).to.not.be.null
  167. expect(error.message).to.equal("Change ({\"op\":{\"i\":\"five\",\"p\":15},\"metadata\":{\"user_id\":\"user-id-123\"}}) doesn't match text (\"our \")")
  168. describe "with an update that collapses a range", ->
  169. beforeEach ->
  170. @updates = [{
  171. meta:
  172. user_id: @user_id
  173. op: [{
  174. d: "one"
  175. p: 0
  176. t: "thread-id-1"
  177. }]
  178. }]
  179. @entries = {
  180. comments: [{
  181. op:
  182. c: "n"
  183. p: 1
  184. t: "thread-id-2"
  185. metadata:
  186. user_id: @user_id
  187. }]
  188. changes: []
  189. }
  190. @RangesManager.applyUpdate @project_id, @doc_id, @entries, @updates, @newDocLines, @callback
  191. it "should return ranges_were_collapsed == true", ->
  192. @callback.called.should.equal true
  193. [error, entries, ranges_were_collapsed] = @callback.args[0]
  194. expect(ranges_were_collapsed).to.equal true
  195. describe "acceptChanges", ->
  196. beforeEach ->
  197. @RangesManager = SandboxedModule.require modulePath,
  198. requires:
  199. "logger-sharelatex": @logger = { error: sinon.stub(), log: sinon.stub(), warn: sinon.stub() }
  200. "./RangesTracker":@RangesTracker = SandboxedModule.require "../../../../app/js/RangesTracker.js"
  201. @ranges = {
  202. comments: []
  203. changes: [{
  204. id: "a1"
  205. op:
  206. i: "lorem"
  207. p: 0
  208. }, {
  209. id: "a2"
  210. op:
  211. i: "ipsum"
  212. p: 10
  213. }, {
  214. id: "a3"
  215. op:
  216. i: "dolor"
  217. p: 20
  218. }, {
  219. id: "a4"
  220. op:
  221. i: "sit"
  222. p: 30
  223. }, {
  224. id: "a5"
  225. op:
  226. i: "amet"
  227. p: 40
  228. }]
  229. }
  230. @removeChangeIdsSpy = sinon.spy @RangesTracker.prototype, "removeChangeIds"
  231. describe "successfully with a single change", ->
  232. beforeEach (done) ->
  233. @change_ids = [ @ranges.changes[1].id ]
  234. @RangesManager.acceptChanges @change_ids, @ranges, (err, ranges) =>
  235. @rangesResponse = ranges
  236. done()
  237. it "should log the call with the correct number of changes", ->
  238. @logger.log
  239. .calledWith("accepting 1 changes in ranges")
  240. .should.equal true
  241. it "should delegate the change removal to the ranges tracker", ->
  242. @removeChangeIdsSpy
  243. .calledWith(@change_ids)
  244. .should.equal true
  245. it "should remove the change", ->
  246. expect(@rangesResponse.changes
  247. .find((change) => change.id == @ranges.changes[1].id))
  248. .to.be.undefined
  249. it "should return the original number of changes minus 1", ->
  250. @rangesResponse.changes.length
  251. .should.equal @ranges.changes.length - 1
  252. it "should not touch other changes", ->
  253. for i in [ 0, 2, 3, 4]
  254. expect(@rangesResponse.changes
  255. .find((change) => change.id == @ranges.changes[i].id))
  256. .to.deep.equal @ranges.changes[i]
  257. describe "successfully with multiple changes", ->
  258. beforeEach (done) ->
  259. @change_ids = [ @ranges.changes[1].id, @ranges.changes[3].id, @ranges.changes[4].id ]
  260. @RangesManager.acceptChanges @change_ids, @ranges, (err, ranges) =>
  261. @rangesResponse = ranges
  262. done()
  263. it "should log the call with the correct number of changes", ->
  264. @logger.log
  265. .calledWith("accepting #{ @change_ids.length } changes in ranges")
  266. .should.equal true
  267. it "should delegate the change removal to the ranges tracker", ->
  268. @removeChangeIdsSpy
  269. .calledWith(@change_ids)
  270. .should.equal true
  271. it "should remove the changes", ->
  272. for i in [ 1, 3, 4]
  273. expect(@rangesResponse.changes
  274. .find((change) => change.id == @ranges.changes[1].id))
  275. .to.be.undefined
  276. it "should return the original number of changes minus the number of accepted changes", ->
  277. @rangesResponse.changes.length
  278. .should.equal @ranges.changes.length - 3
  279. it "should not touch other changes", ->
  280. for i in [ 0, 2 ]
  281. expect(@rangesResponse.changes
  282. .find((change) => change.id == @ranges.changes[i].id))
  283. .to.deep.equal @ranges.changes[i]