HttpControllerTests.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952
  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. * DS206: Consider reworking classes to avoid initClass
  11. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  12. */
  13. const sinon = require('sinon')
  14. const chai = require('chai')
  15. const should = chai.should()
  16. const modulePath = '../../../../app/js/HttpController.js'
  17. const SandboxedModule = require('sandboxed-module')
  18. const Errors = require('../../../../app/js/Errors.js')
  19. describe('HttpController', function () {
  20. beforeEach(function () {
  21. let Timer
  22. this.HttpController = SandboxedModule.require(modulePath, {
  23. requires: {
  24. './DocumentManager': (this.DocumentManager = {}),
  25. './HistoryManager': (this.HistoryManager = {
  26. flushProjectChangesAsync: sinon.stub()
  27. }),
  28. './ProjectManager': (this.ProjectManager = {}),
  29. 'logger-sharelatex': (this.logger = { log: sinon.stub() }),
  30. './ProjectFlusher': { flushAllProjects() {} },
  31. './DeleteQueueManager': (this.DeleteQueueManager = {}),
  32. './Metrics': (this.Metrics = {}),
  33. './Errors': Errors
  34. }
  35. })
  36. this.Metrics.Timer = Timer = (function () {
  37. Timer = class Timer {
  38. static initClass() {
  39. this.prototype.done = sinon.stub()
  40. }
  41. }
  42. Timer.initClass()
  43. return Timer
  44. })()
  45. this.project_id = 'project-id-123'
  46. this.doc_id = 'doc-id-123'
  47. this.next = sinon.stub()
  48. return (this.res = {
  49. send: sinon.stub(),
  50. sendStatus: sinon.stub(),
  51. json: sinon.stub()
  52. })
  53. })
  54. describe('getDoc', function () {
  55. beforeEach(function () {
  56. this.lines = ['one', 'two', 'three']
  57. this.ops = ['mock-op-1', 'mock-op-2']
  58. this.version = 42
  59. this.fromVersion = 42
  60. this.ranges = { changes: 'mock', comments: 'mock' }
  61. this.pathname = '/a/b/c'
  62. return (this.req = {
  63. params: {
  64. project_id: this.project_id,
  65. doc_id: this.doc_id
  66. }
  67. })
  68. })
  69. describe('when the document exists and no recent ops are requested', function () {
  70. beforeEach(function () {
  71. this.DocumentManager.getDocAndRecentOpsWithLock = sinon
  72. .stub()
  73. .callsArgWith(
  74. 3,
  75. null,
  76. this.lines,
  77. this.version,
  78. [],
  79. this.ranges,
  80. this.pathname
  81. )
  82. return this.HttpController.getDoc(this.req, this.res, this.next)
  83. })
  84. it('should get the doc', function () {
  85. return this.DocumentManager.getDocAndRecentOpsWithLock
  86. .calledWith(this.project_id, this.doc_id, -1)
  87. .should.equal(true)
  88. })
  89. it('should return the doc as JSON', function () {
  90. return this.res.json
  91. .calledWith({
  92. id: this.doc_id,
  93. lines: this.lines,
  94. version: this.version,
  95. ops: [],
  96. ranges: this.ranges,
  97. pathname: this.pathname
  98. })
  99. .should.equal(true)
  100. })
  101. it('should log the request', function () {
  102. return this.logger.log
  103. .calledWith(
  104. { doc_id: this.doc_id, project_id: this.project_id },
  105. 'getting doc via http'
  106. )
  107. .should.equal(true)
  108. })
  109. return it('should time the request', function () {
  110. return this.Metrics.Timer.prototype.done.called.should.equal(true)
  111. })
  112. })
  113. describe('when recent ops are requested', function () {
  114. beforeEach(function () {
  115. this.DocumentManager.getDocAndRecentOpsWithLock = sinon
  116. .stub()
  117. .callsArgWith(
  118. 3,
  119. null,
  120. this.lines,
  121. this.version,
  122. this.ops,
  123. this.ranges,
  124. this.pathname
  125. )
  126. this.req.query = { fromVersion: `${this.fromVersion}` }
  127. return this.HttpController.getDoc(this.req, this.res, this.next)
  128. })
  129. it('should get the doc', function () {
  130. return this.DocumentManager.getDocAndRecentOpsWithLock
  131. .calledWith(this.project_id, this.doc_id, this.fromVersion)
  132. .should.equal(true)
  133. })
  134. it('should return the doc as JSON', function () {
  135. return this.res.json
  136. .calledWith({
  137. id: this.doc_id,
  138. lines: this.lines,
  139. version: this.version,
  140. ops: this.ops,
  141. ranges: this.ranges,
  142. pathname: this.pathname
  143. })
  144. .should.equal(true)
  145. })
  146. it('should log the request', function () {
  147. return this.logger.log
  148. .calledWith(
  149. { doc_id: this.doc_id, project_id: this.project_id },
  150. 'getting doc via http'
  151. )
  152. .should.equal(true)
  153. })
  154. return it('should time the request', function () {
  155. return this.Metrics.Timer.prototype.done.called.should.equal(true)
  156. })
  157. })
  158. describe('when the document does not exist', function () {
  159. beforeEach(function () {
  160. this.DocumentManager.getDocAndRecentOpsWithLock = sinon
  161. .stub()
  162. .callsArgWith(3, null, null, null)
  163. return this.HttpController.getDoc(this.req, this.res, this.next)
  164. })
  165. return it('should call next with NotFoundError', function () {
  166. return this.next
  167. .calledWith(new Errors.NotFoundError('not found'))
  168. .should.equal(true)
  169. })
  170. })
  171. return describe('when an errors occurs', function () {
  172. beforeEach(function () {
  173. this.DocumentManager.getDocAndRecentOpsWithLock = sinon
  174. .stub()
  175. .callsArgWith(3, new Error('oops'), null, null)
  176. return this.HttpController.getDoc(this.req, this.res, this.next)
  177. })
  178. return it('should call next with the error', function () {
  179. return this.next.calledWith(new Error('oops')).should.equal(true)
  180. })
  181. })
  182. })
  183. describe('setDoc', function () {
  184. beforeEach(function () {
  185. this.lines = ['one', 'two', 'three']
  186. this.source = 'dropbox'
  187. this.user_id = 'user-id-123'
  188. return (this.req = {
  189. headers: {},
  190. params: {
  191. project_id: this.project_id,
  192. doc_id: this.doc_id
  193. },
  194. body: {
  195. lines: this.lines,
  196. source: this.source,
  197. user_id: this.user_id,
  198. undoing: (this.undoing = true)
  199. }
  200. })
  201. })
  202. describe('successfully', function () {
  203. beforeEach(function () {
  204. this.DocumentManager.setDocWithLock = sinon.stub().callsArgWith(6)
  205. return this.HttpController.setDoc(this.req, this.res, this.next)
  206. })
  207. it('should set the doc', function () {
  208. return this.DocumentManager.setDocWithLock
  209. .calledWith(
  210. this.project_id,
  211. this.doc_id,
  212. this.lines,
  213. this.source,
  214. this.user_id,
  215. this.undoing
  216. )
  217. .should.equal(true)
  218. })
  219. it('should return a successful No Content response', function () {
  220. return this.res.sendStatus.calledWith(204).should.equal(true)
  221. })
  222. it('should log the request', function () {
  223. return this.logger.log
  224. .calledWith(
  225. {
  226. doc_id: this.doc_id,
  227. project_id: this.project_id,
  228. lines: this.lines,
  229. source: this.source,
  230. user_id: this.user_id,
  231. undoing: this.undoing
  232. },
  233. 'setting doc via http'
  234. )
  235. .should.equal(true)
  236. })
  237. return it('should time the request', function () {
  238. return this.Metrics.Timer.prototype.done.called.should.equal(true)
  239. })
  240. })
  241. describe('when an errors occurs', function () {
  242. beforeEach(function () {
  243. this.DocumentManager.setDocWithLock = sinon
  244. .stub()
  245. .callsArgWith(6, new Error('oops'))
  246. return this.HttpController.setDoc(this.req, this.res, this.next)
  247. })
  248. return it('should call next with the error', function () {
  249. return this.next.calledWith(new Error('oops')).should.equal(true)
  250. })
  251. })
  252. return describe('when the payload is too large', function () {
  253. beforeEach(function () {
  254. const lines = []
  255. for (let _ = 0; _ <= 200000; _++) {
  256. lines.push('test test test')
  257. }
  258. this.req.body.lines = lines
  259. this.DocumentManager.setDocWithLock = sinon.stub().callsArgWith(6)
  260. return this.HttpController.setDoc(this.req, this.res, this.next)
  261. })
  262. it('should send back a 406 response', function () {
  263. return this.res.sendStatus.calledWith(406).should.equal(true)
  264. })
  265. return it('should not call setDocWithLock', function () {
  266. return this.DocumentManager.setDocWithLock.callCount.should.equal(0)
  267. })
  268. })
  269. })
  270. describe('flushProject', function () {
  271. beforeEach(function () {
  272. return (this.req = {
  273. params: {
  274. project_id: this.project_id
  275. }
  276. })
  277. })
  278. describe('successfully', function () {
  279. beforeEach(function () {
  280. this.ProjectManager.flushProjectWithLocks = sinon.stub().callsArgWith(1)
  281. return this.HttpController.flushProject(this.req, this.res, this.next)
  282. })
  283. it('should flush the project', function () {
  284. return this.ProjectManager.flushProjectWithLocks
  285. .calledWith(this.project_id)
  286. .should.equal(true)
  287. })
  288. it('should return a successful No Content response', function () {
  289. return this.res.sendStatus.calledWith(204).should.equal(true)
  290. })
  291. it('should log the request', function () {
  292. return this.logger.log
  293. .calledWith(
  294. { project_id: this.project_id },
  295. 'flushing project via http'
  296. )
  297. .should.equal(true)
  298. })
  299. return it('should time the request', function () {
  300. return this.Metrics.Timer.prototype.done.called.should.equal(true)
  301. })
  302. })
  303. return describe('when an errors occurs', function () {
  304. beforeEach(function () {
  305. this.ProjectManager.flushProjectWithLocks = sinon
  306. .stub()
  307. .callsArgWith(1, new Error('oops'))
  308. return this.HttpController.flushProject(this.req, this.res, this.next)
  309. })
  310. return it('should call next with the error', function () {
  311. return this.next.calledWith(new Error('oops')).should.equal(true)
  312. })
  313. })
  314. })
  315. describe('flushDocIfLoaded', function () {
  316. beforeEach(function () {
  317. this.lines = ['one', 'two', 'three']
  318. this.version = 42
  319. return (this.req = {
  320. params: {
  321. project_id: this.project_id,
  322. doc_id: this.doc_id
  323. }
  324. })
  325. })
  326. describe('successfully', function () {
  327. beforeEach(function () {
  328. this.DocumentManager.flushDocIfLoadedWithLock = sinon
  329. .stub()
  330. .callsArgWith(2)
  331. return this.HttpController.flushDocIfLoaded(
  332. this.req,
  333. this.res,
  334. this.next
  335. )
  336. })
  337. it('should flush the doc', function () {
  338. return this.DocumentManager.flushDocIfLoadedWithLock
  339. .calledWith(this.project_id, this.doc_id)
  340. .should.equal(true)
  341. })
  342. it('should return a successful No Content response', function () {
  343. return this.res.sendStatus.calledWith(204).should.equal(true)
  344. })
  345. it('should log the request', function () {
  346. return this.logger.log
  347. .calledWith(
  348. { doc_id: this.doc_id, project_id: this.project_id },
  349. 'flushing doc via http'
  350. )
  351. .should.equal(true)
  352. })
  353. return it('should time the request', function () {
  354. return this.Metrics.Timer.prototype.done.called.should.equal(true)
  355. })
  356. })
  357. return describe('when an errors occurs', function () {
  358. beforeEach(function () {
  359. this.DocumentManager.flushDocIfLoadedWithLock = sinon
  360. .stub()
  361. .callsArgWith(2, new Error('oops'))
  362. return this.HttpController.flushDocIfLoaded(
  363. this.req,
  364. this.res,
  365. this.next
  366. )
  367. })
  368. return it('should call next with the error', function () {
  369. return this.next.calledWith(new Error('oops')).should.equal(true)
  370. })
  371. })
  372. })
  373. describe('deleteDoc', function () {
  374. beforeEach(function () {
  375. return (this.req = {
  376. params: {
  377. project_id: this.project_id,
  378. doc_id: this.doc_id
  379. },
  380. query: {}
  381. })
  382. })
  383. describe('successfully', function () {
  384. beforeEach(function () {
  385. this.DocumentManager.flushAndDeleteDocWithLock = sinon
  386. .stub()
  387. .callsArgWith(3)
  388. return this.HttpController.deleteDoc(this.req, this.res, this.next)
  389. })
  390. it('should flush and delete the doc', function () {
  391. return this.DocumentManager.flushAndDeleteDocWithLock
  392. .calledWith(this.project_id, this.doc_id, {
  393. ignoreFlushErrors: false
  394. })
  395. .should.equal(true)
  396. })
  397. it('should flush project history', function () {
  398. return this.HistoryManager.flushProjectChangesAsync
  399. .calledWithExactly(this.project_id)
  400. .should.equal(true)
  401. })
  402. it('should return a successful No Content response', function () {
  403. return this.res.sendStatus.calledWith(204).should.equal(true)
  404. })
  405. it('should log the request', function () {
  406. return this.logger.log
  407. .calledWith(
  408. { doc_id: this.doc_id, project_id: this.project_id },
  409. 'deleting doc via http'
  410. )
  411. .should.equal(true)
  412. })
  413. return it('should time the request', function () {
  414. return this.Metrics.Timer.prototype.done.called.should.equal(true)
  415. })
  416. })
  417. describe('ignoring errors', function () {
  418. beforeEach(function () {
  419. this.req.query.ignore_flush_errors = 'true'
  420. this.DocumentManager.flushAndDeleteDocWithLock = sinon.stub().yields()
  421. return this.HttpController.deleteDoc(this.req, this.res, this.next)
  422. })
  423. it('should delete the doc', function () {
  424. return this.DocumentManager.flushAndDeleteDocWithLock
  425. .calledWith(this.project_id, this.doc_id, { ignoreFlushErrors: true })
  426. .should.equal(true)
  427. })
  428. return it('should return a successful No Content response', function () {
  429. return this.res.sendStatus.calledWith(204).should.equal(true)
  430. })
  431. })
  432. return describe('when an errors occurs', function () {
  433. beforeEach(function () {
  434. this.DocumentManager.flushAndDeleteDocWithLock = sinon
  435. .stub()
  436. .callsArgWith(3, new Error('oops'))
  437. return this.HttpController.deleteDoc(this.req, this.res, this.next)
  438. })
  439. it('should flush project history', function () {
  440. return this.HistoryManager.flushProjectChangesAsync
  441. .calledWithExactly(this.project_id)
  442. .should.equal(true)
  443. })
  444. return it('should call next with the error', function () {
  445. return this.next.calledWith(new Error('oops')).should.equal(true)
  446. })
  447. })
  448. })
  449. describe('deleteProject', function () {
  450. beforeEach(function () {
  451. return (this.req = {
  452. params: {
  453. project_id: this.project_id
  454. }
  455. })
  456. })
  457. describe('successfully', function () {
  458. beforeEach(function () {
  459. this.ProjectManager.flushAndDeleteProjectWithLocks = sinon
  460. .stub()
  461. .callsArgWith(2)
  462. return this.HttpController.deleteProject(this.req, this.res, this.next)
  463. })
  464. it('should delete the project', function () {
  465. return this.ProjectManager.flushAndDeleteProjectWithLocks
  466. .calledWith(this.project_id)
  467. .should.equal(true)
  468. })
  469. it('should return a successful No Content response', function () {
  470. return this.res.sendStatus.calledWith(204).should.equal(true)
  471. })
  472. it('should log the request', function () {
  473. return this.logger.log
  474. .calledWith(
  475. { project_id: this.project_id },
  476. 'deleting project via http'
  477. )
  478. .should.equal(true)
  479. })
  480. return it('should time the request', function () {
  481. return this.Metrics.Timer.prototype.done.called.should.equal(true)
  482. })
  483. })
  484. describe('with the background=true option from realtime', function () {
  485. beforeEach(function () {
  486. this.ProjectManager.queueFlushAndDeleteProject = sinon
  487. .stub()
  488. .callsArgWith(1)
  489. this.req.query = { background: true, shutdown: true }
  490. return this.HttpController.deleteProject(this.req, this.res, this.next)
  491. })
  492. return it('should queue the flush and delete', function () {
  493. return this.ProjectManager.queueFlushAndDeleteProject
  494. .calledWith(this.project_id)
  495. .should.equal(true)
  496. })
  497. })
  498. return describe('when an errors occurs', function () {
  499. beforeEach(function () {
  500. this.ProjectManager.flushAndDeleteProjectWithLocks = sinon
  501. .stub()
  502. .callsArgWith(2, new Error('oops'))
  503. return this.HttpController.deleteProject(this.req, this.res, this.next)
  504. })
  505. return it('should call next with the error', function () {
  506. return this.next.calledWith(new Error('oops')).should.equal(true)
  507. })
  508. })
  509. })
  510. describe('acceptChanges', function () {
  511. beforeEach(function () {
  512. return (this.req = {
  513. params: {
  514. project_id: this.project_id,
  515. doc_id: this.doc_id,
  516. change_id: (this.change_id = 'mock-change-od-1')
  517. }
  518. })
  519. })
  520. describe('successfully with a single change', function () {
  521. beforeEach(function () {
  522. this.DocumentManager.acceptChangesWithLock = sinon
  523. .stub()
  524. .callsArgWith(3)
  525. return this.HttpController.acceptChanges(this.req, this.res, this.next)
  526. })
  527. it('should accept the change', function () {
  528. return this.DocumentManager.acceptChangesWithLock
  529. .calledWith(this.project_id, this.doc_id, [this.change_id])
  530. .should.equal(true)
  531. })
  532. it('should return a successful No Content response', function () {
  533. return this.res.sendStatus.calledWith(204).should.equal(true)
  534. })
  535. it('should log the request', function () {
  536. return this.logger.log
  537. .calledWith(
  538. { project_id: this.project_id, doc_id: this.doc_id },
  539. 'accepting 1 changes via http'
  540. )
  541. .should.equal(true)
  542. })
  543. return it('should time the request', function () {
  544. return this.Metrics.Timer.prototype.done.called.should.equal(true)
  545. })
  546. })
  547. describe('succesfully with with multiple changes', function () {
  548. beforeEach(function () {
  549. this.change_ids = [
  550. 'mock-change-od-1',
  551. 'mock-change-od-2',
  552. 'mock-change-od-3',
  553. 'mock-change-od-4'
  554. ]
  555. this.req.body = { change_ids: this.change_ids }
  556. this.DocumentManager.acceptChangesWithLock = sinon
  557. .stub()
  558. .callsArgWith(3)
  559. return this.HttpController.acceptChanges(this.req, this.res, this.next)
  560. })
  561. it('should accept the changes in the body payload', function () {
  562. return this.DocumentManager.acceptChangesWithLock
  563. .calledWith(this.project_id, this.doc_id, this.change_ids)
  564. .should.equal(true)
  565. })
  566. return it('should log the request with the correct number of changes', function () {
  567. return this.logger.log
  568. .calledWith(
  569. { project_id: this.project_id, doc_id: this.doc_id },
  570. `accepting ${this.change_ids.length} changes via http`
  571. )
  572. .should.equal(true)
  573. })
  574. })
  575. return describe('when an errors occurs', function () {
  576. beforeEach(function () {
  577. this.DocumentManager.acceptChangesWithLock = sinon
  578. .stub()
  579. .callsArgWith(3, new Error('oops'))
  580. return this.HttpController.acceptChanges(this.req, this.res, this.next)
  581. })
  582. return it('should call next with the error', function () {
  583. return this.next.calledWith(new Error('oops')).should.equal(true)
  584. })
  585. })
  586. })
  587. describe('deleteComment', function () {
  588. beforeEach(function () {
  589. return (this.req = {
  590. params: {
  591. project_id: this.project_id,
  592. doc_id: this.doc_id,
  593. comment_id: (this.comment_id = 'mock-comment-id')
  594. }
  595. })
  596. })
  597. describe('successfully', function () {
  598. beforeEach(function () {
  599. this.DocumentManager.deleteCommentWithLock = sinon
  600. .stub()
  601. .callsArgWith(3)
  602. return this.HttpController.deleteComment(this.req, this.res, this.next)
  603. })
  604. it('should accept the change', function () {
  605. return this.DocumentManager.deleteCommentWithLock
  606. .calledWith(this.project_id, this.doc_id, this.comment_id)
  607. .should.equal(true)
  608. })
  609. it('should return a successful No Content response', function () {
  610. return this.res.sendStatus.calledWith(204).should.equal(true)
  611. })
  612. it('should log the request', function () {
  613. return this.logger.log
  614. .calledWith(
  615. {
  616. project_id: this.project_id,
  617. doc_id: this.doc_id,
  618. comment_id: this.comment_id
  619. },
  620. 'deleting comment via http'
  621. )
  622. .should.equal(true)
  623. })
  624. return it('should time the request', function () {
  625. return this.Metrics.Timer.prototype.done.called.should.equal(true)
  626. })
  627. })
  628. return describe('when an errors occurs', function () {
  629. beforeEach(function () {
  630. this.DocumentManager.deleteCommentWithLock = sinon
  631. .stub()
  632. .callsArgWith(3, new Error('oops'))
  633. return this.HttpController.deleteComment(this.req, this.res, this.next)
  634. })
  635. return it('should call next with the error', function () {
  636. return this.next.calledWith(new Error('oops')).should.equal(true)
  637. })
  638. })
  639. })
  640. describe('getProjectDocsAndFlushIfOld', function () {
  641. beforeEach(function () {
  642. this.state = '01234567890abcdef'
  643. this.docs = [
  644. { _id: '1234', lines: 'hello', v: 23 },
  645. { _id: '4567', lines: 'world', v: 45 }
  646. ]
  647. return (this.req = {
  648. params: {
  649. project_id: this.project_id
  650. },
  651. query: {
  652. state: this.state
  653. }
  654. })
  655. })
  656. describe('successfully', function () {
  657. beforeEach(function () {
  658. this.ProjectManager.getProjectDocsAndFlushIfOld = sinon
  659. .stub()
  660. .callsArgWith(3, null, this.docs)
  661. return this.HttpController.getProjectDocsAndFlushIfOld(
  662. this.req,
  663. this.res,
  664. this.next
  665. )
  666. })
  667. it('should get docs from the project manager', function () {
  668. return this.ProjectManager.getProjectDocsAndFlushIfOld
  669. .calledWith(this.project_id, this.state, {})
  670. .should.equal(true)
  671. })
  672. it('should return a successful response', function () {
  673. return this.res.send.calledWith(this.docs).should.equal(true)
  674. })
  675. it('should log the request', function () {
  676. return this.logger.log
  677. .calledWith(
  678. { project_id: this.project_id, exclude: [] },
  679. 'getting docs via http'
  680. )
  681. .should.equal(true)
  682. })
  683. it('should log the response', function () {
  684. return this.logger.log
  685. .calledWith(
  686. { project_id: this.project_id, result: ['1234:23', '4567:45'] },
  687. 'got docs via http'
  688. )
  689. .should.equal(true)
  690. })
  691. return it('should time the request', function () {
  692. return this.Metrics.Timer.prototype.done.called.should.equal(true)
  693. })
  694. })
  695. describe('when there is a conflict', function () {
  696. beforeEach(function () {
  697. this.ProjectManager.getProjectDocsAndFlushIfOld = sinon
  698. .stub()
  699. .callsArgWith(
  700. 3,
  701. new Errors.ProjectStateChangedError('project state changed')
  702. )
  703. return this.HttpController.getProjectDocsAndFlushIfOld(
  704. this.req,
  705. this.res,
  706. this.next
  707. )
  708. })
  709. return it('should return an HTTP 409 Conflict response', function () {
  710. return this.res.sendStatus.calledWith(409).should.equal(true)
  711. })
  712. })
  713. return describe('when an error occurs', function () {
  714. beforeEach(function () {
  715. this.ProjectManager.getProjectDocsAndFlushIfOld = sinon
  716. .stub()
  717. .callsArgWith(3, new Error('oops'))
  718. return this.HttpController.getProjectDocsAndFlushIfOld(
  719. this.req,
  720. this.res,
  721. this.next
  722. )
  723. })
  724. return it('should call next with the error', function () {
  725. return this.next.calledWith(new Error('oops')).should.equal(true)
  726. })
  727. })
  728. })
  729. describe('updateProject', function () {
  730. beforeEach(function () {
  731. this.projectHistoryId = 'history-id-123'
  732. this.userId = 'user-id-123'
  733. this.docUpdates = sinon.stub()
  734. this.fileUpdates = sinon.stub()
  735. this.version = 1234567
  736. return (this.req = {
  737. body: {
  738. projectHistoryId: this.projectHistoryId,
  739. userId: this.userId,
  740. docUpdates: this.docUpdates,
  741. fileUpdates: this.fileUpdates,
  742. version: this.version
  743. },
  744. params: {
  745. project_id: this.project_id
  746. }
  747. })
  748. })
  749. describe('successfully', function () {
  750. beforeEach(function () {
  751. this.ProjectManager.updateProjectWithLocks = sinon
  752. .stub()
  753. .callsArgWith(6)
  754. return this.HttpController.updateProject(this.req, this.res, this.next)
  755. })
  756. it('should accept the change', function () {
  757. return this.ProjectManager.updateProjectWithLocks
  758. .calledWith(
  759. this.project_id,
  760. this.projectHistoryId,
  761. this.userId,
  762. this.docUpdates,
  763. this.fileUpdates,
  764. this.version
  765. )
  766. .should.equal(true)
  767. })
  768. it('should return a successful No Content response', function () {
  769. return this.res.sendStatus.calledWith(204).should.equal(true)
  770. })
  771. return it('should time the request', function () {
  772. return this.Metrics.Timer.prototype.done.called.should.equal(true)
  773. })
  774. })
  775. return describe('when an errors occurs', function () {
  776. beforeEach(function () {
  777. this.ProjectManager.updateProjectWithLocks = sinon
  778. .stub()
  779. .callsArgWith(6, new Error('oops'))
  780. return this.HttpController.updateProject(this.req, this.res, this.next)
  781. })
  782. return it('should call next with the error', function () {
  783. return this.next.calledWith(new Error('oops')).should.equal(true)
  784. })
  785. })
  786. })
  787. return describe('resyncProjectHistory', function () {
  788. beforeEach(function () {
  789. this.projectHistoryId = 'history-id-123'
  790. this.docs = sinon.stub()
  791. this.files = sinon.stub()
  792. this.fileUpdates = sinon.stub()
  793. return (this.req = {
  794. body: {
  795. projectHistoryId: this.projectHistoryId,
  796. docs: this.docs,
  797. files: this.files
  798. },
  799. params: {
  800. project_id: this.project_id
  801. }
  802. })
  803. })
  804. describe('successfully', function () {
  805. beforeEach(function () {
  806. this.HistoryManager.resyncProjectHistory = sinon.stub().callsArgWith(4)
  807. return this.HttpController.resyncProjectHistory(
  808. this.req,
  809. this.res,
  810. this.next
  811. )
  812. })
  813. it('should accept the change', function () {
  814. return this.HistoryManager.resyncProjectHistory
  815. .calledWith(
  816. this.project_id,
  817. this.projectHistoryId,
  818. this.docs,
  819. this.files
  820. )
  821. .should.equal(true)
  822. })
  823. return it('should return a successful No Content response', function () {
  824. return this.res.sendStatus.calledWith(204).should.equal(true)
  825. })
  826. })
  827. return describe('when an errors occurs', function () {
  828. beforeEach(function () {
  829. this.HistoryManager.resyncProjectHistory = sinon
  830. .stub()
  831. .callsArgWith(4, new Error('oops'))
  832. return this.HttpController.resyncProjectHistory(
  833. this.req,
  834. this.res,
  835. this.next
  836. )
  837. })
  838. return it('should call next with the error', function () {
  839. return this.next.calledWith(new Error('oops')).should.equal(true)
  840. })
  841. })
  842. })
  843. })