HttpControllerTests.js 25 KB

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