|
@@ -1,33 +1,40 @@
|
|
|
|
|
+import { vi, expect } from 'vitest'
|
|
|
|
|
+import sinon from 'sinon'
|
|
|
|
|
+import tk from 'timekeeper'
|
|
|
|
|
+import moment from 'moment'
|
|
|
|
|
+import indirectlyImportModels from '../helpers/indirectlyImportModels.js'
|
|
|
|
|
+import mongodb from 'mongodb-legacy'
|
|
|
|
|
+import Errors from '../../../../app/src/Features/Errors/Errors.js'
|
|
|
const modulePath = '../../../../app/src/Features/Project/ProjectDeleter'
|
|
const modulePath = '../../../../app/src/Features/Project/ProjectDeleter'
|
|
|
-const SandboxedModule = require('sandboxed-module')
|
|
|
|
|
-const sinon = require('sinon')
|
|
|
|
|
-const { expect } = require('chai')
|
|
|
|
|
-const tk = require('timekeeper')
|
|
|
|
|
-const moment = require('moment')
|
|
|
|
|
-const { Project } = require('../helpers/models/Project')
|
|
|
|
|
-const { DeletedProject } = require('../helpers/models/DeletedProject')
|
|
|
|
|
-const { ObjectId, ReadPreference } = require('mongodb-legacy')
|
|
|
|
|
-const Errors = require('../../../../app/src/Features/Errors/Errors')
|
|
|
|
|
|
|
+
|
|
|
|
|
+const { Project, DeletedProject } = indirectlyImportModels([
|
|
|
|
|
+ 'Project',
|
|
|
|
|
+ 'DeletedProject',
|
|
|
|
|
+])
|
|
|
|
|
+const { ObjectId, ReadPreference } = mongodb
|
|
|
|
|
+vi.mock('../../../../app/src/Features/Errors/Errors.js', () =>
|
|
|
|
|
+ vi.importActual('../../../../app/src/Features/Errors/Errors.js')
|
|
|
|
|
+)
|
|
|
|
|
|
|
|
describe('ProjectDeleter', function () {
|
|
describe('ProjectDeleter', function () {
|
|
|
- beforeEach(function () {
|
|
|
|
|
|
|
+ beforeEach(async function (ctx) {
|
|
|
tk.freeze(Date.now())
|
|
tk.freeze(Date.now())
|
|
|
- this.ip = '192.170.18.1'
|
|
|
|
|
- this.project = dummyProject()
|
|
|
|
|
- this.user = {
|
|
|
|
|
|
|
+ ctx.ip = '192.170.18.1'
|
|
|
|
|
+ ctx.project = dummyProject()
|
|
|
|
|
+ ctx.user = {
|
|
|
_id: '588f3ddae8ebc1bac07c9fa4',
|
|
_id: '588f3ddae8ebc1bac07c9fa4',
|
|
|
first_name: 'bjkdsjfk',
|
|
first_name: 'bjkdsjfk',
|
|
|
features: {},
|
|
features: {},
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- this.doc = {
|
|
|
|
|
|
|
+ ctx.doc = {
|
|
|
_id: '5bd975f54f62e803cb8a8fec',
|
|
_id: '5bd975f54f62e803cb8a8fec',
|
|
|
lines: ['a bunch of lines', 'for a sunny day', 'in London town'],
|
|
lines: ['a bunch of lines', 'for a sunny day', 'in London town'],
|
|
|
ranges: {},
|
|
ranges: {},
|
|
|
project_id: '5cf9270b4eff6e186cf8b05e',
|
|
project_id: '5cf9270b4eff6e186cf8b05e',
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- this.deletedProjects = [
|
|
|
|
|
|
|
+ ctx.deletedProjects = [
|
|
|
{
|
|
{
|
|
|
_id: '5cf7f145c1401f0ca0eb1aaa',
|
|
_id: '5cf7f145c1401f0ca0eb1aaa',
|
|
|
deleterData: {
|
|
deleterData: {
|
|
@@ -36,7 +43,7 @@ describe('ProjectDeleter', function () {
|
|
|
deleterId: '588f3ddae8ebc1bac07c9fa4',
|
|
deleterId: '588f3ddae8ebc1bac07c9fa4',
|
|
|
deleterIpAddress: '172.19.0.1',
|
|
deleterIpAddress: '172.19.0.1',
|
|
|
deletedProjectId: '5cf9270b4eff6e186cf8b05e',
|
|
deletedProjectId: '5cf9270b4eff6e186cf8b05e',
|
|
|
- deletedProjectOwnerId: this.user._id,
|
|
|
|
|
|
|
+ deletedProjectOwnerId: ctx.user._id,
|
|
|
},
|
|
},
|
|
|
project: {
|
|
project: {
|
|
|
_id: '5cf9270b4eff6e186cf8b05e',
|
|
_id: '5cf9270b4eff6e186cf8b05e',
|
|
@@ -62,171 +69,224 @@ describe('ProjectDeleter', function () {
|
|
|
},
|
|
},
|
|
|
]
|
|
]
|
|
|
|
|
|
|
|
- this.DocumentUpdaterHandler = {
|
|
|
|
|
|
|
+ ctx.DocumentUpdaterHandler = {
|
|
|
promises: {
|
|
promises: {
|
|
|
flushProjectToMongoAndDelete: sinon.stub().resolves(),
|
|
flushProjectToMongoAndDelete: sinon.stub().resolves(),
|
|
|
},
|
|
},
|
|
|
}
|
|
}
|
|
|
- this.EditorRealTimeController = {
|
|
|
|
|
|
|
+ ctx.EditorRealTimeController = {
|
|
|
emitToRoom: sinon.stub(),
|
|
emitToRoom: sinon.stub(),
|
|
|
}
|
|
}
|
|
|
- this.TagsHandler = {
|
|
|
|
|
|
|
+ ctx.TagsHandler = {
|
|
|
promises: {
|
|
promises: {
|
|
|
removeProjectFromAllTags: sinon.stub().resolves(),
|
|
removeProjectFromAllTags: sinon.stub().resolves(),
|
|
|
},
|
|
},
|
|
|
}
|
|
}
|
|
|
- this.CollaboratorsHandler = {
|
|
|
|
|
|
|
+ ctx.CollaboratorsHandler = {
|
|
|
promises: {
|
|
promises: {
|
|
|
removeUserFromAllProjects: sinon.stub().resolves(),
|
|
removeUserFromAllProjects: sinon.stub().resolves(),
|
|
|
},
|
|
},
|
|
|
}
|
|
}
|
|
|
- this.CollaboratorsGetter = {
|
|
|
|
|
|
|
+ ctx.CollaboratorsGetter = {
|
|
|
promises: {
|
|
promises: {
|
|
|
getMemberIds: sinon
|
|
getMemberIds: sinon
|
|
|
.stub()
|
|
.stub()
|
|
|
- .withArgs(this.project._id)
|
|
|
|
|
|
|
+ .withArgs(ctx.project._id)
|
|
|
.resolves(['member-id-1', 'member-id-2']),
|
|
.resolves(['member-id-1', 'member-id-2']),
|
|
|
},
|
|
},
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- this.ProjectDetailsHandler = {
|
|
|
|
|
|
|
+ ctx.ProjectDetailsHandler = {
|
|
|
promises: {
|
|
promises: {
|
|
|
- generateUniqueName: sinon.stub().resolves(this.project.name),
|
|
|
|
|
|
|
+ generateUniqueName: sinon.stub().resolves(ctx.project.name),
|
|
|
},
|
|
},
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- this.db = {
|
|
|
|
|
|
|
+ ctx.db = {
|
|
|
projects: {
|
|
projects: {
|
|
|
insertOne: sinon.stub().resolves(),
|
|
insertOne: sinon.stub().resolves(),
|
|
|
},
|
|
},
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- this.DocstoreManager = {
|
|
|
|
|
|
|
+ ctx.DocstoreManager = {
|
|
|
promises: {
|
|
promises: {
|
|
|
archiveProject: sinon.stub().resolves(),
|
|
archiveProject: sinon.stub().resolves(),
|
|
|
destroyProject: sinon.stub().resolves(),
|
|
destroyProject: sinon.stub().resolves(),
|
|
|
},
|
|
},
|
|
|
}
|
|
}
|
|
|
- this.HistoryManager = {
|
|
|
|
|
|
|
+ ctx.HistoryManager = {
|
|
|
promises: {
|
|
promises: {
|
|
|
deleteProject: sinon.stub().resolves(),
|
|
deleteProject: sinon.stub().resolves(),
|
|
|
},
|
|
},
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- this.ProjectMock = sinon.mock(Project)
|
|
|
|
|
- this.DeletedProjectMock = sinon.mock(DeletedProject)
|
|
|
|
|
- this.Features = {
|
|
|
|
|
|
|
+ ctx.ProjectMock = sinon.mock(Project)
|
|
|
|
|
+ ctx.DeletedProjectMock = sinon.mock(DeletedProject)
|
|
|
|
|
+ ctx.Features = {
|
|
|
hasFeature: sinon.stub().returns(true),
|
|
hasFeature: sinon.stub().returns(true),
|
|
|
}
|
|
}
|
|
|
- this.ChatApiHandler = {
|
|
|
|
|
|
|
+ ctx.ChatApiHandler = {
|
|
|
promises: {
|
|
promises: {
|
|
|
destroyProject: sinon.stub().resolves(),
|
|
destroyProject: sinon.stub().resolves(),
|
|
|
},
|
|
},
|
|
|
}
|
|
}
|
|
|
- this.ProjectAuditLogEntry = {
|
|
|
|
|
|
|
+ ctx.ProjectAuditLogEntry = {
|
|
|
deleteMany: sinon.stub().returns({ exec: sinon.stub().resolves() }),
|
|
deleteMany: sinon.stub().returns({ exec: sinon.stub().resolves() }),
|
|
|
}
|
|
}
|
|
|
- this.ProjectDeleter = SandboxedModule.require(modulePath, {
|
|
|
|
|
- requires: {
|
|
|
|
|
- '../../infrastructure/Modules': {
|
|
|
|
|
- promises: { hooks: { fire: sinon.stub().resolves() } },
|
|
|
|
|
- },
|
|
|
|
|
- '../../infrastructure/Features': this.Features,
|
|
|
|
|
- '../Editor/EditorRealTimeController': this.EditorRealTimeController,
|
|
|
|
|
- '../../models/Project': { Project },
|
|
|
|
|
- '../../models/DeletedProject': { DeletedProject },
|
|
|
|
|
- '../DocumentUpdater/DocumentUpdaterHandler':
|
|
|
|
|
- this.DocumentUpdaterHandler,
|
|
|
|
|
- '../Tags/TagsHandler': this.TagsHandler,
|
|
|
|
|
- '../Chat/ChatApiHandler': this.ChatApiHandler,
|
|
|
|
|
- '../Collaborators/CollaboratorsHandler': this.CollaboratorsHandler,
|
|
|
|
|
- '../Collaborators/CollaboratorsGetter': this.CollaboratorsGetter,
|
|
|
|
|
- '../Docstore/DocstoreManager': this.DocstoreManager,
|
|
|
|
|
- './ProjectDetailsHandler': this.ProjectDetailsHandler,
|
|
|
|
|
- '../../infrastructure/mongodb': {
|
|
|
|
|
- db: this.db,
|
|
|
|
|
- ObjectId,
|
|
|
|
|
- READ_PREFERENCE_SECONDARY: ReadPreference.secondaryPreferred.mode,
|
|
|
|
|
- },
|
|
|
|
|
- '../History/HistoryManager': this.HistoryManager,
|
|
|
|
|
- '../../models/ProjectAuditLogEntry': {
|
|
|
|
|
- ProjectAuditLogEntry: this.ProjectAuditLogEntry,
|
|
|
|
|
- },
|
|
|
|
|
|
|
+
|
|
|
|
|
+ vi.doMock('../../../../app/src/infrastructure/Modules', () => ({
|
|
|
|
|
+ default: {
|
|
|
|
|
+ promises: { hooks: { fire: sinon.stub().resolves() } },
|
|
|
},
|
|
},
|
|
|
- })
|
|
|
|
|
|
|
+ }))
|
|
|
|
|
+
|
|
|
|
|
+ vi.doMock('../../../../app/src/infrastructure/Features', () => ({
|
|
|
|
|
+ default: ctx.Features,
|
|
|
|
|
+ }))
|
|
|
|
|
+
|
|
|
|
|
+ vi.doMock(
|
|
|
|
|
+ '../../../../app/src/Features/Editor/EditorRealTimeController',
|
|
|
|
|
+ () => ({
|
|
|
|
|
+ default: ctx.EditorRealTimeController,
|
|
|
|
|
+ })
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ vi.doMock('../../../../app/src/models/Project', () => ({
|
|
|
|
|
+ Project,
|
|
|
|
|
+ }))
|
|
|
|
|
+
|
|
|
|
|
+ vi.doMock('../../../../app/src/models/DeletedProject', () => ({
|
|
|
|
|
+ DeletedProject,
|
|
|
|
|
+ }))
|
|
|
|
|
+
|
|
|
|
|
+ vi.doMock(
|
|
|
|
|
+ '../../../../app/src/Features/DocumentUpdater/DocumentUpdaterHandler',
|
|
|
|
|
+ () => ({
|
|
|
|
|
+ default: ctx.DocumentUpdaterHandler,
|
|
|
|
|
+ })
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ vi.doMock('../../../../app/src/Features/Tags/TagsHandler', () => ({
|
|
|
|
|
+ default: ctx.TagsHandler,
|
|
|
|
|
+ }))
|
|
|
|
|
+
|
|
|
|
|
+ vi.doMock('../../../../app/src/Features/Chat/ChatApiHandler', () => ({
|
|
|
|
|
+ default: ctx.ChatApiHandler,
|
|
|
|
|
+ }))
|
|
|
|
|
+
|
|
|
|
|
+ vi.doMock(
|
|
|
|
|
+ '../../../../app/src/Features/Collaborators/CollaboratorsHandler',
|
|
|
|
|
+ () => ({
|
|
|
|
|
+ default: ctx.CollaboratorsHandler,
|
|
|
|
|
+ })
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ vi.doMock(
|
|
|
|
|
+ '../../../../app/src/Features/Collaborators/CollaboratorsGetter',
|
|
|
|
|
+ () => ({
|
|
|
|
|
+ default: ctx.CollaboratorsGetter,
|
|
|
|
|
+ })
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ vi.doMock('../../../../app/src/Features/Docstore/DocstoreManager', () => ({
|
|
|
|
|
+ default: ctx.DocstoreManager,
|
|
|
|
|
+ }))
|
|
|
|
|
+
|
|
|
|
|
+ vi.doMock(
|
|
|
|
|
+ '../../../../app/src/Features/Project/ProjectDetailsHandler',
|
|
|
|
|
+ () => ({
|
|
|
|
|
+ default: ctx.ProjectDetailsHandler,
|
|
|
|
|
+ })
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ vi.doMock('../../../../app/src/infrastructure/mongodb', () => ({
|
|
|
|
|
+ db: ctx.db,
|
|
|
|
|
+ ObjectId,
|
|
|
|
|
+ READ_PREFERENCE_SECONDARY: ReadPreference.secondaryPreferred.mode,
|
|
|
|
|
+ }))
|
|
|
|
|
+
|
|
|
|
|
+ vi.doMock('../../../../app/src/Features/History/HistoryManager', () => ({
|
|
|
|
|
+ default: ctx.HistoryManager,
|
|
|
|
|
+ }))
|
|
|
|
|
+
|
|
|
|
|
+ vi.doMock('../../../../app/src/models/ProjectAuditLogEntry', () => ({
|
|
|
|
|
+ ProjectAuditLogEntry: ctx.ProjectAuditLogEntry,
|
|
|
|
|
+ }))
|
|
|
|
|
+
|
|
|
|
|
+ ctx.ProjectDeleter = (await import(modulePath)).default
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- afterEach(function () {
|
|
|
|
|
|
|
+ afterEach(function (ctx) {
|
|
|
tk.reset()
|
|
tk.reset()
|
|
|
- this.DeletedProjectMock.restore()
|
|
|
|
|
- this.ProjectMock.restore()
|
|
|
|
|
|
|
+ ctx.DeletedProjectMock.restore()
|
|
|
|
|
+ ctx.ProjectMock.restore()
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
describe('mark as deleted by external source', function () {
|
|
describe('mark as deleted by external source', function () {
|
|
|
- beforeEach(function () {
|
|
|
|
|
- this.ProjectMock.expects('updateOne')
|
|
|
|
|
|
|
+ beforeEach(function (ctx) {
|
|
|
|
|
+ ctx.ProjectMock.expects('updateOne')
|
|
|
.withArgs(
|
|
.withArgs(
|
|
|
- { _id: this.project._id },
|
|
|
|
|
|
|
+ { _id: ctx.project._id },
|
|
|
{ deletedByExternalDataSource: true }
|
|
{ deletedByExternalDataSource: true }
|
|
|
)
|
|
)
|
|
|
.chain('exec')
|
|
.chain('exec')
|
|
|
.resolves()
|
|
.resolves()
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('should update the project with the flag set to true', async function () {
|
|
|
|
|
- await this.ProjectDeleter.promises.markAsDeletedByExternalSource(
|
|
|
|
|
- this.project._id
|
|
|
|
|
|
|
+ it('should update the project with the flag set to true', async function (ctx) {
|
|
|
|
|
+ await ctx.ProjectDeleter.promises.markAsDeletedByExternalSource(
|
|
|
|
|
+ ctx.project._id
|
|
|
)
|
|
)
|
|
|
- this.ProjectMock.verify()
|
|
|
|
|
|
|
+ ctx.ProjectMock.verify()
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('should tell the editor controler so users are notified', async function () {
|
|
|
|
|
- await this.ProjectDeleter.promises.markAsDeletedByExternalSource(
|
|
|
|
|
- this.project._id
|
|
|
|
|
|
|
+ it('should tell the editor controler so users are notified', async function (ctx) {
|
|
|
|
|
+ await ctx.ProjectDeleter.promises.markAsDeletedByExternalSource(
|
|
|
|
|
+ ctx.project._id
|
|
|
)
|
|
)
|
|
|
- expect(this.EditorRealTimeController.emitToRoom).to.have.been.calledWith(
|
|
|
|
|
- this.project._id,
|
|
|
|
|
|
|
+ expect(ctx.EditorRealTimeController.emitToRoom).to.have.been.calledWith(
|
|
|
|
|
+ ctx.project._id,
|
|
|
'projectRenamedOrDeletedByExternalSource'
|
|
'projectRenamedOrDeletedByExternalSource'
|
|
|
)
|
|
)
|
|
|
})
|
|
})
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
describe('unmarkAsDeletedByExternalSource', function () {
|
|
describe('unmarkAsDeletedByExternalSource', function () {
|
|
|
- beforeEach(async function () {
|
|
|
|
|
- this.ProjectMock.expects('updateOne')
|
|
|
|
|
|
|
+ beforeEach(async function (ctx) {
|
|
|
|
|
+ ctx.ProjectMock.expects('updateOne')
|
|
|
.withArgs(
|
|
.withArgs(
|
|
|
- { _id: this.project._id },
|
|
|
|
|
|
|
+ { _id: ctx.project._id },
|
|
|
{ deletedByExternalDataSource: false }
|
|
{ deletedByExternalDataSource: false }
|
|
|
)
|
|
)
|
|
|
.chain('exec')
|
|
.chain('exec')
|
|
|
.resolves()
|
|
.resolves()
|
|
|
- await this.ProjectDeleter.promises.unmarkAsDeletedByExternalSource(
|
|
|
|
|
- this.project._id
|
|
|
|
|
|
|
+ await ctx.ProjectDeleter.promises.unmarkAsDeletedByExternalSource(
|
|
|
|
|
+ ctx.project._id
|
|
|
)
|
|
)
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('should remove the flag from the project', function () {
|
|
|
|
|
- this.ProjectMock.verify()
|
|
|
|
|
|
|
+ it('should remove the flag from the project', function (ctx) {
|
|
|
|
|
+ ctx.ProjectMock.verify()
|
|
|
})
|
|
})
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
describe('deleteUsersProjects', function () {
|
|
describe('deleteUsersProjects', function () {
|
|
|
- beforeEach(function () {
|
|
|
|
|
- this.projects = [dummyProject(), dummyProject()]
|
|
|
|
|
- this.ProjectMock.expects('find')
|
|
|
|
|
- .withArgs({ owner_ref: this.user._id })
|
|
|
|
|
|
|
+ beforeEach(function (ctx) {
|
|
|
|
|
+ ctx.projects = [dummyProject(), dummyProject()]
|
|
|
|
|
+ ctx.ProjectMock.expects('find')
|
|
|
|
|
+ .withArgs({ owner_ref: ctx.user._id })
|
|
|
.chain('exec')
|
|
.chain('exec')
|
|
|
- .resolves(this.projects)
|
|
|
|
|
- for (const project of this.projects) {
|
|
|
|
|
- this.ProjectMock.expects('findOne')
|
|
|
|
|
|
|
+ .resolves(ctx.projects)
|
|
|
|
|
+ for (const project of ctx.projects) {
|
|
|
|
|
+ ctx.ProjectMock.expects('findOne')
|
|
|
.withArgs({ _id: project._id })
|
|
.withArgs({ _id: project._id })
|
|
|
.chain('exec')
|
|
.chain('exec')
|
|
|
.resolves(project)
|
|
.resolves(project)
|
|
|
- this.ProjectMock.expects('deleteOne')
|
|
|
|
|
|
|
+ ctx.ProjectMock.expects('deleteOne')
|
|
|
.withArgs({ _id: project._id })
|
|
.withArgs({ _id: project._id })
|
|
|
.chain('exec')
|
|
.chain('exec')
|
|
|
.resolves()
|
|
.resolves()
|
|
|
- this.DeletedProjectMock.expects('updateOne')
|
|
|
|
|
|
|
+ ctx.DeletedProjectMock.expects('updateOne')
|
|
|
.withArgs(
|
|
.withArgs(
|
|
|
{ 'deleterData.deletedProjectId': project._id },
|
|
{ 'deleterData.deletedProjectId': project._id },
|
|
|
{
|
|
{
|
|
@@ -239,146 +299,146 @@ describe('ProjectDeleter', function () {
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('should delete all projects owned by the user', async function () {
|
|
|
|
|
- await this.ProjectDeleter.promises.deleteUsersProjects(this.user._id)
|
|
|
|
|
- this.ProjectMock.verify()
|
|
|
|
|
- this.DeletedProjectMock.verify()
|
|
|
|
|
|
|
+ it('should delete all projects owned by the user', async function (ctx) {
|
|
|
|
|
+ await ctx.ProjectDeleter.promises.deleteUsersProjects(ctx.user._id)
|
|
|
|
|
+ ctx.ProjectMock.verify()
|
|
|
|
|
+ ctx.DeletedProjectMock.verify()
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('should remove any collaboration from this user', async function () {
|
|
|
|
|
- await this.ProjectDeleter.promises.deleteUsersProjects(this.user._id)
|
|
|
|
|
|
|
+ it('should remove any collaboration from this user', async function (ctx) {
|
|
|
|
|
+ await ctx.ProjectDeleter.promises.deleteUsersProjects(ctx.user._id)
|
|
|
sinon.assert.calledWith(
|
|
sinon.assert.calledWith(
|
|
|
- this.CollaboratorsHandler.promises.removeUserFromAllProjects,
|
|
|
|
|
- this.user._id
|
|
|
|
|
|
|
+ ctx.CollaboratorsHandler.promises.removeUserFromAllProjects,
|
|
|
|
|
+ ctx.user._id
|
|
|
)
|
|
)
|
|
|
sinon.assert.calledOnce(
|
|
sinon.assert.calledOnce(
|
|
|
- this.CollaboratorsHandler.promises.removeUserFromAllProjects
|
|
|
|
|
|
|
+ ctx.CollaboratorsHandler.promises.removeUserFromAllProjects
|
|
|
)
|
|
)
|
|
|
})
|
|
})
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
describe('deleteProject', function () {
|
|
describe('deleteProject', function () {
|
|
|
- beforeEach(function () {
|
|
|
|
|
- this.deleterData = {
|
|
|
|
|
|
|
+ beforeEach(function (ctx) {
|
|
|
|
|
+ ctx.deleterData = {
|
|
|
deletedAt: new Date(),
|
|
deletedAt: new Date(),
|
|
|
- deletedProjectId: this.project._id,
|
|
|
|
|
- deletedProjectOwnerId: this.project.owner_ref,
|
|
|
|
|
- deletedProjectCollaboratorIds: this.project.collaberator_refs,
|
|
|
|
|
- deletedProjectReadOnlyIds: this.project.readOnly_refs,
|
|
|
|
|
- deletedProjectReviewerIds: this.project.reviewer_refs,
|
|
|
|
|
|
|
+ deletedProjectId: ctx.project._id,
|
|
|
|
|
+ deletedProjectOwnerId: ctx.project.owner_ref,
|
|
|
|
|
+ deletedProjectCollaboratorIds: ctx.project.collaberator_refs,
|
|
|
|
|
+ deletedProjectReadOnlyIds: ctx.project.readOnly_refs,
|
|
|
|
|
+ deletedProjectReviewerIds: ctx.project.reviewer_refs,
|
|
|
deletedProjectReadWriteTokenAccessIds:
|
|
deletedProjectReadWriteTokenAccessIds:
|
|
|
- this.project.tokenAccessReadAndWrite_refs,
|
|
|
|
|
|
|
+ ctx.project.tokenAccessReadAndWrite_refs,
|
|
|
deletedProjectReadOnlyTokenAccessIds:
|
|
deletedProjectReadOnlyTokenAccessIds:
|
|
|
- this.project.tokenAccessReadOnly_refs,
|
|
|
|
|
- deletedProjectReadWriteToken: this.project.tokens.readAndWrite,
|
|
|
|
|
- deletedProjectReadOnlyToken: this.project.tokens.readOnly,
|
|
|
|
|
- deletedProjectOverleafId: this.project.overleaf.id,
|
|
|
|
|
- deletedProjectOverleafHistoryId: this.project.overleaf.history.id,
|
|
|
|
|
- deletedProjectLastUpdatedAt: this.project.lastUpdated,
|
|
|
|
|
|
|
+ ctx.project.tokenAccessReadOnly_refs,
|
|
|
|
|
+ deletedProjectReadWriteToken: ctx.project.tokens.readAndWrite,
|
|
|
|
|
+ deletedProjectReadOnlyToken: ctx.project.tokens.readOnly,
|
|
|
|
|
+ deletedProjectOverleafId: ctx.project.overleaf.id,
|
|
|
|
|
+ deletedProjectOverleafHistoryId: ctx.project.overleaf.history.id,
|
|
|
|
|
+ deletedProjectLastUpdatedAt: ctx.project.lastUpdated,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- this.ProjectMock.expects('findOne')
|
|
|
|
|
- .withArgs({ _id: this.project._id })
|
|
|
|
|
|
|
+ ctx.ProjectMock.expects('findOne')
|
|
|
|
|
+ .withArgs({ _id: ctx.project._id })
|
|
|
.chain('exec')
|
|
.chain('exec')
|
|
|
- .resolves(this.project)
|
|
|
|
|
|
|
+ .resolves(ctx.project)
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('should save a DeletedProject with additional deleterData', async function () {
|
|
|
|
|
- this.deleterData.deleterIpAddress = this.ip
|
|
|
|
|
- this.deleterData.deleterId = this.user._id
|
|
|
|
|
|
|
+ it('should save a DeletedProject with additional deleterData', async function (ctx) {
|
|
|
|
|
+ ctx.deleterData.deleterIpAddress = ctx.ip
|
|
|
|
|
+ ctx.deleterData.deleterId = ctx.user._id
|
|
|
|
|
|
|
|
- this.ProjectMock.expects('deleteOne').chain('exec').resolves()
|
|
|
|
|
- this.DeletedProjectMock.expects('updateOne')
|
|
|
|
|
|
|
+ ctx.ProjectMock.expects('deleteOne').chain('exec').resolves()
|
|
|
|
|
+ ctx.DeletedProjectMock.expects('updateOne')
|
|
|
.withArgs(
|
|
.withArgs(
|
|
|
- { 'deleterData.deletedProjectId': this.project._id },
|
|
|
|
|
|
|
+ { 'deleterData.deletedProjectId': ctx.project._id },
|
|
|
{
|
|
{
|
|
|
- project: this.project,
|
|
|
|
|
- deleterData: this.deleterData,
|
|
|
|
|
|
|
+ project: ctx.project,
|
|
|
|
|
+ deleterData: ctx.deleterData,
|
|
|
},
|
|
},
|
|
|
{ upsert: true }
|
|
{ upsert: true }
|
|
|
)
|
|
)
|
|
|
.resolves()
|
|
.resolves()
|
|
|
|
|
|
|
|
- await this.ProjectDeleter.promises.deleteProject(this.project._id, {
|
|
|
|
|
- deleterUser: this.user,
|
|
|
|
|
- ipAddress: this.ip,
|
|
|
|
|
|
|
+ await ctx.ProjectDeleter.promises.deleteProject(ctx.project._id, {
|
|
|
|
|
+ deleterUser: ctx.user,
|
|
|
|
|
+ ipAddress: ctx.ip,
|
|
|
})
|
|
})
|
|
|
- this.DeletedProjectMock.verify()
|
|
|
|
|
|
|
+ ctx.DeletedProjectMock.verify()
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('should flushProjectToMongoAndDelete in doc updater', async function () {
|
|
|
|
|
- this.ProjectMock.expects('deleteOne').chain('exec').resolves()
|
|
|
|
|
- this.DeletedProjectMock.expects('updateOne').resolves()
|
|
|
|
|
|
|
+ it('should flushProjectToMongoAndDelete in doc updater', async function (ctx) {
|
|
|
|
|
+ ctx.ProjectMock.expects('deleteOne').chain('exec').resolves()
|
|
|
|
|
+ ctx.DeletedProjectMock.expects('updateOne').resolves()
|
|
|
|
|
|
|
|
- await this.ProjectDeleter.promises.deleteProject(this.project._id, {
|
|
|
|
|
- deleterUser: this.user,
|
|
|
|
|
- ipAddress: this.ip,
|
|
|
|
|
|
|
+ await ctx.ProjectDeleter.promises.deleteProject(ctx.project._id, {
|
|
|
|
|
+ deleterUser: ctx.user,
|
|
|
|
|
+ ipAddress: ctx.ip,
|
|
|
})
|
|
})
|
|
|
- this.DocumentUpdaterHandler.promises.flushProjectToMongoAndDelete
|
|
|
|
|
- .calledWith(this.project._id)
|
|
|
|
|
|
|
+ ctx.DocumentUpdaterHandler.promises.flushProjectToMongoAndDelete
|
|
|
|
|
+ .calledWith(ctx.project._id)
|
|
|
.should.equal(true)
|
|
.should.equal(true)
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('should flush docs out of mongo', async function () {
|
|
|
|
|
- this.ProjectMock.expects('deleteOne').chain('exec').resolves()
|
|
|
|
|
- this.DeletedProjectMock.expects('updateOne').resolves()
|
|
|
|
|
- await this.ProjectDeleter.promises.deleteProject(this.project._id, {
|
|
|
|
|
- deleterUser: this.user,
|
|
|
|
|
- ipAddress: this.ip,
|
|
|
|
|
|
|
+ it('should flush docs out of mongo', async function (ctx) {
|
|
|
|
|
+ ctx.ProjectMock.expects('deleteOne').chain('exec').resolves()
|
|
|
|
|
+ ctx.DeletedProjectMock.expects('updateOne').resolves()
|
|
|
|
|
+ await ctx.ProjectDeleter.promises.deleteProject(ctx.project._id, {
|
|
|
|
|
+ deleterUser: ctx.user,
|
|
|
|
|
+ ipAddress: ctx.ip,
|
|
|
})
|
|
})
|
|
|
expect(
|
|
expect(
|
|
|
- this.DocstoreManager.promises.archiveProject
|
|
|
|
|
- ).to.have.been.calledWith(this.project._id)
|
|
|
|
|
|
|
+ ctx.DocstoreManager.promises.archiveProject
|
|
|
|
|
+ ).to.have.been.calledWith(ctx.project._id)
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('should flush docs out of mongo and ignore errors', async function () {
|
|
|
|
|
- this.ProjectMock.expects('deleteOne').chain('exec').resolves()
|
|
|
|
|
- this.DeletedProjectMock.expects('updateOne').resolves()
|
|
|
|
|
- this.DocstoreManager.promises.archiveProject.rejects(new Error('foo'))
|
|
|
|
|
- await this.ProjectDeleter.promises.deleteProject(this.project._id, {
|
|
|
|
|
- deleterUser: this.user,
|
|
|
|
|
- ipAddress: this.ip,
|
|
|
|
|
|
|
+ it('should flush docs out of mongo and ignore errors', async function (ctx) {
|
|
|
|
|
+ ctx.ProjectMock.expects('deleteOne').chain('exec').resolves()
|
|
|
|
|
+ ctx.DeletedProjectMock.expects('updateOne').resolves()
|
|
|
|
|
+ ctx.DocstoreManager.promises.archiveProject.rejects(new Error('foo'))
|
|
|
|
|
+ await ctx.ProjectDeleter.promises.deleteProject(ctx.project._id, {
|
|
|
|
|
+ deleterUser: ctx.user,
|
|
|
|
|
+ ipAddress: ctx.ip,
|
|
|
})
|
|
})
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('should removeProjectFromAllTags', async function () {
|
|
|
|
|
- this.ProjectMock.expects('deleteOne').chain('exec').resolves()
|
|
|
|
|
- this.DeletedProjectMock.expects('updateOne').resolves()
|
|
|
|
|
|
|
+ it('should removeProjectFromAllTags', async function (ctx) {
|
|
|
|
|
+ ctx.ProjectMock.expects('deleteOne').chain('exec').resolves()
|
|
|
|
|
+ ctx.DeletedProjectMock.expects('updateOne').resolves()
|
|
|
|
|
|
|
|
- await this.ProjectDeleter.promises.deleteProject(this.project._id)
|
|
|
|
|
|
|
+ await ctx.ProjectDeleter.promises.deleteProject(ctx.project._id)
|
|
|
sinon.assert.calledWith(
|
|
sinon.assert.calledWith(
|
|
|
- this.TagsHandler.promises.removeProjectFromAllTags,
|
|
|
|
|
|
|
+ ctx.TagsHandler.promises.removeProjectFromAllTags,
|
|
|
'member-id-1',
|
|
'member-id-1',
|
|
|
- this.project._id
|
|
|
|
|
|
|
+ ctx.project._id
|
|
|
)
|
|
)
|
|
|
sinon.assert.calledWith(
|
|
sinon.assert.calledWith(
|
|
|
- this.TagsHandler.promises.removeProjectFromAllTags,
|
|
|
|
|
|
|
+ ctx.TagsHandler.promises.removeProjectFromAllTags,
|
|
|
'member-id-2',
|
|
'member-id-2',
|
|
|
- this.project._id
|
|
|
|
|
|
|
+ ctx.project._id
|
|
|
)
|
|
)
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('should remove the project from Mongo', async function () {
|
|
|
|
|
- this.ProjectMock.expects('deleteOne')
|
|
|
|
|
- .withArgs({ _id: this.project._id })
|
|
|
|
|
|
|
+ it('should remove the project from Mongo', async function (ctx) {
|
|
|
|
|
+ ctx.ProjectMock.expects('deleteOne')
|
|
|
|
|
+ .withArgs({ _id: ctx.project._id })
|
|
|
.chain('exec')
|
|
.chain('exec')
|
|
|
.resolves()
|
|
.resolves()
|
|
|
- this.DeletedProjectMock.expects('updateOne').resolves()
|
|
|
|
|
|
|
+ ctx.DeletedProjectMock.expects('updateOne').resolves()
|
|
|
|
|
|
|
|
- await this.ProjectDeleter.promises.deleteProject(this.project._id)
|
|
|
|
|
- this.ProjectMock.verify()
|
|
|
|
|
|
|
+ await ctx.ProjectDeleter.promises.deleteProject(ctx.project._id)
|
|
|
|
|
+ ctx.ProjectMock.verify()
|
|
|
})
|
|
})
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
describe('expireDeletedProjectsAfterDuration', function () {
|
|
describe('expireDeletedProjectsAfterDuration', function () {
|
|
|
- beforeEach(async function () {
|
|
|
|
|
- for (const deletedProject of this.deletedProjects) {
|
|
|
|
|
- this.ProjectMock.expects('findById')
|
|
|
|
|
|
|
+ beforeEach(async function (ctx) {
|
|
|
|
|
+ for (const deletedProject of ctx.deletedProjects) {
|
|
|
|
|
+ ctx.ProjectMock.expects('findById')
|
|
|
.withArgs(deletedProject.deleterData.deletedProjectId)
|
|
.withArgs(deletedProject.deleterData.deletedProjectId)
|
|
|
.chain('exec')
|
|
.chain('exec')
|
|
|
.resolves(null)
|
|
.resolves(null)
|
|
|
}
|
|
}
|
|
|
- this.DeletedProjectMock.expects('find')
|
|
|
|
|
|
|
+ ctx.DeletedProjectMock.expects('find')
|
|
|
.withArgs({
|
|
.withArgs({
|
|
|
'deleterData.deletedAt': {
|
|
'deleterData.deletedAt': {
|
|
|
$lt: new Date(moment().subtract(90, 'days')),
|
|
$lt: new Date(moment().subtract(90, 'days')),
|
|
@@ -388,16 +448,16 @@ describe('ProjectDeleter', function () {
|
|
|
},
|
|
},
|
|
|
})
|
|
})
|
|
|
.chain('exec')
|
|
.chain('exec')
|
|
|
- .resolves(this.deletedProjects)
|
|
|
|
|
|
|
+ .resolves(ctx.deletedProjects)
|
|
|
|
|
|
|
|
- for (const deletedProject of this.deletedProjects) {
|
|
|
|
|
- this.DeletedProjectMock.expects('findOne')
|
|
|
|
|
|
|
+ for (const deletedProject of ctx.deletedProjects) {
|
|
|
|
|
+ ctx.DeletedProjectMock.expects('findOne')
|
|
|
.withArgs({
|
|
.withArgs({
|
|
|
'deleterData.deletedProjectId': deletedProject.project._id,
|
|
'deleterData.deletedProjectId': deletedProject.project._id,
|
|
|
})
|
|
})
|
|
|
.chain('exec')
|
|
.chain('exec')
|
|
|
.resolves(deletedProject)
|
|
.resolves(deletedProject)
|
|
|
- this.DeletedProjectMock.expects('updateOne')
|
|
|
|
|
|
|
+ ctx.DeletedProjectMock.expects('updateOne')
|
|
|
.withArgs(
|
|
.withArgs(
|
|
|
{
|
|
{
|
|
|
_id: deletedProject._id,
|
|
_id: deletedProject._id,
|
|
@@ -413,25 +473,25 @@ describe('ProjectDeleter', function () {
|
|
|
.resolves()
|
|
.resolves()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- await this.ProjectDeleter.promises.expireDeletedProjectsAfterDuration()
|
|
|
|
|
|
|
+ await ctx.ProjectDeleter.promises.expireDeletedProjectsAfterDuration()
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('should expire projects older than 90 days', function () {
|
|
|
|
|
- this.DeletedProjectMock.verify()
|
|
|
|
|
|
|
+ it('should expire projects older than 90 days', function (ctx) {
|
|
|
|
|
+ ctx.DeletedProjectMock.verify()
|
|
|
})
|
|
})
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
describe('expireDeletedProject', function () {
|
|
describe('expireDeletedProject', function () {
|
|
|
describe('on an inactive project', function () {
|
|
describe('on an inactive project', function () {
|
|
|
- beforeEach(async function () {
|
|
|
|
|
- this.ProjectMock.expects('findById')
|
|
|
|
|
- .withArgs(this.deletedProjects[0].deleterData.deletedProjectId)
|
|
|
|
|
|
|
+ beforeEach(async function (ctx) {
|
|
|
|
|
+ ctx.ProjectMock.expects('findById')
|
|
|
|
|
+ .withArgs(ctx.deletedProjects[0].deleterData.deletedProjectId)
|
|
|
.chain('exec')
|
|
.chain('exec')
|
|
|
.resolves(null)
|
|
.resolves(null)
|
|
|
- this.DeletedProjectMock.expects('updateOne')
|
|
|
|
|
|
|
+ ctx.DeletedProjectMock.expects('updateOne')
|
|
|
.withArgs(
|
|
.withArgs(
|
|
|
{
|
|
{
|
|
|
- _id: this.deletedProjects[0]._id,
|
|
|
|
|
|
|
+ _id: ctx.deletedProjects[0]._id,
|
|
|
},
|
|
},
|
|
|
{
|
|
{
|
|
|
$set: {
|
|
$set: {
|
|
@@ -443,54 +503,54 @@ describe('ProjectDeleter', function () {
|
|
|
.chain('exec')
|
|
.chain('exec')
|
|
|
.resolves()
|
|
.resolves()
|
|
|
|
|
|
|
|
- this.DeletedProjectMock.expects('findOne')
|
|
|
|
|
|
|
+ ctx.DeletedProjectMock.expects('findOne')
|
|
|
.withArgs({
|
|
.withArgs({
|
|
|
- 'deleterData.deletedProjectId': this.deletedProjects[0].project._id,
|
|
|
|
|
|
|
+ 'deleterData.deletedProjectId': ctx.deletedProjects[0].project._id,
|
|
|
})
|
|
})
|
|
|
.chain('exec')
|
|
.chain('exec')
|
|
|
- .resolves(this.deletedProjects[0])
|
|
|
|
|
|
|
+ .resolves(ctx.deletedProjects[0])
|
|
|
|
|
|
|
|
- await this.ProjectDeleter.promises.expireDeletedProject(
|
|
|
|
|
- this.deletedProjects[0].project._id
|
|
|
|
|
|
|
+ await ctx.ProjectDeleter.promises.expireDeletedProject(
|
|
|
|
|
+ ctx.deletedProjects[0].project._id
|
|
|
)
|
|
)
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('should find the specified deletedProject and remove its project and ip address', function () {
|
|
|
|
|
- this.DeletedProjectMock.verify()
|
|
|
|
|
|
|
+ it('should find the specified deletedProject and remove its project and ip address', function (ctx) {
|
|
|
|
|
+ ctx.DeletedProjectMock.verify()
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('should destroy the docs in docstore', function () {
|
|
|
|
|
|
|
+ it('should destroy the docs in docstore', function (ctx) {
|
|
|
expect(
|
|
expect(
|
|
|
- this.DocstoreManager.promises.destroyProject
|
|
|
|
|
- ).to.have.been.calledWith(this.deletedProjects[0].project._id)
|
|
|
|
|
|
|
+ ctx.DocstoreManager.promises.destroyProject
|
|
|
|
|
+ ).to.have.been.calledWith(ctx.deletedProjects[0].project._id)
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('should delete the project in history', function () {
|
|
|
|
|
|
|
+ it('should delete the project in history', function (ctx) {
|
|
|
expect(
|
|
expect(
|
|
|
- this.HistoryManager.promises.deleteProject
|
|
|
|
|
|
|
+ ctx.HistoryManager.promises.deleteProject
|
|
|
).to.have.been.calledWith(
|
|
).to.have.been.calledWith(
|
|
|
- this.deletedProjects[0].project._id,
|
|
|
|
|
- this.deletedProjects[0].project.overleaf.history.id
|
|
|
|
|
|
|
+ ctx.deletedProjects[0].project._id,
|
|
|
|
|
+ ctx.deletedProjects[0].project.overleaf.history.id
|
|
|
)
|
|
)
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('should destroy the chat threads and messages', function () {
|
|
|
|
|
|
|
+ it('should destroy the chat threads and messages', function (ctx) {
|
|
|
expect(
|
|
expect(
|
|
|
- this.ChatApiHandler.promises.destroyProject
|
|
|
|
|
- ).to.have.been.calledWith(this.deletedProjects[0].project._id)
|
|
|
|
|
|
|
+ ctx.ChatApiHandler.promises.destroyProject
|
|
|
|
|
+ ).to.have.been.calledWith(ctx.deletedProjects[0].project._id)
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('should delete audit logs', async function () {
|
|
|
|
|
- expect(this.ProjectAuditLogEntry.deleteMany).to.have.been.calledWith({
|
|
|
|
|
- projectId: this.deletedProjects[0].project._id,
|
|
|
|
|
|
|
+ it('should delete audit logs', async function (ctx) {
|
|
|
|
|
+ expect(ctx.ProjectAuditLogEntry.deleteMany).to.have.been.calledWith({
|
|
|
|
|
+ projectId: ctx.deletedProjects[0].project._id,
|
|
|
})
|
|
})
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('should log a completed deletion', async function () {
|
|
|
|
|
- expect(this.logger.info).to.have.been.calledWith(
|
|
|
|
|
|
|
+ it('should log a completed deletion', async function (ctx) {
|
|
|
|
|
+ expect(ctx.logger.info).toHaveBeenCalledWith(
|
|
|
{
|
|
{
|
|
|
- projectId: this.deletedProjects[0].project._id,
|
|
|
|
|
- userId: this.user._id,
|
|
|
|
|
|
|
+ projectId: ctx.deletedProjects[0].project._id,
|
|
|
|
|
+ userId: ctx.user._id,
|
|
|
},
|
|
},
|
|
|
'expired deleted project successfully'
|
|
'expired deleted project successfully'
|
|
|
)
|
|
)
|
|
@@ -498,131 +558,131 @@ describe('ProjectDeleter', function () {
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
describe('on an active project (from an incomplete delete)', function () {
|
|
describe('on an active project (from an incomplete delete)', function () {
|
|
|
- beforeEach(async function () {
|
|
|
|
|
- this.ProjectMock.expects('findById')
|
|
|
|
|
- .withArgs(this.deletedProjects[0].deleterData.deletedProjectId)
|
|
|
|
|
|
|
+ beforeEach(async function (ctx) {
|
|
|
|
|
+ ctx.ProjectMock.expects('findById')
|
|
|
|
|
+ .withArgs(ctx.deletedProjects[0].deleterData.deletedProjectId)
|
|
|
.chain('exec')
|
|
.chain('exec')
|
|
|
- .resolves(this.deletedProjects[0].project)
|
|
|
|
|
- this.DeletedProjectMock.expects('deleteOne')
|
|
|
|
|
|
|
+ .resolves(ctx.deletedProjects[0].project)
|
|
|
|
|
+ ctx.DeletedProjectMock.expects('deleteOne')
|
|
|
.withArgs({
|
|
.withArgs({
|
|
|
- 'deleterData.deletedProjectId': this.deletedProjects[0].project._id,
|
|
|
|
|
|
|
+ 'deleterData.deletedProjectId': ctx.deletedProjects[0].project._id,
|
|
|
})
|
|
})
|
|
|
.chain('exec')
|
|
.chain('exec')
|
|
|
.resolves()
|
|
.resolves()
|
|
|
- await this.ProjectDeleter.promises.expireDeletedProject(
|
|
|
|
|
- this.deletedProjects[0].project._id
|
|
|
|
|
|
|
+ await ctx.ProjectDeleter.promises.expireDeletedProject(
|
|
|
|
|
+ ctx.deletedProjects[0].project._id
|
|
|
)
|
|
)
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('should delete the spurious deleted project record', function () {
|
|
|
|
|
- this.DeletedProjectMock.verify()
|
|
|
|
|
|
|
+ it('should delete the spurious deleted project record', function (ctx) {
|
|
|
|
|
+ ctx.DeletedProjectMock.verify()
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('should not destroy the docs in docstore', function () {
|
|
|
|
|
- expect(this.DocstoreManager.promises.destroyProject).to.not.have.been
|
|
|
|
|
|
|
+ it('should not destroy the docs in docstore', function (ctx) {
|
|
|
|
|
+ expect(ctx.DocstoreManager.promises.destroyProject).to.not.have.been
|
|
|
.called
|
|
.called
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('should not delete the project in history', function () {
|
|
|
|
|
- expect(this.HistoryManager.promises.deleteProject).to.not.have.been
|
|
|
|
|
|
|
+ it('should not delete the project in history', function (ctx) {
|
|
|
|
|
+ expect(ctx.HistoryManager.promises.deleteProject).to.not.have.been
|
|
|
.called
|
|
.called
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('should not destroy the chat threads and messages', function () {
|
|
|
|
|
- expect(this.ChatApiHandler.promises.destroyProject).to.not.have.been
|
|
|
|
|
|
|
+ it('should not destroy the chat threads and messages', function (ctx) {
|
|
|
|
|
+ expect(ctx.ChatApiHandler.promises.destroyProject).to.not.have.been
|
|
|
.called
|
|
.called
|
|
|
})
|
|
})
|
|
|
})
|
|
})
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
describe('archiveProject', function () {
|
|
describe('archiveProject', function () {
|
|
|
- beforeEach(function () {
|
|
|
|
|
- this.ProjectMock.expects('updateOne')
|
|
|
|
|
|
|
+ beforeEach(function (ctx) {
|
|
|
|
|
+ ctx.ProjectMock.expects('updateOne')
|
|
|
.withArgs(
|
|
.withArgs(
|
|
|
- { _id: this.project._id },
|
|
|
|
|
|
|
+ { _id: ctx.project._id },
|
|
|
{
|
|
{
|
|
|
- $addToSet: { archived: new ObjectId(this.user._id) },
|
|
|
|
|
- $pull: { trashed: new ObjectId(this.user._id) },
|
|
|
|
|
|
|
+ $addToSet: { archived: new ObjectId(ctx.user._id) },
|
|
|
|
|
+ $pull: { trashed: new ObjectId(ctx.user._id) },
|
|
|
}
|
|
}
|
|
|
)
|
|
)
|
|
|
.resolves()
|
|
.resolves()
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('should update the project', async function () {
|
|
|
|
|
- await this.ProjectDeleter.promises.archiveProject(
|
|
|
|
|
- this.project._id,
|
|
|
|
|
- this.user._id
|
|
|
|
|
|
|
+ it('should update the project', async function (ctx) {
|
|
|
|
|
+ await ctx.ProjectDeleter.promises.archiveProject(
|
|
|
|
|
+ ctx.project._id,
|
|
|
|
|
+ ctx.user._id
|
|
|
)
|
|
)
|
|
|
- this.ProjectMock.verify()
|
|
|
|
|
|
|
+ ctx.ProjectMock.verify()
|
|
|
})
|
|
})
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
describe('unarchiveProject', function () {
|
|
describe('unarchiveProject', function () {
|
|
|
- beforeEach(function () {
|
|
|
|
|
- this.ProjectMock.expects('updateOne')
|
|
|
|
|
|
|
+ beforeEach(function (ctx) {
|
|
|
|
|
+ ctx.ProjectMock.expects('updateOne')
|
|
|
.withArgs(
|
|
.withArgs(
|
|
|
- { _id: this.project._id },
|
|
|
|
|
- { $pull: { archived: new ObjectId(this.user._id) } }
|
|
|
|
|
|
|
+ { _id: ctx.project._id },
|
|
|
|
|
+ { $pull: { archived: new ObjectId(ctx.user._id) } }
|
|
|
)
|
|
)
|
|
|
.resolves()
|
|
.resolves()
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('should update the project', async function () {
|
|
|
|
|
- await this.ProjectDeleter.promises.unarchiveProject(
|
|
|
|
|
- this.project._id,
|
|
|
|
|
- this.user._id
|
|
|
|
|
|
|
+ it('should update the project', async function (ctx) {
|
|
|
|
|
+ await ctx.ProjectDeleter.promises.unarchiveProject(
|
|
|
|
|
+ ctx.project._id,
|
|
|
|
|
+ ctx.user._id
|
|
|
)
|
|
)
|
|
|
- this.ProjectMock.verify()
|
|
|
|
|
|
|
+ ctx.ProjectMock.verify()
|
|
|
})
|
|
})
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
describe('trashProject', function () {
|
|
describe('trashProject', function () {
|
|
|
- beforeEach(function () {
|
|
|
|
|
- this.ProjectMock.expects('updateOne')
|
|
|
|
|
|
|
+ beforeEach(function (ctx) {
|
|
|
|
|
+ ctx.ProjectMock.expects('updateOne')
|
|
|
.withArgs(
|
|
.withArgs(
|
|
|
- { _id: this.project._id },
|
|
|
|
|
|
|
+ { _id: ctx.project._id },
|
|
|
{
|
|
{
|
|
|
- $addToSet: { trashed: new ObjectId(this.user._id) },
|
|
|
|
|
- $pull: { archived: new ObjectId(this.user._id) },
|
|
|
|
|
|
|
+ $addToSet: { trashed: new ObjectId(ctx.user._id) },
|
|
|
|
|
+ $pull: { archived: new ObjectId(ctx.user._id) },
|
|
|
}
|
|
}
|
|
|
)
|
|
)
|
|
|
.resolves()
|
|
.resolves()
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('should update the project', async function () {
|
|
|
|
|
- await this.ProjectDeleter.promises.trashProject(
|
|
|
|
|
- this.project._id,
|
|
|
|
|
- this.user._id
|
|
|
|
|
|
|
+ it('should update the project', async function (ctx) {
|
|
|
|
|
+ await ctx.ProjectDeleter.promises.trashProject(
|
|
|
|
|
+ ctx.project._id,
|
|
|
|
|
+ ctx.user._id
|
|
|
)
|
|
)
|
|
|
- this.ProjectMock.verify()
|
|
|
|
|
|
|
+ ctx.ProjectMock.verify()
|
|
|
})
|
|
})
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
describe('untrashProject', function () {
|
|
describe('untrashProject', function () {
|
|
|
- beforeEach(function () {
|
|
|
|
|
- this.ProjectMock.expects('updateOne')
|
|
|
|
|
|
|
+ beforeEach(function (ctx) {
|
|
|
|
|
+ ctx.ProjectMock.expects('updateOne')
|
|
|
.withArgs(
|
|
.withArgs(
|
|
|
- { _id: this.project._id },
|
|
|
|
|
- { $pull: { trashed: new ObjectId(this.user._id) } }
|
|
|
|
|
|
|
+ { _id: ctx.project._id },
|
|
|
|
|
+ { $pull: { trashed: new ObjectId(ctx.user._id) } }
|
|
|
)
|
|
)
|
|
|
.resolves()
|
|
.resolves()
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('should update the project', async function () {
|
|
|
|
|
- await this.ProjectDeleter.promises.untrashProject(
|
|
|
|
|
- this.project._id,
|
|
|
|
|
- this.user._id
|
|
|
|
|
|
|
+ it('should update the project', async function (ctx) {
|
|
|
|
|
+ await ctx.ProjectDeleter.promises.untrashProject(
|
|
|
|
|
+ ctx.project._id,
|
|
|
|
|
+ ctx.user._id
|
|
|
)
|
|
)
|
|
|
- this.ProjectMock.verify()
|
|
|
|
|
|
|
+ ctx.ProjectMock.verify()
|
|
|
})
|
|
})
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
describe('restoreProject', function () {
|
|
describe('restoreProject', function () {
|
|
|
- beforeEach(function () {
|
|
|
|
|
- this.ProjectMock.expects('updateOne')
|
|
|
|
|
|
|
+ beforeEach(function (ctx) {
|
|
|
|
|
+ ctx.ProjectMock.expects('updateOne')
|
|
|
.withArgs(
|
|
.withArgs(
|
|
|
{
|
|
{
|
|
|
- _id: this.project._id,
|
|
|
|
|
|
|
+ _id: ctx.project._id,
|
|
|
},
|
|
},
|
|
|
{
|
|
{
|
|
|
$unset: { archived: true },
|
|
$unset: { archived: true },
|
|
@@ -632,115 +692,115 @@ describe('ProjectDeleter', function () {
|
|
|
.resolves()
|
|
.resolves()
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('should unset the archive attribute', async function () {
|
|
|
|
|
- await this.ProjectDeleter.promises.restoreProject(this.project._id)
|
|
|
|
|
|
|
+ it('should unset the archive attribute', async function (ctx) {
|
|
|
|
|
+ await ctx.ProjectDeleter.promises.restoreProject(ctx.project._id)
|
|
|
})
|
|
})
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
describe('undeleteProject', function () {
|
|
describe('undeleteProject', function () {
|
|
|
- beforeEach(function () {
|
|
|
|
|
- this.unknownProjectId = new ObjectId()
|
|
|
|
|
- this.purgedProjectId = new ObjectId()
|
|
|
|
|
|
|
+ beforeEach(function (ctx) {
|
|
|
|
|
+ ctx.unknownProjectId = new ObjectId()
|
|
|
|
|
+ ctx.purgedProjectId = new ObjectId()
|
|
|
|
|
|
|
|
- this.deletedProject = {
|
|
|
|
|
|
|
+ ctx.deletedProject = {
|
|
|
_id: 'deleted',
|
|
_id: 'deleted',
|
|
|
- project: this.project,
|
|
|
|
|
|
|
+ project: ctx.project,
|
|
|
deleterData: {
|
|
deleterData: {
|
|
|
- deletedProjectId: this.project._id,
|
|
|
|
|
- deletedProjectOwnerId: this.project.owner_ref,
|
|
|
|
|
|
|
+ deletedProjectId: ctx.project._id,
|
|
|
|
|
+ deletedProjectOwnerId: ctx.project.owner_ref,
|
|
|
},
|
|
},
|
|
|
}
|
|
}
|
|
|
- this.purgedProject = {
|
|
|
|
|
|
|
+ ctx.purgedProject = {
|
|
|
_id: 'purged',
|
|
_id: 'purged',
|
|
|
deleterData: {
|
|
deleterData: {
|
|
|
- deletedProjectId: this.purgedProjectId,
|
|
|
|
|
|
|
+ deletedProjectId: ctx.purgedProjectId,
|
|
|
deletedProjectOwnerId: 'potato',
|
|
deletedProjectOwnerId: 'potato',
|
|
|
},
|
|
},
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- this.DeletedProjectMock.expects('findOne')
|
|
|
|
|
- .withArgs({ 'deleterData.deletedProjectId': this.project._id })
|
|
|
|
|
|
|
+ ctx.DeletedProjectMock.expects('findOne')
|
|
|
|
|
+ .withArgs({ 'deleterData.deletedProjectId': ctx.project._id })
|
|
|
.chain('exec')
|
|
.chain('exec')
|
|
|
- .resolves(this.deletedProject)
|
|
|
|
|
- this.DeletedProjectMock.expects('findOne')
|
|
|
|
|
- .withArgs({ 'deleterData.deletedProjectId': this.purgedProjectId })
|
|
|
|
|
|
|
+ .resolves(ctx.deletedProject)
|
|
|
|
|
+ ctx.DeletedProjectMock.expects('findOne')
|
|
|
|
|
+ .withArgs({ 'deleterData.deletedProjectId': ctx.purgedProjectId })
|
|
|
.chain('exec')
|
|
.chain('exec')
|
|
|
- .resolves(this.purgedProject)
|
|
|
|
|
- this.DeletedProjectMock.expects('findOne')
|
|
|
|
|
- .withArgs({ 'deleterData.deletedProjectId': this.unknownProjectId })
|
|
|
|
|
|
|
+ .resolves(ctx.purgedProject)
|
|
|
|
|
+ ctx.DeletedProjectMock.expects('findOne')
|
|
|
|
|
+ .withArgs({ 'deleterData.deletedProjectId': ctx.unknownProjectId })
|
|
|
.chain('exec')
|
|
.chain('exec')
|
|
|
.resolves(null)
|
|
.resolves(null)
|
|
|
- this.DeletedProjectMock.expects('deleteOne').chain('exec').resolves()
|
|
|
|
|
|
|
+ ctx.DeletedProjectMock.expects('deleteOne').chain('exec').resolves()
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('should return not found if the project does not exist', async function () {
|
|
|
|
|
|
|
+ it('should return not found if the project does not exist', async function (ctx) {
|
|
|
await expect(
|
|
await expect(
|
|
|
- this.ProjectDeleter.promises.undeleteProject(
|
|
|
|
|
- this.unknownProjectId.toString()
|
|
|
|
|
|
|
+ ctx.ProjectDeleter.promises.undeleteProject(
|
|
|
|
|
+ ctx.unknownProjectId.toString()
|
|
|
)
|
|
)
|
|
|
).to.be.rejectedWith(Errors.NotFoundError, 'project_not_found')
|
|
).to.be.rejectedWith(Errors.NotFoundError, 'project_not_found')
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('should return not found if the project has been expired', async function () {
|
|
|
|
|
|
|
+ it('should return not found if the project has been expired', async function (ctx) {
|
|
|
await expect(
|
|
await expect(
|
|
|
- this.ProjectDeleter.promises.undeleteProject(
|
|
|
|
|
- this.purgedProjectId.toString()
|
|
|
|
|
|
|
+ ctx.ProjectDeleter.promises.undeleteProject(
|
|
|
|
|
+ ctx.purgedProjectId.toString()
|
|
|
)
|
|
)
|
|
|
).to.be.rejectedWith(Errors.NotFoundError, 'project_too_old_to_restore')
|
|
).to.be.rejectedWith(Errors.NotFoundError, 'project_too_old_to_restore')
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('should insert the project into the collection', async function () {
|
|
|
|
|
- await this.ProjectDeleter.promises.undeleteProject(this.project._id)
|
|
|
|
|
|
|
+ it('should insert the project into the collection', async function (ctx) {
|
|
|
|
|
+ await ctx.ProjectDeleter.promises.undeleteProject(ctx.project._id)
|
|
|
sinon.assert.calledWith(
|
|
sinon.assert.calledWith(
|
|
|
- this.db.projects.insertOne,
|
|
|
|
|
|
|
+ ctx.db.projects.insertOne,
|
|
|
sinon.match({
|
|
sinon.match({
|
|
|
- _id: this.project._id,
|
|
|
|
|
- name: this.project.name,
|
|
|
|
|
|
|
+ _id: ctx.project._id,
|
|
|
|
|
+ name: ctx.project.name,
|
|
|
})
|
|
})
|
|
|
)
|
|
)
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('should clear the archive bit', async function () {
|
|
|
|
|
- this.project.archived = true
|
|
|
|
|
- await this.ProjectDeleter.promises.undeleteProject(this.project._id)
|
|
|
|
|
|
|
+ it('should clear the archive bit', async function (ctx) {
|
|
|
|
|
+ ctx.project.archived = true
|
|
|
|
|
+ await ctx.ProjectDeleter.promises.undeleteProject(ctx.project._id)
|
|
|
sinon.assert.calledWith(
|
|
sinon.assert.calledWith(
|
|
|
- this.db.projects.insertOne,
|
|
|
|
|
|
|
+ ctx.db.projects.insertOne,
|
|
|
sinon.match({ archived: undefined })
|
|
sinon.match({ archived: undefined })
|
|
|
)
|
|
)
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('should generate a unique name for the project', async function () {
|
|
|
|
|
- await this.ProjectDeleter.promises.undeleteProject(this.project._id)
|
|
|
|
|
|
|
+ it('should generate a unique name for the project', async function (ctx) {
|
|
|
|
|
+ await ctx.ProjectDeleter.promises.undeleteProject(ctx.project._id)
|
|
|
sinon.assert.calledWith(
|
|
sinon.assert.calledWith(
|
|
|
- this.ProjectDetailsHandler.promises.generateUniqueName,
|
|
|
|
|
- this.project.owner_ref
|
|
|
|
|
|
|
+ ctx.ProjectDetailsHandler.promises.generateUniqueName,
|
|
|
|
|
+ ctx.project.owner_ref
|
|
|
)
|
|
)
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('should add a suffix to the project name', async function () {
|
|
|
|
|
- await this.ProjectDeleter.promises.undeleteProject(this.project._id)
|
|
|
|
|
|
|
+ it('should add a suffix to the project name', async function (ctx) {
|
|
|
|
|
+ await ctx.ProjectDeleter.promises.undeleteProject(ctx.project._id)
|
|
|
sinon.assert.calledWith(
|
|
sinon.assert.calledWith(
|
|
|
- this.ProjectDetailsHandler.promises.generateUniqueName,
|
|
|
|
|
- this.project.owner_ref,
|
|
|
|
|
- this.project.name + ' (Restored)'
|
|
|
|
|
|
|
+ ctx.ProjectDetailsHandler.promises.generateUniqueName,
|
|
|
|
|
+ ctx.project.owner_ref,
|
|
|
|
|
+ ctx.project.name + ' (Restored)'
|
|
|
)
|
|
)
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- it('should remove the DeletedProject', async function () {
|
|
|
|
|
|
|
+ it('should remove the DeletedProject', async function (ctx) {
|
|
|
// need to change the mock just to include the methods we want
|
|
// need to change the mock just to include the methods we want
|
|
|
- this.DeletedProjectMock.restore()
|
|
|
|
|
- this.DeletedProjectMock = sinon.mock(DeletedProject)
|
|
|
|
|
- this.DeletedProjectMock.expects('findOne')
|
|
|
|
|
- .withArgs({ 'deleterData.deletedProjectId': this.project._id })
|
|
|
|
|
|
|
+ ctx.DeletedProjectMock.restore()
|
|
|
|
|
+ ctx.DeletedProjectMock = sinon.mock(DeletedProject)
|
|
|
|
|
+ ctx.DeletedProjectMock.expects('findOne')
|
|
|
|
|
+ .withArgs({ 'deleterData.deletedProjectId': ctx.project._id })
|
|
|
.chain('exec')
|
|
.chain('exec')
|
|
|
- .resolves(this.deletedProject)
|
|
|
|
|
- this.DeletedProjectMock.expects('deleteOne')
|
|
|
|
|
|
|
+ .resolves(ctx.deletedProject)
|
|
|
|
|
+ ctx.DeletedProjectMock.expects('deleteOne')
|
|
|
.withArgs({ _id: 'deleted' })
|
|
.withArgs({ _id: 'deleted' })
|
|
|
.chain('exec')
|
|
.chain('exec')
|
|
|
.resolves()
|
|
.resolves()
|
|
|
|
|
|
|
|
- await this.ProjectDeleter.promises.undeleteProject(this.project._id)
|
|
|
|
|
- this.DeletedProjectMock.verify()
|
|
|
|
|
|
|
+ await ctx.ProjectDeleter.promises.undeleteProject(ctx.project._id)
|
|
|
|
|
+ ctx.DeletedProjectMock.verify()
|
|
|
})
|
|
})
|
|
|
})
|
|
})
|
|
|
})
|
|
})
|