HttpControllerTests.coffee 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. sinon = require('sinon')
  2. chai = require('chai')
  3. should = chai.should()
  4. modulePath = "../../../../app/js/HttpController.js"
  5. SandboxedModule = require('sandboxed-module')
  6. Errors = require "../../../../app/js/Errors.js"
  7. describe "HttpController", ->
  8. beforeEach ->
  9. @HttpController = SandboxedModule.require modulePath, requires:
  10. "./DocumentManager": @DocumentManager = {}
  11. "./ProjectManager": @ProjectManager = {}
  12. "logger-sharelatex" : @logger = { log: sinon.stub() }
  13. "./Metrics": @Metrics = {}
  14. @Metrics.Timer = class Timer
  15. done: sinon.stub()
  16. @project_id = "project-id-123"
  17. @doc_id = "doc-id-123"
  18. @next = sinon.stub()
  19. @res =
  20. send: sinon.stub()
  21. describe "getDoc", ->
  22. beforeEach ->
  23. @lines = ["one", "two", "three"]
  24. @ops = ["mock-op-1", "mock-op-2"]
  25. @version = 42
  26. @fromVersion = 42
  27. @ranges = { changes: "mock", comments: "mock" }
  28. @req =
  29. params:
  30. project_id: @project_id
  31. doc_id: @doc_id
  32. describe "when the document exists and no recent ops are requested", ->
  33. beforeEach ->
  34. @DocumentManager.getDocAndRecentOpsWithLock = sinon.stub().callsArgWith(3, null, @lines, @version, [], @ranges)
  35. @HttpController.getDoc(@req, @res, @next)
  36. it "should get the doc", ->
  37. @DocumentManager.getDocAndRecentOpsWithLock
  38. .calledWith(@project_id, @doc_id, -1)
  39. .should.equal true
  40. it "should return the doc as JSON", ->
  41. @res.send
  42. .calledWith(JSON.stringify({
  43. id: @doc_id
  44. lines: @lines
  45. version: @version
  46. ops: []
  47. ranges: @ranges
  48. }))
  49. .should.equal true
  50. it "should log the request", ->
  51. @logger.log
  52. .calledWith(doc_id: @doc_id, project_id: @project_id, "getting doc via http")
  53. .should.equal true
  54. it "should time the request", ->
  55. @Metrics.Timer::done.called.should.equal true
  56. describe "when recent ops are requested", ->
  57. beforeEach ->
  58. @DocumentManager.getDocAndRecentOpsWithLock = sinon.stub().callsArgWith(3, null, @lines, @version, @ops)
  59. @req.query = fromVersion: "#{@fromVersion}"
  60. @HttpController.getDoc(@req, @res, @next)
  61. it "should get the doc", ->
  62. @DocumentManager.getDocAndRecentOpsWithLock
  63. .calledWith(@project_id, @doc_id, @fromVersion)
  64. .should.equal true
  65. it "should return the doc as JSON", ->
  66. @res.send
  67. .calledWith(JSON.stringify({
  68. id: @doc_id
  69. lines: @lines
  70. version: @version
  71. ops: @ops
  72. }))
  73. .should.equal true
  74. it "should log the request", ->
  75. @logger.log
  76. .calledWith(doc_id: @doc_id, project_id: @project_id, "getting doc via http")
  77. .should.equal true
  78. it "should time the request", ->
  79. @Metrics.Timer::done.called.should.equal true
  80. describe "when the document does not exist", ->
  81. beforeEach ->
  82. @DocumentManager.getDocAndRecentOpsWithLock = sinon.stub().callsArgWith(3, null, null, null)
  83. @HttpController.getDoc(@req, @res, @next)
  84. it "should call next with NotFoundError", ->
  85. @next
  86. .calledWith(new Errors.NotFoundError("not found"))
  87. .should.equal true
  88. describe "when an errors occurs", ->
  89. beforeEach ->
  90. @DocumentManager.getDocAndRecentOpsWithLock = sinon.stub().callsArgWith(3, new Error("oops"), null, null)
  91. @HttpController.getDoc(@req, @res, @next)
  92. it "should call next with the error", ->
  93. @next
  94. .calledWith(new Error("oops"))
  95. .should.equal true
  96. describe "setDoc", ->
  97. beforeEach ->
  98. @lines = ["one", "two", "three"]
  99. @source = "dropbox"
  100. @user_id = "user-id-123"
  101. @req =
  102. headers: {}
  103. params:
  104. project_id: @project_id
  105. doc_id: @doc_id
  106. body:
  107. lines: @lines
  108. source: @source
  109. user_id: @user_id
  110. undoing: @undoing = true
  111. describe "successfully", ->
  112. beforeEach ->
  113. @DocumentManager.setDocWithLock = sinon.stub().callsArgWith(6)
  114. @HttpController.setDoc(@req, @res, @next)
  115. it "should set the doc", ->
  116. @DocumentManager.setDocWithLock
  117. .calledWith(@project_id, @doc_id, @lines, @source, @user_id, @undoing)
  118. .should.equal true
  119. it "should return a successful No Content response", ->
  120. @res.send
  121. .calledWith(204)
  122. .should.equal true
  123. it "should log the request", ->
  124. @logger.log
  125. .calledWith(doc_id: @doc_id, project_id: @project_id, lines: @lines, source: @source, user_id: @user_id, undoing: @undoing, "setting doc via http")
  126. .should.equal true
  127. it "should time the request", ->
  128. @Metrics.Timer::done.called.should.equal true
  129. describe "when an errors occurs", ->
  130. beforeEach ->
  131. @DocumentManager.setDocWithLock = sinon.stub().callsArgWith(6, new Error("oops"))
  132. @HttpController.setDoc(@req, @res, @next)
  133. it "should call next with the error", ->
  134. @next
  135. .calledWith(new Error("oops"))
  136. .should.equal true
  137. describe "when the payload is too large", ->
  138. beforeEach ->
  139. lines = []
  140. for _ in [0..200000]
  141. lines.push "test test test"
  142. @req.body.lines = lines
  143. @DocumentManager.setDocWithLock = sinon.stub().callsArgWith(6)
  144. @HttpController.setDoc(@req, @res, @next)
  145. it 'should send back a 406 response', ->
  146. @res.send.calledWith(406).should.equal true
  147. it 'should not call setDocWithLock', ->
  148. @DocumentManager.setDocWithLock.callCount.should.equal 0
  149. describe "flushProject", ->
  150. beforeEach ->
  151. @req =
  152. params:
  153. project_id: @project_id
  154. describe "successfully", ->
  155. beforeEach ->
  156. @ProjectManager.flushProjectWithLocks = sinon.stub().callsArgWith(1)
  157. @HttpController.flushProject(@req, @res, @next)
  158. it "should flush the project", ->
  159. @ProjectManager.flushProjectWithLocks
  160. .calledWith(@project_id)
  161. .should.equal true
  162. it "should return a successful No Content response", ->
  163. @res.send
  164. .calledWith(204)
  165. .should.equal true
  166. it "should log the request", ->
  167. @logger.log
  168. .calledWith(project_id: @project_id, "flushing project via http")
  169. .should.equal true
  170. it "should time the request", ->
  171. @Metrics.Timer::done.called.should.equal true
  172. describe "when an errors occurs", ->
  173. beforeEach ->
  174. @ProjectManager.flushProjectWithLocks = sinon.stub().callsArgWith(1, new Error("oops"))
  175. @HttpController.flushProject(@req, @res, @next)
  176. it "should call next with the error", ->
  177. @next
  178. .calledWith(new Error("oops"))
  179. .should.equal true
  180. describe "flushDocIfLoaded", ->
  181. beforeEach ->
  182. @lines = ["one", "two", "three"]
  183. @version = 42
  184. @req =
  185. params:
  186. project_id: @project_id
  187. doc_id: @doc_id
  188. describe "successfully", ->
  189. beforeEach ->
  190. @DocumentManager.flushDocIfLoadedWithLock = sinon.stub().callsArgWith(2)
  191. @HttpController.flushDocIfLoaded(@req, @res, @next)
  192. it "should flush the doc", ->
  193. @DocumentManager.flushDocIfLoadedWithLock
  194. .calledWith(@project_id, @doc_id)
  195. .should.equal true
  196. it "should return a successful No Content response", ->
  197. @res.send
  198. .calledWith(204)
  199. .should.equal true
  200. it "should log the request", ->
  201. @logger.log
  202. .calledWith(doc_id: @doc_id, project_id: @project_id, "flushing doc via http")
  203. .should.equal true
  204. it "should time the request", ->
  205. @Metrics.Timer::done.called.should.equal true
  206. describe "when an errors occurs", ->
  207. beforeEach ->
  208. @DocumentManager.flushDocIfLoadedWithLock = sinon.stub().callsArgWith(2, new Error("oops"))
  209. @HttpController.flushDocIfLoaded(@req, @res, @next)
  210. it "should call next with the error", ->
  211. @next
  212. .calledWith(new Error("oops"))
  213. .should.equal true
  214. describe "flushAndDeleteDoc", ->
  215. beforeEach ->
  216. @req =
  217. params:
  218. project_id: @project_id
  219. doc_id: @doc_id
  220. describe "successfully", ->
  221. beforeEach ->
  222. @DocumentManager.flushAndDeleteDocWithLock = sinon.stub().callsArgWith(2)
  223. @HttpController.flushAndDeleteDoc(@req, @res, @next)
  224. it "should flush and delete the doc", ->
  225. @DocumentManager.flushAndDeleteDocWithLock
  226. .calledWith(@project_id, @doc_id)
  227. .should.equal true
  228. it "should return a successful No Content response", ->
  229. @res.send
  230. .calledWith(204)
  231. .should.equal true
  232. it "should log the request", ->
  233. @logger.log
  234. .calledWith(doc_id: @doc_id, project_id: @project_id, "deleting doc via http")
  235. .should.equal true
  236. it "should time the request", ->
  237. @Metrics.Timer::done.called.should.equal true
  238. describe "when an errors occurs", ->
  239. beforeEach ->
  240. @DocumentManager.flushAndDeleteDocWithLock = sinon.stub().callsArgWith(2, new Error("oops"))
  241. @HttpController.flushAndDeleteDoc(@req, @res, @next)
  242. it "should call next with the error", ->
  243. @next
  244. .calledWith(new Error("oops"))
  245. .should.equal true
  246. describe "deleteProject", ->
  247. beforeEach ->
  248. @req =
  249. params:
  250. project_id: @project_id
  251. describe "successfully", ->
  252. beforeEach ->
  253. @ProjectManager.flushAndDeleteProjectWithLocks = sinon.stub().callsArgWith(1)
  254. @HttpController.deleteProject(@req, @res, @next)
  255. it "should delete the project", ->
  256. @ProjectManager.flushAndDeleteProjectWithLocks
  257. .calledWith(@project_id)
  258. .should.equal true
  259. it "should return a successful No Content response", ->
  260. @res.send
  261. .calledWith(204)
  262. .should.equal true
  263. it "should log the request", ->
  264. @logger.log
  265. .calledWith(project_id: @project_id, "deleting project via http")
  266. .should.equal true
  267. it "should time the request", ->
  268. @Metrics.Timer::done.called.should.equal true
  269. describe "when an errors occurs", ->
  270. beforeEach ->
  271. @ProjectManager.flushAndDeleteProjectWithLocks = sinon.stub().callsArgWith(1, new Error("oops"))
  272. @HttpController.deleteProject(@req, @res, @next)
  273. it "should call next with the error", ->
  274. @next
  275. .calledWith(new Error("oops"))
  276. .should.equal true
  277. describe "acceptChange", ->
  278. beforeEach ->
  279. @req =
  280. params:
  281. project_id: @project_id
  282. doc_id: @doc_id
  283. change_id: @change_id = "mock-change-od-1"
  284. describe "successfully", ->
  285. beforeEach ->
  286. @DocumentManager.acceptChangeWithLock = sinon.stub().callsArgWith(3)
  287. @HttpController.acceptChange(@req, @res, @next)
  288. it "should accept the change", ->
  289. @DocumentManager.acceptChangeWithLock
  290. .calledWith(@project_id, @doc_id, @change_id)
  291. .should.equal true
  292. it "should return a successful No Content response", ->
  293. @res.send
  294. .calledWith(204)
  295. .should.equal true
  296. it "should log the request", ->
  297. @logger.log
  298. .calledWith({@project_id, @doc_id, @change_id}, "accepting change via http")
  299. .should.equal true
  300. it "should time the request", ->
  301. @Metrics.Timer::done.called.should.equal true
  302. describe "when an errors occurs", ->
  303. beforeEach ->
  304. @DocumentManager.acceptChangeWithLock = sinon.stub().callsArgWith(3, new Error("oops"))
  305. @HttpController.acceptChange(@req, @res, @next)
  306. it "should call next with the error", ->
  307. @next
  308. .calledWith(new Error("oops"))
  309. .should.equal true
  310. describe "deleteComment", ->
  311. beforeEach ->
  312. @req =
  313. params:
  314. project_id: @project_id
  315. doc_id: @doc_id
  316. comment_id: @comment_id = "mock-comment-id"
  317. describe "successfully", ->
  318. beforeEach ->
  319. @DocumentManager.deleteCommentWithLock = sinon.stub().callsArgWith(3)
  320. @HttpController.deleteComment(@req, @res, @next)
  321. it "should accept the change", ->
  322. @DocumentManager.deleteCommentWithLock
  323. .calledWith(@project_id, @doc_id, @comment_id)
  324. .should.equal true
  325. it "should return a successful No Content response", ->
  326. @res.send
  327. .calledWith(204)
  328. .should.equal true
  329. it "should log the request", ->
  330. @logger.log
  331. .calledWith({@project_id, @doc_id, @comment_id}, "deleting comment via http")
  332. .should.equal true
  333. it "should time the request", ->
  334. @Metrics.Timer::done.called.should.equal true
  335. describe "when an errors occurs", ->
  336. beforeEach ->
  337. @DocumentManager.deleteCommentWithLock = sinon.stub().callsArgWith(3, new Error("oops"))
  338. @HttpController.deleteComment(@req, @res, @next)
  339. it "should call next with the error", ->
  340. @next
  341. .calledWith(new Error("oops"))
  342. .should.equal true