BackFillDeletedFilesTests.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. const { exec } = require('child_process')
  2. const { promisify } = require('util')
  3. const { expect } = require('chai')
  4. const logger = require('@overleaf/logger')
  5. const { db, ObjectId } = require('../../../app/src/infrastructure/mongodb')
  6. const User = require('./helpers/User').promises
  7. async function getDeletedFiles(projectId) {
  8. return (await db.projects.findOne({ _id: projectId })).deletedFiles
  9. }
  10. async function setDeletedFiles(projectId, deletedFiles) {
  11. await db.projects.updateOne({ _id: projectId }, { $set: { deletedFiles } })
  12. }
  13. async function unsetDeletedFiles(projectId) {
  14. await db.projects.updateOne(
  15. { _id: projectId },
  16. { $unset: { deletedFiles: 1 } }
  17. )
  18. }
  19. describe('BackFillDeletedFiles', function () {
  20. let user, projectId1, projectId2, projectId3, projectId4, projectId5
  21. beforeEach('create projects', async function () {
  22. user = new User()
  23. await user.login()
  24. projectId1 = ObjectId(await user.createProject('project1'))
  25. projectId2 = ObjectId(await user.createProject('project2'))
  26. projectId3 = ObjectId(await user.createProject('project3'))
  27. projectId4 = ObjectId(await user.createProject('project4'))
  28. projectId5 = ObjectId(await user.createProject('project5'))
  29. })
  30. let fileId1, fileId2, fileId3, fileId4
  31. beforeEach('create files', function () {
  32. // take a short cut and just allocate file ids
  33. fileId1 = ObjectId()
  34. fileId2 = ObjectId()
  35. fileId3 = ObjectId()
  36. fileId4 = ObjectId()
  37. })
  38. const otherFileDetails = {
  39. name: 'universe.jpg',
  40. linkedFileData: null,
  41. hash: 'ed19e7d6779b47d8c63f6fa5a21954dcfb6cac00',
  42. deletedAt: new Date(),
  43. __v: 0,
  44. }
  45. let deletedFiles1, deletedFiles2, deletedFiles3
  46. beforeEach('set deletedFiles details', async function () {
  47. deletedFiles1 = [
  48. { _id: fileId1, ...otherFileDetails },
  49. { _id: fileId2, ...otherFileDetails },
  50. ]
  51. deletedFiles2 = [{ _id: fileId3, ...otherFileDetails }]
  52. await setDeletedFiles(projectId1, deletedFiles1)
  53. await setDeletedFiles(projectId2, deletedFiles2)
  54. // a project without deletedFiles entries
  55. await setDeletedFiles(projectId3, [])
  56. // a project without deletedFiles array
  57. await unsetDeletedFiles(projectId4)
  58. // duplicate entry
  59. deletedFiles3 = [
  60. { _id: fileId4, ...otherFileDetails },
  61. { _id: fileId4, ...otherFileDetails },
  62. ]
  63. await setDeletedFiles(projectId5, deletedFiles3)
  64. })
  65. async function runScript(args = []) {
  66. let result
  67. try {
  68. result = await promisify(exec)(
  69. ['LET_USER_DOUBLE_CHECK_INPUTS_FOR=1', 'VERBOSE_LOGGING=true']
  70. .concat(['node', 'scripts/back_fill_deleted_files'])
  71. .concat(args)
  72. .join(' ')
  73. )
  74. } catch (error) {
  75. // dump details like exit code, stdErr and stdOut
  76. logger.error({ error }, 'script failed')
  77. throw error
  78. }
  79. const { stdout: stdOut } = result
  80. expect(stdOut).to.match(
  81. new RegExp(`Running update on batch with ids .+${projectId1}`)
  82. )
  83. expect(stdOut).to.match(
  84. new RegExp(`Running update on batch with ids .+${projectId2}`)
  85. )
  86. expect(stdOut).to.not.match(
  87. new RegExp(`Running update on batch with ids .+${projectId3}`)
  88. )
  89. expect(stdOut).to.not.match(
  90. new RegExp(`Running update on batch with ids .+${projectId4}`)
  91. )
  92. expect(stdOut).to.match(
  93. new RegExp(`Running update on batch with ids .+${projectId5}`)
  94. )
  95. }
  96. function checkAreFilesBackFilled() {
  97. it('should back fill file and set projectId', async function () {
  98. const docs = await db.deletedFiles
  99. .find({}, { sort: { _id: 1 } })
  100. .toArray()
  101. expect(docs).to.deep.equal([
  102. { _id: fileId1, projectId: projectId1, ...otherFileDetails },
  103. { _id: fileId2, projectId: projectId1, ...otherFileDetails },
  104. { _id: fileId3, projectId: projectId2, ...otherFileDetails },
  105. { _id: fileId4, projectId: projectId5, ...otherFileDetails },
  106. ])
  107. })
  108. }
  109. describe('back fill only', function () {
  110. beforeEach('run script', runScript)
  111. checkAreFilesBackFilled()
  112. it('should leave the deletedFiles as is', async function () {
  113. expect(await getDeletedFiles(projectId1)).to.deep.equal(deletedFiles1)
  114. expect(await getDeletedFiles(projectId2)).to.deep.equal(deletedFiles2)
  115. expect(await getDeletedFiles(projectId5)).to.deep.equal(deletedFiles3)
  116. })
  117. })
  118. describe('back fill and cleanup', function () {
  119. beforeEach('run script with cleanup flag', async function () {
  120. await runScript(['--perform-cleanup'])
  121. })
  122. checkAreFilesBackFilled()
  123. it('should cleanup the deletedFiles', async function () {
  124. expect(await getDeletedFiles(projectId1)).to.deep.equal([])
  125. expect(await getDeletedFiles(projectId2)).to.deep.equal([])
  126. expect(await getDeletedFiles(projectId5)).to.deep.equal([])
  127. })
  128. })
  129. describe('fix partial inserts and cleanup', function () {
  130. beforeEach('simulate one missing insert', async function () {
  131. await setDeletedFiles(projectId1, [deletedFiles1[0]])
  132. })
  133. beforeEach('run script with cleanup flag', async function () {
  134. await runScript(['--perform-cleanup'])
  135. })
  136. beforeEach('add case for one missing file', async function () {
  137. await setDeletedFiles(projectId1, deletedFiles1)
  138. })
  139. beforeEach('add cases for no more files to insert', async function () {
  140. await setDeletedFiles(projectId2, deletedFiles2)
  141. await setDeletedFiles(projectId5, deletedFiles3)
  142. })
  143. beforeEach('fixing partial insert and cleanup', async function () {
  144. await runScript(['--fix-partial-inserts', '--perform-cleanup'])
  145. })
  146. checkAreFilesBackFilled()
  147. it('should cleanup the deletedFiles', async function () {
  148. expect(await getDeletedFiles(projectId1)).to.deep.equal([])
  149. expect(await getDeletedFiles(projectId2)).to.deep.equal([])
  150. expect(await getDeletedFiles(projectId5)).to.deep.equal([])
  151. })
  152. })
  153. })