HttpControllerTests.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. /* eslint-disable
  2. no-return-assign,
  3. no-unused-vars,
  4. */
  5. // TODO: This file was created by bulk-decaffeinate.
  6. // Fix any style issues and re-enable lint.
  7. /*
  8. * decaffeinate suggestions:
  9. * DS102: Remove unnecessary code created because of implicit returns
  10. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  11. */
  12. const SandboxedModule = require('sandboxed-module')
  13. const { assert } = require('chai')
  14. const sinon = require('sinon')
  15. const chai = require('chai')
  16. chai.should()
  17. const { expect } = chai
  18. const modulePath = require('path').join(
  19. __dirname,
  20. '../../../app/js/HttpController'
  21. )
  22. const { ObjectId } = require('mongojs')
  23. describe('HttpController', function () {
  24. beforeEach(function () {
  25. this.HttpController = SandboxedModule.require(modulePath, {
  26. requires: {
  27. './DocManager': (this.DocManager = {}),
  28. './DocArchiveManager': (this.DocArchiveManager = {}),
  29. 'logger-sharelatex': (this.logger = {
  30. log: sinon.stub(),
  31. error: sinon.stub()
  32. }),
  33. './HealthChecker': {}
  34. },
  35. globals: { process }
  36. })
  37. this.res = {
  38. send: sinon.stub(),
  39. json: sinon.stub(),
  40. setHeader: sinon.stub()
  41. }
  42. this.res.status = sinon.stub().returns(this.res)
  43. this.req = { query: {} }
  44. this.next = sinon.stub()
  45. this.project_id = 'mock-project-id'
  46. this.doc_id = 'mock-doc-id'
  47. this.doc = {
  48. _id: this.doc_id,
  49. lines: ['mock', 'lines', ' here', '', '', ' spaces '],
  50. version: 42,
  51. rev: 5
  52. }
  53. return (this.deletedDoc = {
  54. deleted: true,
  55. _id: this.doc_id,
  56. lines: ['mock', 'lines', ' here', '', '', ' spaces '],
  57. version: 42,
  58. rev: 5
  59. })
  60. })
  61. describe('getDoc', function () {
  62. describe('without deleted docs', function () {
  63. beforeEach(function () {
  64. this.req.params = {
  65. project_id: this.project_id,
  66. doc_id: this.doc_id
  67. }
  68. this.DocManager.getFullDoc = sinon
  69. .stub()
  70. .callsArgWith(2, null, this.doc)
  71. return this.HttpController.getDoc(this.req, this.res, this.next)
  72. })
  73. it('should get the document with the version (including deleted)', function () {
  74. return this.DocManager.getFullDoc
  75. .calledWith(this.project_id, this.doc_id)
  76. .should.equal(true)
  77. })
  78. return it('should return the doc as JSON', function () {
  79. return this.res.json
  80. .calledWith({
  81. _id: this.doc_id,
  82. lines: this.doc.lines,
  83. rev: this.doc.rev,
  84. version: this.doc.version
  85. })
  86. .should.equal(true)
  87. })
  88. })
  89. return describe('which is deleted', function () {
  90. beforeEach(function () {
  91. this.req.params = {
  92. project_id: this.project_id,
  93. doc_id: this.doc_id
  94. }
  95. return (this.DocManager.getFullDoc = sinon
  96. .stub()
  97. .callsArgWith(2, null, this.deletedDoc))
  98. })
  99. it('should get the doc from the doc manager', function () {
  100. this.HttpController.getDoc(this.req, this.res, this.next)
  101. return this.DocManager.getFullDoc
  102. .calledWith(this.project_id, this.doc_id)
  103. .should.equal(true)
  104. })
  105. it('should return 404 if the query string delete is not set ', function () {
  106. this.HttpController.getDoc(this.req, this.res, this.next)
  107. return this.res.send.calledWith(404).should.equal(true)
  108. })
  109. return it('should return the doc as JSON if include_deleted is set to true', function () {
  110. this.req.query.include_deleted = 'true'
  111. this.HttpController.getDoc(this.req, this.res, this.next)
  112. return this.res.json
  113. .calledWith({
  114. _id: this.doc_id,
  115. lines: this.doc.lines,
  116. rev: this.doc.rev,
  117. deleted: true,
  118. version: this.doc.version
  119. })
  120. .should.equal(true)
  121. })
  122. })
  123. })
  124. describe('getRawDoc', function () {
  125. beforeEach(function () {
  126. this.req.params = {
  127. project_id: this.project_id,
  128. doc_id: this.doc_id
  129. }
  130. this.DocManager.getDocLines = sinon.stub().callsArgWith(2, null, this.doc)
  131. return this.HttpController.getRawDoc(this.req, this.res, this.next)
  132. })
  133. it('should get the document without the version', function () {
  134. return this.DocManager.getDocLines
  135. .calledWith(this.project_id, this.doc_id)
  136. .should.equal(true)
  137. })
  138. it('should set the content type header', function () {
  139. return this.res.setHeader
  140. .calledWith('content-type', 'text/plain')
  141. .should.equal(true)
  142. })
  143. return it('should send the raw version of the doc', function () {
  144. return assert.deepEqual(
  145. this.res.send.args[0][0],
  146. `${this.doc.lines[0]}\n${this.doc.lines[1]}\n${this.doc.lines[2]}\n${this.doc.lines[3]}\n${this.doc.lines[4]}\n${this.doc.lines[5]}`
  147. )
  148. })
  149. })
  150. describe('getAllDocs', function () {
  151. describe('normally', function () {
  152. beforeEach(function () {
  153. this.req.params = { project_id: this.project_id }
  154. this.docs = [
  155. {
  156. _id: ObjectId(),
  157. lines: ['mock', 'lines', 'one'],
  158. rev: 2
  159. },
  160. {
  161. _id: ObjectId(),
  162. lines: ['mock', 'lines', 'two'],
  163. rev: 4
  164. }
  165. ]
  166. this.DocManager.getAllNonDeletedDocs = sinon
  167. .stub()
  168. .callsArgWith(2, null, this.docs)
  169. return this.HttpController.getAllDocs(this.req, this.res, this.next)
  170. })
  171. it('should get all the (non-deleted) docs', function () {
  172. return this.DocManager.getAllNonDeletedDocs
  173. .calledWith(this.project_id, { lines: true, rev: true })
  174. .should.equal(true)
  175. })
  176. return it('should return the doc as JSON', function () {
  177. return this.res.json
  178. .calledWith([
  179. {
  180. _id: this.docs[0]._id.toString(),
  181. lines: this.docs[0].lines,
  182. rev: this.docs[0].rev
  183. },
  184. {
  185. _id: this.docs[1]._id.toString(),
  186. lines: this.docs[1].lines,
  187. rev: this.docs[1].rev
  188. }
  189. ])
  190. .should.equal(true)
  191. })
  192. })
  193. return describe('with a null doc', function () {
  194. beforeEach(function () {
  195. this.req.params = { project_id: this.project_id }
  196. this.docs = [
  197. {
  198. _id: ObjectId(),
  199. lines: ['mock', 'lines', 'one'],
  200. rev: 2
  201. },
  202. null,
  203. {
  204. _id: ObjectId(),
  205. lines: ['mock', 'lines', 'two'],
  206. rev: 4
  207. }
  208. ]
  209. this.DocManager.getAllNonDeletedDocs = sinon
  210. .stub()
  211. .callsArgWith(2, null, this.docs)
  212. return this.HttpController.getAllDocs(this.req, this.res, this.next)
  213. })
  214. it('should return the non null docs as JSON', function () {
  215. return this.res.json
  216. .calledWith([
  217. {
  218. _id: this.docs[0]._id.toString(),
  219. lines: this.docs[0].lines,
  220. rev: this.docs[0].rev
  221. },
  222. {
  223. _id: this.docs[2]._id.toString(),
  224. lines: this.docs[2].lines,
  225. rev: this.docs[2].rev
  226. }
  227. ])
  228. .should.equal(true)
  229. })
  230. return it('should log out an error', function () {
  231. return this.logger.error
  232. .calledWith(
  233. {
  234. err: sinon.match.has('message', 'null doc'),
  235. project_id: this.project_id
  236. },
  237. 'encountered null doc'
  238. )
  239. .should.equal(true)
  240. })
  241. })
  242. })
  243. describe('getAllRanges', function () {
  244. return describe('normally', function () {
  245. beforeEach(function () {
  246. this.req.params = { project_id: this.project_id }
  247. this.docs = [
  248. {
  249. _id: ObjectId(),
  250. ranges: { mock_ranges: 'one' }
  251. },
  252. {
  253. _id: ObjectId(),
  254. ranges: { mock_ranges: 'two' }
  255. }
  256. ]
  257. this.DocManager.getAllNonDeletedDocs = sinon
  258. .stub()
  259. .callsArgWith(2, null, this.docs)
  260. return this.HttpController.getAllRanges(this.req, this.res, this.next)
  261. })
  262. it('should get all the (non-deleted) doc ranges', function () {
  263. return this.DocManager.getAllNonDeletedDocs
  264. .calledWith(this.project_id, { ranges: true })
  265. .should.equal(true)
  266. })
  267. return it('should return the doc as JSON', function () {
  268. return this.res.json
  269. .calledWith([
  270. {
  271. _id: this.docs[0]._id.toString(),
  272. ranges: this.docs[0].ranges
  273. },
  274. {
  275. _id: this.docs[1]._id.toString(),
  276. ranges: this.docs[1].ranges
  277. }
  278. ])
  279. .should.equal(true)
  280. })
  281. })
  282. })
  283. describe('updateDoc', function () {
  284. beforeEach(function () {
  285. return (this.req.params = {
  286. project_id: this.project_id,
  287. doc_id: this.doc_id
  288. })
  289. })
  290. describe('when the doc lines exist and were updated', function () {
  291. beforeEach(function () {
  292. this.req.body = {
  293. lines: (this.lines = ['hello', 'world']),
  294. version: (this.version = 42),
  295. ranges: (this.ranges = { changes: 'mock' })
  296. }
  297. this.DocManager.updateDoc = sinon
  298. .stub()
  299. .yields(null, true, (this.rev = 5))
  300. return this.HttpController.updateDoc(this.req, this.res, this.next)
  301. })
  302. it('should update the document', function () {
  303. return this.DocManager.updateDoc
  304. .calledWith(
  305. this.project_id,
  306. this.doc_id,
  307. this.lines,
  308. this.version,
  309. this.ranges
  310. )
  311. .should.equal(true)
  312. })
  313. return it('should return a modified status', function () {
  314. return this.res.json
  315. .calledWith({ modified: true, rev: this.rev })
  316. .should.equal(true)
  317. })
  318. })
  319. describe('when the doc lines exist and were not updated', function () {
  320. beforeEach(function () {
  321. this.req.body = {
  322. lines: (this.lines = ['hello', 'world']),
  323. version: (this.version = 42),
  324. ranges: {}
  325. }
  326. this.DocManager.updateDoc = sinon
  327. .stub()
  328. .yields(null, false, (this.rev = 5))
  329. return this.HttpController.updateDoc(this.req, this.res, this.next)
  330. })
  331. return it('should return a modified status', function () {
  332. return this.res.json
  333. .calledWith({ modified: false, rev: this.rev })
  334. .should.equal(true)
  335. })
  336. })
  337. describe('when the doc lines are not provided', function () {
  338. beforeEach(function () {
  339. this.req.body = { version: 42, ranges: {} }
  340. this.DocManager.updateDoc = sinon.stub().yields(null, false)
  341. return this.HttpController.updateDoc(this.req, this.res, this.next)
  342. })
  343. it('should not update the document', function () {
  344. return this.DocManager.updateDoc.called.should.equal(false)
  345. })
  346. return it('should return a 400 (bad request) response', function () {
  347. return this.res.send.calledWith(400).should.equal(true)
  348. })
  349. })
  350. describe('when the doc version are not provided', function () {
  351. beforeEach(function () {
  352. this.req.body = { version: 42, lines: ['hello world'] }
  353. this.DocManager.updateDoc = sinon.stub().yields(null, false)
  354. return this.HttpController.updateDoc(this.req, this.res, this.next)
  355. })
  356. it('should not update the document', function () {
  357. return this.DocManager.updateDoc.called.should.equal(false)
  358. })
  359. return it('should return a 400 (bad request) response', function () {
  360. return this.res.send.calledWith(400).should.equal(true)
  361. })
  362. })
  363. describe('when the doc ranges is not provided', function () {
  364. beforeEach(function () {
  365. this.req.body = { lines: ['foo'], version: 42 }
  366. this.DocManager.updateDoc = sinon.stub().yields(null, false)
  367. return this.HttpController.updateDoc(this.req, this.res, this.next)
  368. })
  369. it('should not update the document', function () {
  370. return this.DocManager.updateDoc.called.should.equal(false)
  371. })
  372. return it('should return a 400 (bad request) response', function () {
  373. return this.res.send.calledWith(400).should.equal(true)
  374. })
  375. })
  376. return describe('when the doc body is too large', function () {
  377. beforeEach(function () {
  378. this.req.body = {
  379. lines: (this.lines = Array(2049).fill('a'.repeat(1024))),
  380. version: (this.version = 42),
  381. ranges: (this.ranges = { changes: 'mock' })
  382. }
  383. return this.HttpController.updateDoc(this.req, this.res, this.next)
  384. })
  385. it('should return a 413 (too large) response', function () {
  386. return sinon.assert.calledWith(this.res.status, 413)
  387. })
  388. return it('should report that the document body is too large', function () {
  389. return sinon.assert.calledWith(this.res.send, 'document body too large')
  390. })
  391. })
  392. })
  393. describe('deleteDoc', function () {
  394. beforeEach(function () {
  395. this.req.params = {
  396. project_id: this.project_id,
  397. doc_id: this.doc_id
  398. }
  399. this.DocManager.deleteDoc = sinon.stub().callsArg(2)
  400. return this.HttpController.deleteDoc(this.req, this.res, this.next)
  401. })
  402. it('should delete the document', function () {
  403. return this.DocManager.deleteDoc
  404. .calledWith(this.project_id, this.doc_id)
  405. .should.equal(true)
  406. })
  407. return it('should return a 204 (No Content)', function () {
  408. return this.res.send.calledWith(204).should.equal(true)
  409. })
  410. })
  411. describe('archiveAllDocs', function () {
  412. beforeEach(function () {
  413. this.req.params = { project_id: this.project_id }
  414. this.DocArchiveManager.archiveAllDocs = sinon.stub().callsArg(1)
  415. return this.HttpController.archiveAllDocs(this.req, this.res, this.next)
  416. })
  417. it('should archive the project', function () {
  418. return this.DocArchiveManager.archiveAllDocs
  419. .calledWith(this.project_id)
  420. .should.equal(true)
  421. })
  422. return it('should return a 204 (No Content)', function () {
  423. return this.res.send.calledWith(204).should.equal(true)
  424. })
  425. })
  426. return describe('destroyAllDocs', function () {
  427. beforeEach(function () {
  428. this.req.params = { project_id: this.project_id }
  429. this.DocArchiveManager.destroyAllDocs = sinon.stub().callsArg(1)
  430. return this.HttpController.destroyAllDocs(this.req, this.res, this.next)
  431. })
  432. it('should destroy the docs', function () {
  433. return sinon.assert.calledWith(
  434. this.DocArchiveManager.destroyAllDocs,
  435. this.project_id
  436. )
  437. })
  438. return it('should return 204', function () {
  439. return sinon.assert.calledWith(this.res.send, 204)
  440. })
  441. })
  442. })