ProjectStructureMongoLockTest.mjs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. /* eslint-disable
  2. n/handle-callback-err,
  3. max-len,
  4. */
  5. // TODO: This file was created by bulk-decaffeinate.
  6. // Fix any style issues and re-enable lint.
  7. /*
  8. * decaffeinate suggestions:
  9. * DS101: Remove unnecessary use of Array.from
  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. import LockManager from '../../../app/src/infrastructure/LockManager.mjs'
  15. import ProjectCreationHandler from '../../../app/src/Features/Project/ProjectCreationHandler.mjs'
  16. import ProjectGetter from '../../../app/src/Features/Project/ProjectGetter.mjs'
  17. import ProjectEntityMongoUpdateHandler from '../../../app/src/Features/Project/ProjectEntityMongoUpdateHandler.mjs'
  18. import UserCreator from '../../../app/src/Features/User/UserCreator.mjs'
  19. import { expect } from 'chai'
  20. import _ from 'lodash'
  21. import crypto from 'node:crypto'
  22. // These tests are neither acceptance tests nor unit tests. It's difficult to
  23. // test/verify that our locking is doing what we hope.
  24. // These tests call methods in ProjectGetter and ProjectEntityMongoUpdateHandler
  25. // to see that they DO NOT work when a lock has been taken.
  26. //
  27. // It is tested that these methods DO work when the lock has not been taken in
  28. // other acceptance tests.
  29. describe('ProjectStructureMongoLock', function () {
  30. describe('whilst a project lock is taken', function () {
  31. let oldMaxLockWaitTime
  32. before(function () {
  33. oldMaxLockWaitTime = LockManager.MAX_LOCK_WAIT_TIME
  34. })
  35. after(function () {
  36. LockManager.MAX_LOCK_WAIT_TIME = oldMaxLockWaitTime
  37. })
  38. beforeEach(function (done) {
  39. // We want to instantly fail if the lock is taken
  40. LockManager.MAX_LOCK_WAIT_TIME = 1
  41. this.lockValue = 'lock-value'
  42. const userDetails = {
  43. holdingAccount: false,
  44. email: 'test@example.com',
  45. analyticsId: crypto.randomUUID(),
  46. }
  47. UserCreator.createNewUser(userDetails, {}, (err, user) => {
  48. this.user = user
  49. if (err != null) {
  50. throw err
  51. }
  52. return ProjectCreationHandler.createBlankProject(
  53. user._id,
  54. 'locked-project',
  55. (err, project) => {
  56. if (err != null) {
  57. throw err
  58. }
  59. this.locked_project = project
  60. const namespace = ProjectEntityMongoUpdateHandler.LOCK_NAMESPACE
  61. this.lock_key = `lock:web:${namespace}:${project._id}`
  62. return LockManager._getLock(
  63. this.lock_key,
  64. namespace,
  65. (err, lockValue) => {
  66. this.lockValue = lockValue
  67. return done()
  68. }
  69. )
  70. }
  71. )
  72. })
  73. })
  74. afterEach(function (done) {
  75. return LockManager._releaseLock(this.lock_key, this.lockValue, done)
  76. })
  77. describe('interacting with the locked project', function () {
  78. const LOCKING_UPDATE_METHODS = [
  79. 'addDoc',
  80. 'addFile',
  81. 'mkdirp',
  82. 'moveEntity',
  83. 'renameEntity',
  84. 'addFolder',
  85. ]
  86. for (const methodName of Array.from(LOCKING_UPDATE_METHODS)) {
  87. it(`cannot call ProjectEntityMongoUpdateHandler.${methodName}`, function (done) {
  88. const method = ProjectEntityMongoUpdateHandler[methodName]
  89. const args = _.times(method.length - 2, _.constant(null))
  90. return method(this.locked_project._id, args, err => {
  91. expect(err).to.be.instanceOf(Error)
  92. expect(err).to.have.property('message', 'Timeout')
  93. return done()
  94. })
  95. })
  96. }
  97. it('cannot get the project without a projection', function (done) {
  98. return ProjectGetter.getProject(this.locked_project._id, err => {
  99. expect(err).to.be.instanceOf(Error)
  100. expect(err).to.have.property('message', 'Timeout')
  101. return done()
  102. })
  103. })
  104. it('cannot get the project if rootFolder is in the projection', function (done) {
  105. return ProjectGetter.getProject(
  106. this.locked_project._id,
  107. { rootFolder: true },
  108. err => {
  109. expect(err).to.be.instanceOf(Error)
  110. expect(err).to.have.property('message', 'Timeout')
  111. return done()
  112. }
  113. )
  114. })
  115. it('can get the project if rootFolder is not in the projection', function (done) {
  116. return ProjectGetter.getProject(
  117. this.locked_project._id,
  118. { _id: true },
  119. (err, project) => {
  120. expect(err).to.equal(null)
  121. expect(project._id).to.deep.equal(this.locked_project._id)
  122. return done()
  123. }
  124. )
  125. })
  126. })
  127. describe('interacting with other projects', function () {
  128. beforeEach(function (done) {
  129. return ProjectCreationHandler.createBlankProject(
  130. this.user._id,
  131. 'unlocked-project',
  132. (err, project) => {
  133. if (err != null) {
  134. throw err
  135. }
  136. this.unlocked_project = project
  137. return done()
  138. }
  139. )
  140. })
  141. it('can add folders to other projects', function (done) {
  142. return ProjectEntityMongoUpdateHandler.addFolder(
  143. this.unlocked_project._id,
  144. this.unlocked_project.rootFolder[0]._id,
  145. 'new folder',
  146. (err, folder) => {
  147. expect(err).to.equal(null)
  148. expect(folder).to.exist
  149. return done()
  150. }
  151. )
  152. })
  153. it('can get other projects without a projection', function (done) {
  154. return ProjectGetter.getProject(
  155. this.unlocked_project._id,
  156. (err, project) => {
  157. expect(err).to.equal(null)
  158. expect(project._id).to.deep.equal(this.unlocked_project._id)
  159. return done()
  160. }
  161. )
  162. })
  163. })
  164. })
  165. })