DispatchManagerTests.coffee 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. sinon = require('sinon')
  2. chai = require('chai')
  3. should = chai.should()
  4. modulePath = "../../../../app/js/DispatchManager.js"
  5. SandboxedModule = require('sandboxed-module')
  6. Errors = require "../../../../app/js/Errors.js"
  7. describe "DispatchManager", ->
  8. beforeEach ->
  9. @timeout(3000)
  10. @DispatchManager = SandboxedModule.require modulePath, requires:
  11. "./UpdateManager" : @UpdateManager = {}
  12. "logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub(), warn: sinon.stub() }
  13. "settings-sharelatex": @settings =
  14. redis:
  15. realtime: {}
  16. "redis-sharelatex": @redis = {}
  17. "./RateLimitManager": {}
  18. "./Errors": Errors
  19. "./Metrics":
  20. Timer: ->
  21. done: ->
  22. @callback = sinon.stub()
  23. @RateLimiter = { run: (task,cb) -> task(cb) } # run task without rate limit
  24. describe "each worker", ->
  25. beforeEach ->
  26. @client =
  27. auth: sinon.stub()
  28. @redis.createClient = sinon.stub().returns @client
  29. @worker = @DispatchManager.createDispatcher(@RateLimiter)
  30. it "should create a new redis client", ->
  31. @redis.createClient.called.should.equal true
  32. describe "_waitForUpdateThenDispatchWorker", ->
  33. beforeEach ->
  34. @project_id = "project-id-123"
  35. @doc_id = "doc-id-123"
  36. @doc_key = "#{@project_id}:#{@doc_id}"
  37. @client.blpop = sinon.stub().callsArgWith(2, null, ["pending-updates-list", @doc_key])
  38. describe "in the normal case", ->
  39. beforeEach ->
  40. @UpdateManager.processOutstandingUpdatesWithLock = sinon.stub().callsArg(2)
  41. @worker._waitForUpdateThenDispatchWorker @callback
  42. it "should call redis with BLPOP", ->
  43. @client.blpop
  44. .calledWith("pending-updates-list", 0)
  45. .should.equal true
  46. it "should call processOutstandingUpdatesWithLock", ->
  47. @UpdateManager.processOutstandingUpdatesWithLock
  48. .calledWith(@project_id, @doc_id)
  49. .should.equal true
  50. it "should not log any errors", ->
  51. @logger.error.called.should.equal false
  52. @logger.warn.called.should.equal false
  53. it "should call the callback", ->
  54. @callback.called.should.equal true
  55. describe "with an error", ->
  56. beforeEach ->
  57. @UpdateManager.processOutstandingUpdatesWithLock = sinon.stub().callsArgWith(2, new Error("a generic error"))
  58. @worker._waitForUpdateThenDispatchWorker @callback
  59. it "should log an error", ->
  60. @logger.error.called.should.equal true
  61. it "should call the callback", ->
  62. @callback.called.should.equal true
  63. describe "with a 'Delete component' error", ->
  64. beforeEach ->
  65. @UpdateManager.processOutstandingUpdatesWithLock = sinon.stub().callsArgWith(2, new Errors.DeleteMismatchError())
  66. @worker._waitForUpdateThenDispatchWorker @callback
  67. it "should log a warning", ->
  68. @logger.warn.called.should.equal true
  69. it "should call the callback", ->
  70. @callback.called.should.equal true
  71. describe "run", ->
  72. it "should call _waitForUpdateThenDispatchWorker until shutting down", (done) ->
  73. callCount = 0
  74. @worker._waitForUpdateThenDispatchWorker = (callback = (error) ->) =>
  75. callCount++
  76. if callCount == 3
  77. @settings.shuttingDown = true
  78. setTimeout () ->
  79. callback()
  80. , 10
  81. sinon.spy @worker, "_waitForUpdateThenDispatchWorker"
  82. @worker.run()
  83. checkStatus = () =>
  84. if not @settings.shuttingDown # retry until shutdown
  85. setTimeout checkStatus, 100
  86. return
  87. else
  88. @worker._waitForUpdateThenDispatchWorker.callCount.should.equal 3
  89. done()
  90. checkStatus()