RealTimeRedisManagerTests.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. /* eslint-disable
  2. no-return-assign,
  3. no-unused-vars,
  4. */
  5. // TODO: This file was created by bulk-decaffeinate.
  6. // Fix any style issues and re-enable lint.
  7. /*
  8. * decaffeinate suggestions:
  9. * DS102: Remove unnecessary code created because of implicit returns
  10. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  11. */
  12. const sinon = require('sinon')
  13. const modulePath = '../../../../app/js/RealTimeRedisManager.js'
  14. const SandboxedModule = require('sandboxed-module')
  15. const Errors = require('../../../../app/js/Errors')
  16. describe('RealTimeRedisManager', function () {
  17. beforeEach(function () {
  18. this.rclient = {
  19. auth() {},
  20. exec: sinon.stub(),
  21. }
  22. this.rclient.multi = () => this.rclient
  23. this.pubsubClient = { publish: sinon.stub() }
  24. this.RealTimeRedisManager = SandboxedModule.require(modulePath, {
  25. requires: {
  26. '@overleaf/redis-wrapper': {
  27. createClient: config =>
  28. config.name === 'pubsub' ? this.pubsubClient : this.rclient,
  29. },
  30. '@overleaf/settings': {
  31. redis: {
  32. documentupdater: (this.settings = {
  33. key_schema: {
  34. pendingUpdates({ doc_id: docId }) {
  35. return `PendingUpdates:${docId}`
  36. },
  37. },
  38. }),
  39. pubsub: {
  40. name: 'pubsub',
  41. },
  42. },
  43. },
  44. crypto: (this.crypto = {
  45. randomBytes: sinon
  46. .stub()
  47. .withArgs(4)
  48. .returns(Buffer.from([0x1, 0x2, 0x3, 0x4])),
  49. }),
  50. os: (this.os = { hostname: sinon.stub().returns('somehost') }),
  51. './Metrics': (this.metrics = {
  52. summary: sinon.stub(),
  53. histogram: sinon.stub(),
  54. }),
  55. },
  56. })
  57. this.doc_id = 'doc-id-123'
  58. this.project_id = 'project-id-123'
  59. return (this.callback = sinon.stub())
  60. })
  61. describe('getPendingUpdatesForDoc', function () {
  62. beforeEach(function () {
  63. this.rclient.llen = sinon.stub()
  64. this.rclient.lrange = sinon.stub()
  65. return (this.rclient.ltrim = sinon.stub())
  66. })
  67. describe('successfully', function () {
  68. beforeEach(function () {
  69. this.updates = [
  70. { op: [{ i: 'foo', p: 4 }] },
  71. { op: [{ i: 'foo', p: 4 }] },
  72. ]
  73. this.jsonUpdates = this.updates.map(update => JSON.stringify(update))
  74. this.rclient.exec = sinon.stub().yields(null, [2, this.jsonUpdates])
  75. return this.RealTimeRedisManager.getPendingUpdatesForDoc(
  76. this.doc_id,
  77. this.callback
  78. )
  79. })
  80. it('should get the pending updates', function () {
  81. return this.rclient.lrange
  82. .calledWith(`PendingUpdates:${this.doc_id}`, 0, 7)
  83. .should.equal(true)
  84. })
  85. it('should delete the pending updates', function () {
  86. return this.rclient.ltrim
  87. .calledWith(`PendingUpdates:${this.doc_id}`, 8, -1)
  88. .should.equal(true)
  89. })
  90. return it('should call the callback with the updates', function () {
  91. return this.callback.calledWith(null, this.updates).should.equal(true)
  92. })
  93. })
  94. return describe("when the JSON doesn't parse", function () {
  95. beforeEach(function () {
  96. this.jsonUpdates = [
  97. JSON.stringify({ op: [{ i: 'foo', p: 4 }] }),
  98. 'broken json',
  99. ]
  100. this.rclient.exec = sinon.stub().yields(null, [2, this.jsonUpdates])
  101. return this.RealTimeRedisManager.getPendingUpdatesForDoc(
  102. this.doc_id,
  103. this.callback
  104. )
  105. })
  106. return it('should return an error to the callback', function () {
  107. return this.callback
  108. .calledWith(sinon.match.has('name', 'SyntaxError'))
  109. .should.equal(true)
  110. })
  111. })
  112. })
  113. describe('getUpdatesLength', function () {
  114. beforeEach(function () {
  115. this.rclient.llen = sinon.stub().yields(null, (this.length = 3))
  116. return this.RealTimeRedisManager.getUpdatesLength(
  117. this.doc_id,
  118. this.callback
  119. )
  120. })
  121. it('should look up the length', function () {
  122. return this.rclient.llen
  123. .calledWith(`PendingUpdates:${this.doc_id}`)
  124. .should.equal(true)
  125. })
  126. return it('should return the length', function () {
  127. return this.callback.calledWith(null, this.length).should.equal(true)
  128. })
  129. })
  130. return describe('sendData', function () {
  131. beforeEach(function () {
  132. this.message_id = 'doc:somehost:01020304-0'
  133. return this.RealTimeRedisManager.sendData({ op: 'thisop' })
  134. })
  135. it('should send the op with a message id', function () {
  136. return this.pubsubClient.publish
  137. .calledWith(
  138. 'applied-ops',
  139. JSON.stringify({ op: 'thisop', _id: this.message_id })
  140. )
  141. .should.equal(true)
  142. })
  143. return it('should track the payload size', function () {
  144. return this.metrics.summary
  145. .calledWith(
  146. 'redis.publish.applied-ops',
  147. JSON.stringify({ op: 'thisop', _id: this.message_id }).length
  148. )
  149. .should.equal(true)
  150. })
  151. })
  152. })