ProjectDetailsHandlerTests.coffee 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. should = require('chai').should()
  2. modulePath = "../../../../app/js/Features/Project/ProjectDetailsHandler"
  3. Errors = require "../../../../app/js/Features/Errors/Errors"
  4. SandboxedModule = require('sandboxed-module')
  5. sinon = require('sinon')
  6. assert = require("chai").assert
  7. expect = require("chai").expect
  8. require('chai').should()
  9. describe 'ProjectDetailsHandler', ->
  10. beforeEach ->
  11. @project_id = "321l3j1kjkjl"
  12. @user_id = "user-id-123"
  13. @project =
  14. name: "project"
  15. description: "this is a great project"
  16. something:"should not exist"
  17. compiler: "latexxxxxx"
  18. owner_ref: @user_id
  19. @user =
  20. features: "mock-features"
  21. @ProjectGetter =
  22. getProjectWithoutDocLines: sinon.stub().callsArgWith(1, null, @project)
  23. getProject: sinon.stub().callsArgWith(2, null, @project)
  24. @ProjectModel =
  25. update: sinon.stub()
  26. findOne: sinon.stub()
  27. @UserGetter =
  28. getUser: sinon.stub().callsArgWith(1, null, @user)
  29. @tpdsUpdateSender =
  30. moveEntity:sinon.stub().callsArgWith 1
  31. @handler = SandboxedModule.require modulePath, requires:
  32. "./ProjectGetter":@ProjectGetter
  33. '../../models/Project': Project:@ProjectModel
  34. "../User/UserGetter": @UserGetter
  35. '../ThirdPartyDataStore/TpdsUpdateSender':@tpdsUpdateSender
  36. 'logger-sharelatex':
  37. log:->
  38. err:->
  39. './ProjectTokenGenerator': @ProjectTokenGenerator = {}
  40. describe "getDetails", ->
  41. it "should find the project and owner", (done)->
  42. @handler.getDetails @project_id, (err, details)=>
  43. details.name.should.equal @project.name
  44. details.description.should.equal @project.description
  45. details.compiler.should.equal @project.compiler
  46. details.features.should.equal @user.features
  47. assert.equal(details.something, undefined)
  48. done()
  49. it "should find overleaf metadata if it exists", (done)->
  50. @project.overleaf = { id: 'id' }
  51. @handler.getDetails @project_id, (err, details)=>
  52. details.overleaf.should.equal @project.overleaf
  53. assert.equal(details.something, undefined)
  54. done()
  55. it "should return an error for a non-existent project", (done)->
  56. @ProjectGetter.getProject.callsArg(2, null, null)
  57. err = new Errors.NotFoundError("project not found")
  58. @handler.getDetails "0123456789012345678901234", (error, details) =>
  59. err.should.eql error
  60. done()
  61. it "should return the error", (done)->
  62. error = "some error"
  63. @ProjectGetter.getProject.callsArgWith(2, error)
  64. @handler.getDetails @project_id, (err)=>
  65. err.should.equal error
  66. done()
  67. describe "getProjectDescription", ->
  68. it "should make a call to mongo just for the description", (done)->
  69. @ProjectGetter.getProject.callsArgWith(2)
  70. @handler.getProjectDescription @project_id, (err, description)=>
  71. @ProjectGetter.getProject
  72. .calledWith(@project_id, description: true)
  73. .should.equal true
  74. done()
  75. it "should return what the mongo call returns", (done)->
  76. err = "error"
  77. description = "cool project"
  78. @ProjectGetter.getProject.callsArgWith(2, err, {description:description})
  79. @handler.getProjectDescription @project_id, (returnedErr, returnedDescription)=>
  80. err.should.equal returnedErr
  81. description.should.equal returnedDescription
  82. done()
  83. describe "setProjectDescription", ->
  84. beforeEach ->
  85. @description = "updated teh description"
  86. it "should update the project detials", (done)->
  87. @ProjectModel.update.callsArgWith(2)
  88. @handler.setProjectDescription @project_id, @description, =>
  89. @ProjectModel.update.calledWith({_id:@project_id}, {description:@description}).should.equal true
  90. done()
  91. describe "renameProject", ->
  92. beforeEach ->
  93. @handler.validateProjectName = sinon.stub().yields()
  94. @ProjectModel.update.callsArgWith(2)
  95. @newName = "new name here"
  96. it "should update the project with the new name", (done)->
  97. newName = "new name here"
  98. @handler.renameProject @project_id, @newName, =>
  99. @ProjectModel.update.calledWith({_id: @project_id}, {name: @newName}).should.equal true
  100. done()
  101. it "should tell the tpdsUpdateSender", (done)->
  102. @handler.renameProject @project_id, @newName, =>
  103. @tpdsUpdateSender.moveEntity.calledWith({project_id:@project_id, project_name:@project.name, newProjectName:@newName}).should.equal true
  104. done()
  105. it "should not do anything with an invalid name", (done) ->
  106. @handler.validateProjectName = sinon.stub().yields(new Error("invalid name"))
  107. @handler.renameProject @project_id, @newName, =>
  108. @tpdsUpdateSender.moveEntity.called.should.equal false
  109. @ProjectModel.update.called.should.equal false
  110. done()
  111. describe "validateProjectName", ->
  112. it "should reject undefined names", (done) ->
  113. @handler.validateProjectName undefined, (error) ->
  114. expect(error).to.exist
  115. done()
  116. it "should reject empty names", (done) ->
  117. @handler.validateProjectName "", (error) ->
  118. expect(error).to.exist
  119. done()
  120. it "should reject empty names with /s", (done) ->
  121. @handler.validateProjectName "foo/bar", (error) ->
  122. expect(error).to.exist
  123. done()
  124. it "should reject long names", (done) ->
  125. @handler.validateProjectName new Array(1000).join("a"), (error) ->
  126. expect(error).to.exist
  127. done()
  128. it "should accept normal names", (done) ->
  129. @handler.validateProjectName "foobar", (error) ->
  130. expect(error).to.not.exist
  131. done()
  132. describe "ensureProjectNameIsUnique", ->
  133. beforeEach ->
  134. @result = {
  135. owned: [{_id: 1, name:"name"}, {_id: 2, name: "name1"}, {_id: 3, name: "name11"}]
  136. readAndWrite: [{_id: 4, name:"name2"}, {_id: 5, name:"name22"}]
  137. readOnly: [{_id:6, name:"name3"}, {_id:7, name: "name33"}]
  138. tokenReadAndWrite: [{_id:8, name:"name4"}, {_id:9, name:"name44"}]
  139. tokenReadOnly: [{_id:10, name:"name5"}, {_id:11, name:"name55"}, {_id:12, name:"x".repeat(15)}]
  140. }
  141. @ProjectGetter.findAllUsersProjects = sinon.stub().callsArgWith(2, null, @result)
  142. it "should leave a unique name unchanged", (done) ->
  143. @handler.ensureProjectNameIsUnique @user_id, "unique-name", ["-test-suffix"], (error, name, changed) ->
  144. expect(name).to.equal "unique-name"
  145. expect(changed).to.equal false
  146. done()
  147. it "should append a suffix to an existing name", (done) ->
  148. @handler.ensureProjectNameIsUnique @user_id, "name1", ["-test-suffix"], (error, name, changed) ->
  149. expect(name).to.equal "name1-test-suffix"
  150. expect(changed).to.equal true
  151. done()
  152. it "should fallback to a second suffix when needed", (done) ->
  153. @handler.ensureProjectNameIsUnique @user_id, "name1", ["1", "-test-suffix"], (error, name, changed) ->
  154. expect(name).to.equal "name1-test-suffix"
  155. expect(changed).to.equal true
  156. done()
  157. it "should truncate the name when append a suffix if the result is too long", (done) ->
  158. @handler.MAX_PROJECT_NAME_LENGTH = 20
  159. @handler.ensureProjectNameIsUnique @user_id, "x".repeat(15), ["-test-suffix"], (error, name, changed) ->
  160. expect(name).to.equal "x".repeat(8) + "-test-suffix"
  161. expect(changed).to.equal true
  162. done()
  163. it "should return an error if the name cannot be made unique", (done) ->
  164. @handler.ensureProjectNameIsUnique @user_id, "name", ["1", "5", "55"], (error, name, changed) ->
  165. expect(error).to.eql new Errors.InvalidNameError("Project name could not be made unique")
  166. done()
  167. describe "fixProjectName", ->
  168. it "should change empty names to Untitled", () ->
  169. expect(@handler.fixProjectName "").to.equal "Untitled"
  170. it "should replace / with -", () ->
  171. expect(@handler.fixProjectName "foo/bar").to.equal "foo-bar"
  172. it "should truncate long names", () ->
  173. expect(@handler.fixProjectName new Array(1000).join("a")).to.equal "a".repeat(150)
  174. it "should accept normal names", () ->
  175. expect(@handler.fixProjectName "foobar").to.equal "foobar"
  176. describe "setPublicAccessLevel", ->
  177. beforeEach ->
  178. @ProjectModel.update.callsArgWith(2)
  179. @accessLevel = "readOnly"
  180. it "should update the project with the new level", (done)->
  181. @handler.setPublicAccessLevel @project_id, @accessLevel, =>
  182. @ProjectModel.update.calledWith({_id: @project_id}, {publicAccesLevel: @accessLevel}).should.equal true
  183. done()
  184. it 'should not produce an error', (done) ->
  185. @handler.setPublicAccessLevel @project_id, @accessLevel, (err) =>
  186. expect(err).to.not.exist
  187. done()
  188. describe 'when update produces an error', ->
  189. beforeEach ->
  190. @ProjectModel.update.callsArgWith(2, new Error('woops'))
  191. it 'should produce an error', (done) ->
  192. @handler.setPublicAccessLevel @project_id, @accessLevel, (err) =>
  193. expect(err).to.exist
  194. expect(err).to.be.instanceof Error
  195. done()
  196. describe "ensureTokensArePresent", ->
  197. beforeEach ->
  198. describe 'when the project has tokens', ->
  199. beforeEach ->
  200. @project =
  201. _id: @project_id
  202. tokens:
  203. readOnly: 'aaa'
  204. readAndWrite: '42bbb'
  205. @ProjectGetter.getProject = sinon.stub()
  206. .callsArgWith(2, null, @project)
  207. @ProjectModel.update = sinon.stub()
  208. it 'should get the project', (done) ->
  209. @handler.ensureTokensArePresent @project_id, (err, tokens) =>
  210. expect(@ProjectGetter.getProject.callCount).to.equal 1
  211. expect(@ProjectGetter.getProject.calledWith(@project_id, {tokens: 1}))
  212. .to.equal true
  213. done()
  214. it 'should not update the project with new tokens', (done) ->
  215. @handler.ensureTokensArePresent @project_id, (err, tokens) =>
  216. expect(@ProjectModel.update.callCount).to.equal 0
  217. done()
  218. it 'should produce the tokens without error', (done) ->
  219. @handler.ensureTokensArePresent @project_id, (err, tokens) =>
  220. expect(err).to.not.exist
  221. expect(tokens).to.deep.equal @project.tokens
  222. done()
  223. describe 'when tokens are missing', ->
  224. beforeEach ->
  225. @project =
  226. _id: @project_id
  227. @ProjectGetter.getProject = sinon.stub()
  228. .callsArgWith(2, null, @project)
  229. @readOnlyToken = 'abc'
  230. @readAndWriteToken = '42def'
  231. @ProjectTokenGenerator.readOnlyToken = sinon.stub().returns(@readOnlyToken)
  232. @ProjectTokenGenerator.readAndWriteToken = sinon.stub().returns(@readAndWriteToken)
  233. @ProjectModel.update = sinon.stub()
  234. .callsArgWith(2, null)
  235. it 'should get the project', (done) ->
  236. @handler.ensureTokensArePresent @project_id, (err, tokens) =>
  237. expect(@ProjectGetter.getProject.callCount).to.equal 1
  238. expect(@ProjectGetter.getProject.calledWith(@project_id, {tokens: 1}))
  239. .to.equal true
  240. done()
  241. it 'should update the project with new tokens', (done) ->
  242. @handler.ensureTokensArePresent @project_id, (err, tokens) =>
  243. expect(@ProjectTokenGenerator.readOnlyToken.callCount)
  244. .to.equal 1
  245. expect(@ProjectTokenGenerator.readAndWriteToken.callCount)
  246. .to.equal 1
  247. expect(@ProjectModel.update.callCount).to.equal 1
  248. expect(@ProjectModel.update.calledWith(
  249. {_id: @project_id},
  250. {$set: {tokens: {readOnly: @readOnlyToken, readAndWrite: @readAndWriteToken}}}
  251. )).to.equal true
  252. done()
  253. it 'should produce the tokens without error', (done) ->
  254. @handler.ensureTokensArePresent @project_id, (err, tokens) =>
  255. expect(err).to.not.exist
  256. expect(tokens).to.deep.equal {
  257. readOnly: @readOnlyToken,
  258. readAndWrite: @readAndWriteToken
  259. }
  260. done()