ReceiveUpdateTests.js 9.7 KB

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