DocManagerTests.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874
  1. /* eslint-disable
  2. camelcase,
  3. handle-callback-err,
  4. no-dupe-keys,
  5. no-return-assign,
  6. no-unused-vars,
  7. */
  8. // TODO: This file was created by bulk-decaffeinate.
  9. // Fix any style issues and re-enable lint.
  10. /*
  11. * decaffeinate suggestions:
  12. * DS102: Remove unnecessary code created because of implicit returns
  13. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  14. */
  15. const SandboxedModule = require('sandboxed-module')
  16. const sinon = require('sinon')
  17. const chai = require('chai')
  18. chai.use(require('sinon-chai'))
  19. const { assert } = require('chai')
  20. chai.should()
  21. const { expect } = chai
  22. const modulePath = require('path').join(__dirname, '../../../app/js/DocManager')
  23. const { ObjectId } = require('mongodb')
  24. const Errors = require('../../../app/js/Errors')
  25. describe('DocManager', function () {
  26. beforeEach(function () {
  27. this.DocManager = SandboxedModule.require(modulePath, {
  28. requires: {
  29. './MongoManager': (this.MongoManager = {}),
  30. './DocArchiveManager': (this.DocArchiveManager = {}),
  31. './RangeManager': (this.RangeManager = {
  32. jsonRangesToMongo(r) {
  33. return r
  34. },
  35. shouldUpdateRanges: sinon.stub().returns(false)
  36. }),
  37. 'settings-sharelatex': (this.settings = { docstore: {} }),
  38. 'logger-sharelatex': (this.logger = {
  39. log: sinon.stub(),
  40. warn() {},
  41. err() {}
  42. }),
  43. './Errors': Errors
  44. }
  45. })
  46. this.doc_id = ObjectId().toString()
  47. this.project_id = ObjectId().toString()
  48. this.another_project_id = ObjectId().toString()
  49. this.callback = sinon.stub()
  50. return (this.stubbedError = new Error('blew up'))
  51. })
  52. describe('checkDocExists', function () {
  53. beforeEach(function () {
  54. return (this.DocManager._getDoc = sinon.stub())
  55. })
  56. it('should call get doc with a quick filter', function (done) {
  57. this.DocManager._getDoc.callsArgWith(3, null, { _id: this.doc_id })
  58. return this.DocManager.checkDocExists(
  59. this.project_id,
  60. this.doc_id,
  61. (err, exist) => {
  62. exist.should.equal(true)
  63. this.DocManager._getDoc
  64. .calledWith(this.project_id, this.doc_id, { _id: 1, inS3: true })
  65. .should.equal(true)
  66. return done()
  67. }
  68. )
  69. })
  70. it('should return false when doc is not there', function (done) {
  71. this.DocManager._getDoc.callsArgWith(3, null)
  72. return this.DocManager.checkDocExists(
  73. this.project_id,
  74. this.doc_id,
  75. (err, exist) => {
  76. exist.should.equal(false)
  77. return done()
  78. }
  79. )
  80. })
  81. return it('should return error when get doc errors', function (done) {
  82. this.DocManager._getDoc.callsArgWith(3, 'error')
  83. return this.DocManager.checkDocExists(
  84. this.project_id,
  85. this.doc_id,
  86. (err, exist) => {
  87. err.should.equal('error')
  88. return done()
  89. }
  90. )
  91. })
  92. })
  93. describe('getFullDoc', function () {
  94. beforeEach(function () {
  95. this.DocManager._getDoc = sinon.stub()
  96. return (this.doc = {
  97. _id: this.doc_id,
  98. lines: ['2134']
  99. })
  100. })
  101. it('should call get doc with a quick filter', function (done) {
  102. this.DocManager._getDoc.callsArgWith(3, null, this.doc)
  103. return this.DocManager.getFullDoc(
  104. this.project_id,
  105. this.doc_id,
  106. (err, doc) => {
  107. doc.should.equal(this.doc)
  108. this.DocManager._getDoc
  109. .calledWith(this.project_id, this.doc_id, {
  110. lines: true,
  111. rev: true,
  112. deleted: true,
  113. version: true,
  114. ranges: true,
  115. inS3: true
  116. })
  117. .should.equal(true)
  118. return done()
  119. }
  120. )
  121. })
  122. return it('should return error when get doc errors', function (done) {
  123. this.DocManager._getDoc.callsArgWith(3, 'error')
  124. return this.DocManager.getFullDoc(
  125. this.project_id,
  126. this.doc_id,
  127. (err, exist) => {
  128. err.should.equal('error')
  129. return done()
  130. }
  131. )
  132. })
  133. })
  134. describe('getRawDoc', function () {
  135. beforeEach(function () {
  136. this.DocManager._getDoc = sinon.stub()
  137. return (this.doc = { lines: ['2134'] })
  138. })
  139. it('should call get doc with a quick filter', function (done) {
  140. this.DocManager._getDoc.callsArgWith(3, null, this.doc)
  141. return this.DocManager.getDocLines(
  142. this.project_id,
  143. this.doc_id,
  144. (err, doc) => {
  145. doc.should.equal(this.doc)
  146. this.DocManager._getDoc
  147. .calledWith(this.project_id, this.doc_id, {
  148. lines: true,
  149. inS3: true
  150. })
  151. .should.equal(true)
  152. return done()
  153. }
  154. )
  155. })
  156. return it('should return error when get doc errors', function (done) {
  157. this.DocManager._getDoc.callsArgWith(3, 'error')
  158. return this.DocManager.getDocLines(
  159. this.project_id,
  160. this.doc_id,
  161. (err, exist) => {
  162. err.should.equal('error')
  163. return done()
  164. }
  165. )
  166. })
  167. })
  168. describe('getDoc', function () {
  169. beforeEach(function () {
  170. this.project = { name: 'mock-project' }
  171. this.doc = {
  172. _id: this.doc_id,
  173. project_id: this.project_id,
  174. lines: ['mock-lines']
  175. }
  176. this.version = 42
  177. this.MongoManager.findDoc = sinon.stub()
  178. return (this.MongoManager.getDocVersion = sinon
  179. .stub()
  180. .yields(null, this.version))
  181. })
  182. describe('when using a filter', function () {
  183. beforeEach(function () {
  184. return this.MongoManager.findDoc.yields(null, this.doc)
  185. })
  186. it('should error if inS3 is not set to true', function (done) {
  187. return this.DocManager._getDoc(
  188. this.project_id,
  189. this.doc_id,
  190. { inS3: false },
  191. (err) => {
  192. expect(err).to.exist
  193. return done()
  194. }
  195. )
  196. })
  197. it('should always get inS3 even when no filter is passed', function (done) {
  198. return this.DocManager._getDoc(
  199. this.project_id,
  200. this.doc_id,
  201. undefined,
  202. (err) => {
  203. this.MongoManager.findDoc.called.should.equal(false)
  204. expect(err).to.exist
  205. return done()
  206. }
  207. )
  208. })
  209. return it('should not error if inS3 is set to true', function (done) {
  210. return this.DocManager._getDoc(
  211. this.project_id,
  212. this.doc_id,
  213. { inS3: true },
  214. (err) => {
  215. expect(err).to.not.exist
  216. return done()
  217. }
  218. )
  219. })
  220. })
  221. describe('when the doc is in the doc collection', function () {
  222. beforeEach(function () {
  223. this.MongoManager.findDoc.yields(null, this.doc)
  224. return this.DocManager._getDoc(
  225. this.project_id,
  226. this.doc_id,
  227. { version: true, inS3: true },
  228. this.callback
  229. )
  230. })
  231. it('should get the doc from the doc collection', function () {
  232. return this.MongoManager.findDoc
  233. .calledWith(this.project_id, this.doc_id)
  234. .should.equal(true)
  235. })
  236. it('should get the doc version from the docOps collection', function () {
  237. return this.MongoManager.getDocVersion
  238. .calledWith(this.doc_id)
  239. .should.equal(true)
  240. })
  241. return it('should return the callback with the doc with the version', function () {
  242. this.callback.called.should.equal(true)
  243. const doc = this.callback.args[0][1]
  244. doc.lines.should.equal(this.doc.lines)
  245. return doc.version.should.equal(this.version)
  246. })
  247. })
  248. describe('without the version filter', function () {
  249. beforeEach(function () {
  250. this.MongoManager.findDoc.yields(null, this.doc)
  251. return this.DocManager._getDoc(
  252. this.project_id,
  253. this.doc_id,
  254. { version: false, inS3: true },
  255. this.callback
  256. )
  257. })
  258. return it('should not get the doc version from the docOps collection', function () {
  259. return this.MongoManager.getDocVersion.called.should.equal(false)
  260. })
  261. })
  262. describe('when MongoManager.findDoc errors', function () {
  263. beforeEach(function () {
  264. this.MongoManager.findDoc.yields(this.stubbedError)
  265. return this.DocManager._getDoc(
  266. this.project_id,
  267. this.doc_id,
  268. { version: true, inS3: true },
  269. this.callback
  270. )
  271. })
  272. return it('should return the error', function () {
  273. return this.callback.calledWith(this.stubbedError).should.equal(true)
  274. })
  275. })
  276. describe('when the doc is archived', function () {
  277. beforeEach(function () {
  278. this.doc = {
  279. _id: this.doc_id,
  280. project_id: this.project_id,
  281. lines: ['mock-lines'],
  282. inS3: true
  283. }
  284. this.MongoManager.findDoc.yields(null, this.doc)
  285. this.DocArchiveManager.unarchiveDoc = (
  286. project_id,
  287. doc_id,
  288. callback
  289. ) => {
  290. this.doc.inS3 = false
  291. return callback()
  292. }
  293. sinon.spy(this.DocArchiveManager, 'unarchiveDoc')
  294. return this.DocManager._getDoc(
  295. this.project_id,
  296. this.doc_id,
  297. { version: true, inS3: true },
  298. this.callback
  299. )
  300. })
  301. it('should call the DocArchive to unarchive the doc', function () {
  302. return this.DocArchiveManager.unarchiveDoc
  303. .calledWith(this.project_id, this.doc_id)
  304. .should.equal(true)
  305. })
  306. it('should look up the doc twice', function () {
  307. return this.MongoManager.findDoc.calledTwice.should.equal(true)
  308. })
  309. return it('should return the doc', function () {
  310. return this.callback.calledWith(null, this.doc).should.equal(true)
  311. })
  312. })
  313. return describe('when the doc does not exist in the docs collection', function () {
  314. beforeEach(function () {
  315. this.MongoManager.findDoc = sinon.stub().yields(null, null)
  316. return this.DocManager._getDoc(
  317. this.project_id,
  318. this.doc_id,
  319. { version: true, inS3: true },
  320. this.callback
  321. )
  322. })
  323. return it('should return a NotFoundError', function () {
  324. return this.callback
  325. .calledWith(
  326. sinon.match.has(
  327. 'message',
  328. `No such doc: ${this.doc_id} in project ${this.project_id}`
  329. )
  330. )
  331. .should.equal(true)
  332. })
  333. })
  334. })
  335. describe('getAllNonDeletedDocs', function () {
  336. describe('when the project exists', function () {
  337. beforeEach(function () {
  338. this.docs = [
  339. {
  340. _id: this.doc_id,
  341. project_id: this.project_id,
  342. lines: ['mock-lines']
  343. }
  344. ]
  345. this.MongoManager.getProjectsDocs = sinon
  346. .stub()
  347. .callsArgWith(3, null, this.docs)
  348. this.DocArchiveManager.unArchiveAllDocs = sinon
  349. .stub()
  350. .callsArgWith(1, null, this.docs)
  351. this.filter = { lines: true }
  352. return this.DocManager.getAllNonDeletedDocs(
  353. this.project_id,
  354. this.filter,
  355. this.callback
  356. )
  357. })
  358. it('should get the project from the database', function () {
  359. return this.MongoManager.getProjectsDocs
  360. .calledWith(this.project_id, { include_deleted: false }, this.filter)
  361. .should.equal(true)
  362. })
  363. return it('should return the docs', function () {
  364. return this.callback.calledWith(null, this.docs).should.equal(true)
  365. })
  366. })
  367. return describe('when there are no docs for the project', function () {
  368. beforeEach(function () {
  369. this.MongoManager.getProjectsDocs = sinon
  370. .stub()
  371. .callsArgWith(3, null, null)
  372. this.DocArchiveManager.unArchiveAllDocs = sinon
  373. .stub()
  374. .callsArgWith(1, null)
  375. return this.DocManager.getAllNonDeletedDocs(
  376. this.project_id,
  377. this.filter,
  378. this.callback
  379. )
  380. })
  381. return it('should return a NotFoundError', function () {
  382. return this.callback
  383. .calledWith(
  384. sinon.match.has('message', `No docs for project ${this.project_id}`)
  385. )
  386. .should.equal(true)
  387. })
  388. })
  389. })
  390. describe('deleteDoc', function () {
  391. describe('when the doc exists', function () {
  392. beforeEach(function (done) {
  393. this.callback = sinon.stub().callsFake(done)
  394. this.lines = ['mock', 'doc', 'lines']
  395. this.rev = 77
  396. this.DocManager.checkDocExists = sinon
  397. .stub()
  398. .callsArgWith(2, null, true)
  399. this.MongoManager.markDocAsDeleted = sinon.stub().yields(null)
  400. this.DocArchiveManager.archiveDocById = sinon.stub().yields(null)
  401. return this.DocManager.deleteDoc(
  402. this.project_id,
  403. this.doc_id,
  404. this.callback
  405. )
  406. })
  407. it('should get the doc', function () {
  408. return this.DocManager.checkDocExists
  409. .calledWith(this.project_id, this.doc_id)
  410. .should.equal(true)
  411. })
  412. it('should mark doc as deleted', function () {
  413. return this.MongoManager.markDocAsDeleted
  414. .calledWith(this.project_id, this.doc_id)
  415. .should.equal(true)
  416. })
  417. it('should return the callback', function () {
  418. return this.callback.called.should.equal(true)
  419. })
  420. describe('background flush disabled', function () {
  421. beforeEach(function () {
  422. this.settings.docstore.archiveOnSoftDelete = false
  423. })
  424. it('should not flush the doc out of mongo', function () {
  425. this.DocArchiveManager.archiveDocById.should.not.have.been.called
  426. })
  427. })
  428. describe('background flush enabled', function () {
  429. beforeEach(function (done) {
  430. this.settings.docstore.archiveOnSoftDelete = true
  431. this.callback = sinon.stub().callsFake(done)
  432. this.DocManager.deleteDoc(this.project_id, this.doc_id, this.callback)
  433. })
  434. it('should not fail the delete process', function () {
  435. this.callback.should.have.been.calledWith(null)
  436. })
  437. it('should flush the doc out of mongo', function () {
  438. this.DocArchiveManager.archiveDocById.should.have.been.calledWith(
  439. this.project_id,
  440. this.doc_id
  441. )
  442. })
  443. describe('when the background flush fails', function () {
  444. beforeEach(function (done) {
  445. this.err = new Error('foo')
  446. this.DocManager.checkDocExists = sinon.stub().yields(null, true)
  447. this.MongoManager.markDocAsDeleted = sinon.stub().yields(null)
  448. this.DocArchiveManager.archiveDocById = sinon
  449. .stub()
  450. .yields(this.err)
  451. this.logger.warn = sinon.stub()
  452. this.callback = sinon.stub().callsFake(done)
  453. this.DocManager.deleteDoc(
  454. this.project_id,
  455. this.doc_id,
  456. this.callback
  457. )
  458. })
  459. it('should log a warning', function () {
  460. this.logger.warn.should.have.been.calledWith(
  461. sinon.match({
  462. project_id: this.project_id,
  463. doc_id: this.doc_id,
  464. err: this.err
  465. }),
  466. 'archiving a single doc in the background failed'
  467. )
  468. })
  469. it('should not fail the delete process', function () {
  470. this.callback.should.have.been.calledWith(null)
  471. })
  472. })
  473. })
  474. })
  475. return describe('when the doc does not exist', function () {
  476. beforeEach(function () {
  477. this.DocManager.checkDocExists = sinon
  478. .stub()
  479. .callsArgWith(2, null, false)
  480. return this.DocManager.deleteDoc(
  481. this.project_id,
  482. this.doc_id,
  483. this.callback
  484. )
  485. })
  486. return it('should return a NotFoundError', function () {
  487. return this.callback
  488. .calledWith(
  489. sinon.match.has(
  490. 'message',
  491. `No such project/doc to delete: ${this.project_id}/${this.doc_id}`
  492. )
  493. )
  494. .should.equal(true)
  495. })
  496. })
  497. })
  498. return describe('updateDoc', function () {
  499. beforeEach(function () {
  500. this.oldDocLines = ['old', 'doc', 'lines']
  501. this.newDocLines = ['new', 'doc', 'lines']
  502. this.originalRanges = {
  503. changes: [
  504. {
  505. id: ObjectId().toString(),
  506. op: { i: 'foo', p: 3 },
  507. meta: {
  508. user_id: ObjectId().toString(),
  509. ts: new Date().toString()
  510. }
  511. }
  512. ]
  513. }
  514. this.newRanges = {
  515. changes: [
  516. {
  517. id: ObjectId().toString(),
  518. op: { i: 'bar', p: 6 },
  519. meta: {
  520. user_id: ObjectId().toString(),
  521. ts: new Date().toString()
  522. }
  523. }
  524. ]
  525. }
  526. this.version = 42
  527. this.doc = {
  528. _id: this.doc_id,
  529. project_id: this.project_id,
  530. lines: this.oldDocLines,
  531. rev: (this.rev = 5),
  532. version: this.version,
  533. ranges: this.originalRanges
  534. }
  535. this.MongoManager.upsertIntoDocCollection = sinon.stub().callsArg(3)
  536. this.MongoManager.setDocVersion = sinon.stub().yields()
  537. return (this.DocManager._getDoc = sinon.stub())
  538. })
  539. describe('when only the doc lines have changed', function () {
  540. beforeEach(function () {
  541. this.DocManager._getDoc = sinon.stub().callsArgWith(3, null, this.doc)
  542. return this.DocManager.updateDoc(
  543. this.project_id,
  544. this.doc_id,
  545. this.newDocLines,
  546. this.version,
  547. this.originalRanges,
  548. this.callback
  549. )
  550. })
  551. it('should get the existing doc', function () {
  552. return this.DocManager._getDoc
  553. .calledWith(this.project_id, this.doc_id, {
  554. version: true,
  555. rev: true,
  556. lines: true,
  557. version: true,
  558. ranges: true,
  559. inS3: true
  560. })
  561. .should.equal(true)
  562. })
  563. it('should upsert the document to the doc collection', function () {
  564. return this.MongoManager.upsertIntoDocCollection
  565. .calledWith(this.project_id, this.doc_id, { lines: this.newDocLines })
  566. .should.equal(true)
  567. })
  568. it('should not update the version', function () {
  569. return this.MongoManager.setDocVersion.called.should.equal(false)
  570. })
  571. return it('should return the callback with the new rev', function () {
  572. return this.callback
  573. .calledWith(null, true, this.rev + 1)
  574. .should.equal(true)
  575. })
  576. })
  577. describe('when the doc ranges have changed', function () {
  578. beforeEach(function () {
  579. this.DocManager._getDoc = sinon.stub().callsArgWith(3, null, this.doc)
  580. this.RangeManager.shouldUpdateRanges.returns(true)
  581. return this.DocManager.updateDoc(
  582. this.project_id,
  583. this.doc_id,
  584. this.oldDocLines,
  585. this.version,
  586. this.newRanges,
  587. this.callback
  588. )
  589. })
  590. it('should upsert the ranges', function () {
  591. return this.MongoManager.upsertIntoDocCollection
  592. .calledWith(this.project_id, this.doc_id, { ranges: this.newRanges })
  593. .should.equal(true)
  594. })
  595. it('should not update the version', function () {
  596. return this.MongoManager.setDocVersion.called.should.equal(false)
  597. })
  598. return it('should return the callback with the new rev', function () {
  599. return this.callback
  600. .calledWith(null, true, this.rev + 1)
  601. .should.equal(true)
  602. })
  603. })
  604. describe('when only the version has changed', function () {
  605. beforeEach(function () {
  606. this.DocManager._getDoc = sinon.stub().callsArgWith(3, null, this.doc)
  607. return this.DocManager.updateDoc(
  608. this.project_id,
  609. this.doc_id,
  610. this.oldDocLines,
  611. this.version + 1,
  612. this.originalRanges,
  613. this.callback
  614. )
  615. })
  616. it('should not change the lines or ranges', function () {
  617. return this.MongoManager.upsertIntoDocCollection.called.should.equal(
  618. false
  619. )
  620. })
  621. it('should update the version', function () {
  622. return this.MongoManager.setDocVersion
  623. .calledWith(this.doc_id, this.version + 1)
  624. .should.equal(true)
  625. })
  626. return it('should return the callback with the old rev', function () {
  627. return this.callback.calledWith(null, true, this.rev).should.equal(true)
  628. })
  629. })
  630. describe('when the doc has not changed at all', function () {
  631. beforeEach(function () {
  632. this.DocManager._getDoc = sinon.stub().callsArgWith(3, null, this.doc)
  633. return this.DocManager.updateDoc(
  634. this.project_id,
  635. this.doc_id,
  636. this.oldDocLines,
  637. this.version,
  638. this.originalRanges,
  639. this.callback
  640. )
  641. })
  642. it('should not update the ranges or lines', function () {
  643. return this.MongoManager.upsertIntoDocCollection.called.should.equal(
  644. false
  645. )
  646. })
  647. it('should not update the version', function () {
  648. return this.MongoManager.setDocVersion.called.should.equal(false)
  649. })
  650. return it('should return the callback with the old rev and modified == false', function () {
  651. return this.callback
  652. .calledWith(null, false, this.rev)
  653. .should.equal(true)
  654. })
  655. })
  656. describe('when the version is null', function () {
  657. beforeEach(function () {
  658. return this.DocManager.updateDoc(
  659. this.project_id,
  660. this.doc_id,
  661. this.newDocLines,
  662. null,
  663. this.originalRanges,
  664. this.callback
  665. )
  666. })
  667. return it('should return an error', function () {
  668. return this.callback
  669. .calledWith(
  670. sinon.match.has('message', 'no lines, version or ranges provided')
  671. )
  672. .should.equal(true)
  673. })
  674. })
  675. describe('when the lines are null', function () {
  676. beforeEach(function () {
  677. return this.DocManager.updateDoc(
  678. this.project_id,
  679. this.doc_id,
  680. null,
  681. this.version,
  682. this.originalRanges,
  683. this.callback
  684. )
  685. })
  686. return it('should return an error', function () {
  687. return this.callback
  688. .calledWith(
  689. sinon.match.has('message', 'no lines, version or ranges provided')
  690. )
  691. .should.equal(true)
  692. })
  693. })
  694. describe('when the ranges are null', function () {
  695. beforeEach(function () {
  696. return this.DocManager.updateDoc(
  697. this.project_id,
  698. this.doc_id,
  699. this.newDocLines,
  700. this.version,
  701. null,
  702. this.callback
  703. )
  704. })
  705. return it('should return an error', function () {
  706. return this.callback
  707. .calledWith(
  708. sinon.match.has('message', 'no lines, version or ranges provided')
  709. )
  710. .should.equal(true)
  711. })
  712. })
  713. describe('when there is a generic error getting the doc', function () {
  714. beforeEach(function () {
  715. this.error = new Error('doc could not be found')
  716. this.DocManager._getDoc = sinon
  717. .stub()
  718. .callsArgWith(3, this.error, null, null)
  719. return this.DocManager.updateDoc(
  720. this.project_id,
  721. this.doc_id,
  722. this.newDocLines,
  723. this.version,
  724. this.originalRanges,
  725. this.callback
  726. )
  727. })
  728. it('should not upsert the document to the doc collection', function () {
  729. return this.MongoManager.upsertIntoDocCollection.called.should.equal(
  730. false
  731. )
  732. })
  733. return it('should return the callback with the error', function () {
  734. return this.callback.calledWith(this.error).should.equal(true)
  735. })
  736. })
  737. describe('when the doc lines have not changed', function () {
  738. beforeEach(function () {
  739. this.DocManager._getDoc = sinon.stub().callsArgWith(3, null, this.doc)
  740. return this.DocManager.updateDoc(
  741. this.project_id,
  742. this.doc_id,
  743. this.oldDocLines.slice(),
  744. this.version,
  745. this.originalRanges,
  746. this.callback
  747. )
  748. })
  749. it('should not update the doc', function () {
  750. return this.MongoManager.upsertIntoDocCollection.called.should.equal(
  751. false
  752. )
  753. })
  754. return it('should return the callback with the existing rev', function () {
  755. return this.callback
  756. .calledWith(null, false, this.rev)
  757. .should.equal(true)
  758. })
  759. })
  760. return describe('when the doc does not exist', function () {
  761. beforeEach(function () {
  762. this.DocManager._getDoc = sinon.stub().callsArgWith(3, null, null, null)
  763. return this.DocManager.updateDoc(
  764. this.project_id,
  765. this.doc_id,
  766. this.newDocLines,
  767. this.version,
  768. this.originalRanges,
  769. this.callback
  770. )
  771. })
  772. it('should upsert the document to the doc collection', function () {
  773. return this.MongoManager.upsertIntoDocCollection
  774. .calledWith(this.project_id, this.doc_id, {
  775. lines: this.newDocLines,
  776. ranges: this.originalRanges
  777. })
  778. .should.equal(true)
  779. })
  780. it('should set the version', function () {
  781. return this.MongoManager.setDocVersion
  782. .calledWith(this.doc_id, this.version)
  783. .should.equal(true)
  784. })
  785. return it('should return the callback with the new rev', function () {
  786. return this.callback.calledWith(null, true, 1).should.equal(true)
  787. })
  788. })
  789. })
  790. })