ReceiveUpdateTests.js 9.0 KB

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