|
|
@@ -1,35 +1,33 @@
|
|
|
-/* eslint-disable
|
|
|
- camelcase,
|
|
|
- handle-callback-err,
|
|
|
- max-len,
|
|
|
- no-return-assign,
|
|
|
- no-unused-vars,
|
|
|
- no-useless-constructor,
|
|
|
-*/
|
|
|
-// TODO: This file was created by bulk-decaffeinate.
|
|
|
-// Fix any style issues and re-enable lint.
|
|
|
-/*
|
|
|
- * decaffeinate suggestions:
|
|
|
- * DS102: Remove unnecessary code created because of implicit returns
|
|
|
- * DS206: Consider reworking classes to avoid initClass
|
|
|
- * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
|
|
- */
|
|
|
-const should = require('chai').should()
|
|
|
const modulePath = '../../../../app/src/Features/Project/ProjectDeleter'
|
|
|
const SandboxedModule = require('sandboxed-module')
|
|
|
const sinon = require('sinon')
|
|
|
+const chai = require('chai')
|
|
|
+const { expect } = chai
|
|
|
+const tk = require('timekeeper')
|
|
|
+const moment = require('moment')
|
|
|
+const { Project } = require('../helpers/models/Project')
|
|
|
+const { DeletedProject } = require('../helpers/models/DeletedProject')
|
|
|
+const { ObjectId } = require('mongoose').Types
|
|
|
|
|
|
-describe('ProjectDeleter', function() {
|
|
|
- beforeEach(function() {
|
|
|
- let DeletedProject
|
|
|
- this.project_id = '12312'
|
|
|
+describe('ProjectDeleter', () => {
|
|
|
+ beforeEach(() => {
|
|
|
+ tk.freeze(Date.now())
|
|
|
+ this.project_id = ObjectId('588fffffffffffffffffffff')
|
|
|
+ this.ip = '192.170.18.1'
|
|
|
this.project = {
|
|
|
_id: this.project_id,
|
|
|
+ lastUpdated: new Date(),
|
|
|
rootFolder: [],
|
|
|
collaberator_refs: ['collab1', 'collab2'],
|
|
|
readOnly_refs: ['readOnly1', 'readOnly2'],
|
|
|
- owner_ref: 'owner ref here',
|
|
|
- remove: sinon.stub().callsArg(0)
|
|
|
+ tokenAccessReadAndWrite_refs: ['tokenCollab1', 'tokenCollab2'],
|
|
|
+ tokenAccessReadOnly_refs: ['tokenReadOnly1', 'tokenReadOnly2'],
|
|
|
+ owner_ref: ObjectId('588aaaaaaaaaaaaaaaaaaaaa'),
|
|
|
+ tokens: {
|
|
|
+ readOnly: 'wombat',
|
|
|
+ readAndWrite: 'potato'
|
|
|
+ },
|
|
|
+ name: 'a very scientific analysis of spooky ghosts'
|
|
|
}
|
|
|
|
|
|
this.user = {
|
|
|
@@ -38,23 +36,45 @@ describe('ProjectDeleter', function() {
|
|
|
features: {}
|
|
|
}
|
|
|
|
|
|
- this.Project = {
|
|
|
- update: sinon.stub().callsArgWith(3),
|
|
|
- remove: sinon.stub().callsArgWith(1),
|
|
|
- findOne: sinon.stub().callsArgWith(1, null, this.project),
|
|
|
- find: sinon.stub().callsArgWith(1, null, [this.project]),
|
|
|
- applyToAllFilesRecursivly: sinon.stub()
|
|
|
+ this.doc = {
|
|
|
+ _id: '5bd975f54f62e803cb8a8fec',
|
|
|
+ lines: ['a bunch of lines', 'for a sunny day', 'in London town'],
|
|
|
+ ranges: {},
|
|
|
+ project_id: '5cf9270b4eff6e186cf8b05e'
|
|
|
}
|
|
|
- this.DeletedProject = DeletedProject = (function() {
|
|
|
- DeletedProject = class DeletedProject {
|
|
|
- static initClass() {
|
|
|
- this.prototype.save = sinon.stub().callsArgWith(0)
|
|
|
+
|
|
|
+ this.deletedProjects = [
|
|
|
+ {
|
|
|
+ _id: '5cf7f145c1401f0ca0eb1aaa',
|
|
|
+ deleterData: {
|
|
|
+ _id: '5cf7f145c1401f0ca0eb1aac',
|
|
|
+ deletedAt: moment()
|
|
|
+ .subtract(95, 'days')
|
|
|
+ .toDate(),
|
|
|
+ deleterId: '588f3ddae8ebc1bac07c9fa4',
|
|
|
+ deleterIpAddress: '172.19.0.1'
|
|
|
+ },
|
|
|
+ project: {
|
|
|
+ _id: '5cf9270b4eff6e186cf8b05e'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ _id: '5cf8eb11c1401f0ca0eb1ad7',
|
|
|
+ deleterData: {
|
|
|
+ _id: '5b74360c0fbe57011ae9938f',
|
|
|
+ deletedAt: moment()
|
|
|
+ .subtract(95, 'days')
|
|
|
+ .toDate(),
|
|
|
+ deleterId: '588f3ddae8ebc1bac07c9fa4',
|
|
|
+ deleterIpAddress: '172.20.0.1',
|
|
|
+ deletedProjectId: '5cf8f95a0c87371362c23919'
|
|
|
+ },
|
|
|
+ project: {
|
|
|
+ _id: '5cf8f95a0c87371362c23919'
|
|
|
}
|
|
|
- constructor() {}
|
|
|
}
|
|
|
- DeletedProject.initClass()
|
|
|
- return DeletedProject
|
|
|
- })()
|
|
|
+ ]
|
|
|
+
|
|
|
this.documentUpdaterHandler = {
|
|
|
flushProjectToMongoAndDelete: sinon.stub().callsArgWith(1)
|
|
|
}
|
|
|
@@ -71,208 +91,475 @@ describe('ProjectDeleter', function() {
|
|
|
.withArgs(this.project_id)
|
|
|
.yields(null, ['member-id-1', 'member-id-2'])
|
|
|
}
|
|
|
- return (this.deleter = SandboxedModule.require(modulePath, {
|
|
|
- globals: {
|
|
|
- console: console
|
|
|
- },
|
|
|
+
|
|
|
+ this.logger = {
|
|
|
+ err: sinon.stub(),
|
|
|
+ log: sinon.stub(),
|
|
|
+ warn: sinon.stub()
|
|
|
+ }
|
|
|
+
|
|
|
+ this.ProjectDetailsHandler = {
|
|
|
+ generateUniqueName: sinon.stub().yields(null, this.project.name)
|
|
|
+ }
|
|
|
+
|
|
|
+ this.db = {
|
|
|
+ projects: {
|
|
|
+ insert: sinon.stub().yields()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ this.DocstoreManager = {
|
|
|
+ destroyProject: sinon.stub().yields()
|
|
|
+ }
|
|
|
+
|
|
|
+ this.ProjectMock = sinon.mock(Project)
|
|
|
+ this.DeletedProjectMock = sinon.mock(DeletedProject)
|
|
|
+
|
|
|
+ this.ProjectDeleter = SandboxedModule.require(modulePath, {
|
|
|
requires: {
|
|
|
'../Editor/EditorController': this.editorController,
|
|
|
- '../../models/Project': { Project: this.Project },
|
|
|
- '../../models/DeletedProject': { DeletedProject: this.DeletedProject },
|
|
|
+ '../../models/Project': { Project: Project },
|
|
|
+ '../../models/DeletedProject': { DeletedProject: DeletedProject },
|
|
|
'../DocumentUpdater/DocumentUpdaterHandler': this
|
|
|
.documentUpdaterHandler,
|
|
|
'../Tags/TagsHandler': this.TagsHandler,
|
|
|
'../FileStore/FileStoreHandler': (this.FileStoreHandler = {}),
|
|
|
'../Collaborators/CollaboratorsHandler': this.CollaboratorsHandler,
|
|
|
- 'logger-sharelatex': {
|
|
|
- log() {}
|
|
|
- }
|
|
|
+ '../Docstore/DocstoreManager': this.DocstoreManager,
|
|
|
+ './ProjectDetailsHandler': this.ProjectDetailsHandler,
|
|
|
+ '../../infrastructure/mongojs': { db: this.db },
|
|
|
+ 'logger-sharelatex': this.logger
|
|
|
+ },
|
|
|
+ globals: {
|
|
|
+ console: console
|
|
|
}
|
|
|
- }))
|
|
|
+ })
|
|
|
})
|
|
|
|
|
|
- describe('mark as deleted by external source', function() {
|
|
|
- const project_id = 1234
|
|
|
- it('should update the project with the flag set to true', function(done) {
|
|
|
- return this.deleter.markAsDeletedByExternalSource(project_id, () => {
|
|
|
- const conditions = { _id: project_id }
|
|
|
- const update = { deletedByExternalDataSource: true }
|
|
|
- this.Project.update.calledWith(conditions, update).should.equal(true)
|
|
|
- return done()
|
|
|
+ afterEach(() => {
|
|
|
+ tk.reset()
|
|
|
+ this.DeletedProjectMock.restore()
|
|
|
+ this.ProjectMock.restore()
|
|
|
+ })
|
|
|
+
|
|
|
+ describe('mark as deleted by external source', () => {
|
|
|
+ beforeEach(() => {
|
|
|
+ this.ProjectMock.expects('update')
|
|
|
+ .withArgs(
|
|
|
+ { _id: this.project_id },
|
|
|
+ { deletedByExternalDataSource: true }
|
|
|
+ )
|
|
|
+ .yields()
|
|
|
+ })
|
|
|
+
|
|
|
+ it('should update the project with the flag set to true', done => {
|
|
|
+ this.ProjectDeleter.markAsDeletedByExternalSource(this.project_id, () => {
|
|
|
+ this.ProjectMock.verify()
|
|
|
+ done()
|
|
|
})
|
|
|
})
|
|
|
|
|
|
- it('should tell the editor controler so users are notified', function(done) {
|
|
|
- return this.deleter.markAsDeletedByExternalSource(project_id, () => {
|
|
|
+ it('should tell the editor controler so users are notified', done => {
|
|
|
+ this.ProjectDeleter.markAsDeletedByExternalSource(this.project_id, () => {
|
|
|
this.editorController.notifyUsersProjectHasBeenDeletedOrRenamed
|
|
|
- .calledWith(project_id)
|
|
|
+ .calledWith(this.project_id)
|
|
|
.should.equal(true)
|
|
|
- return done()
|
|
|
+ done()
|
|
|
})
|
|
|
})
|
|
|
})
|
|
|
|
|
|
- describe('unmarkAsDeletedByExternalSource', function() {
|
|
|
- beforeEach(function() {
|
|
|
- this.Project.update = sinon.stub().callsArg(3)
|
|
|
- this.callback = sinon.stub()
|
|
|
- this.project = {
|
|
|
- _id: this.project_id
|
|
|
- }
|
|
|
- return this.deleter.unmarkAsDeletedByExternalSource(
|
|
|
- this.project_id,
|
|
|
- this.callback
|
|
|
- )
|
|
|
- })
|
|
|
-
|
|
|
- it('should remove the flag from the project', function() {
|
|
|
- return this.Project.update
|
|
|
- .calledWith(
|
|
|
+ describe('unmarkAsDeletedByExternalSource', done => {
|
|
|
+ beforeEach(() => {
|
|
|
+ this.ProjectMock.expects('update')
|
|
|
+ .withArgs(
|
|
|
{ _id: this.project_id },
|
|
|
{ deletedByExternalDataSource: false }
|
|
|
)
|
|
|
- .should.equal(true)
|
|
|
+ .yields()
|
|
|
+ this.ProjectDeleter.unmarkAsDeletedByExternalSource(this.project_id, done)
|
|
|
})
|
|
|
- })
|
|
|
|
|
|
- describe('deleteUsersProjects', function() {
|
|
|
- beforeEach(function() {
|
|
|
- return (this.deleter.deleteProject = sinon.stub().callsArg(1))
|
|
|
+ it('should remove the flag from the project', () => {
|
|
|
+ this.ProjectMock.verify()
|
|
|
})
|
|
|
+ })
|
|
|
|
|
|
- it('should find all the projects owned by the user_id', function(done) {
|
|
|
- return this.deleter.deleteUsersProjects(this.user._id, () => {
|
|
|
- sinon.assert.calledWith(this.Project.find, { owner_ref: this.user._id })
|
|
|
- return done()
|
|
|
- })
|
|
|
+ describe('deleteUsersProjects', () => {
|
|
|
+ beforeEach(() => {
|
|
|
+ this.ProjectMock.expects('find')
|
|
|
+ .withArgs({ owner_ref: this.user._id })
|
|
|
+ .yields(null, [{ _id: 'wombat' }, { _id: 'potato' }])
|
|
|
+
|
|
|
+ this.ProjectDeleter.deleteProject = sinon.stub().yields()
|
|
|
})
|
|
|
|
|
|
- it('should call deleteProject on the found projects', function(done) {
|
|
|
- return this.deleter.deleteUsersProjects(this.user._id, () => {
|
|
|
- sinon.assert.calledWith(this.deleter.deleteProject, this.project._id)
|
|
|
- return done()
|
|
|
+ it('should find all the projects owned by the user_id', done => {
|
|
|
+ this.ProjectDeleter.deleteUsersProjects(this.user._id, () => {
|
|
|
+ this.ProjectMock.verify()
|
|
|
+ done()
|
|
|
})
|
|
|
})
|
|
|
|
|
|
- it('should call deleteProject once for each project', function(done) {
|
|
|
- this.Project.find.callsArgWith(1, null, [
|
|
|
- { _id: 'potato' },
|
|
|
- { _id: 'wombat' }
|
|
|
- ])
|
|
|
- return this.deleter.deleteUsersProjects(this.user._id, () => {
|
|
|
- sinon.assert.calledTwice(this.deleter.deleteProject)
|
|
|
- sinon.assert.calledWith(this.deleter.deleteProject, 'wombat')
|
|
|
- sinon.assert.calledWith(this.deleter.deleteProject, 'potato')
|
|
|
- return done()
|
|
|
+ it('should call deleteProject once for each project', done => {
|
|
|
+ this.ProjectDeleter.deleteUsersProjects(this.user._id, () => {
|
|
|
+ sinon.assert.calledTwice(this.ProjectDeleter.deleteProject)
|
|
|
+ sinon.assert.calledWith(this.ProjectDeleter.deleteProject, 'wombat')
|
|
|
+ sinon.assert.calledWith(this.ProjectDeleter.deleteProject, 'potato')
|
|
|
+ done()
|
|
|
})
|
|
|
})
|
|
|
|
|
|
- it('should remove all the projects the user is a collaborator of', function(done) {
|
|
|
- return this.deleter.deleteUsersProjects(this.user._id, () => {
|
|
|
- this.CollaboratorsHandler.removeUserFromAllProjets
|
|
|
- .calledWith(this.user._id)
|
|
|
- .should.equal(true)
|
|
|
- return done()
|
|
|
+ it('should remove all the projects the user is a collaborator of', done => {
|
|
|
+ this.ProjectDeleter.deleteUsersProjects(this.user._id, () => {
|
|
|
+ sinon.assert.calledWith(
|
|
|
+ this.CollaboratorsHandler.removeUserFromAllProjets,
|
|
|
+ this.user._id
|
|
|
+ )
|
|
|
+ sinon.assert.calledOnce(
|
|
|
+ this.CollaboratorsHandler.removeUserFromAllProjets
|
|
|
+ )
|
|
|
+ done()
|
|
|
})
|
|
|
})
|
|
|
})
|
|
|
|
|
|
- describe('deleteProject', function() {
|
|
|
- beforeEach(function() {
|
|
|
- this.project_id = 'mock-project-id-123'
|
|
|
- return (this.ip = '192.170.18.1')
|
|
|
+ describe('deleteProject', () => {
|
|
|
+ beforeEach(() => {
|
|
|
+ this.deleterData = {
|
|
|
+ deletedAt: new Date(),
|
|
|
+ deletedProjectId: this.project._id,
|
|
|
+ deletedProjectOwnerId: this.project.owner_ref,
|
|
|
+ deletedProjectCollaboratorIds: this.project.collaberator_refs,
|
|
|
+ deletedProjectReadOnlyIds: this.project.readOnly_refs,
|
|
|
+ deletedProjectReadWriteTokenAccessIds: this.project
|
|
|
+ .tokenAccessReadAndWrite_refs,
|
|
|
+ deletedProjectReadOnlyTokenAccessIds: this.project
|
|
|
+ .tokenAccessReadOnly_refs,
|
|
|
+ deletedProjectReadWriteToken: this.project.tokens.readAndWrite,
|
|
|
+ deletedProjectReadOnlyToken: this.project.tokens.readOnly,
|
|
|
+ deletedProjectLastUpdatedAt: this.project.lastUpdated
|
|
|
+ }
|
|
|
+
|
|
|
+ this.ProjectMock.expects('findOne')
|
|
|
+ .withArgs({ _id: this.project_id })
|
|
|
+ .chain('exec')
|
|
|
+ .resolves(this.project)
|
|
|
})
|
|
|
|
|
|
- it('should save a DeletedProject with additional deleterData', function(done) {
|
|
|
- return this.deleter.deleteProject(
|
|
|
+ it('should save a DeletedProject with additional deleterData', done => {
|
|
|
+ this.deleterData.deleterIpAddress = this.ip
|
|
|
+ this.deleterData.deleterId = this.user._id
|
|
|
+
|
|
|
+ this.ProjectMock.expects('remove')
|
|
|
+ .chain('exec')
|
|
|
+ .resolves()
|
|
|
+ this.DeletedProjectMock.expects('create')
|
|
|
+ .withArgs({
|
|
|
+ project: this.project,
|
|
|
+ deleterData: this.deleterData
|
|
|
+ })
|
|
|
+ .resolves()
|
|
|
+
|
|
|
+ this.ProjectDeleter.deleteProject(
|
|
|
this.project_id,
|
|
|
{ deleterUser: this.user, ipAddress: this.ip },
|
|
|
- (err, deletedProject) => {
|
|
|
- this.DeletedProject.prototype.save.called.should.equal(true)
|
|
|
- deletedProject.deleterData.deleterIpAddress.should.equal(this.ip)
|
|
|
- deletedProject.deleterData.deleterId.should.equal(this.user._id)
|
|
|
- return done()
|
|
|
+ err => {
|
|
|
+ expect(err).not.to.exist
|
|
|
+ this.DeletedProjectMock.verify()
|
|
|
+ done()
|
|
|
}
|
|
|
)
|
|
|
})
|
|
|
|
|
|
- it('should flushProjectToMongoAndDelete in doc updater', function(done) {
|
|
|
- return this.deleter.deleteProject(
|
|
|
+ it('should flushProjectToMongoAndDelete in doc updater', done => {
|
|
|
+ this.ProjectMock.expects('remove')
|
|
|
+ .chain('exec')
|
|
|
+ .resolves()
|
|
|
+ this.DeletedProjectMock.expects('create').resolves()
|
|
|
+
|
|
|
+ this.ProjectDeleter.deleteProject(
|
|
|
this.project_id,
|
|
|
{ deleterUser: this.user, ipAddress: this.ip },
|
|
|
() => {
|
|
|
this.documentUpdaterHandler.flushProjectToMongoAndDelete
|
|
|
.calledWith(this.project_id)
|
|
|
.should.equal(true)
|
|
|
- return done()
|
|
|
+ done()
|
|
|
}
|
|
|
)
|
|
|
})
|
|
|
|
|
|
- it('should removeProjectFromAllTags', function(done) {
|
|
|
- return this.deleter.deleteProject(this.project_id, () => {
|
|
|
- this.TagsHandler.removeProjectFromAllTags
|
|
|
- .calledWith('member-id-1', this.project_id)
|
|
|
- .should.equal(true)
|
|
|
- this.TagsHandler.removeProjectFromAllTags
|
|
|
- .calledWith('member-id-2', this.project_id)
|
|
|
- .should.equal(true)
|
|
|
- return done()
|
|
|
+ it('should removeProjectFromAllTags', done => {
|
|
|
+ this.ProjectMock.expects('remove')
|
|
|
+ .chain('exec')
|
|
|
+ .resolves()
|
|
|
+ this.DeletedProjectMock.expects('create').resolves()
|
|
|
+
|
|
|
+ this.ProjectDeleter.deleteProject(this.project_id, () => {
|
|
|
+ sinon.assert.calledWith(
|
|
|
+ this.TagsHandler.removeProjectFromAllTags,
|
|
|
+ 'member-id-1',
|
|
|
+ this.project_id
|
|
|
+ )
|
|
|
+ sinon.assert.calledWith(
|
|
|
+ this.TagsHandler.removeProjectFromAllTags,
|
|
|
+ 'member-id-2',
|
|
|
+ this.project_id
|
|
|
+ )
|
|
|
+ done()
|
|
|
})
|
|
|
})
|
|
|
|
|
|
- it('should remove the project from Mongo', function(done) {
|
|
|
- return this.deleter.deleteProject(this.project_id, () => {
|
|
|
- this.Project.remove
|
|
|
- .calledWith({
|
|
|
- _id: this.project_id
|
|
|
- })
|
|
|
- .should.equal(true)
|
|
|
- return done()
|
|
|
+ it('should remove the project from Mongo', done => {
|
|
|
+ this.ProjectMock.expects('remove')
|
|
|
+ .withArgs({ _id: this.project_id })
|
|
|
+ .chain('exec')
|
|
|
+ .resolves()
|
|
|
+ this.DeletedProjectMock.expects('create').resolves()
|
|
|
+
|
|
|
+ this.ProjectDeleter.deleteProject(this.project_id, () => {
|
|
|
+ this.ProjectMock.verify()
|
|
|
+ done()
|
|
|
})
|
|
|
})
|
|
|
})
|
|
|
|
|
|
- describe('archiveProject', function() {
|
|
|
- beforeEach(function() {
|
|
|
- return this.Project.update.callsArgWith(2)
|
|
|
+ describe('expireDeletedProjectsAfterDuration', () => {
|
|
|
+ beforeEach(done => {
|
|
|
+ this.ProjectDeleter.expireDeletedProject = sinon
|
|
|
+ .stub()
|
|
|
+ .callsArgWith(1, null)
|
|
|
+
|
|
|
+ this.DeletedProjectMock.expects('find')
|
|
|
+ .withArgs({
|
|
|
+ 'deleterData.deletedAt': {
|
|
|
+ $lt: new Date(moment().subtract(90, 'days'))
|
|
|
+ },
|
|
|
+ project: {
|
|
|
+ $ne: null
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .yields(null, this.deletedProjects)
|
|
|
+
|
|
|
+ this.ProjectDeleter.expireDeletedProjectsAfterDuration(done)
|
|
|
+ })
|
|
|
+
|
|
|
+ it('should call find with a date 90 days earlier than today', () => {
|
|
|
+ this.DeletedProjectMock.verify()
|
|
|
})
|
|
|
|
|
|
- it('should update the project', function(done) {
|
|
|
- return this.deleter.archiveProject(this.project_id, () => {
|
|
|
- this.Project.update
|
|
|
- .calledWith(
|
|
|
- {
|
|
|
- _id: this.project_id
|
|
|
- },
|
|
|
- {
|
|
|
- $set: { archived: true }
|
|
|
+ it('should call expireDeletedProject', done => {
|
|
|
+ expect(this.ProjectDeleter.expireDeletedProject).to.have.been.calledWith(
|
|
|
+ this.deletedProjects[0].deletedProjectId
|
|
|
+ )
|
|
|
+ done()
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ describe('expireDeletedProject', () => {
|
|
|
+ beforeEach(done => {
|
|
|
+ this.DeletedProjectMock.expects('update')
|
|
|
+ .withArgs(
|
|
|
+ {
|
|
|
+ _id: this.deletedProjects[0]._id
|
|
|
+ },
|
|
|
+ {
|
|
|
+ $set: {
|
|
|
+ 'deleterData.deleterIpAddress': null,
|
|
|
+ project: null
|
|
|
}
|
|
|
- )
|
|
|
- .should.equal(true)
|
|
|
- return done()
|
|
|
+ }
|
|
|
+ )
|
|
|
+ .chain('exec')
|
|
|
+ .resolves()
|
|
|
+
|
|
|
+ this.DeletedProjectMock.expects('findOne')
|
|
|
+ .withArgs({
|
|
|
+ 'deleterData.deletedProjectId': this.deletedProjects[0].project._id
|
|
|
+ })
|
|
|
+ .chain('exec')
|
|
|
+ .resolves(this.deletedProjects[0])
|
|
|
+
|
|
|
+ this.ProjectDeleter.expireDeletedProject(
|
|
|
+ this.deletedProjects[0].project._id,
|
|
|
+ done
|
|
|
+ )
|
|
|
+ })
|
|
|
+
|
|
|
+ it('should find the specified deletedProject and remove its project and ip address', () => {
|
|
|
+ this.DeletedProjectMock.verify()
|
|
|
+ })
|
|
|
+
|
|
|
+ it('should destroy the docs in docstore', () => {
|
|
|
+ expect(this.DocstoreManager.destroyProject).to.have.been.calledWith(
|
|
|
+ this.deletedProjects[0].project._id
|
|
|
+ )
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ describe('archiveProject', () => {
|
|
|
+ beforeEach(() => {
|
|
|
+ this.ProjectMock.expects('update')
|
|
|
+ .withArgs(
|
|
|
+ {
|
|
|
+ _id: this.project_id
|
|
|
+ },
|
|
|
+ {
|
|
|
+ $set: { archived: true }
|
|
|
+ }
|
|
|
+ )
|
|
|
+ .yields()
|
|
|
+ })
|
|
|
+
|
|
|
+ it('should update the project', done => {
|
|
|
+ this.ProjectDeleter.archiveProject(this.project_id, () => {
|
|
|
+ this.ProjectMock.verify()
|
|
|
+ done()
|
|
|
})
|
|
|
})
|
|
|
})
|
|
|
|
|
|
- describe('restoreProject', function() {
|
|
|
- beforeEach(function() {
|
|
|
- return this.Project.update.callsArgWith(2)
|
|
|
+ describe('restoreProject', () => {
|
|
|
+ beforeEach(() => {
|
|
|
+ this.ProjectMock.expects('update')
|
|
|
+ .withArgs(
|
|
|
+ {
|
|
|
+ _id: this.project_id
|
|
|
+ },
|
|
|
+ {
|
|
|
+ $unset: { archived: true }
|
|
|
+ }
|
|
|
+ )
|
|
|
+ .yields()
|
|
|
})
|
|
|
|
|
|
- it('should unset the archive attribute', function(done) {
|
|
|
- return this.deleter.restoreProject(this.project_id, () => {
|
|
|
- this.Project.update
|
|
|
- .calledWith(
|
|
|
- {
|
|
|
- _id: this.project_id
|
|
|
- },
|
|
|
- {
|
|
|
- $unset: { archived: true }
|
|
|
- }
|
|
|
- )
|
|
|
- .should.equal(true)
|
|
|
- return done()
|
|
|
+ it('should unset the archive attribute', done => {
|
|
|
+ this.ProjectDeleter.restoreProject(this.project_id, () => {
|
|
|
+ this.ProjectMock.verify()
|
|
|
+ done()
|
|
|
+ })
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ describe('undeleteProject', () => {
|
|
|
+ beforeEach(() => {
|
|
|
+ this.deletedProject = {
|
|
|
+ _id: 'deleted',
|
|
|
+ project: this.project,
|
|
|
+ deleterData: {
|
|
|
+ deletedProjectId: this.project._id,
|
|
|
+ deletedProjectOwnerId: this.project.owner_ref
|
|
|
+ }
|
|
|
+ }
|
|
|
+ this.purgedProject = {
|
|
|
+ _id: 'purged',
|
|
|
+ deleterData: {
|
|
|
+ deletedProjectId: 'purgedProject',
|
|
|
+ deletedProjectOwnerId: 'potato'
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ this.DeletedProjectMock.expects('findOne')
|
|
|
+ .withArgs({ 'deleterData.deletedProjectId': this.project._id })
|
|
|
+ .chain('exec')
|
|
|
+ .resolves(this.deletedProject)
|
|
|
+ this.DeletedProjectMock.expects('findOne')
|
|
|
+ .withArgs({ 'deleterData.deletedProjectId': 'purgedProject' })
|
|
|
+ .chain('exec')
|
|
|
+ .resolves(this.purgedProject)
|
|
|
+ this.DeletedProjectMock.expects('findOne')
|
|
|
+ .withArgs({ 'deleterData.deletedProjectId': 'wombat' })
|
|
|
+ .chain('exec')
|
|
|
+ .resolves(null)
|
|
|
+ this.DeletedProjectMock.expects('deleteOne')
|
|
|
+ .chain('exec')
|
|
|
+ .resolves()
|
|
|
+ })
|
|
|
+
|
|
|
+ it('should return not found if the project does not exist', done => {
|
|
|
+ this.ProjectDeleter.undeleteProject('wombat', err => {
|
|
|
+ expect(err).to.exist
|
|
|
+ expect(err.name).to.equal('NotFoundError')
|
|
|
+ expect(err.message).to.equal('project_not_found')
|
|
|
+ done()
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ it('should return not found if the project has been expired', done => {
|
|
|
+ this.ProjectDeleter.undeleteProject('purgedProject', err => {
|
|
|
+ expect(err.name).to.equal('NotFoundError')
|
|
|
+ expect(err.message).to.equal('project_too_old_to_restore')
|
|
|
+ done()
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ it('should insert the project into the collection', done => {
|
|
|
+ this.ProjectDeleter.undeleteProject(this.project._id, err => {
|
|
|
+ expect(err).not.to.exist
|
|
|
+ sinon.assert.calledWith(
|
|
|
+ this.db.projects.insert,
|
|
|
+ sinon.match({
|
|
|
+ _id: this.project._id,
|
|
|
+ name: this.project.name
|
|
|
+ })
|
|
|
+ )
|
|
|
+ done()
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ it('should remove the DeletedProject', done => {
|
|
|
+ // 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 })
|
|
|
+ .chain('exec')
|
|
|
+ .resolves(this.deletedProject)
|
|
|
+ this.DeletedProjectMock.expects('deleteOne')
|
|
|
+ .withArgs({ _id: 'deleted' })
|
|
|
+ .chain('exec')
|
|
|
+ .resolves()
|
|
|
+
|
|
|
+ this.ProjectDeleter.undeleteProject(this.project._id, err => {
|
|
|
+ expect(err).not.to.exist
|
|
|
+ this.DeletedProjectMock.verify()
|
|
|
+ done()
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ it('should clear the archive bit', done => {
|
|
|
+ this.project.archived = true
|
|
|
+ this.ProjectDeleter.undeleteProject(this.project._id, err => {
|
|
|
+ expect(err).not.to.exist
|
|
|
+ sinon.assert.calledWith(
|
|
|
+ this.db.projects.insert,
|
|
|
+ sinon.match({ archived: undefined })
|
|
|
+ )
|
|
|
+ done()
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ it('should generate a unique name for the project', done => {
|
|
|
+ this.ProjectDeleter.undeleteProject(this.project._id, err => {
|
|
|
+ expect(err).not.to.exist
|
|
|
+ sinon.assert.calledWith(
|
|
|
+ this.ProjectDetailsHandler.generateUniqueName,
|
|
|
+ this.project.owner_ref
|
|
|
+ )
|
|
|
+ done()
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ it('should add a suffix to the project name', done => {
|
|
|
+ this.ProjectDeleter.undeleteProject(this.project._id, err => {
|
|
|
+ expect(err).not.to.exist
|
|
|
+ sinon.assert.calledWith(
|
|
|
+ this.ProjectDetailsHandler.generateUniqueName,
|
|
|
+ this.project.owner_ref,
|
|
|
+ this.project.name + ' (Restored)'
|
|
|
+ )
|
|
|
+ done()
|
|
|
})
|
|
|
})
|
|
|
})
|