| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751 |
- /* eslint-disable
- handle-callback-err,
- max-len,
- no-return-assign,
- no-unused-vars,
- */
- // TODO: This file was created by bulk-decaffeinate.
- // Fix any style issues and re-enable lint.
- /*
- * decaffeinate suggestions:
- * DS101: Remove unnecessary use of Array.from
- * DS102: Remove unnecessary code created because of implicit returns
- * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
- */
- const should = require('chai').should()
- const modulePath = '../../../../app/src/Features/Project/ProjectDetailsHandler'
- const Errors = require('../../../../app/src/Features/Errors/Errors')
- const SandboxedModule = require('sandboxed-module')
- const sinon = require('sinon')
- const { assert } = require('chai')
- const { expect } = require('chai')
- require('chai').should()
- describe('ProjectDetailsHandler', function() {
- beforeEach(function() {
- this.project_id = '321l3j1kjkjl'
- this.user_id = 'user-id-123'
- this.project = {
- name: 'project',
- description: 'this is a great project',
- something: 'should not exist',
- compiler: 'latexxxxxx',
- owner_ref: this.user_id
- }
- this.user = { features: 'mock-features' }
- this.ProjectGetter = {
- getProjectWithoutDocLines: sinon
- .stub()
- .callsArgWith(1, null, this.project),
- getProject: sinon.stub().callsArgWith(2, null, this.project)
- }
- this.ProjectModel = {
- update: sinon.stub(),
- findOne: sinon.stub()
- }
- this.UserGetter = { getUser: sinon.stub().callsArgWith(1, null, this.user) }
- this.tpdsUpdateSender = { moveEntity: sinon.stub().callsArgWith(1) }
- this.ProjectEntityHandler = {
- flushProjectToThirdPartyDataStore: sinon.stub().callsArg(1)
- }
- this.CollaboratorsHandler = {
- removeUserFromProject: sinon.stub().callsArg(2)
- }
- return (this.handler = SandboxedModule.require(modulePath, {
- globals: {
- console: console
- },
- requires: {
- './ProjectGetter': this.ProjectGetter,
- '../../models/Project': {
- Project: this.ProjectModel
- },
- '../User/UserGetter': this.UserGetter,
- '../ThirdPartyDataStore/TpdsUpdateSender': this.tpdsUpdateSender,
- './ProjectEntityHandler': this.ProjectEntityHandler,
- '../Collaborators/CollaboratorsHandler': this.CollaboratorsHandler,
- 'logger-sharelatex': {
- log() {},
- warn() {},
- err() {}
- },
- './ProjectTokenGenerator': (this.ProjectTokenGenerator = {}),
- 'settings-sharelatex': (this.settings = {
- defaultFeatures: 'default-features'
- })
- }
- }))
- })
- describe('getDetails', function() {
- it('should find the project and owner', function(done) {
- return this.handler.getDetails(this.project_id, (err, details) => {
- details.name.should.equal(this.project.name)
- details.description.should.equal(this.project.description)
- details.compiler.should.equal(this.project.compiler)
- details.features.should.equal(this.user.features)
- assert.equal(details.something, undefined)
- return done()
- })
- })
- it('should find overleaf metadata if it exists', function(done) {
- this.project.overleaf = { id: 'id' }
- return this.handler.getDetails(this.project_id, (err, details) => {
- details.overleaf.should.equal(this.project.overleaf)
- assert.equal(details.something, undefined)
- return done()
- })
- })
- it('should return an error for a non-existent project', function(done) {
- this.ProjectGetter.getProject.callsArg(2, null, null)
- const err = new Errors.NotFoundError('project not found')
- return this.handler.getDetails(
- '0123456789012345678901234',
- (error, details) => {
- err.should.eql(error)
- return done()
- }
- )
- })
- it('should return the default features if no owner found', function(done) {
- this.UserGetter.getUser.callsArgWith(1, null, null)
- return this.handler.getDetails(this.project_id, (err, details) => {
- details.features.should.equal(this.settings.defaultFeatures)
- return done()
- })
- })
- it('should return the error', function(done) {
- const error = 'some error'
- this.ProjectGetter.getProject.callsArgWith(2, error)
- return this.handler.getDetails(this.project_id, err => {
- err.should.equal(error)
- return done()
- })
- })
- })
- describe('transferOwnership', function() {
- beforeEach(function() {
- this.handler.generateUniqueName = sinon
- .stub()
- .callsArgWith(2, null, 'teapot')
- return this.ProjectModel.update.callsArgWith(2)
- })
- it("should return a not found error if the project can't be found", function(done) {
- this.ProjectGetter.getProject.callsArgWith(2)
- return this.handler.transferOwnership('abc', '123', function(err) {
- err.should.exist
- err.name.should.equal('NotFoundError')
- return done()
- })
- })
- it("should return a not found error if the user can't be found", function(done) {
- this.ProjectGetter.getProject.callsArgWith(2)
- return this.handler.transferOwnership('abc', '123', function(err) {
- err.should.exist
- err.name.should.equal('NotFoundError')
- return done()
- })
- })
- it('should return an error if user cannot be removed as collaborator ', function(done) {
- const errorMessage = 'user-cannot-be-removed'
- this.CollaboratorsHandler.removeUserFromProject.callsArgWith(
- 2,
- errorMessage
- )
- return this.handler.transferOwnership('abc', '123', function(err) {
- err.should.exist
- err.should.equal(errorMessage)
- return done()
- })
- })
- it('should transfer ownership of the project', function(done) {
- return this.handler.transferOwnership('abc', '123', () => {
- sinon.assert.calledWith(
- this.ProjectModel.update,
- { _id: 'abc' },
- sinon.match({ $set: { name: 'teapot' } })
- )
- return done()
- })
- })
- it("should remove the user from the project's collaborators", function(done) {
- return this.handler.transferOwnership('abc', '123', () => {
- sinon.assert.calledWith(
- this.CollaboratorsHandler.removeUserFromProject,
- 'abc',
- '123'
- )
- return done()
- })
- })
- it('should flush the project to tpds', function(done) {
- return this.handler.transferOwnership('abc', '123', () => {
- sinon.assert.calledWith(
- this.ProjectEntityHandler.flushProjectToThirdPartyDataStore,
- 'abc'
- )
- return done()
- })
- })
- it('should generate a unique name for the project', function(done) {
- return this.handler.transferOwnership('abc', '123', () => {
- sinon.assert.calledWith(
- this.handler.generateUniqueName,
- '123',
- this.project.name
- )
- return done()
- })
- })
- it('should append the supplied suffix to the project name, if passed', function(done) {
- return this.handler.transferOwnership('abc', '123', ' wombat', () => {
- sinon.assert.calledWith(
- this.handler.generateUniqueName,
- '123',
- `${this.project.name} wombat`
- )
- return done()
- })
- })
- })
- describe('getProjectDescription', function() {
- it('should make a call to mongo just for the description', function(done) {
- this.ProjectGetter.getProject.callsArgWith(2)
- return this.handler.getProjectDescription(
- this.project_id,
- (err, description) => {
- this.ProjectGetter.getProject
- .calledWith(this.project_id, { description: true })
- .should.equal(true)
- return done()
- }
- )
- })
- it('should return what the mongo call returns', function(done) {
- const err = 'error'
- const description = 'cool project'
- this.ProjectGetter.getProject.callsArgWith(2, err, { description })
- return this.handler.getProjectDescription(
- this.project_id,
- (returnedErr, returnedDescription) => {
- err.should.equal(returnedErr)
- description.should.equal(returnedDescription)
- return done()
- }
- )
- })
- })
- describe('setProjectDescription', function() {
- beforeEach(function() {
- return (this.description = 'updated teh description')
- })
- it('should update the project detials', function(done) {
- this.ProjectModel.update.callsArgWith(2)
- return this.handler.setProjectDescription(
- this.project_id,
- this.description,
- () => {
- this.ProjectModel.update
- .calledWith(
- { _id: this.project_id },
- { description: this.description }
- )
- .should.equal(true)
- return done()
- }
- )
- })
- })
- describe('renameProject', function() {
- beforeEach(function() {
- this.handler.validateProjectName = sinon.stub().yields()
- this.ProjectModel.update.callsArgWith(2)
- return (this.newName = 'new name here')
- })
- it('should update the project with the new name', function(done) {
- const newName = 'new name here'
- return this.handler.renameProject(this.project_id, this.newName, () => {
- this.ProjectModel.update
- .calledWith({ _id: this.project_id }, { name: this.newName })
- .should.equal(true)
- return done()
- })
- })
- it('should tell the tpdsUpdateSender', function(done) {
- return this.handler.renameProject(this.project_id, this.newName, () => {
- this.tpdsUpdateSender.moveEntity
- .calledWith({
- project_id: this.project_id,
- project_name: this.project.name,
- newProjectName: this.newName
- })
- .should.equal(true)
- return done()
- })
- })
- it('should not do anything with an invalid name', function(done) {
- this.handler.validateProjectName = sinon
- .stub()
- .yields(new Error('invalid name'))
- return this.handler.renameProject(this.project_id, this.newName, () => {
- this.tpdsUpdateSender.moveEntity.called.should.equal(false)
- this.ProjectModel.update.called.should.equal(false)
- return done()
- })
- })
- })
- describe('validateProjectName', function() {
- it('should reject undefined names', function(done) {
- return this.handler.validateProjectName(undefined, function(error) {
- expect(error).to.exist
- return done()
- })
- })
- it('should reject empty names', function(done) {
- return this.handler.validateProjectName('', function(error) {
- expect(error).to.exist
- return done()
- })
- })
- it('should reject names with /s', function(done) {
- return this.handler.validateProjectName('foo/bar', function(error) {
- expect(error).to.exist
- return done()
- })
- })
- it('should reject names with \\s', function(done) {
- return this.handler.validateProjectName('foo\\bar', function(error) {
- expect(error).to.exist
- return done()
- })
- })
- it('should reject long names', function(done) {
- return this.handler.validateProjectName(
- new Array(1000).join('a'),
- function(error) {
- expect(error).to.exist
- return done()
- }
- )
- })
- it('should accept normal names', function(done) {
- return this.handler.validateProjectName('foobar', function(error) {
- expect(error).to.not.exist
- return done()
- })
- })
- })
- describe('ensureProjectNameIsUnique', function() {
- beforeEach(function() {
- this.result = {
- owned: [
- { _id: 1, name: 'name' },
- { _id: 2, name: 'name1' },
- { _id: 3, name: 'name11' },
- { _id: 100, name: 'numeric' }
- ],
- readAndWrite: [{ _id: 4, name: 'name2' }, { _id: 5, name: 'name22' }],
- readOnly: [{ _id: 6, name: 'name3' }, { _id: 7, name: 'name33' }],
- tokenReadAndWrite: [
- { _id: 8, name: 'name4' },
- { _id: 9, name: 'name44' }
- ],
- tokenReadOnly: [
- { _id: 10, name: 'name5' },
- { _id: 11, name: 'name55' },
- { _id: 12, name: 'x'.repeat(15) }
- ]
- }
- for (let i of Array.from(
- [
- 1,
- 2,
- 3,
- 4,
- 5,
- 6,
- 7,
- 8,
- 9,
- 10,
- 11,
- 12,
- 13,
- 14,
- 15,
- 16,
- 17,
- 18,
- 19,
- 20
- ].concat([30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40])
- )) {
- this.result.owned.push({ _id: 100 + i, name: `numeric (${i})` })
- }
- return (this.ProjectGetter.findAllUsersProjects = sinon
- .stub()
- .callsArgWith(2, null, this.result))
- })
- it('should leave a unique name unchanged', function(done) {
- return this.handler.ensureProjectNameIsUnique(
- this.user_id,
- 'unique-name',
- ['-test-suffix'],
- function(error, name, changed) {
- expect(name).to.equal('unique-name')
- expect(changed).to.equal(false)
- return done()
- }
- )
- })
- it('should append a suffix to an existing name', function(done) {
- return this.handler.ensureProjectNameIsUnique(
- this.user_id,
- 'name1',
- ['-test-suffix'],
- function(error, name, changed) {
- expect(name).to.equal('name1-test-suffix')
- expect(changed).to.equal(true)
- return done()
- }
- )
- })
- it('should fallback to a second suffix when needed', function(done) {
- return this.handler.ensureProjectNameIsUnique(
- this.user_id,
- 'name1',
- ['1', '-test-suffix'],
- function(error, name, changed) {
- expect(name).to.equal('name1-test-suffix')
- expect(changed).to.equal(true)
- return done()
- }
- )
- })
- it('should truncate the name when append a suffix if the result is too long', function(done) {
- this.handler.MAX_PROJECT_NAME_LENGTH = 20
- return this.handler.ensureProjectNameIsUnique(
- this.user_id,
- 'x'.repeat(15),
- ['-test-suffix'],
- function(error, name, changed) {
- expect(name).to.equal('x'.repeat(8) + '-test-suffix')
- expect(changed).to.equal(true)
- return done()
- }
- )
- })
- it('should use a numeric index if no suffix is supplied', function(done) {
- return this.handler.ensureProjectNameIsUnique(
- this.user_id,
- 'name1',
- [],
- function(error, name, changed) {
- expect(name).to.equal('name1 (1)')
- expect(changed).to.equal(true)
- return done()
- }
- )
- })
- it('should use a numeric index if all suffixes are exhausted', function(done) {
- return this.handler.ensureProjectNameIsUnique(
- this.user_id,
- 'name',
- ['1', '11'],
- function(error, name, changed) {
- expect(name).to.equal('name (1)')
- expect(changed).to.equal(true)
- return done()
- }
- )
- })
- it('should find the next lowest available numeric index for the base name', function(done) {
- return this.handler.ensureProjectNameIsUnique(
- this.user_id,
- 'numeric',
- [],
- function(error, name, changed) {
- expect(name).to.equal('numeric (21)')
- expect(changed).to.equal(true)
- return done()
- }
- )
- })
- it('should find the next available numeric index when a numeric index is already present', function(done) {
- return this.handler.ensureProjectNameIsUnique(
- this.user_id,
- 'numeric (5)',
- [],
- function(error, name, changed) {
- expect(name).to.equal('numeric (21)')
- expect(changed).to.equal(true)
- return done()
- }
- )
- })
- it('should not find a numeric index lower than the one already present', function(done) {
- return this.handler.ensureProjectNameIsUnique(
- this.user_id,
- 'numeric (31)',
- [],
- function(error, name, changed) {
- expect(name).to.equal('numeric (41)')
- expect(changed).to.equal(true)
- return done()
- }
- )
- })
- })
- describe('fixProjectName', function() {
- it('should change empty names to Untitled', function() {
- return expect(this.handler.fixProjectName('')).to.equal('Untitled')
- })
- it('should replace / with -', function() {
- return expect(this.handler.fixProjectName('foo/bar')).to.equal('foo-bar')
- })
- it("should replace \\ with ''", function() {
- return expect(this.handler.fixProjectName('foo \\ bar')).to.equal(
- 'foo bar'
- )
- })
- it('should truncate long names', function() {
- return expect(
- this.handler.fixProjectName(new Array(1000).join('a'))
- ).to.equal('a'.repeat(150))
- })
- it('should accept normal names', function() {
- return expect(this.handler.fixProjectName('foobar')).to.equal('foobar')
- })
- })
- describe('setPublicAccessLevel', function() {
- beforeEach(function() {
- this.ProjectModel.update.callsArgWith(2)
- return (this.accessLevel = 'readOnly')
- })
- it('should update the project with the new level', function(done) {
- return this.handler.setPublicAccessLevel(
- this.project_id,
- this.accessLevel,
- () => {
- this.ProjectModel.update
- .calledWith(
- { _id: this.project_id },
- { publicAccesLevel: this.accessLevel }
- )
- .should.equal(true)
- return done()
- }
- )
- })
- it('should not produce an error', function(done) {
- return this.handler.setPublicAccessLevel(
- this.project_id,
- this.accessLevel,
- err => {
- expect(err).to.not.exist
- return done()
- }
- )
- })
- describe('when update produces an error', function() {
- beforeEach(function() {
- return this.ProjectModel.update.callsArgWith(2, new Error('woops'))
- })
- it('should produce an error', function(done) {
- return this.handler.setPublicAccessLevel(
- this.project_id,
- this.accessLevel,
- err => {
- expect(err).to.exist
- expect(err).to.be.instanceof(Error)
- return done()
- }
- )
- })
- })
- })
- describe('ensureTokensArePresent', function() {
- beforeEach(function() {})
- describe('when the project has tokens', function() {
- beforeEach(function() {
- this.project = {
- _id: this.project_id,
- tokens: {
- readOnly: 'aaa',
- readAndWrite: '42bbb',
- readAndWritePrefix: '42'
- }
- }
- this.ProjectGetter.getProject = sinon
- .stub()
- .callsArgWith(2, null, this.project)
- return (this.ProjectModel.update = sinon.stub())
- })
- it('should get the project', function(done) {
- return this.handler.ensureTokensArePresent(
- this.project_id,
- (err, tokens) => {
- expect(this.ProjectGetter.getProject.callCount).to.equal(1)
- expect(
- this.ProjectGetter.getProject.calledWith(this.project_id, {
- tokens: 1
- })
- ).to.equal(true)
- return done()
- }
- )
- })
- it('should not update the project with new tokens', function(done) {
- return this.handler.ensureTokensArePresent(
- this.project_id,
- (err, tokens) => {
- expect(this.ProjectModel.update.callCount).to.equal(0)
- return done()
- }
- )
- })
- it('should produce the tokens without error', function(done) {
- return this.handler.ensureTokensArePresent(
- this.project_id,
- (err, tokens) => {
- expect(err).to.not.exist
- expect(tokens).to.deep.equal(this.project.tokens)
- return done()
- }
- )
- })
- })
- describe('when tokens are missing', function() {
- beforeEach(function() {
- this.project = { _id: this.project_id }
- this.ProjectGetter.getProject = sinon
- .stub()
- .callsArgWith(2, null, this.project)
- this.readOnlyToken = 'abc'
- this.readAndWriteToken = '42def'
- this.readAndWriteTokenPrefix = '42'
- this.ProjectTokenGenerator.generateUniqueReadOnlyToken = sinon
- .stub()
- .callsArgWith(0, null, this.readOnlyToken)
- this.ProjectTokenGenerator.readAndWriteToken = sinon.stub().returns({
- token: this.readAndWriteToken,
- numericPrefix: this.readAndWriteTokenPrefix
- })
- return (this.ProjectModel.update = sinon.stub().callsArgWith(2, null))
- })
- it('should get the project', function(done) {
- return this.handler.ensureTokensArePresent(
- this.project_id,
- (err, tokens) => {
- expect(this.ProjectGetter.getProject.callCount).to.equal(1)
- expect(
- this.ProjectGetter.getProject.calledWith(this.project_id, {
- tokens: 1
- })
- ).to.equal(true)
- return done()
- }
- )
- })
- it('should update the project with new tokens', function(done) {
- return this.handler.ensureTokensArePresent(
- this.project_id,
- (err, tokens) => {
- expect(
- this.ProjectTokenGenerator.generateUniqueReadOnlyToken.callCount
- ).to.equal(1)
- expect(
- this.ProjectTokenGenerator.readAndWriteToken.callCount
- ).to.equal(1)
- expect(this.ProjectModel.update.callCount).to.equal(1)
- expect(
- this.ProjectModel.update.calledWith(
- { _id: this.project_id },
- {
- $set: {
- tokens: {
- readOnly: this.readOnlyToken,
- readAndWrite: this.readAndWriteToken,
- readAndWritePrefix: this.readAndWriteTokenPrefix
- }
- }
- }
- )
- ).to.equal(true)
- return done()
- }
- )
- })
- it('should produce the tokens without error', function(done) {
- return this.handler.ensureTokensArePresent(
- this.project_id,
- (err, tokens) => {
- expect(err).to.not.exist
- expect(tokens).to.deep.equal({
- readOnly: this.readOnlyToken,
- readAndWrite: this.readAndWriteToken,
- readAndWritePrefix: this.readAndWriteTokenPrefix
- })
- return done()
- }
- )
- })
- })
- })
- })
|