SendingFileTest.js 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. /* eslint-disable
  2. handle-callback-err,
  3. no-path-concat,
  4. no-return-assign,
  5. no-unused-vars,
  6. */
  7. // TODO: This file was created by bulk-decaffeinate.
  8. // Fix any style issues and re-enable lint.
  9. /*
  10. * decaffeinate suggestions:
  11. * DS102: Remove unnecessary code created because of implicit returns
  12. * DS103: Rewrite code to no longer use __guard__
  13. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  14. */
  15. const { assert } = require('chai')
  16. const sinon = require('sinon')
  17. const chai = require('chai')
  18. const should = chai.should()
  19. const { expect } = chai
  20. const modulePath = '../../../app/js/LocalFileWriter.js'
  21. const SandboxedModule = require('sandboxed-module')
  22. const fs = require('fs')
  23. const request = require('request')
  24. const settings = require('settings-sharelatex')
  25. const FilestoreApp = require('./FilestoreApp')
  26. const async = require('async')
  27. const getMetric = (filestoreUrl, metric, cb) =>
  28. request.get(`${filestoreUrl}/metrics`, function(err, res) {
  29. expect(res.statusCode).to.equal(200)
  30. const metricRegex = new RegExp(`^${metric}{[^}]+} ([0-9]+)$`, 'm')
  31. return cb(parseInt(__guard__(metricRegex.exec(res.body), x => x[1]) || '0'))
  32. })
  33. describe('Filestore', function() {
  34. before(function(done) {
  35. this.localFileReadPath = '/tmp/filestore_acceptence_tests_file_read.txt'
  36. this.localFileWritePath = '/tmp/filestore_acceptence_tests_file_write.txt'
  37. this.constantFileContent = [
  38. 'hello world',
  39. `line 2 goes here ${Math.random()}`,
  40. 'there are 3 lines in all'
  41. ].join('\n')
  42. this.filestoreUrl = `http://localhost:${settings.internal.filestore.port}`
  43. return fs.writeFile(
  44. this.localFileReadPath,
  45. this.constantFileContent,
  46. function(err) {
  47. if (err) {
  48. return done(err)
  49. }
  50. return FilestoreApp.waitForS3(done)
  51. }
  52. )
  53. })
  54. beforeEach(function(done) {
  55. return FilestoreApp.ensureRunning(() => {
  56. return async.parallel(
  57. [
  58. cb => {
  59. return fs.unlink(this.localFileWritePath, () => cb())
  60. },
  61. cb => {
  62. return getMetric(this.filestoreUrl, 's3_egress', metric => {
  63. this.previousEgress = metric
  64. return cb()
  65. })
  66. },
  67. cb => {
  68. return getMetric(this.filestoreUrl, 's3_ingress', metric => {
  69. this.previousIngress = metric
  70. return cb()
  71. })
  72. }
  73. ],
  74. done
  75. )
  76. })
  77. })
  78. it('should send a 200 for status endpoint', function(done) {
  79. return request(`${this.filestoreUrl}/status`, function(
  80. err,
  81. response,
  82. body
  83. ) {
  84. response.statusCode.should.equal(200)
  85. body.indexOf('filestore').should.not.equal(-1)
  86. body.indexOf('up').should.not.equal(-1)
  87. return done()
  88. })
  89. })
  90. describe('with a file on the server', function() {
  91. beforeEach(function(done) {
  92. this.timeout(1000 * 10)
  93. this.file_id = Math.random()
  94. this.fileUrl = `${this.filestoreUrl}/project/acceptence_tests/file/${this.file_id}`
  95. const writeStream = request.post(this.fileUrl)
  96. writeStream.on('end', done)
  97. return fs.createReadStream(this.localFileReadPath).pipe(writeStream)
  98. })
  99. it('should return 404 for a non-existant id', function(done) {
  100. this.timeout(1000 * 20)
  101. const options = { uri: this.fileUrl + '___this_is_clearly_wrong___' }
  102. return request.get(options, (err, response, body) => {
  103. response.statusCode.should.equal(404)
  104. return done()
  105. })
  106. })
  107. it('should record an egress metric for the upload', function(done) {
  108. return getMetric(this.filestoreUrl, 's3_egress', metric => {
  109. expect(metric - this.previousEgress).to.equal(
  110. this.constantFileContent.length
  111. )
  112. return done()
  113. })
  114. })
  115. it('should return the file size on a HEAD request', function(done) {
  116. const expectedLength = Buffer.byteLength(this.constantFileContent)
  117. return request.head(this.fileUrl, (err, res) => {
  118. expect(res.statusCode).to.equal(200)
  119. expect(res.headers['content-length']).to.equal(
  120. expectedLength.toString()
  121. )
  122. return done()
  123. })
  124. })
  125. it('should be able get the file back', function(done) {
  126. this.timeout(1000 * 10)
  127. return request.get(this.fileUrl, (err, response, body) => {
  128. body.should.equal(this.constantFileContent)
  129. return done()
  130. })
  131. })
  132. it('should record an ingress metric when downloading the file', function(done) {
  133. this.timeout(1000 * 10)
  134. return request.get(this.fileUrl, () => {
  135. return getMetric(this.filestoreUrl, 's3_ingress', metric => {
  136. expect(metric - this.previousIngress).to.equal(
  137. this.constantFileContent.length
  138. )
  139. return done()
  140. })
  141. })
  142. })
  143. it('should be able to get back the first 9 bytes of the file', function(done) {
  144. this.timeout(1000 * 10)
  145. const options = {
  146. uri: this.fileUrl,
  147. headers: {
  148. Range: 'bytes=0-8'
  149. }
  150. }
  151. return request.get(options, (err, response, body) => {
  152. body.should.equal('hello wor')
  153. return done()
  154. })
  155. })
  156. it('should record an ingress metric for a partial download', function(done) {
  157. this.timeout(1000 * 10)
  158. const options = {
  159. uri: this.fileUrl,
  160. headers: {
  161. Range: 'bytes=0-8'
  162. }
  163. }
  164. return request.get(options, () => {
  165. return getMetric(this.filestoreUrl, 's3_ingress', metric => {
  166. expect(metric - this.previousIngress).to.equal(9)
  167. return done()
  168. })
  169. })
  170. })
  171. it('should be able to get back bytes 4 through 10 of the file', function(done) {
  172. this.timeout(1000 * 10)
  173. const options = {
  174. uri: this.fileUrl,
  175. headers: {
  176. Range: 'bytes=4-10'
  177. }
  178. }
  179. return request.get(options, (err, response, body) => {
  180. body.should.equal('o world')
  181. return done()
  182. })
  183. })
  184. it('should be able to delete the file', function(done) {
  185. this.timeout(1000 * 20)
  186. return request.del(this.fileUrl, (err, response, body) => {
  187. response.statusCode.should.equal(204)
  188. return request.get(this.fileUrl, (err, response, body) => {
  189. response.statusCode.should.equal(404)
  190. return done()
  191. })
  192. })
  193. })
  194. return it('should be able to copy files', function(done) {
  195. this.timeout(1000 * 20)
  196. const newProjectID = 'acceptence_tests_copyied_project'
  197. const newFileId = Math.random()
  198. const newFileUrl = `${this.filestoreUrl}/project/${newProjectID}/file/${newFileId}`
  199. const opts = {
  200. method: 'put',
  201. uri: newFileUrl,
  202. json: {
  203. source: {
  204. project_id: 'acceptence_tests',
  205. file_id: this.file_id
  206. }
  207. }
  208. }
  209. return request(opts, (err, response, body) => {
  210. response.statusCode.should.equal(200)
  211. return request.del(this.fileUrl, (err, response, body) => {
  212. response.statusCode.should.equal(204)
  213. return request.get(newFileUrl, (err, response, body) => {
  214. body.should.equal(this.constantFileContent)
  215. return done()
  216. })
  217. })
  218. })
  219. })
  220. })
  221. return describe('with a pdf file', function() {
  222. beforeEach(function(done) {
  223. this.timeout(1000 * 10)
  224. this.file_id = Math.random()
  225. this.fileUrl = `${this.filestoreUrl}/project/acceptence_tests/file/${this.file_id}`
  226. this.localFileReadPath = __dirname + '/../../fixtures/test.pdf'
  227. return fs.stat(this.localFileReadPath, (err, stat) => {
  228. this.localFileSize = stat.size
  229. const writeStream = request.post(this.fileUrl)
  230. writeStream.on('end', done)
  231. return fs.createReadStream(this.localFileReadPath).pipe(writeStream)
  232. })
  233. })
  234. it('should record an egress metric for the upload', function(done) {
  235. return getMetric(this.filestoreUrl, 's3_egress', metric => {
  236. expect(metric - this.previousEgress).to.equal(this.localFileSize)
  237. return done()
  238. })
  239. })
  240. it('should be able get the file back', function(done) {
  241. this.timeout(1000 * 10)
  242. return request.get(this.fileUrl, (err, response, body) => {
  243. expect(body.substring(0, 8)).to.equal('%PDF-1.5')
  244. return done()
  245. })
  246. })
  247. describe('getting the preview image', function() {
  248. beforeEach(function() {
  249. return (this.previewFileUrl = `${this.fileUrl}?style=preview`)
  250. })
  251. it('should not time out', function(done) {
  252. this.timeout(1000 * 20)
  253. return request.get(this.previewFileUrl, (err, response, body) => {
  254. expect(response).to.not.equal(null)
  255. return done()
  256. })
  257. })
  258. return it('should respond with image data', function(done) {
  259. // note: this test relies of the imagemagick conversion working
  260. this.timeout(1000 * 20)
  261. return request.get(this.previewFileUrl, (err, response, body) => {
  262. expect(response.statusCode).to.equal(200)
  263. expect(body.length).to.be.greaterThan(400)
  264. return done()
  265. })
  266. })
  267. })
  268. return describe('warming the cache', function() {
  269. beforeEach(function() {
  270. return (this.fileUrl = this.fileUrl + '?style=preview&cacheWarm=true')
  271. })
  272. it('should not time out', function(done) {
  273. this.timeout(1000 * 20)
  274. return request.get(this.fileUrl, (err, response, body) => {
  275. expect(response).to.not.equal(null)
  276. return done()
  277. })
  278. })
  279. return it("should respond with only an 'OK'", function(done) {
  280. // note: this test relies of the imagemagick conversion working
  281. this.timeout(1000 * 20)
  282. return request.get(this.fileUrl, (err, response, body) => {
  283. expect(response.statusCode).to.equal(200)
  284. body.should.equal('OK')
  285. return done()
  286. })
  287. })
  288. })
  289. })
  290. })
  291. function __guard__(value, transform) {
  292. return typeof value !== 'undefined' && value !== null
  293. ? transform(value)
  294. : undefined
  295. }