CompileManagerTests.coffee 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  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. "../../infrastructure/RedisWrapper":
  18. client: () => @rclient = { auth: () -> }
  19. "../Project/ProjectRootDocManager": @ProjectRootDocManager = {}
  20. '../Project/ProjectGetter': @ProjectGetter = {}
  21. "../User/UserGetter": @UserGetter = {}
  22. "./ClsiManager": @ClsiManager = {}
  23. "../../infrastructure/RateLimiter": @ratelimiter
  24. "metrics-sharelatex": @Metrics =
  25. Timer: class Timer
  26. done: sinon.stub()
  27. inc: sinon.stub()
  28. "logger-sharelatex": @logger = { log: sinon.stub(), warn: sinon.stub() }
  29. @project_id = "mock-project-id-123"
  30. @user_id = "mock-user-id-123"
  31. @callback = sinon.stub()
  32. @limits = {
  33. timeout: 42
  34. }
  35. describe "compile", ->
  36. beforeEach ->
  37. @CompileManager._checkIfRecentlyCompiled = sinon.stub().callsArgWith(2, null, false)
  38. @ProjectRootDocManager.ensureRootDocumentIsSet = sinon.stub().callsArgWith(1, null)
  39. @CompileManager.getProjectCompileLimits = sinon.stub().callsArgWith(1, null, @limits)
  40. @ClsiManager.sendRequest = sinon.stub().callsArgWith(3, null, @status = "mock-status", @outputFiles = "mock output files", @output = "mock output")
  41. describe "succesfully", ->
  42. beforeEach ->
  43. @CompileManager._checkIfAutoCompileLimitHasBeenHit = (isAutoCompile, compileGroup, cb)-> cb(null, true)
  44. @CompileManager.compile @project_id, @user_id, {}, @callback
  45. it "should check the project has not been recently compiled", ->
  46. @CompileManager._checkIfRecentlyCompiled
  47. .calledWith(@project_id, @user_id)
  48. .should.equal true
  49. it "should ensure that the root document is set", ->
  50. @ProjectRootDocManager.ensureRootDocumentIsSet
  51. .calledWith(@project_id)
  52. .should.equal true
  53. it "should get the project compile limits", ->
  54. @CompileManager.getProjectCompileLimits
  55. .calledWith(@project_id)
  56. .should.equal true
  57. it "should run the compile with the compile limits", ->
  58. @ClsiManager.sendRequest
  59. .calledWith(@project_id, @user_id, {
  60. timeout: @limits.timeout
  61. })
  62. .should.equal true
  63. it "should call the callback with the output", ->
  64. @callback
  65. .calledWith(null, @status, @outputFiles, @output)
  66. .should.equal true
  67. it "should time the compile", ->
  68. @Metrics.Timer::done.called.should.equal true
  69. it "should log out the compile", ->
  70. @logger.log
  71. .calledWith(project_id: @project_id, user_id: @user_id, "compiling project")
  72. .should.equal true
  73. describe "when the project has been recently compiled", ->
  74. it "should return", (done)->
  75. @CompileManager._checkIfAutoCompileLimitHasBeenHit = (isAutoCompile, compileGroup, cb)-> cb(null, true)
  76. @CompileManager._checkIfRecentlyCompiled = sinon.stub().callsArgWith(2, null, true)
  77. @CompileManager.compile @project_id, @user_id, {}, (err, status)->
  78. status.should.equal "too-recently-compiled"
  79. done()
  80. describe "should check the rate limit", ->
  81. it "should return", (done)->
  82. @CompileManager._checkIfAutoCompileLimitHasBeenHit = sinon.stub().callsArgWith(2, null, false)
  83. @CompileManager.compile @project_id, @user_id, {}, (err, status)->
  84. status.should.equal "autocompile-backoff"
  85. done()
  86. describe "getProjectCompileLimits", ->
  87. beforeEach ->
  88. @features = {
  89. compileTimeout: @timeout = 42
  90. compileGroup: @group = "priority"
  91. }
  92. @ProjectGetter.getProject = sinon.stub().callsArgWith(2, null, @project = { owner_ref: @owner_id = "owner-id-123" })
  93. @UserGetter.getUser = sinon.stub().callsArgWith(2, null, @user = { features: @features })
  94. @CompileManager.getProjectCompileLimits @project_id, @callback
  95. it "should look up the owner of the project", ->
  96. @ProjectGetter.getProject
  97. .calledWith(@project_id, { owner_ref: 1 })
  98. .should.equal true
  99. it "should look up the owner's features", ->
  100. @UserGetter.getUser
  101. .calledWith(@project.owner_ref, { features: 1 })
  102. .should.equal true
  103. it "should return the limits", ->
  104. @callback
  105. .calledWith(null, {
  106. timeout: @timeout
  107. compileGroup: @group
  108. })
  109. .should.equal true
  110. describe "deleteAuxFiles", ->
  111. beforeEach ->
  112. @CompileManager.getProjectCompileLimits = sinon.stub().callsArgWith 1, null, @limits = { compileGroup: "mock-compile-group" }
  113. @ClsiManager.deleteAuxFiles = sinon.stub().callsArg(3)
  114. @CompileManager.deleteAuxFiles @project_id, @user_id, @callback
  115. it "should look up the compile group to use", ->
  116. @CompileManager.getProjectCompileLimits
  117. .calledWith(@project_id)
  118. .should.equal true
  119. it "should delete the aux files", ->
  120. @ClsiManager.deleteAuxFiles
  121. .calledWith(@project_id, @user_id, @limits)
  122. .should.equal true
  123. it "should call the callback", ->
  124. @callback.called.should.equal true
  125. describe "_checkIfRecentlyCompiled", ->
  126. describe "when the key exists in redis", ->
  127. beforeEach ->
  128. @rclient.set = sinon.stub().callsArgWith(5, null, null)
  129. @CompileManager._checkIfRecentlyCompiled(@project_id, @user_id, @callback)
  130. it "should try to set the key", ->
  131. @rclient.set
  132. .calledWith("compile:#{@project_id}:#{@user_id}", true, "EX", @CompileManager.COMPILE_DELAY, "NX")
  133. .should.equal true
  134. it "should call the callback with true", ->
  135. @callback.calledWith(null, true).should.equal true
  136. describe "when the key does not exist in redis", ->
  137. beforeEach ->
  138. @rclient.set = sinon.stub().callsArgWith(5, null, "OK")
  139. @CompileManager._checkIfRecentlyCompiled(@project_id, @user_id, @callback)
  140. it "should try to set the key", ->
  141. @rclient.set
  142. .calledWith("compile:#{@project_id}:#{@user_id}", true, "EX", @CompileManager.COMPILE_DELAY, "NX")
  143. .should.equal true
  144. it "should call the callback with false", ->
  145. @callback.calledWith(null, false).should.equal true
  146. describe "_checkIfAutoCompileLimitHasBeenHit", ->
  147. it "should be able to compile if it is not an autocompile", (done)->
  148. @ratelimiter.addCount.callsArgWith(2, null, true)
  149. @CompileManager._checkIfAutoCompileLimitHasBeenHit false, "everyone", (err, canCompile)=>
  150. canCompile.should.equal true
  151. done()
  152. it "should be able to compile if rate limit has remianing", (done)->
  153. @ratelimiter.addCount.callsArgWith(1, null, true)
  154. @CompileManager._checkIfAutoCompileLimitHasBeenHit true, "everyone", (err, canCompile)=>
  155. args = @ratelimiter.addCount.args[0][0]
  156. args.throttle.should.equal 25
  157. args.subjectName.should.equal "everyone"
  158. args.timeInterval.should.equal 20
  159. args.endpointName.should.equal "auto_compile"
  160. canCompile.should.equal true
  161. done()
  162. it "should be not able to compile if rate limit has no remianing", (done)->
  163. @ratelimiter.addCount.callsArgWith(1, null, false)
  164. @CompileManager._checkIfAutoCompileLimitHasBeenHit true, "everyone", (err, canCompile)=>
  165. canCompile.should.equal false
  166. done()
  167. it "should return false if there is an error in the rate limit", (done)->
  168. @ratelimiter.addCount.callsArgWith(1, "error")
  169. @CompileManager._checkIfAutoCompileLimitHasBeenHit true, "everyone", (err, canCompile)=>
  170. canCompile.should.equal false
  171. done()
  172. describe "wordCount", ->
  173. beforeEach ->
  174. @CompileManager.getProjectCompileLimits = sinon.stub().callsArgWith 1, null, @limits = { compileGroup: "mock-compile-group" }
  175. @ClsiManager.wordCount = sinon.stub().callsArg(4)
  176. @CompileManager.wordCount @project_id, @user_id, false, @callback
  177. it "should look up the compile group to use", ->
  178. @CompileManager.getProjectCompileLimits
  179. .calledWith(@project_id)
  180. .should.equal true
  181. it "should call wordCount for project", ->
  182. @ClsiManager.wordCount
  183. .calledWith(@project_id, @user_id, false, @limits)
  184. .should.equal true
  185. it "should call the callback", ->
  186. @callback.called.should.equal true