TpdsControllerTests.js 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. const SandboxedModule = require('sandboxed-module')
  2. const sinon = require('sinon')
  3. require('chai').should()
  4. const Errors = require('../../../../app/src/Features/Errors/Errors')
  5. const modulePath = require('path').join(
  6. __dirname,
  7. '../../../../app/src/Features/ThirdPartyDataStore/TpdsController.js'
  8. )
  9. describe('TpdsController', function() {
  10. beforeEach(function() {
  11. this.TpdsUpdateHandler = {}
  12. this.AuthenticationController = {
  13. getLoggedInUserId: sinon.stub().returns('user-id')
  14. }
  15. this.TpdsQueueManager = {
  16. promises: {
  17. getQueues: sinon.stub().returns('queues')
  18. }
  19. }
  20. this.TpdsController = SandboxedModule.require(modulePath, {
  21. globals: {
  22. console: console
  23. },
  24. requires: {
  25. './TpdsUpdateHandler': this.TpdsUpdateHandler,
  26. './UpdateMerger': (this.UpdateMerger = {}),
  27. '../Notifications/NotificationsBuilder': (this.NotificationsBuilder = {
  28. tpdsFileLimit: sinon.stub().returns({ create: sinon.stub() })
  29. }),
  30. '../Authentication/AuthenticationController': this
  31. .AuthenticationController,
  32. './TpdsQueueManager': this.TpdsQueueManager,
  33. 'logger-sharelatex': {
  34. log() {},
  35. warn() {},
  36. info() {},
  37. err() {}
  38. },
  39. '@overleaf/metrics': {
  40. inc() {}
  41. }
  42. }
  43. })
  44. this.user_id = 'dsad29jlkjas'
  45. })
  46. describe('getting an update', function() {
  47. it('should process the update with the update receiver', function(done) {
  48. const path = '/projectName/here.txt'
  49. const req = {
  50. pause() {},
  51. params: { 0: path, user_id: this.user_id },
  52. session: {
  53. destroy() {}
  54. },
  55. headers: {
  56. 'x-sl-update-source': (this.source = 'dropbox')
  57. }
  58. }
  59. this.TpdsUpdateHandler.newUpdate = sinon.stub().callsArg(5)
  60. const res = {
  61. sendStatus: () => {
  62. this.TpdsUpdateHandler.newUpdate
  63. .calledWith(
  64. this.user_id,
  65. 'projectName',
  66. '/here.txt',
  67. req,
  68. this.source
  69. )
  70. .should.equal(true)
  71. done()
  72. }
  73. }
  74. this.TpdsController.mergeUpdate(req, res)
  75. })
  76. it('should return a 500 error when the update receiver fails', function() {
  77. const path = '/projectName/here.txt'
  78. const req = {
  79. pause() {},
  80. params: { 0: path, user_id: this.user_id },
  81. session: {
  82. destroy() {}
  83. },
  84. headers: {
  85. 'x-sl-update-source': (this.source = 'dropbox')
  86. }
  87. }
  88. this.TpdsUpdateHandler.newUpdate = sinon
  89. .stub()
  90. .callsArgWith(5, 'update-receiver-error')
  91. const res = {
  92. sendStatus: sinon.stub()
  93. }
  94. this.TpdsController.mergeUpdate(req, res)
  95. res.sendStatus.calledWith(500).should.equal(true)
  96. })
  97. it('should return a 400 error when the project is too big', function() {
  98. const path = '/projectName/here.txt'
  99. const req = {
  100. pause() {},
  101. params: { 0: path, user_id: this.user_id, projectName: 'projectName' },
  102. session: {
  103. destroy() {}
  104. },
  105. headers: {
  106. 'x-sl-update-source': (this.source = 'dropbox')
  107. }
  108. }
  109. this.TpdsUpdateHandler.newUpdate = sinon
  110. .stub()
  111. .callsArgWith(5, { message: 'project_has_too_many_files' })
  112. const res = {
  113. sendStatus: sinon.stub()
  114. }
  115. this.TpdsController.mergeUpdate(req, res)
  116. res.sendStatus.calledWith(400).should.equal(true)
  117. this.NotificationsBuilder.tpdsFileLimit
  118. .calledWith(this.user_id)
  119. .should.equal(true)
  120. })
  121. it('should return a 429 error when the update receiver fails due to too many requests error', function() {
  122. const path = '/projectName/here.txt'
  123. const req = {
  124. pause() {},
  125. params: { 0: path, user_id: this.user_id },
  126. session: {
  127. destroy() {}
  128. },
  129. headers: {
  130. 'x-sl-update-source': (this.source = 'dropbox')
  131. }
  132. }
  133. this.TpdsUpdateHandler.newUpdate = sinon
  134. .stub()
  135. .callsArgWith(5, new Errors.TooManyRequestsError('project on cooldown'))
  136. const res = {
  137. sendStatus: sinon.stub()
  138. }
  139. this.TpdsController.mergeUpdate(req, res)
  140. res.sendStatus.calledWith(429).should.equal(true)
  141. })
  142. })
  143. describe('getting a delete update', function() {
  144. it('should process the delete with the update receiver', function(done) {
  145. const path = '/projectName/here.txt'
  146. const req = {
  147. params: { 0: path, user_id: this.user_id },
  148. session: {
  149. destroy() {}
  150. },
  151. headers: {
  152. 'x-sl-update-source': (this.source = 'dropbox')
  153. }
  154. }
  155. this.TpdsUpdateHandler.deleteUpdate = sinon.stub().callsArg(4)
  156. const res = {
  157. sendStatus: () => {
  158. this.TpdsUpdateHandler.deleteUpdate
  159. .calledWith(this.user_id, 'projectName', '/here.txt', this.source)
  160. .should.equal(true)
  161. done()
  162. }
  163. }
  164. this.TpdsController.deleteUpdate(req, res)
  165. })
  166. })
  167. describe('parseParams', function() {
  168. it('should take the project name off the start and replace with slash', function() {
  169. const path = 'noSlashHere'
  170. const req = { params: { 0: path, user_id: this.user_id } }
  171. const result = this.TpdsController.parseParams(req)
  172. result.userId.should.equal(this.user_id)
  173. result.filePath.should.equal('/')
  174. result.projectName.should.equal(path)
  175. })
  176. it('should take the project name off the start and it with no slashes in', function() {
  177. const path = '/project/file.tex'
  178. const req = { params: { 0: path, user_id: this.user_id } }
  179. const result = this.TpdsController.parseParams(req)
  180. result.userId.should.equal(this.user_id)
  181. result.filePath.should.equal('/file.tex')
  182. result.projectName.should.equal('project')
  183. })
  184. it('should take the project name of and return a slash for the file path', function() {
  185. const path = '/project_name'
  186. const req = { params: { 0: path, user_id: this.user_id } }
  187. const result = this.TpdsController.parseParams(req)
  188. result.projectName.should.equal('project_name')
  189. result.filePath.should.equal('/')
  190. })
  191. })
  192. describe('updateProjectContents', function() {
  193. beforeEach(function() {
  194. this.UpdateMerger.mergeUpdate = sinon.stub().callsArg(5)
  195. this.req = {
  196. params: {
  197. 0: (this.path = 'chapters/main.tex'),
  198. project_id: (this.project_id = 'project-id-123')
  199. },
  200. session: {
  201. destroy: sinon.stub()
  202. },
  203. headers: {
  204. 'x-sl-update-source': (this.source = 'github')
  205. }
  206. }
  207. this.res = { sendStatus: sinon.stub() }
  208. this.TpdsController.updateProjectContents(this.req, this.res, this.next)
  209. })
  210. it('should merge the update', function() {
  211. this.UpdateMerger.mergeUpdate
  212. .calledWith(
  213. null,
  214. this.project_id,
  215. `/${this.path}`,
  216. this.req,
  217. this.source
  218. )
  219. .should.equal(true)
  220. })
  221. it('should return a success', function() {
  222. this.res.sendStatus.calledWith(200).should.equal(true)
  223. })
  224. })
  225. describe('deleteProjectContents', function() {
  226. beforeEach(function() {
  227. this.UpdateMerger.deleteUpdate = sinon.stub().callsArg(4)
  228. this.req = {
  229. params: {
  230. 0: (this.path = 'chapters/main.tex'),
  231. project_id: (this.project_id = 'project-id-123')
  232. },
  233. session: {
  234. destroy: sinon.stub()
  235. },
  236. headers: {
  237. 'x-sl-update-source': (this.source = 'github')
  238. }
  239. }
  240. this.res = { sendStatus: sinon.stub() }
  241. this.TpdsController.deleteProjectContents(this.req, this.res, this.next)
  242. })
  243. it('should delete the file', function() {
  244. this.UpdateMerger.deleteUpdate
  245. .calledWith(null, this.project_id, `/${this.path}`, this.source)
  246. .should.equal(true)
  247. })
  248. it('should return a success', function() {
  249. this.res.sendStatus.calledWith(200).should.equal(true)
  250. })
  251. })
  252. describe('getQueues', function() {
  253. beforeEach(function() {
  254. this.req = {}
  255. this.res = { json: sinon.stub() }
  256. this.next = sinon.stub()
  257. })
  258. describe('success', function() {
  259. beforeEach(async function() {
  260. await this.TpdsController.getQueues(this.req, this.res, this.next)
  261. })
  262. it('should use userId from session', function() {
  263. this.AuthenticationController.getLoggedInUserId.should.have.been
  264. .calledOnce
  265. this.TpdsQueueManager.promises.getQueues.should.have.been.calledWith(
  266. 'user-id'
  267. )
  268. })
  269. it('should call json with response', function() {
  270. this.res.json.should.have.been.calledWith('queues')
  271. this.next.should.not.have.been.called
  272. })
  273. })
  274. describe('error', function() {
  275. beforeEach(async function() {
  276. this.err = new Error()
  277. this.TpdsQueueManager.promises.getQueues = sinon
  278. .stub()
  279. .rejects(this.err)
  280. await this.TpdsController.getQueues(this.req, this.res, this.next)
  281. })
  282. it('should call next with error', function() {
  283. this.res.json.should.not.have.been.called
  284. this.next.should.have.been.calledWith(this.err)
  285. })
  286. })
  287. })
  288. })