HttpControllerTests.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. SandboxedModule = require('sandboxed-module')
  2. assert = require("chai").assert
  3. sinon = require('sinon')
  4. chai = require('chai')
  5. chai.should()
  6. expect = chai.expect
  7. modulePath = require('path').join __dirname, '../../../app/js/HttpController'
  8. ObjectId = require("mongojs").ObjectId
  9. describe "HttpController", ->
  10. beforeEach ->
  11. @HttpController = SandboxedModule.require modulePath, requires:
  12. "./DocManager": @DocManager = {}
  13. "./DocArchiveManager": @DocArchiveManager = {}
  14. "logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() }
  15. "./HealthChecker": {}
  16. @res = { send: sinon.stub(), json: sinon.stub(), setHeader:sinon.stub() }
  17. @res.status = sinon.stub().returns(@res)
  18. @req = { query:{}}
  19. @next = sinon.stub()
  20. @project_id = "mock-project-id"
  21. @doc_id = "mock-doc-id"
  22. @doc = {
  23. _id: @doc_id
  24. lines: ["mock", "lines", " here", "", "", " spaces "]
  25. version: 42
  26. rev: 5
  27. }
  28. @deletedDoc = {
  29. deleted:true
  30. _id: @doc_id
  31. lines: ["mock", "lines", " here", "", "", " spaces "]
  32. version: 42
  33. rev: 5
  34. }
  35. describe "getDoc", ->
  36. describe "without deleted docs", ->
  37. beforeEach ->
  38. @req.params =
  39. project_id: @project_id
  40. doc_id: @doc_id
  41. @DocManager.getFullDoc = sinon.stub().callsArgWith(2, null, @doc)
  42. @HttpController.getDoc @req, @res, @next
  43. it "should get the document with the version (including deleted)", ->
  44. @DocManager.getFullDoc
  45. .calledWith(@project_id, @doc_id)
  46. .should.equal true
  47. it "should return the doc as JSON", ->
  48. @res.json
  49. .calledWith({
  50. _id: @doc_id
  51. lines: @doc.lines
  52. rev: @doc.rev
  53. version: @doc.version
  54. })
  55. .should.equal true
  56. describe "which is deleted", ->
  57. beforeEach ->
  58. @req.params =
  59. project_id: @project_id
  60. doc_id: @doc_id
  61. @DocManager.getFullDoc = sinon.stub().callsArgWith(2, null, @deletedDoc)
  62. it "should get the doc from the doc manager", ->
  63. @HttpController.getDoc @req, @res, @next
  64. @DocManager.getFullDoc.calledWith(@project_id, @doc_id).should.equal true
  65. it "should return 404 if the query string delete is not set ", ->
  66. @HttpController.getDoc @req, @res, @next
  67. @res.send.calledWith(404).should.equal true
  68. it "should return the doc as JSON if include_deleted is set to true", ->
  69. @req.query.include_deleted = "true"
  70. @HttpController.getDoc @req, @res, @next
  71. @res.json
  72. .calledWith({
  73. _id: @doc_id
  74. lines: @doc.lines
  75. rev: @doc.rev
  76. deleted: true
  77. version: @doc.version
  78. })
  79. .should.equal true
  80. describe "getRawDoc", ->
  81. beforeEach ->
  82. @req.params =
  83. project_id: @project_id
  84. doc_id: @doc_id
  85. @DocManager.getDocLines = sinon.stub().callsArgWith(2, null, @doc)
  86. @HttpController.getRawDoc @req, @res, @next
  87. it "should get the document without the version", ->
  88. @DocManager.getDocLines
  89. .calledWith(@project_id, @doc_id)
  90. .should.equal true
  91. it "should set the content type header", ->
  92. @res.setHeader.calledWith('content-type', 'text/plain').should.equal true
  93. it "should send the raw version of the doc", ->
  94. assert.deepEqual @res.send.args[0][0], "#{@doc.lines[0]}\n#{@doc.lines[1]}\n#{@doc.lines[2]}\n#{@doc.lines[3]}\n#{@doc.lines[4]}\n#{@doc.lines[5]}"
  95. describe "getAllDocs", ->
  96. describe "normally", ->
  97. beforeEach ->
  98. @req.params =
  99. project_id: @project_id
  100. @docs = [{
  101. _id: ObjectId()
  102. lines: ["mock", "lines", "one"]
  103. rev: 2
  104. }, {
  105. _id: ObjectId()
  106. lines: ["mock", "lines", "two"]
  107. rev: 4
  108. }]
  109. @DocManager.getAllNonDeletedDocs = sinon.stub().callsArgWith(2, null, @docs)
  110. @HttpController.getAllDocs @req, @res, @next
  111. it "should get all the (non-deleted) docs", ->
  112. @DocManager.getAllNonDeletedDocs
  113. .calledWith(@project_id, {lines: true, rev: true})
  114. .should.equal true
  115. it "should return the doc as JSON", ->
  116. @res.json
  117. .calledWith([{
  118. _id: @docs[0]._id.toString()
  119. lines: @docs[0].lines
  120. rev: @docs[0].rev
  121. }, {
  122. _id: @docs[1]._id.toString()
  123. lines: @docs[1].lines
  124. rev: @docs[1].rev
  125. }])
  126. .should.equal true
  127. describe "with a null doc", ->
  128. beforeEach ->
  129. @req.params =
  130. project_id: @project_id
  131. @docs = [{
  132. _id: ObjectId()
  133. lines: ["mock", "lines", "one"]
  134. rev: 2
  135. },
  136. null,
  137. {
  138. _id: ObjectId()
  139. lines: ["mock", "lines", "two"]
  140. rev: 4
  141. }]
  142. @DocManager.getAllNonDeletedDocs = sinon.stub().callsArgWith(2, null, @docs)
  143. @HttpController.getAllDocs @req, @res, @next
  144. it "should return the non null docs as JSON", ->
  145. @res.json
  146. .calledWith([{
  147. _id: @docs[0]._id.toString()
  148. lines: @docs[0].lines
  149. rev: @docs[0].rev
  150. }, {
  151. _id: @docs[2]._id.toString()
  152. lines: @docs[2].lines
  153. rev: @docs[2].rev
  154. }])
  155. .should.equal true
  156. it "should log out an error", ->
  157. @logger.error
  158. .calledWith(
  159. err: sinon.match.has('message', "null doc")
  160. project_id: @project_id
  161. "encountered null doc"
  162. )
  163. .should.equal true
  164. describe "getAllRanges", ->
  165. describe "normally", ->
  166. beforeEach ->
  167. @req.params =
  168. project_id: @project_id
  169. @docs = [{
  170. _id: ObjectId()
  171. ranges: {"mock_ranges": "one"}
  172. }, {
  173. _id: ObjectId()
  174. ranges: {"mock_ranges": "two"}
  175. }]
  176. @DocManager.getAllNonDeletedDocs = sinon.stub().callsArgWith(2, null, @docs)
  177. @HttpController.getAllRanges @req, @res, @next
  178. it "should get all the (non-deleted) doc ranges", ->
  179. @DocManager.getAllNonDeletedDocs
  180. .calledWith(@project_id, {ranges: true})
  181. .should.equal true
  182. it "should return the doc as JSON", ->
  183. @res.json
  184. .calledWith([{
  185. _id: @docs[0]._id.toString()
  186. ranges: @docs[0].ranges
  187. }, {
  188. _id: @docs[1]._id.toString()
  189. ranges: @docs[1].ranges
  190. }])
  191. .should.equal true
  192. describe "updateDoc", ->
  193. beforeEach ->
  194. @req.params =
  195. project_id: @project_id
  196. doc_id: @doc_id
  197. describe "when the doc lines exist and were updated", ->
  198. beforeEach ->
  199. @req.body =
  200. lines: @lines = ["hello", "world"]
  201. version: @version = 42
  202. ranges: @ranges = { changes: "mock" }
  203. @DocManager.updateDoc = sinon.stub().yields(null, true, @rev = 5)
  204. @HttpController.updateDoc @req, @res, @next
  205. it "should update the document", ->
  206. @DocManager.updateDoc
  207. .calledWith(@project_id, @doc_id, @lines, @version, @ranges)
  208. .should.equal true
  209. it "should return a modified status", ->
  210. @res.json
  211. .calledWith(modified: true, rev: @rev)
  212. .should.equal true
  213. describe "when the doc lines exist and were not updated", ->
  214. beforeEach ->
  215. @req.body =
  216. lines: @lines = ["hello", "world"]
  217. version: @version = 42
  218. ranges: {}
  219. @DocManager.updateDoc = sinon.stub().yields(null, false, @rev = 5)
  220. @HttpController.updateDoc @req, @res, @next
  221. it "should return a modified status", ->
  222. @res.json
  223. .calledWith(modified: false, rev: @rev)
  224. .should.equal true
  225. describe "when the doc lines are not provided", ->
  226. beforeEach ->
  227. @req.body = { version: 42, ranges: {} }
  228. @DocManager.updateDoc = sinon.stub().yields(null, false)
  229. @HttpController.updateDoc @req, @res, @next
  230. it "should not update the document", ->
  231. @DocManager.updateDoc.called.should.equal false
  232. it "should return a 400 (bad request) response", ->
  233. @res.send
  234. .calledWith(400)
  235. .should.equal true
  236. describe "when the doc version are not provided", ->
  237. beforeEach ->
  238. @req.body = { version: 42, lines: ["hello world"] }
  239. @DocManager.updateDoc = sinon.stub().yields(null, false)
  240. @HttpController.updateDoc @req, @res, @next
  241. it "should not update the document", ->
  242. @DocManager.updateDoc.called.should.equal false
  243. it "should return a 400 (bad request) response", ->
  244. @res.send
  245. .calledWith(400)
  246. .should.equal true
  247. describe "when the doc ranges is not provided", ->
  248. beforeEach ->
  249. @req.body = { lines : [ "foo" ], version: 42 }
  250. @DocManager.updateDoc = sinon.stub().yields(null, false)
  251. @HttpController.updateDoc @req, @res, @next
  252. it "should not update the document", ->
  253. @DocManager.updateDoc.called.should.equal false
  254. it "should return a 400 (bad request) response", ->
  255. @res.send
  256. .calledWith(400)
  257. .should.equal true
  258. describe "when the doc body is too large", ->
  259. beforeEach ->
  260. @req.body =
  261. lines: @lines = Array(2049).fill('a'.repeat(1024))
  262. version: @version = 42
  263. ranges: @ranges = { changes: "mock" }
  264. @HttpController.updateDoc @req, @res, @next
  265. it "should return a 413 (too large) response", ->
  266. sinon.assert.calledWith(@res.status, 413)
  267. it "should report that the document body is too large", ->
  268. sinon.assert.calledWith(@res.send, "document body too large")
  269. describe "deleteDoc", ->
  270. beforeEach ->
  271. @req.params =
  272. project_id: @project_id
  273. doc_id: @doc_id
  274. @DocManager.deleteDoc = sinon.stub().callsArg(2)
  275. @HttpController.deleteDoc @req, @res, @next
  276. it "should delete the document", ->
  277. @DocManager.deleteDoc
  278. .calledWith(@project_id, @doc_id)
  279. .should.equal true
  280. it "should return a 204 (No Content)", ->
  281. @res.send
  282. .calledWith(204)
  283. .should.equal true
  284. describe "archiveAllDocs", ->
  285. beforeEach ->
  286. @req.params =
  287. project_id: @project_id
  288. @DocArchiveManager.archiveAllDocs = sinon.stub().callsArg(1)
  289. @HttpController.archiveAllDocs @req, @res, @next
  290. it "should archive the project", ->
  291. @DocArchiveManager.archiveAllDocs
  292. .calledWith(@project_id)
  293. .should.equal true
  294. it "should return a 204 (No Content)", ->
  295. @res.send
  296. .calledWith(204)
  297. .should.equal true
  298. describe "destroyAllDocs", ->
  299. beforeEach ->
  300. @req.params =
  301. project_id: @project_id
  302. @DocArchiveManager.destroyAllDocs = sinon.stub().callsArg(1)
  303. @HttpController.destroyAllDocs @req, @res, @next
  304. it "should destroy the docs", ->
  305. sinon.assert.calledWith(@DocArchiveManager.destroyAllDocs, @project_id)
  306. it "should return 204", ->
  307. sinon.assert.calledWith(@res.send, 204)