DeletingDocsTests.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. /* eslint-disable
  2. camelcase,
  3. handle-callback-err,
  4. no-unused-vars,
  5. */
  6. // TODO: This file was created by bulk-decaffeinate.
  7. // Fix any style issues and re-enable lint.
  8. /*
  9. * decaffeinate suggestions:
  10. * DS102: Remove unnecessary code created because of implicit returns
  11. * DS207: Consider shorter variations of null checks
  12. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  13. */
  14. const { db, ObjectId } = require('../../../app/js/mongodb')
  15. const { expect } = require('chai')
  16. const DocstoreApp = require('./helpers/DocstoreApp')
  17. const Errors = require('../../../app/js/Errors')
  18. const Settings = require('@overleaf/settings')
  19. const DocstoreClient = require('./helpers/DocstoreClient')
  20. function deleteTestSuite(deleteDoc) {
  21. beforeEach(function (done) {
  22. this.project_id = ObjectId()
  23. this.doc_id = ObjectId()
  24. this.lines = ['original', 'lines']
  25. this.version = 42
  26. this.ranges = []
  27. return DocstoreApp.ensureRunning(() => {
  28. return DocstoreClient.createDoc(
  29. this.project_id,
  30. this.doc_id,
  31. this.lines,
  32. this.version,
  33. this.ranges,
  34. error => {
  35. if (error != null) {
  36. throw error
  37. }
  38. return done()
  39. }
  40. )
  41. })
  42. })
  43. it('should show as not deleted on /deleted', function (done) {
  44. DocstoreClient.isDocDeleted(
  45. this.project_id,
  46. this.doc_id,
  47. (error, res, body) => {
  48. if (error) return done(error)
  49. expect(res.statusCode).to.equal(200)
  50. expect(body).to.have.property('deleted').to.equal(false)
  51. done()
  52. }
  53. )
  54. })
  55. describe('when the doc exists', function () {
  56. beforeEach(function (done) {
  57. deleteDoc(this.project_id, this.doc_id, (error, res, doc) => {
  58. this.res = res
  59. return done()
  60. })
  61. })
  62. afterEach(function (done) {
  63. return db.docs.remove({ _id: this.doc_id }, done)
  64. })
  65. it('should mark the doc as deleted on /deleted', function (done) {
  66. DocstoreClient.isDocDeleted(
  67. this.project_id,
  68. this.doc_id,
  69. (error, res, body) => {
  70. if (error) return done(error)
  71. expect(res.statusCode).to.equal(200)
  72. expect(body).to.have.property('deleted').to.equal(true)
  73. done()
  74. }
  75. )
  76. })
  77. it('should insert a deleted doc into the docs collection', function (done) {
  78. return db.docs.find({ _id: this.doc_id }).toArray((error, docs) => {
  79. docs[0]._id.should.deep.equal(this.doc_id)
  80. docs[0].lines.should.deep.equal(this.lines)
  81. docs[0].deleted.should.equal(true)
  82. return done()
  83. })
  84. })
  85. it('should not export the doc to s3', function (done) {
  86. setTimeout(() => {
  87. DocstoreClient.getS3Doc(this.project_id, this.doc_id, error => {
  88. expect(error).to.be.instanceOf(Errors.NotFoundError)
  89. done()
  90. })
  91. }, 1000)
  92. })
  93. })
  94. describe('when archiveOnSoftDelete is enabled', function () {
  95. let archiveOnSoftDelete
  96. beforeEach('overwrite settings', function () {
  97. archiveOnSoftDelete = Settings.docstore.archiveOnSoftDelete
  98. Settings.docstore.archiveOnSoftDelete = true
  99. })
  100. afterEach('restore settings', function () {
  101. Settings.docstore.archiveOnSoftDelete = archiveOnSoftDelete
  102. })
  103. beforeEach('delete Doc', function (done) {
  104. deleteDoc(this.project_id, this.doc_id, (error, res) => {
  105. this.res = res
  106. done()
  107. })
  108. })
  109. beforeEach(function waitForBackgroundFlush(done) {
  110. setTimeout(done, 500)
  111. })
  112. afterEach(function cleanupDoc(done) {
  113. db.docs.remove({ _id: this.doc_id }, done)
  114. })
  115. it('should set the deleted flag in the doc', function (done) {
  116. db.docs.findOne({ _id: this.doc_id }, (error, doc) => {
  117. if (error) {
  118. return done(error)
  119. }
  120. expect(doc.deleted).to.equal(true)
  121. done()
  122. })
  123. })
  124. it('should set inS3 and unset lines and ranges in the doc', function (done) {
  125. db.docs.findOne({ _id: this.doc_id }, (error, doc) => {
  126. if (error) {
  127. return done(error)
  128. }
  129. expect(doc.lines).to.not.exist
  130. expect(doc.ranges).to.not.exist
  131. expect(doc.inS3).to.equal(true)
  132. done()
  133. })
  134. })
  135. it('should set the doc in s3 correctly', function (done) {
  136. DocstoreClient.getS3Doc(this.project_id, this.doc_id, (error, s3_doc) => {
  137. if (error) {
  138. return done(error)
  139. }
  140. expect(s3_doc.lines).to.deep.equal(this.lines)
  141. expect(s3_doc.ranges).to.deep.equal(this.ranges)
  142. done()
  143. })
  144. })
  145. })
  146. describe('when the doc exists in another project', function () {
  147. const otherProjectId = ObjectId()
  148. it('should show as not existing on /deleted', function (done) {
  149. DocstoreClient.isDocDeleted(otherProjectId, this.doc_id, (error, res) => {
  150. if (error) return done(error)
  151. expect(res.statusCode).to.equal(404)
  152. done()
  153. })
  154. })
  155. it('should return a 404 when trying to delete', function (done) {
  156. deleteDoc(otherProjectId, this.doc_id, (error, res) => {
  157. if (error) return done(error)
  158. expect(res.statusCode).to.equal(404)
  159. done()
  160. })
  161. })
  162. })
  163. return describe('when the doc does not exist', function () {
  164. it('should show as not existing on /deleted', function (done) {
  165. const missing_doc_id = ObjectId()
  166. DocstoreClient.isDocDeleted(
  167. this.project_id,
  168. missing_doc_id,
  169. (error, res) => {
  170. if (error) return done(error)
  171. expect(res.statusCode).to.equal(404)
  172. done()
  173. }
  174. )
  175. })
  176. return it('should return a 404', function (done) {
  177. const missing_doc_id = ObjectId()
  178. deleteDoc(this.project_id, missing_doc_id, (error, res, doc) => {
  179. res.statusCode.should.equal(404)
  180. return done()
  181. })
  182. })
  183. })
  184. }
  185. describe('Delete via PATCH', function () {
  186. deleteTestSuite(DocstoreClient.deleteDoc)
  187. describe('when providing a custom doc name in the delete request', function () {
  188. beforeEach(function (done) {
  189. DocstoreClient.deleteDocWithName(
  190. this.project_id,
  191. this.doc_id,
  192. 'wombat.tex',
  193. done
  194. )
  195. })
  196. it('should insert the doc name into the docs collection', function (done) {
  197. db.docs.find({ _id: this.doc_id }).toArray((error, docs) => {
  198. if (error) return done(error)
  199. expect(docs[0].name).to.equal('wombat.tex')
  200. done()
  201. })
  202. })
  203. })
  204. describe('when providing a custom deletedAt date in the delete request', function () {
  205. beforeEach('record date and delay', function (done) {
  206. this.deletedAt = new Date()
  207. setTimeout(done, 5)
  208. })
  209. beforeEach('perform deletion with past date', function (done) {
  210. DocstoreClient.deleteDocWithDate(
  211. this.project_id,
  212. this.doc_id,
  213. this.deletedAt,
  214. done
  215. )
  216. })
  217. it('should insert the date into the docs collection', function (done) {
  218. db.docs.find({ _id: this.doc_id }).toArray((error, docs) => {
  219. if (error) return done(error)
  220. expect(docs[0].deletedAt.toISOString()).to.equal(
  221. this.deletedAt.toISOString()
  222. )
  223. done()
  224. })
  225. })
  226. })
  227. describe('when providing no doc name in the delete request', function () {
  228. beforeEach(function (done) {
  229. DocstoreClient.deleteDocWithName(
  230. this.project_id,
  231. this.doc_id,
  232. '',
  233. (error, res) => {
  234. this.res = res
  235. done(error)
  236. }
  237. )
  238. })
  239. it('should reject the request', function () {
  240. expect(this.res.statusCode).to.equal(400)
  241. })
  242. })
  243. describe('when providing no date in the delete request', function () {
  244. beforeEach(function (done) {
  245. DocstoreClient.deleteDocWithDate(
  246. this.project_id,
  247. this.doc_id,
  248. '',
  249. (error, res) => {
  250. this.res = res
  251. done(error)
  252. }
  253. )
  254. })
  255. it('should reject the request', function () {
  256. expect(this.res.statusCode).to.equal(400)
  257. })
  258. })
  259. describe('before deleting anything', function () {
  260. it('should show nothing in deleted docs response', function (done) {
  261. DocstoreClient.getAllDeletedDocs(
  262. this.project_id,
  263. (error, deletedDocs) => {
  264. if (error) return done(error)
  265. expect(deletedDocs).to.deep.equal([])
  266. done()
  267. }
  268. )
  269. })
  270. })
  271. describe('when the doc gets a name on delete', function () {
  272. beforeEach(function (done) {
  273. DocstoreClient.deleteDoc(this.project_id, this.doc_id, done)
  274. })
  275. it('should show the doc in deleted docs response', function (done) {
  276. DocstoreClient.getAllDeletedDocs(
  277. this.project_id,
  278. (error, deletedDocs) => {
  279. if (error) return done(error)
  280. expect(deletedDocs).to.deep.equal([
  281. { _id: this.doc_id.toString(), name: 'main.tex' },
  282. ])
  283. done()
  284. }
  285. )
  286. })
  287. describe('after deleting multiple docs', function () {
  288. beforeEach('create doc2', function (done) {
  289. this.doc_id2 = ObjectId()
  290. DocstoreClient.createDoc(
  291. this.project_id,
  292. this.doc_id2,
  293. this.lines,
  294. this.version,
  295. this.ranges,
  296. done
  297. )
  298. })
  299. beforeEach('delete doc2', function (done) {
  300. DocstoreClient.deleteDocWithName(
  301. this.project_id,
  302. this.doc_id2,
  303. 'two.tex',
  304. done
  305. )
  306. })
  307. beforeEach('create doc3', function (done) {
  308. this.doc_id3 = ObjectId()
  309. DocstoreClient.createDoc(
  310. this.project_id,
  311. this.doc_id3,
  312. this.lines,
  313. this.version,
  314. this.ranges,
  315. done
  316. )
  317. })
  318. beforeEach('delete doc3', function (done) {
  319. DocstoreClient.deleteDocWithName(
  320. this.project_id,
  321. this.doc_id3,
  322. 'three.tex',
  323. done
  324. )
  325. })
  326. it('should show all the docs as deleted', function (done) {
  327. DocstoreClient.getAllDeletedDocs(
  328. this.project_id,
  329. (error, deletedDocs) => {
  330. if (error) return done(error)
  331. expect(deletedDocs).to.deep.equal([
  332. { _id: this.doc_id3.toString(), name: 'three.tex' },
  333. { _id: this.doc_id2.toString(), name: 'two.tex' },
  334. { _id: this.doc_id.toString(), name: 'main.tex' },
  335. ])
  336. done()
  337. }
  338. )
  339. })
  340. describe('with one more than max_deleted_docs permits', function () {
  341. let maxDeletedDocsBefore
  342. beforeEach(function () {
  343. maxDeletedDocsBefore = Settings.max_deleted_docs
  344. Settings.max_deleted_docs = 2
  345. })
  346. afterEach(function () {
  347. Settings.max_deleted_docs = maxDeletedDocsBefore
  348. })
  349. it('should omit the first deleted doc', function (done) {
  350. DocstoreClient.getAllDeletedDocs(
  351. this.project_id,
  352. (error, deletedDocs) => {
  353. if (error) return done(error)
  354. expect(deletedDocs).to.deep.equal([
  355. { _id: this.doc_id3.toString(), name: 'three.tex' },
  356. { _id: this.doc_id2.toString(), name: 'two.tex' },
  357. // dropped main.tex
  358. ])
  359. done()
  360. }
  361. )
  362. })
  363. })
  364. })
  365. })
  366. })
  367. describe("Destroying a project's documents", function () {
  368. describe('when the doc exists', function () {
  369. beforeEach(function (done) {
  370. return db.docOps.insert(
  371. { doc_id: ObjectId(this.doc_id), version: 1 },
  372. function (err) {
  373. if (err != null) {
  374. return done(err)
  375. }
  376. return DocstoreClient.destroyAllDoc(this.project_id, done)
  377. }
  378. )
  379. })
  380. it('should remove the doc from the docs collection', function (done) {
  381. return db.docs.find({ _id: this.doc_id }).toArray((err, docs) => {
  382. expect(err).not.to.exist
  383. expect(docs).to.deep.equal([])
  384. return done()
  385. })
  386. })
  387. return it('should remove the docOps from the docOps collection', function (done) {
  388. return db.docOps.find({ doc_id: this.doc_id }).toArray((err, docOps) => {
  389. expect(err).not.to.exist
  390. expect(docOps).to.deep.equal([])
  391. return done()
  392. })
  393. })
  394. })
  395. return describe('when the doc is archived', function () {
  396. beforeEach(function (done) {
  397. return DocstoreClient.archiveAllDoc(this.project_id, err => {
  398. if (err != null) {
  399. return done(err)
  400. }
  401. return DocstoreClient.destroyAllDoc(this.project_id, done)
  402. })
  403. })
  404. it('should remove the doc from the docs collection', function (done) {
  405. return db.docs.find({ _id: this.doc_id }).toArray((err, docs) => {
  406. expect(err).not.to.exist
  407. expect(docs).to.deep.equal([])
  408. return done()
  409. })
  410. })
  411. it('should remove the docOps from the docOps collection', function (done) {
  412. return db.docOps.find({ doc_id: this.doc_id }).toArray((err, docOps) => {
  413. expect(err).not.to.exist
  414. expect(docOps).to.deep.equal([])
  415. return done()
  416. })
  417. })
  418. return it('should remove the doc contents from s3', function (done) {
  419. return DocstoreClient.getS3Doc(this.project_id, this.doc_id, error => {
  420. expect(error).to.be.instanceOf(Errors.NotFoundError)
  421. done()
  422. })
  423. })
  424. })
  425. })