ReceiveUpdateTests.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. /* eslint-disable
  2. camelcase,
  3. handle-callback-err,
  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. * DS102: Remove unnecessary code created because of implicit returns
  11. * DS207: Consider shorter variations of null checks
  12. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  13. */
  14. const { expect } = require('chai')
  15. const RealTimeClient = require('./helpers/RealTimeClient')
  16. const MockWebServer = require('./helpers/MockWebServer')
  17. const FixturesManager = require('./helpers/FixturesManager')
  18. const async = require('async')
  19. const settings = require('settings-sharelatex')
  20. const redis = require('@overleaf/redis-wrapper')
  21. const rclient = redis.createClient(settings.redis.pubsub)
  22. describe('receiveUpdate', function () {
  23. beforeEach(function (done) {
  24. this.lines = ['test', 'doc', 'lines']
  25. this.version = 42
  26. this.ops = ['mock', 'doc', 'ops']
  27. return async.series(
  28. [
  29. (cb) => {
  30. return FixturesManager.setUpProject(
  31. {
  32. privilegeLevel: 'owner',
  33. project: { name: 'Test Project' }
  34. },
  35. (error, { user_id, project_id }) => {
  36. this.user_id = user_id
  37. this.project_id = project_id
  38. return cb()
  39. }
  40. )
  41. },
  42. (cb) => {
  43. return FixturesManager.setUpDoc(
  44. this.project_id,
  45. { lines: this.lines, version: this.version, ops: this.ops },
  46. (e, { doc_id }) => {
  47. this.doc_id = doc_id
  48. return cb(e)
  49. }
  50. )
  51. },
  52. (cb) => {
  53. this.clientA = RealTimeClient.connect()
  54. return this.clientA.on('connectionAccepted', cb)
  55. },
  56. (cb) => {
  57. this.clientB = RealTimeClient.connect()
  58. return this.clientB.on('connectionAccepted', cb)
  59. },
  60. (cb) => {
  61. return this.clientA.emit(
  62. 'joinProject',
  63. {
  64. project_id: this.project_id
  65. },
  66. cb
  67. )
  68. },
  69. (cb) => {
  70. return this.clientA.emit('joinDoc', this.doc_id, cb)
  71. },
  72. (cb) => {
  73. return this.clientB.emit(
  74. 'joinProject',
  75. {
  76. project_id: this.project_id
  77. },
  78. cb
  79. )
  80. },
  81. (cb) => {
  82. return this.clientB.emit('joinDoc', this.doc_id, cb)
  83. },
  84. (cb) => {
  85. return FixturesManager.setUpProject(
  86. {
  87. privilegeLevel: 'owner',
  88. project: { name: 'Test Project' }
  89. },
  90. (
  91. error,
  92. { user_id: user_id_second, project_id: project_id_second }
  93. ) => {
  94. this.user_id_second = user_id_second
  95. this.project_id_second = project_id_second
  96. return cb()
  97. }
  98. )
  99. },
  100. (cb) => {
  101. return FixturesManager.setUpDoc(
  102. this.project_id_second,
  103. { lines: this.lines, version: this.version, ops: this.ops },
  104. (e, { doc_id: doc_id_second }) => {
  105. this.doc_id_second = doc_id_second
  106. return cb(e)
  107. }
  108. )
  109. },
  110. (cb) => {
  111. this.clientC = RealTimeClient.connect()
  112. return this.clientC.on('connectionAccepted', cb)
  113. },
  114. (cb) => {
  115. return this.clientC.emit(
  116. 'joinProject',
  117. {
  118. project_id: this.project_id_second
  119. },
  120. cb
  121. )
  122. },
  123. (cb) => {
  124. return this.clientC.emit('joinDoc', this.doc_id_second, cb)
  125. },
  126. (cb) => {
  127. this.clientAUpdates = []
  128. this.clientA.on('otUpdateApplied', (update) =>
  129. this.clientAUpdates.push(update)
  130. )
  131. this.clientBUpdates = []
  132. this.clientB.on('otUpdateApplied', (update) =>
  133. this.clientBUpdates.push(update)
  134. )
  135. this.clientCUpdates = []
  136. this.clientC.on('otUpdateApplied', (update) =>
  137. this.clientCUpdates.push(update)
  138. )
  139. this.clientAErrors = []
  140. this.clientA.on('otUpdateError', (error) =>
  141. this.clientAErrors.push(error)
  142. )
  143. this.clientBErrors = []
  144. this.clientB.on('otUpdateError', (error) =>
  145. this.clientBErrors.push(error)
  146. )
  147. this.clientCErrors = []
  148. this.clientC.on('otUpdateError', (error) =>
  149. this.clientCErrors.push(error)
  150. )
  151. return cb()
  152. }
  153. ],
  154. done
  155. )
  156. })
  157. afterEach(function () {
  158. if (this.clientA != null) {
  159. this.clientA.disconnect()
  160. }
  161. if (this.clientB != null) {
  162. this.clientB.disconnect()
  163. }
  164. return this.clientC != null ? this.clientC.disconnect() : undefined
  165. })
  166. describe('with an update from clientA', function () {
  167. beforeEach(function (done) {
  168. this.update = {
  169. doc_id: this.doc_id,
  170. op: {
  171. meta: {
  172. source: this.clientA.publicId
  173. },
  174. v: this.version,
  175. doc: this.doc_id,
  176. op: [{ i: 'foo', p: 50 }]
  177. }
  178. }
  179. rclient.publish('applied-ops', JSON.stringify(this.update))
  180. return setTimeout(done, 200)
  181. }) // Give clients time to get message
  182. it('should send the full op to clientB', function () {
  183. return this.clientBUpdates.should.deep.equal([this.update.op])
  184. })
  185. it('should send an ack to clientA', function () {
  186. return this.clientAUpdates.should.deep.equal([
  187. {
  188. v: this.version,
  189. doc: this.doc_id
  190. }
  191. ])
  192. })
  193. return it('should send nothing to clientC', function () {
  194. return this.clientCUpdates.should.deep.equal([])
  195. })
  196. })
  197. describe('with an update from clientC', function () {
  198. beforeEach(function (done) {
  199. this.update = {
  200. doc_id: this.doc_id_second,
  201. op: {
  202. meta: {
  203. source: this.clientC.publicId
  204. },
  205. v: this.version,
  206. doc: this.doc_id_second,
  207. op: [{ i: 'update from clientC', p: 50 }]
  208. }
  209. }
  210. rclient.publish('applied-ops', JSON.stringify(this.update))
  211. return setTimeout(done, 200)
  212. }) // Give clients time to get message
  213. it('should send nothing to clientA', function () {
  214. return this.clientAUpdates.should.deep.equal([])
  215. })
  216. it('should send nothing to clientB', function () {
  217. return this.clientBUpdates.should.deep.equal([])
  218. })
  219. return it('should send an ack to clientC', function () {
  220. return this.clientCUpdates.should.deep.equal([
  221. {
  222. v: this.version,
  223. doc: this.doc_id_second
  224. }
  225. ])
  226. })
  227. })
  228. describe('with an update from a remote client for project 1', function () {
  229. beforeEach(function (done) {
  230. this.update = {
  231. doc_id: this.doc_id,
  232. op: {
  233. meta: {
  234. source: 'this-is-a-remote-client-id'
  235. },
  236. v: this.version,
  237. doc: this.doc_id,
  238. op: [{ i: 'foo', p: 50 }]
  239. }
  240. }
  241. rclient.publish('applied-ops', JSON.stringify(this.update))
  242. return setTimeout(done, 200)
  243. }) // Give clients time to get message
  244. it('should send the full op to clientA', function () {
  245. return this.clientAUpdates.should.deep.equal([this.update.op])
  246. })
  247. it('should send the full op to clientB', function () {
  248. return this.clientBUpdates.should.deep.equal([this.update.op])
  249. })
  250. return it('should send nothing to clientC', function () {
  251. return this.clientCUpdates.should.deep.equal([])
  252. })
  253. })
  254. describe('with an error for the first project', function () {
  255. beforeEach(function (done) {
  256. rclient.publish(
  257. 'applied-ops',
  258. JSON.stringify({
  259. doc_id: this.doc_id,
  260. error: (this.error = 'something went wrong')
  261. })
  262. )
  263. return setTimeout(done, 200)
  264. }) // Give clients time to get message
  265. it('should send the error to the clients in the first project', function () {
  266. this.clientAErrors.should.deep.equal([this.error])
  267. return this.clientBErrors.should.deep.equal([this.error])
  268. })
  269. it('should not send any errors to the client in the second project', function () {
  270. return this.clientCErrors.should.deep.equal([])
  271. })
  272. it('should disconnect the clients of the first project', function () {
  273. this.clientA.socket.connected.should.equal(false)
  274. return this.clientB.socket.connected.should.equal(false)
  275. })
  276. return it('should not disconnect the client in the second project', function () {
  277. return this.clientC.socket.connected.should.equal(true)
  278. })
  279. })
  280. return describe('with an error for the second project', function () {
  281. beforeEach(function (done) {
  282. rclient.publish(
  283. 'applied-ops',
  284. JSON.stringify({
  285. doc_id: this.doc_id_second,
  286. error: (this.error = 'something went wrong')
  287. })
  288. )
  289. return setTimeout(done, 200)
  290. }) // Give clients time to get message
  291. it('should not send any errors to the clients in the first project', function () {
  292. this.clientAErrors.should.deep.equal([])
  293. return this.clientBErrors.should.deep.equal([])
  294. })
  295. it('should send the error to the client in the second project', function () {
  296. return this.clientCErrors.should.deep.equal([this.error])
  297. })
  298. it('should not disconnect the clients of the first project', function () {
  299. this.clientA.socket.connected.should.equal(true)
  300. return this.clientB.socket.connected.should.equal(true)
  301. })
  302. return it('should disconnect the client in the second project', function () {
  303. return this.clientC.socket.connected.should.equal(false)
  304. })
  305. })
  306. })