ReceiveUpdateTests.js 9.8 KB

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