RangesManagerTests.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. /* eslint-disable
  2. camelcase,
  3. no-return-assign,
  4. no-unused-vars,
  5. */
  6. // TODO: This file was created by bulk-decaffeinate.
  7. // Fix any style issues and re-enable lint.
  8. /*
  9. * decaffeinate suggestions:
  10. * DS101: Remove unnecessary use of Array.from
  11. * DS102: Remove unnecessary code created because of implicit returns
  12. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  13. */
  14. const sinon = require('sinon')
  15. const { expect } = require('chai')
  16. const modulePath = '../../../../app/js/RangesManager.js'
  17. const SandboxedModule = require('sandboxed-module')
  18. describe('RangesManager', function () {
  19. beforeEach(function () {
  20. this.RangesManager = SandboxedModule.require(modulePath)
  21. this.doc_id = 'doc-id-123'
  22. this.project_id = 'project-id-123'
  23. this.user_id = 'user-id-123'
  24. return (this.callback = sinon.stub())
  25. })
  26. describe('applyUpdate', function () {
  27. beforeEach(function () {
  28. this.updates = [
  29. {
  30. meta: {
  31. user_id: this.user_id,
  32. },
  33. op: [
  34. {
  35. i: 'two ',
  36. p: 4,
  37. },
  38. ],
  39. },
  40. ]
  41. this.entries = {
  42. comments: [
  43. {
  44. op: {
  45. c: 'three ',
  46. p: 4,
  47. },
  48. metadata: {
  49. user_id: this.user_id,
  50. },
  51. },
  52. ],
  53. changes: [
  54. {
  55. op: {
  56. i: 'five',
  57. p: 15,
  58. },
  59. metadata: {
  60. user_id: this.user_id,
  61. },
  62. },
  63. ],
  64. }
  65. return (this.newDocLines = ['one two three four five'])
  66. }) // old is "one three four five"
  67. describe('successfully', function () {
  68. beforeEach(function () {
  69. return this.RangesManager.applyUpdate(
  70. this.project_id,
  71. this.doc_id,
  72. this.entries,
  73. this.updates,
  74. this.newDocLines,
  75. this.callback
  76. )
  77. })
  78. return it('should return the modified the comments and changes', function () {
  79. this.callback.called.should.equal(true)
  80. const [error, entries, ranges_were_collapsed] = Array.from(
  81. this.callback.args[0]
  82. )
  83. expect(error).to.be.null
  84. expect(ranges_were_collapsed).to.equal(false)
  85. entries.comments[0].op.should.deep.equal({
  86. c: 'three ',
  87. p: 8,
  88. })
  89. return entries.changes[0].op.should.deep.equal({
  90. i: 'five',
  91. p: 19,
  92. })
  93. })
  94. })
  95. describe('with empty comments', function () {
  96. beforeEach(function () {
  97. this.entries.comments = []
  98. return this.RangesManager.applyUpdate(
  99. this.project_id,
  100. this.doc_id,
  101. this.entries,
  102. this.updates,
  103. this.newDocLines,
  104. this.callback
  105. )
  106. })
  107. return it('should return an object with no comments', function () {
  108. // Save space in redis and don't store just {}
  109. this.callback.called.should.equal(true)
  110. const [error, entries] = Array.from(this.callback.args[0])
  111. expect(error).to.be.null
  112. return expect(entries.comments).to.be.undefined
  113. })
  114. })
  115. describe('with empty changes', function () {
  116. beforeEach(function () {
  117. this.entries.changes = []
  118. return this.RangesManager.applyUpdate(
  119. this.project_id,
  120. this.doc_id,
  121. this.entries,
  122. this.updates,
  123. this.newDocLines,
  124. this.callback
  125. )
  126. })
  127. return it('should return an object with no changes', function () {
  128. // Save space in redis and don't store just {}
  129. this.callback.called.should.equal(true)
  130. const [error, entries] = Array.from(this.callback.args[0])
  131. expect(error).to.be.null
  132. return expect(entries.changes).to.be.undefined
  133. })
  134. })
  135. describe('with too many comments', function () {
  136. beforeEach(function () {
  137. this.RangesManager.MAX_COMMENTS = 2
  138. this.updates = [
  139. {
  140. meta: {
  141. user_id: this.user_id,
  142. },
  143. op: [
  144. {
  145. c: 'one',
  146. p: 0,
  147. t: 'thread-id-1',
  148. },
  149. ],
  150. },
  151. ]
  152. this.entries = {
  153. comments: [
  154. {
  155. op: {
  156. c: 'three ',
  157. p: 4,
  158. t: 'thread-id-2',
  159. },
  160. metadata: {
  161. user_id: this.user_id,
  162. },
  163. },
  164. {
  165. op: {
  166. c: 'four ',
  167. p: 10,
  168. t: 'thread-id-3',
  169. },
  170. metadata: {
  171. user_id: this.user_id,
  172. },
  173. },
  174. ],
  175. changes: [],
  176. }
  177. return this.RangesManager.applyUpdate(
  178. this.project_id,
  179. this.doc_id,
  180. this.entries,
  181. this.updates,
  182. this.newDocLines,
  183. this.callback
  184. )
  185. })
  186. return it('should return an error', function () {
  187. this.callback.called.should.equal(true)
  188. const [error, entries] = Array.from(this.callback.args[0])
  189. expect(error).to.not.be.null
  190. return expect(error.message).to.equal(
  191. 'too many comments or tracked changes'
  192. )
  193. })
  194. })
  195. describe('with too many changes', function () {
  196. beforeEach(function () {
  197. this.RangesManager.MAX_CHANGES = 2
  198. this.updates = [
  199. {
  200. meta: {
  201. user_id: this.user_id,
  202. tc: 'track-changes-id-yes',
  203. },
  204. op: [
  205. {
  206. i: 'one ',
  207. p: 0,
  208. },
  209. ],
  210. },
  211. ]
  212. this.entries = {
  213. changes: [
  214. {
  215. op: {
  216. i: 'three',
  217. p: 4,
  218. },
  219. metadata: {
  220. user_id: this.user_id,
  221. },
  222. },
  223. {
  224. op: {
  225. i: 'four',
  226. p: 10,
  227. },
  228. metadata: {
  229. user_id: this.user_id,
  230. },
  231. },
  232. ],
  233. comments: [],
  234. }
  235. this.newDocLines = ['one two three four']
  236. return this.RangesManager.applyUpdate(
  237. this.project_id,
  238. this.doc_id,
  239. this.entries,
  240. this.updates,
  241. this.newDocLines,
  242. this.callback
  243. )
  244. })
  245. return it('should return an error', function () {
  246. // Save space in redis and don't store just {}
  247. this.callback.called.should.equal(true)
  248. const [error, entries] = Array.from(this.callback.args[0])
  249. expect(error).to.not.be.null
  250. return expect(error.message).to.equal(
  251. 'too many comments or tracked changes'
  252. )
  253. })
  254. })
  255. describe('inconsistent changes', function () {
  256. beforeEach(function () {
  257. this.updates = [
  258. {
  259. meta: {
  260. user_id: this.user_id,
  261. },
  262. op: [
  263. {
  264. c: "doesn't match",
  265. p: 0,
  266. },
  267. ],
  268. },
  269. ]
  270. return this.RangesManager.applyUpdate(
  271. this.project_id,
  272. this.doc_id,
  273. this.entries,
  274. this.updates,
  275. this.newDocLines,
  276. this.callback
  277. )
  278. })
  279. return it('should return an error', function () {
  280. // Save space in redis and don't store just {}
  281. this.callback.called.should.equal(true)
  282. const [error, entries] = Array.from(this.callback.args[0])
  283. expect(error).to.not.be.null
  284. return expect(error.message).to.equal(
  285. 'Change ({"op":{"i":"five","p":15},"metadata":{"user_id":"user-id-123"}}) doesn\'t match text ("our ")'
  286. )
  287. })
  288. })
  289. return describe('with an update that collapses a range', function () {
  290. beforeEach(function () {
  291. this.updates = [
  292. {
  293. meta: {
  294. user_id: this.user_id,
  295. },
  296. op: [
  297. {
  298. d: 'one',
  299. p: 0,
  300. t: 'thread-id-1',
  301. },
  302. ],
  303. },
  304. ]
  305. this.entries = {
  306. comments: [
  307. {
  308. op: {
  309. c: 'n',
  310. p: 1,
  311. t: 'thread-id-2',
  312. },
  313. metadata: {
  314. user_id: this.user_id,
  315. },
  316. },
  317. ],
  318. changes: [],
  319. }
  320. return this.RangesManager.applyUpdate(
  321. this.project_id,
  322. this.doc_id,
  323. this.entries,
  324. this.updates,
  325. this.newDocLines,
  326. this.callback
  327. )
  328. })
  329. return it('should return ranges_were_collapsed == true', function () {
  330. this.callback.called.should.equal(true)
  331. const [error, entries, ranges_were_collapsed] = Array.from(
  332. this.callback.args[0]
  333. )
  334. return expect(ranges_were_collapsed).to.equal(true)
  335. })
  336. })
  337. })
  338. return describe('acceptChanges', function () {
  339. beforeEach(function () {
  340. this.RangesManager = SandboxedModule.require(modulePath, {
  341. requires: {
  342. './RangesTracker': (this.RangesTracker = SandboxedModule.require(
  343. '../../../../app/js/RangesTracker.js'
  344. )),
  345. },
  346. })
  347. this.ranges = {
  348. comments: [],
  349. changes: [
  350. {
  351. id: 'a1',
  352. op: {
  353. i: 'lorem',
  354. p: 0,
  355. },
  356. },
  357. {
  358. id: 'a2',
  359. op: {
  360. i: 'ipsum',
  361. p: 10,
  362. },
  363. },
  364. {
  365. id: 'a3',
  366. op: {
  367. i: 'dolor',
  368. p: 20,
  369. },
  370. },
  371. {
  372. id: 'a4',
  373. op: {
  374. i: 'sit',
  375. p: 30,
  376. },
  377. },
  378. {
  379. id: 'a5',
  380. op: {
  381. i: 'amet',
  382. p: 40,
  383. },
  384. },
  385. ],
  386. }
  387. return (this.removeChangeIdsSpy = sinon.spy(
  388. this.RangesTracker.prototype,
  389. 'removeChangeIds'
  390. ))
  391. })
  392. describe('successfully with a single change', function () {
  393. beforeEach(function (done) {
  394. this.change_ids = [this.ranges.changes[1].id]
  395. return this.RangesManager.acceptChanges(
  396. this.change_ids,
  397. this.ranges,
  398. (err, ranges) => {
  399. if (err) return done(err)
  400. this.rangesResponse = ranges
  401. return done()
  402. }
  403. )
  404. })
  405. it('should log the call with the correct number of changes', function () {
  406. return this.logger.debug
  407. .calledWith('accepting 1 changes in ranges')
  408. .should.equal(true)
  409. })
  410. it('should delegate the change removal to the ranges tracker', function () {
  411. return this.removeChangeIdsSpy
  412. .calledWith(this.change_ids)
  413. .should.equal(true)
  414. })
  415. it('should remove the change', function () {
  416. return expect(
  417. this.rangesResponse.changes.find(
  418. change => change.id === this.ranges.changes[1].id
  419. )
  420. ).to.be.undefined
  421. })
  422. it('should return the original number of changes minus 1', function () {
  423. return this.rangesResponse.changes.length.should.equal(
  424. this.ranges.changes.length - 1
  425. )
  426. })
  427. return it('should not touch other changes', function () {
  428. return [0, 2, 3, 4].map(i =>
  429. expect(
  430. this.rangesResponse.changes.find(
  431. change => change.id === this.ranges.changes[i].id
  432. )
  433. ).to.deep.equal(this.ranges.changes[i])
  434. )
  435. })
  436. })
  437. return describe('successfully with multiple changes', function () {
  438. beforeEach(function (done) {
  439. this.change_ids = [
  440. this.ranges.changes[1].id,
  441. this.ranges.changes[3].id,
  442. this.ranges.changes[4].id,
  443. ]
  444. return this.RangesManager.acceptChanges(
  445. this.change_ids,
  446. this.ranges,
  447. (err, ranges) => {
  448. if (err) return done(err)
  449. this.rangesResponse = ranges
  450. return done()
  451. }
  452. )
  453. })
  454. it('should log the call with the correct number of changes', function () {
  455. return this.logger.debug
  456. .calledWith(`accepting ${this.change_ids.length} changes in ranges`)
  457. .should.equal(true)
  458. })
  459. it('should delegate the change removal to the ranges tracker', function () {
  460. return this.removeChangeIdsSpy
  461. .calledWith(this.change_ids)
  462. .should.equal(true)
  463. })
  464. it('should remove the changes', function () {
  465. return [1, 3, 4].map(
  466. i =>
  467. expect(
  468. this.rangesResponse.changes.find(
  469. change => change.id === this.ranges.changes[1].id
  470. )
  471. ).to.be.undefined
  472. )
  473. })
  474. it('should return the original number of changes minus the number of accepted changes', function () {
  475. return this.rangesResponse.changes.length.should.equal(
  476. this.ranges.changes.length - 3
  477. )
  478. })
  479. return it('should not touch other changes', function () {
  480. return [0, 2].map(i =>
  481. expect(
  482. this.rangesResponse.changes.find(
  483. change => change.id === this.ranges.changes[i].id
  484. )
  485. ).to.deep.equal(this.ranges.changes[i])
  486. )
  487. })
  488. })
  489. })
  490. })