DeletingDocsTests.js 14 KB

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