FileControllerTests.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. const sinon = require('sinon')
  2. const chai = require('chai')
  3. const { expect } = chai
  4. const SandboxedModule = require('sandboxed-module')
  5. const Errors = require('../../../app/js/Errors')
  6. const modulePath = '../../../app/js/FileController.js'
  7. describe('FileController', function() {
  8. let PersistorManager,
  9. FileHandler,
  10. LocalFileWriter,
  11. FileController,
  12. req,
  13. res,
  14. stream
  15. const settings = {
  16. s3: {
  17. buckets: {
  18. user_files: 'user_files'
  19. }
  20. }
  21. }
  22. const fileSize = 1234
  23. const fileStream = 'fileStream'
  24. const projectId = 'projectId'
  25. const fileId = 'file_id'
  26. const bucket = 'user_files'
  27. const key = `${projectId}/${fileId}`
  28. beforeEach(function() {
  29. PersistorManager = {
  30. sendStream: sinon.stub().yields(),
  31. copyFile: sinon.stub().yields(),
  32. deleteFile: sinon.stub().yields()
  33. }
  34. FileHandler = {
  35. getFile: sinon.stub().yields(null, fileStream),
  36. getFileSize: sinon.stub().yields(null, fileSize),
  37. deleteFile: sinon.stub().yields(),
  38. insertFile: sinon.stub().yields(),
  39. getDirectorySize: sinon.stub().yields(null, fileSize)
  40. }
  41. LocalFileWriter = {}
  42. stream = {
  43. pipeline: sinon.stub()
  44. }
  45. FileController = SandboxedModule.require(modulePath, {
  46. requires: {
  47. './LocalFileWriter': LocalFileWriter,
  48. './FileHandler': FileHandler,
  49. './PersistorManager': PersistorManager,
  50. './Errors': Errors,
  51. stream: stream,
  52. 'settings-sharelatex': settings,
  53. 'metrics-sharelatex': {
  54. inc() {}
  55. },
  56. 'logger-sharelatex': {
  57. log() {},
  58. err() {}
  59. }
  60. },
  61. globals: { console }
  62. })
  63. req = {
  64. key: key,
  65. bucket: bucket,
  66. query: {},
  67. params: {
  68. project_id: projectId,
  69. file_id: fileId
  70. },
  71. headers: {}
  72. }
  73. res = {
  74. set: sinon.stub().returnsThis(),
  75. sendStatus: sinon.stub().returnsThis(),
  76. status: sinon.stub().returnsThis()
  77. }
  78. })
  79. describe('getFile', function() {
  80. it('should pipe the stream', function() {
  81. FileController.getFile(req, res)
  82. expect(stream.pipeline).to.have.been.calledWith(fileStream, res)
  83. })
  84. it('should send a 200 if the cacheWarm param is true', function(done) {
  85. req.query.cacheWarm = true
  86. res.sendStatus = statusCode => {
  87. statusCode.should.equal(200)
  88. done()
  89. }
  90. FileController.getFile(req, res)
  91. })
  92. it('should send a 500 if there is a problem', function(done) {
  93. FileHandler.getFile.yields('error')
  94. res.sendStatus = code => {
  95. code.should.equal(500)
  96. done()
  97. }
  98. FileController.getFile(req, res)
  99. })
  100. describe('with a range header', function() {
  101. let expectedOptions
  102. beforeEach(function() {
  103. expectedOptions = {
  104. bucket,
  105. key,
  106. format: undefined,
  107. style: undefined
  108. }
  109. })
  110. it('should pass range options to FileHandler', function() {
  111. req.headers.range = 'bytes=0-8'
  112. expectedOptions.start = 0
  113. expectedOptions.end = 8
  114. FileController.getFile(req, res)
  115. expect(FileHandler.getFile).to.have.been.calledWith(
  116. bucket,
  117. key,
  118. expectedOptions
  119. )
  120. })
  121. it('should ignore an invalid range header', function() {
  122. req.headers.range = 'potato'
  123. FileController.getFile(req, res)
  124. expect(FileHandler.getFile).to.have.been.calledWith(
  125. bucket,
  126. key,
  127. expectedOptions
  128. )
  129. })
  130. it("should ignore any type other than 'bytes'", function() {
  131. req.headers.range = 'wombats=0-8'
  132. FileController.getFile(req, res)
  133. expect(FileHandler.getFile).to.have.been.calledWith(
  134. bucket,
  135. key,
  136. expectedOptions
  137. )
  138. })
  139. })
  140. })
  141. describe('getFileHead', function() {
  142. it('should return the file size in a Content-Length header', function(done) {
  143. res.end = () => {
  144. expect(res.status).to.have.been.calledWith(200)
  145. expect(res.set).to.have.been.calledWith('Content-Length', fileSize)
  146. done()
  147. }
  148. FileController.getFileHead(req, res)
  149. })
  150. it('should return a 404 is the file is not found', function(done) {
  151. FileHandler.getFileSize.yields(new Errors.NotFoundError())
  152. res.sendStatus = code => {
  153. expect(code).to.equal(404)
  154. done()
  155. }
  156. FileController.getFileHead(req, res)
  157. })
  158. it('should return a 500 on internal errors', function(done) {
  159. FileHandler.getFileSize.yields(new Error())
  160. res.sendStatus = code => {
  161. expect(code).to.equal(500)
  162. done()
  163. }
  164. FileController.getFileHead(req, res)
  165. })
  166. })
  167. describe('insertFile', function() {
  168. it('should send bucket name key and res to PersistorManager', function(done) {
  169. res.sendStatus = code => {
  170. expect(FileHandler.insertFile).to.have.been.calledWith(bucket, key, req)
  171. expect(code).to.equal(200)
  172. done()
  173. }
  174. FileController.insertFile(req, res)
  175. })
  176. })
  177. describe('copyFile', function() {
  178. const oldFileId = 'oldFileId'
  179. const oldProjectId = 'oldProjectid'
  180. const oldKey = `${oldProjectId}/${oldFileId}`
  181. beforeEach(function() {
  182. req.body = {
  183. source: {
  184. project_id: oldProjectId,
  185. file_id: oldFileId
  186. }
  187. }
  188. })
  189. it('should send bucket name and both keys to PersistorManager', function(done) {
  190. res.sendStatus = code => {
  191. code.should.equal(200)
  192. expect(PersistorManager.copyFile).to.have.been.calledWith(
  193. bucket,
  194. oldKey,
  195. key
  196. )
  197. done()
  198. }
  199. FileController.copyFile(req, res)
  200. })
  201. it('should send a 404 if the original file was not found', function(done) {
  202. PersistorManager.copyFile.yields(new Errors.NotFoundError())
  203. res.sendStatus = code => {
  204. code.should.equal(404)
  205. done()
  206. }
  207. FileController.copyFile(req, res)
  208. })
  209. it('should send a 500 if there was an error', function(done) {
  210. PersistorManager.copyFile.yields('error')
  211. res.sendStatus = code => {
  212. code.should.equal(500)
  213. done()
  214. }
  215. FileController.copyFile(req, res)
  216. })
  217. })
  218. describe('delete file', function() {
  219. it('should tell the file handler', function(done) {
  220. res.sendStatus = code => {
  221. code.should.equal(204)
  222. expect(FileHandler.deleteFile).to.have.been.calledWith(bucket, key)
  223. done()
  224. }
  225. FileController.deleteFile(req, res)
  226. })
  227. it('should send a 500 if there was an error', function(done) {
  228. FileHandler.deleteFile.yields('error')
  229. res.sendStatus = code => {
  230. code.should.equal(500)
  231. done()
  232. }
  233. FileController.deleteFile(req, res)
  234. })
  235. })
  236. describe('directorySize', function() {
  237. it('should return total directory size bytes', function(done) {
  238. FileController.directorySize(req, {
  239. json: result => {
  240. expect(result['total bytes']).to.equal(fileSize)
  241. done()
  242. }
  243. })
  244. })
  245. it('should send a 500 if there was an error', function(done) {
  246. FileHandler.getDirectorySize.callsArgWith(2, 'error')
  247. res.sendStatus = code => {
  248. code.should.equal(500)
  249. done()
  250. }
  251. FileController.directorySize(req, res)
  252. })
  253. })
  254. })