CompileManagerTests.coffee 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. sinon = require('sinon')
  2. chai = require('chai')
  3. should = chai.should()
  4. expect = chai.expect
  5. modulePath = "../../../../app/js/Features/Compile/CompileManager.js"
  6. assert = require("chai").assert
  7. SandboxedModule = require('sandboxed-module')
  8. describe "CompileManager", ->
  9. beforeEach ->
  10. @rateLimitGetStub = sinon.stub()
  11. rateLimitGetStub = @rateLimitGetStub
  12. @ratelimiter =
  13. addCount: sinon.stub()
  14. @CompileManager = SandboxedModule.require modulePath, requires:
  15. "settings-sharelatex": @settings =
  16. redis: web: {host: "localhost", port: 42}
  17. "redis-sharelatex":
  18. createClient: () => @rclient = { auth: () -> }
  19. "../DocumentUpdater/DocumentUpdaterHandler": @DocumentUpdaterHandler = {}
  20. "../Project/ProjectRootDocManager": @ProjectRootDocManager = {}
  21. "../../models/Project": Project: @Project = {}
  22. "./ClsiManager": @ClsiManager = {}
  23. "../../infrastructure/RateLimiter": @ratelimiter
  24. "../../infrastructure/Metrics": @Metrics =
  25. Timer: class Timer
  26. done: sinon.stub()
  27. inc: sinon.stub()
  28. "logger-sharelatex": @logger = { log: sinon.stub() }
  29. @project_id = "mock-project-id-123"
  30. @user_id = "mock-user-id-123"
  31. @callback = sinon.stub()
  32. describe "compile", ->
  33. beforeEach ->
  34. @CompileManager._checkIfRecentlyCompiled = sinon.stub().callsArgWith(2, null, false)
  35. @CompileManager._ensureRootDocumentIsSet = sinon.stub().callsArgWith(1, null)
  36. @DocumentUpdaterHandler.flushProjectToMongo = sinon.stub().callsArgWith(1, null)
  37. @ClsiManager.sendRequest = sinon.stub().callsArgWith(2, null, @status = "mock-status")
  38. describe "succesfully", ->
  39. beforeEach ->
  40. @CompileManager._checkIfAutoCompileLimitHasBeenHit = (_, cb)-> cb(null, true)
  41. @CompileManager.compile @project_id, @user_id, {}, @callback
  42. it "should check the project has not been recently compiled", ->
  43. @CompileManager._checkIfRecentlyCompiled
  44. .calledWith(@project_id, @user_id)
  45. .should.equal true
  46. it "should flush the project to the database", ->
  47. @DocumentUpdaterHandler.flushProjectToMongo
  48. .calledWith(@project_id)
  49. .should.equal true
  50. it "should ensure that the root document is set", ->
  51. @CompileManager._ensureRootDocumentIsSet
  52. .calledWith(@project_id)
  53. .should.equal true
  54. it "should run the compile with the new compiler API", ->
  55. @ClsiManager.sendRequest
  56. .calledWith(@project_id)
  57. .should.equal true
  58. it "should call the callback", ->
  59. @callback
  60. .calledWith(null, @status)
  61. .should.equal true
  62. it "should time the compile", ->
  63. @Metrics.Timer::done.called.should.equal true
  64. it "should log out the compile", ->
  65. @logger.log
  66. .calledWith(project_id: @project_id, user_id: @user_id, "compiling project")
  67. .should.equal true
  68. describe "when the project has been recently compiled", ->
  69. beforeEach ->
  70. @CompileManager._checkIfAutoCompileLimitHasBeenHit = (_, cb)-> cb(null, true)
  71. @CompileManager._checkIfRecentlyCompiled = sinon.stub().callsArgWith(2, null, true)
  72. @CompileManager.compile @project_id, @user_id, {}, @callback
  73. it "should return the callback with an error", ->
  74. @callback
  75. .calledWith(new Error("project was recently compiled so not continuing"))
  76. .should.equal true
  77. describe "should check the rate limit", ->
  78. it "should return", (done)->
  79. @CompileManager._checkIfAutoCompileLimitHasBeenHit = sinon.stub().callsArgWith(1, null, false)
  80. @CompileManager.compile @project_id, @user_id, {}, (err, status)->
  81. status.should.equal "autocompile-backoff"
  82. done()
  83. describe "getLogLines", ->
  84. beforeEach ->
  85. @ClsiManager.getLogLines = sinon.stub().callsArgWith(1, null, @lines = ["log", "lines"])
  86. @CompileManager.getLogLines @project_id, @callback
  87. it "should call the new api", ->
  88. @ClsiManager.getLogLines
  89. .calledWith(@project_id)
  90. .should.equal true
  91. it "should call the callback with the lines", ->
  92. @callback
  93. .calledWith(null, @lines)
  94. .should.equal true
  95. it "should increase the log count metric", ->
  96. @Metrics.inc
  97. .calledWith("editor.raw-logs")
  98. .should.equal true
  99. describe "_checkIfRecentlyCompiled", ->
  100. describe "when the key exists in redis", ->
  101. beforeEach ->
  102. @rclient.set = sinon.stub().callsArgWith(5, null, null)
  103. @CompileManager._checkIfRecentlyCompiled(@project_id, @user_id, @callback)
  104. it "should try to set the key", ->
  105. @rclient.set
  106. .calledWith("compile:#{@project_id}:#{@user_id}", true, "EX", @CompileManager.COMPILE_DELAY, "NX")
  107. .should.equal true
  108. it "should call the callback with true", ->
  109. @callback.calledWith(null, true).should.equal true
  110. describe "when the key does not exist in redis", ->
  111. beforeEach ->
  112. @rclient.set = sinon.stub().callsArgWith(5, null, "OK")
  113. @CompileManager._checkIfRecentlyCompiled(@project_id, @user_id, @callback)
  114. it "should try to set the key", ->
  115. @rclient.set
  116. .calledWith("compile:#{@project_id}:#{@user_id}", true, "EX", @CompileManager.COMPILE_DELAY, "NX")
  117. .should.equal true
  118. it "should call the callback with false", ->
  119. @callback.calledWith(null, false).should.equal true
  120. describe "_ensureRootDocumentIsSet", ->
  121. beforeEach ->
  122. @project = {}
  123. @Project.findById = sinon.stub().callsArgWith(2, null, @project)
  124. @ProjectRootDocManager.setRootDocAutomatically = sinon.stub().callsArgWith(1, null)
  125. describe "when the root doc is set", ->
  126. beforeEach ->
  127. @project.rootDoc_id = "root-doc-id"
  128. @CompileManager._ensureRootDocumentIsSet(@project_id, @callback)
  129. it "should find the project with only the rootDoc_id fiel", ->
  130. @Project.findById
  131. .calledWith(@project_id, "rootDoc_id")
  132. .should.equal true
  133. it "should not try to update the project rootDoc_id", ->
  134. @ProjectRootDocManager.setRootDocAutomatically
  135. .called.should.equal false
  136. it "should call the callback", ->
  137. @callback.called.should.equal true
  138. describe "when the root doc is not set", ->
  139. beforeEach ->
  140. @CompileManager._ensureRootDocumentIsSet(@project_id, @callback)
  141. it "should find the project with only the rootDoc_id fiel", ->
  142. @Project.findById
  143. .calledWith(@project_id, "rootDoc_id")
  144. .should.equal true
  145. it "should update the project rootDoc_id", ->
  146. @ProjectRootDocManager.setRootDocAutomatically
  147. .calledWith(@project_id)
  148. .should.equal true
  149. it "should call the callback", ->
  150. @callback.called.should.equal true
  151. describe "when the project does not exist", ->
  152. beforeEach ->
  153. @Project.findById = sinon.stub().callsArgWith(2, null, null)
  154. @CompileManager._ensureRootDocumentIsSet(@project_id, @callback)
  155. it "should call the callback with an error", ->
  156. @callback.calledWith(new Error("project not found")).should.equal true
  157. describe "_checkIfAutoCompileLimitHasBeenHit", ->
  158. it "should be able to compile if it is not an autocompile", (done)->
  159. @ratelimiter.addCount.callsArgWith(1, null, true)
  160. @CompileManager._checkIfAutoCompileLimitHasBeenHit false, (err, canCompile)=>
  161. canCompile.should.equal true
  162. done()
  163. it "should be able to compile if rate limit has remianing", (done)->
  164. @ratelimiter.addCount.callsArgWith(1, null, true)
  165. @CompileManager._checkIfAutoCompileLimitHasBeenHit true, (err, canCompile)=>
  166. args = @ratelimiter.addCount.args[0][0]
  167. args.throttle.should.equal 10
  168. args.subjectName.should.equal "everyone"
  169. args.timeInterval.should.equal 15
  170. args.endpointName.should.equal "auto_compile"
  171. canCompile.should.equal true
  172. done()
  173. it "should be not able to compile if rate limit has no remianing", (done)->
  174. @ratelimiter.addCount.callsArgWith(1, null, false)
  175. @CompileManager._checkIfAutoCompileLimitHasBeenHit true, (err, canCompile)=>
  176. canCompile.should.equal false
  177. done()
  178. it "should return false if there is an error in the rate limit", (done)->
  179. @ratelimiter.addCount.callsArgWith(1, "error")
  180. @CompileManager._checkIfAutoCompileLimitHasBeenHit true, (err, canCompile)=>
  181. canCompile.should.equal false
  182. done()