UrlFetcherTests.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. /* eslint-disable
  2. no-return-assign,
  3. */
  4. // TODO: This file was created by bulk-decaffeinate.
  5. // Fix any style issues and re-enable lint.
  6. /*
  7. * decaffeinate suggestions:
  8. * DS102: Remove unnecessary code created because of implicit returns
  9. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  10. */
  11. const SandboxedModule = require('sandboxed-module')
  12. const sinon = require('sinon')
  13. const { expect } = require('chai')
  14. const modulePath = require('path').join(__dirname, '../../../app/js/UrlFetcher')
  15. const { EventEmitter } = require('events')
  16. describe('UrlFetcher', function () {
  17. beforeEach(function () {
  18. this.callback = sinon.stub()
  19. this.url = 'https://www.example.com/file/here?query=string'
  20. return (this.UrlFetcher = SandboxedModule.require(modulePath, {
  21. requires: {
  22. request: {
  23. defaults: (this.defaults = sinon.stub().returns((this.request = {})))
  24. },
  25. fs: (this.fs = {}),
  26. 'settings-sharelatex': (this.settings = {})
  27. }
  28. }))
  29. })
  30. describe('pipeUrlToFileWithRetry', function () {
  31. this.beforeEach(function () {
  32. this.UrlFetcher.pipeUrlToFile = sinon.stub()
  33. })
  34. it('should call pipeUrlToFile', function (done) {
  35. this.UrlFetcher.pipeUrlToFile.callsArgWith(2)
  36. this.UrlFetcher.pipeUrlToFileWithRetry(this.url, this.path, (err) => {
  37. expect(err).to.equal(undefined)
  38. this.UrlFetcher.pipeUrlToFile.called.should.equal(true)
  39. done()
  40. })
  41. })
  42. it('should call pipeUrlToFile multiple times on error', function (done) {
  43. const error = new Error("couldn't download file")
  44. this.UrlFetcher.pipeUrlToFile.callsArgWith(2, error)
  45. this.UrlFetcher.pipeUrlToFileWithRetry(this.url, this.path, (err) => {
  46. expect(err).to.equal(error)
  47. this.UrlFetcher.pipeUrlToFile.callCount.should.equal(3)
  48. done()
  49. })
  50. })
  51. it('should call pipeUrlToFile twice if only 1 error', function (done) {
  52. this.UrlFetcher.pipeUrlToFile.onCall(0).callsArgWith(2, 'error')
  53. this.UrlFetcher.pipeUrlToFile.onCall(1).callsArgWith(2)
  54. this.UrlFetcher.pipeUrlToFileWithRetry(this.url, this.path, (err) => {
  55. expect(err).to.equal(undefined)
  56. this.UrlFetcher.pipeUrlToFile.callCount.should.equal(2)
  57. done()
  58. })
  59. })
  60. })
  61. describe('pipeUrlToFile', function () {
  62. it('should turn off the cookie jar in request', function () {
  63. return this.defaults.calledWith({ jar: false }).should.equal(true)
  64. })
  65. describe('rewrite url domain if filestoreDomainOveride is set', function () {
  66. beforeEach(function () {
  67. this.path = '/path/to/file/on/disk'
  68. this.request.get = sinon
  69. .stub()
  70. .returns((this.urlStream = new EventEmitter()))
  71. this.urlStream.pipe = sinon.stub()
  72. this.urlStream.pause = sinon.stub()
  73. this.urlStream.resume = sinon.stub()
  74. this.fs.createWriteStream = sinon
  75. .stub()
  76. .returns((this.fileStream = new EventEmitter()))
  77. return (this.fs.unlink = (file, callback) => callback())
  78. })
  79. it('should use the normal domain when override not set', function (done) {
  80. this.UrlFetcher.pipeUrlToFile(this.url, this.path, () => {
  81. this.request.get.args[0][0].url.should.equal(this.url)
  82. return done()
  83. })
  84. this.res = { statusCode: 200 }
  85. this.urlStream.emit('response', this.res)
  86. this.urlStream.emit('end')
  87. return this.fileStream.emit('finish')
  88. })
  89. return it('should use override domain when filestoreDomainOveride is set', function (done) {
  90. this.settings.filestoreDomainOveride = '192.11.11.11'
  91. this.UrlFetcher.pipeUrlToFile(this.url, this.path, () => {
  92. this.request.get.args[0][0].url.should.equal(
  93. '192.11.11.11/file/here?query=string'
  94. )
  95. return done()
  96. })
  97. this.res = { statusCode: 200 }
  98. this.urlStream.emit('response', this.res)
  99. this.urlStream.emit('end')
  100. return this.fileStream.emit('finish')
  101. })
  102. })
  103. return describe('pipeUrlToFile', function () {
  104. beforeEach(function (done) {
  105. this.path = '/path/to/file/on/disk'
  106. this.request.get = sinon
  107. .stub()
  108. .returns((this.urlStream = new EventEmitter()))
  109. this.urlStream.pipe = sinon.stub()
  110. this.urlStream.pause = sinon.stub()
  111. this.urlStream.resume = sinon.stub()
  112. this.fs.createWriteStream = sinon
  113. .stub()
  114. .returns((this.fileStream = new EventEmitter()))
  115. this.fs.unlink = (file, callback) => callback()
  116. return done()
  117. })
  118. describe('successfully', function () {
  119. beforeEach(function (done) {
  120. this.UrlFetcher.pipeUrlToFile(this.url, this.path, () => {
  121. this.callback()
  122. return done()
  123. })
  124. this.res = { statusCode: 200 }
  125. this.urlStream.emit('response', this.res)
  126. this.urlStream.emit('end')
  127. return this.fileStream.emit('finish')
  128. })
  129. it('should request the URL', function () {
  130. return this.request.get
  131. .calledWith(sinon.match({ url: this.url }))
  132. .should.equal(true)
  133. })
  134. it('should open the file for writing', function () {
  135. return this.fs.createWriteStream
  136. .calledWith(this.path)
  137. .should.equal(true)
  138. })
  139. it('should pipe the URL to the file', function () {
  140. return this.urlStream.pipe
  141. .calledWith(this.fileStream)
  142. .should.equal(true)
  143. })
  144. return it('should call the callback', function () {
  145. return this.callback.called.should.equal(true)
  146. })
  147. })
  148. describe('with non success status code', function () {
  149. beforeEach(function (done) {
  150. this.UrlFetcher.pipeUrlToFile(this.url, this.path, (err) => {
  151. this.callback(err)
  152. return done()
  153. })
  154. this.res = { statusCode: 404 }
  155. this.urlStream.emit('response', this.res)
  156. return this.urlStream.emit('end')
  157. })
  158. it('should call the callback with an error', function () {
  159. this.callback.calledWith(sinon.match(Error)).should.equal(true)
  160. const message = this.callback.args[0][0].message
  161. expect(message).to.include(
  162. 'URL returned non-success status code: 404'
  163. )
  164. })
  165. })
  166. return describe('with error', function () {
  167. beforeEach(function (done) {
  168. this.UrlFetcher.pipeUrlToFile(this.url, this.path, (err) => {
  169. this.callback(err)
  170. return done()
  171. })
  172. return this.urlStream.emit(
  173. 'error',
  174. (this.error = new Error('something went wrong'))
  175. )
  176. })
  177. it('should call the callback with the error', function () {
  178. return this.callback.calledWith(this.error).should.equal(true)
  179. })
  180. return it('should only call the callback once, even if end is called', function () {
  181. this.urlStream.emit('end')
  182. return this.callback.calledOnce.should.equal(true)
  183. })
  184. })
  185. })
  186. })
  187. })