ApplyUpdateTests.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. /* eslint-disable
  2. camelcase,
  3. handle-callback-err,
  4. no-return-assign,
  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. * DS201: Simplify complex destructure assignments
  13. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  14. */
  15. const async = require('async')
  16. const chai = require('chai')
  17. const { expect } = chai
  18. chai.should()
  19. const RealTimeClient = require('./helpers/RealTimeClient')
  20. const FixturesManager = require('./helpers/FixturesManager')
  21. const settings = require('settings-sharelatex')
  22. const redis = require('redis-sharelatex')
  23. const rclient = redis.createClient(settings.redis.documentupdater)
  24. const redisSettings = settings.redis
  25. describe('applyOtUpdate', function () {
  26. before(function () {
  27. return (this.update = {
  28. op: [{ i: 'foo', p: 42 }]
  29. })
  30. })
  31. describe('when authorized', function () {
  32. before(function (done) {
  33. return async.series(
  34. [
  35. (cb) => {
  36. return FixturesManager.setUpProject(
  37. {
  38. privilegeLevel: 'readAndWrite'
  39. },
  40. (e, { project_id, user_id }) => {
  41. this.project_id = project_id
  42. this.user_id = user_id
  43. return cb(e)
  44. }
  45. )
  46. },
  47. (cb) => {
  48. return FixturesManager.setUpDoc(
  49. this.project_id,
  50. { lines: this.lines, version: this.version, ops: this.ops },
  51. (e, { doc_id }) => {
  52. this.doc_id = doc_id
  53. return cb(e)
  54. }
  55. )
  56. },
  57. (cb) => {
  58. this.client = RealTimeClient.connect()
  59. return this.client.on('connectionAccepted', cb)
  60. },
  61. (cb) => {
  62. return this.client.emit(
  63. 'joinProject',
  64. { project_id: this.project_id },
  65. cb
  66. )
  67. },
  68. (cb) => {
  69. return this.client.emit('joinDoc', this.doc_id, cb)
  70. },
  71. (cb) => {
  72. return this.client.emit(
  73. 'applyOtUpdate',
  74. this.doc_id,
  75. this.update,
  76. cb
  77. )
  78. }
  79. ],
  80. done
  81. )
  82. })
  83. it('should push the doc into the pending updates list', function (done) {
  84. rclient.lrange('pending-updates-list', 0, -1, (error, ...rest) => {
  85. const [doc_id] = Array.from(rest[0])
  86. doc_id.should.equal(`${this.project_id}:${this.doc_id}`)
  87. return done()
  88. })
  89. return null
  90. })
  91. it('should push the update into redis', function (done) {
  92. rclient.lrange(
  93. redisSettings.documentupdater.key_schema.pendingUpdates({
  94. doc_id: this.doc_id
  95. }),
  96. 0,
  97. -1,
  98. (error, ...rest) => {
  99. let [update] = Array.from(rest[0])
  100. update = JSON.parse(update)
  101. update.op.should.deep.equal(this.update.op)
  102. update.meta.should.deep.equal({
  103. source: this.client.publicId,
  104. user_id: this.user_id
  105. })
  106. return done()
  107. }
  108. )
  109. return null
  110. })
  111. return after(function (done) {
  112. return async.series(
  113. [
  114. (cb) => rclient.del('pending-updates-list', cb),
  115. (cb) =>
  116. rclient.del(
  117. 'DocsWithPendingUpdates',
  118. `${this.project_id}:${this.doc_id}`,
  119. cb
  120. ),
  121. (cb) =>
  122. rclient.del(
  123. redisSettings.documentupdater.key_schema.pendingUpdates(
  124. this.doc_id
  125. ),
  126. cb
  127. )
  128. ],
  129. done
  130. )
  131. })
  132. })
  133. describe('when authorized with a huge edit update', function () {
  134. before(function (done) {
  135. this.update = {
  136. op: {
  137. p: 12,
  138. t: 'update is too large'.repeat(1024 * 400) // >7MB
  139. }
  140. }
  141. return async.series(
  142. [
  143. (cb) => {
  144. return FixturesManager.setUpProject(
  145. {
  146. privilegeLevel: 'readAndWrite'
  147. },
  148. (e, { project_id, user_id }) => {
  149. this.project_id = project_id
  150. this.user_id = user_id
  151. return cb(e)
  152. }
  153. )
  154. },
  155. (cb) => {
  156. return FixturesManager.setUpDoc(
  157. this.project_id,
  158. { lines: this.lines, version: this.version, ops: this.ops },
  159. (e, { doc_id }) => {
  160. this.doc_id = doc_id
  161. return cb(e)
  162. }
  163. )
  164. },
  165. (cb) => {
  166. this.client = RealTimeClient.connect()
  167. this.client.on('connectionAccepted', cb)
  168. return this.client.on('otUpdateError', (otUpdateError) => {
  169. this.otUpdateError = otUpdateError
  170. })
  171. },
  172. (cb) => {
  173. return this.client.emit(
  174. 'joinProject',
  175. { project_id: this.project_id },
  176. cb
  177. )
  178. },
  179. (cb) => {
  180. return this.client.emit('joinDoc', this.doc_id, cb)
  181. },
  182. (cb) => {
  183. return this.client.emit(
  184. 'applyOtUpdate',
  185. this.doc_id,
  186. this.update,
  187. (error) => {
  188. this.error = error
  189. return cb()
  190. }
  191. )
  192. }
  193. ],
  194. done
  195. )
  196. })
  197. it('should not return an error', function () {
  198. return expect(this.error).to.not.exist
  199. })
  200. it('should send an otUpdateError to the client', function (done) {
  201. return setTimeout(() => {
  202. expect(this.otUpdateError).to.exist
  203. return done()
  204. }, 300)
  205. })
  206. it('should disconnect the client', function (done) {
  207. return setTimeout(() => {
  208. this.client.socket.connected.should.equal(false)
  209. return done()
  210. }, 300)
  211. })
  212. return it('should not put the update in redis', function (done) {
  213. rclient.llen(
  214. redisSettings.documentupdater.key_schema.pendingUpdates({
  215. doc_id: this.doc_id
  216. }),
  217. (error, len) => {
  218. len.should.equal(0)
  219. return done()
  220. }
  221. )
  222. return null
  223. })
  224. })
  225. describe('when authorized to read-only with an edit update', function () {
  226. before(function (done) {
  227. return async.series(
  228. [
  229. (cb) => {
  230. return FixturesManager.setUpProject(
  231. {
  232. privilegeLevel: 'readOnly'
  233. },
  234. (e, { project_id, user_id }) => {
  235. this.project_id = project_id
  236. this.user_id = user_id
  237. return cb(e)
  238. }
  239. )
  240. },
  241. (cb) => {
  242. return FixturesManager.setUpDoc(
  243. this.project_id,
  244. { lines: this.lines, version: this.version, ops: this.ops },
  245. (e, { doc_id }) => {
  246. this.doc_id = doc_id
  247. return cb(e)
  248. }
  249. )
  250. },
  251. (cb) => {
  252. this.client = RealTimeClient.connect()
  253. return this.client.on('connectionAccepted', cb)
  254. },
  255. (cb) => {
  256. return this.client.emit(
  257. 'joinProject',
  258. { project_id: this.project_id },
  259. cb
  260. )
  261. },
  262. (cb) => {
  263. return this.client.emit('joinDoc', this.doc_id, cb)
  264. },
  265. (cb) => {
  266. return this.client.emit(
  267. 'applyOtUpdate',
  268. this.doc_id,
  269. this.update,
  270. (error) => {
  271. this.error = error
  272. return cb()
  273. }
  274. )
  275. }
  276. ],
  277. done
  278. )
  279. })
  280. it('should return an error', function () {
  281. return expect(this.error).to.exist
  282. })
  283. it('should disconnect the client', function (done) {
  284. return setTimeout(() => {
  285. this.client.socket.connected.should.equal(false)
  286. return done()
  287. }, 300)
  288. })
  289. return it('should not put the update in redis', function (done) {
  290. rclient.llen(
  291. redisSettings.documentupdater.key_schema.pendingUpdates({
  292. doc_id: this.doc_id
  293. }),
  294. (error, len) => {
  295. len.should.equal(0)
  296. return done()
  297. }
  298. )
  299. return null
  300. })
  301. })
  302. return describe('when authorized to read-only with a comment update', function () {
  303. before(function (done) {
  304. this.comment_update = {
  305. op: [{ c: 'foo', p: 42 }]
  306. }
  307. return async.series(
  308. [
  309. (cb) => {
  310. return FixturesManager.setUpProject(
  311. {
  312. privilegeLevel: 'readOnly'
  313. },
  314. (e, { project_id, user_id }) => {
  315. this.project_id = project_id
  316. this.user_id = user_id
  317. return cb(e)
  318. }
  319. )
  320. },
  321. (cb) => {
  322. return FixturesManager.setUpDoc(
  323. this.project_id,
  324. { lines: this.lines, version: this.version, ops: this.ops },
  325. (e, { doc_id }) => {
  326. this.doc_id = doc_id
  327. return cb(e)
  328. }
  329. )
  330. },
  331. (cb) => {
  332. this.client = RealTimeClient.connect()
  333. return this.client.on('connectionAccepted', cb)
  334. },
  335. (cb) => {
  336. return this.client.emit(
  337. 'joinProject',
  338. { project_id: this.project_id },
  339. cb
  340. )
  341. },
  342. (cb) => {
  343. return this.client.emit('joinDoc', this.doc_id, cb)
  344. },
  345. (cb) => {
  346. return this.client.emit(
  347. 'applyOtUpdate',
  348. this.doc_id,
  349. this.comment_update,
  350. cb
  351. )
  352. }
  353. ],
  354. done
  355. )
  356. })
  357. it('should push the doc into the pending updates list', function (done) {
  358. rclient.lrange('pending-updates-list', 0, -1, (error, ...rest) => {
  359. const [doc_id] = Array.from(rest[0])
  360. doc_id.should.equal(`${this.project_id}:${this.doc_id}`)
  361. return done()
  362. })
  363. return null
  364. })
  365. it('should push the update into redis', function (done) {
  366. rclient.lrange(
  367. redisSettings.documentupdater.key_schema.pendingUpdates({
  368. doc_id: this.doc_id
  369. }),
  370. 0,
  371. -1,
  372. (error, ...rest) => {
  373. let [update] = Array.from(rest[0])
  374. update = JSON.parse(update)
  375. update.op.should.deep.equal(this.comment_update.op)
  376. update.meta.should.deep.equal({
  377. source: this.client.publicId,
  378. user_id: this.user_id
  379. })
  380. return done()
  381. }
  382. )
  383. return null
  384. })
  385. return after(function (done) {
  386. return async.series(
  387. [
  388. (cb) => rclient.del('pending-updates-list', cb),
  389. (cb) =>
  390. rclient.del(
  391. 'DocsWithPendingUpdates',
  392. `${this.project_id}:${this.doc_id}`,
  393. cb
  394. ),
  395. (cb) =>
  396. rclient.del(
  397. redisSettings.documentupdater.key_schema.pendingUpdates({
  398. doc_id: this.doc_id
  399. }),
  400. cb
  401. )
  402. ],
  403. done
  404. )
  405. })
  406. })
  407. })