ProjectZipStreamManagerTests.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. /* eslint-disable
  2. max-len,
  3. no-return-assign,
  4. no-unused-vars,
  5. one-var,
  6. */
  7. // TODO: This file was created by bulk-decaffeinate.
  8. // Fix any style issues and re-enable lint.
  9. /*
  10. * decaffeinate suggestions:
  11. * DS101: Remove unnecessary use of Array.from
  12. * DS102: Remove unnecessary code created because of implicit returns
  13. * DS201: Simplify complex destructure assignments
  14. * DS205: Consider reworking code to avoid use of IIFEs
  15. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  16. */
  17. const sinon = require('sinon')
  18. const { expect } = require('chai')
  19. const modulePath =
  20. '../../../../app/src/Features/Downloads/ProjectZipStreamManager.js'
  21. const SandboxedModule = require('sandboxed-module')
  22. const { EventEmitter } = require('events')
  23. describe('ProjectZipStreamManager', function () {
  24. beforeEach(function () {
  25. this.project_id = 'project-id-123'
  26. this.callback = sinon.stub()
  27. this.archive = {
  28. on() {},
  29. append: sinon.stub(),
  30. }
  31. return (this.ProjectZipStreamManager = SandboxedModule.require(modulePath, {
  32. requires: {
  33. archiver: (this.archiver = sinon.stub().returns(this.archive)),
  34. '../Project/ProjectEntityHandler': (this.ProjectEntityHandler = {}),
  35. '../FileStore/FileStoreHandler': (this.FileStoreHandler = {}),
  36. '../Project/ProjectGetter': (this.ProjectGetter = {}),
  37. },
  38. }))
  39. })
  40. describe('createZipStreamForMultipleProjects', function () {
  41. describe('successfully', function () {
  42. beforeEach(function (done) {
  43. this.project_ids = ['project-1', 'project-2']
  44. this.zip_streams = {
  45. 'project-1': new EventEmitter(),
  46. 'project-2': new EventEmitter(),
  47. }
  48. this.project_names = {
  49. 'project-1': 'Project One Name',
  50. 'project-2': 'Project Two Name',
  51. }
  52. this.ProjectZipStreamManager.createZipStreamForProject = (
  53. projectId,
  54. callback
  55. ) => {
  56. callback(null, this.zip_streams[projectId])
  57. setTimeout(() => {
  58. return this.zip_streams[projectId].emit('end')
  59. })
  60. return 0
  61. }
  62. sinon.spy(this.ProjectZipStreamManager, 'createZipStreamForProject')
  63. this.ProjectGetter.getProject = (projectId, fields, callback) => {
  64. return callback(null, { name: this.project_names[projectId] })
  65. }
  66. sinon.spy(this.ProjectGetter, 'getProject')
  67. this.ProjectZipStreamManager.createZipStreamForMultipleProjects(
  68. this.project_ids,
  69. (...args) => {
  70. return this.callback(...Array.from(args || []))
  71. }
  72. )
  73. return (this.archive.finalize = () => done())
  74. })
  75. it('should create a zip archive', function () {
  76. return this.archiver.calledWith('zip').should.equal(true)
  77. })
  78. it('should return a stream before any processing is done', function () {
  79. this.callback
  80. .calledWith(sinon.match.falsy, this.archive)
  81. .should.equal(true)
  82. return this.callback
  83. .calledBefore(this.ProjectZipStreamManager.createZipStreamForProject)
  84. .should.equal(true)
  85. })
  86. it('should get a zip stream for all of the projects', function () {
  87. return Array.from(this.project_ids).map(projectId =>
  88. this.ProjectZipStreamManager.createZipStreamForProject
  89. .calledWith(projectId)
  90. .should.equal(true)
  91. )
  92. })
  93. it('should get the names of each project', function () {
  94. return Array.from(this.project_ids).map(projectId =>
  95. this.ProjectGetter.getProject
  96. .calledWith(projectId, { name: true })
  97. .should.equal(true)
  98. )
  99. })
  100. it('should add all of the projects to the zip', function () {
  101. return Array.from(this.project_ids).map(projectId =>
  102. this.archive.append
  103. .calledWith(this.zip_streams[projectId], {
  104. name: this.project_names[projectId] + '.zip',
  105. })
  106. .should.equal(true)
  107. )
  108. })
  109. })
  110. describe('with a project not existing', function () {
  111. beforeEach(function (done) {
  112. this.project_ids = ['project-1', 'wrong-id']
  113. this.project_names = {
  114. 'project-1': 'Project One Name',
  115. }
  116. this.zip_streams = {
  117. 'project-1': new EventEmitter(),
  118. }
  119. this.ProjectZipStreamManager.createZipStreamForProject = (
  120. projectId,
  121. callback
  122. ) => {
  123. callback(null, this.zip_streams[projectId])
  124. setTimeout(() => {
  125. this.zip_streams[projectId].emit('end')
  126. })
  127. }
  128. sinon.spy(this.ProjectZipStreamManager, 'createZipStreamForProject')
  129. this.ProjectGetter.getProject = (projectId, fields, callback) => {
  130. const name = this.project_names[projectId]
  131. callback(null, name ? { name } : undefined)
  132. }
  133. sinon.spy(this.ProjectGetter, 'getProject')
  134. this.ProjectZipStreamManager.createZipStreamForMultipleProjects(
  135. this.project_ids,
  136. this.callback
  137. )
  138. this.archive.finalize = () => done()
  139. })
  140. it('should create a zip archive', function () {
  141. this.archiver.calledWith('zip').should.equal(true)
  142. })
  143. it('should return a stream before any processing is done', function () {
  144. this.callback
  145. .calledWith(sinon.match.falsy, this.archive)
  146. .should.equal(true)
  147. this.callback
  148. .calledBefore(this.ProjectZipStreamManager.createZipStreamForProject)
  149. .should.equal(true)
  150. })
  151. it('should get the names of each project', function () {
  152. this.project_ids.map(projectId =>
  153. this.ProjectGetter.getProject
  154. .calledWith(projectId, { name: true })
  155. .should.equal(true)
  156. )
  157. })
  158. it('should get a zip stream only for the existing project', function () {
  159. this.ProjectZipStreamManager.createZipStreamForProject
  160. .calledWith('project-1')
  161. .should.equal(true)
  162. this.ProjectZipStreamManager.createZipStreamForProject
  163. .calledWith('wrong-id')
  164. .should.equal(false)
  165. })
  166. it('should only add the existing project to the zip', function () {
  167. sinon.assert.calledOnce(this.archive.append)
  168. this.archive.append
  169. .calledWith(this.zip_streams['project-1'], {
  170. name: this.project_names['project-1'] + '.zip',
  171. })
  172. .should.equal(true)
  173. })
  174. })
  175. })
  176. describe('createZipStreamForProject', function () {
  177. describe('successfully', function () {
  178. beforeEach(function () {
  179. this.ProjectZipStreamManager.addAllDocsToArchive = sinon
  180. .stub()
  181. .callsArg(2)
  182. this.ProjectZipStreamManager.addAllFilesToArchive = sinon
  183. .stub()
  184. .callsArg(2)
  185. this.archive.finalize = sinon.stub()
  186. return this.ProjectZipStreamManager.createZipStreamForProject(
  187. this.project_id,
  188. this.callback
  189. )
  190. })
  191. it('should create a zip archive', function () {
  192. return this.archiver.calledWith('zip').should.equal(true)
  193. })
  194. it('should return a stream before any processing is done', function () {
  195. this.callback
  196. .calledWith(sinon.match.falsy, this.archive)
  197. .should.equal(true)
  198. this.callback
  199. .calledBefore(this.ProjectZipStreamManager.addAllDocsToArchive)
  200. .should.equal(true)
  201. return this.callback
  202. .calledBefore(this.ProjectZipStreamManager.addAllFilesToArchive)
  203. .should.equal(true)
  204. })
  205. it('should add all of the project docs to the zip', function () {
  206. return this.ProjectZipStreamManager.addAllDocsToArchive
  207. .calledWith(this.project_id, this.archive)
  208. .should.equal(true)
  209. })
  210. it('should add all of the project files to the zip', function () {
  211. return this.ProjectZipStreamManager.addAllFilesToArchive
  212. .calledWith(this.project_id, this.archive)
  213. .should.equal(true)
  214. })
  215. it('should finalise the stream', function () {
  216. return this.archive.finalize.called.should.equal(true)
  217. })
  218. })
  219. describe('with an error adding docs', function () {
  220. beforeEach(function () {
  221. this.ProjectZipStreamManager.addAllDocsToArchive = sinon
  222. .stub()
  223. .callsArgWith(2, new Error('something went wrong'))
  224. this.ProjectZipStreamManager.addAllFilesToArchive = sinon
  225. .stub()
  226. .callsArg(2)
  227. this.archive.finalize = sinon.stub()
  228. return this.ProjectZipStreamManager.createZipStreamForProject(
  229. this.project_id,
  230. this.callback
  231. )
  232. })
  233. it('should log out an error', function () {
  234. return this.logger.error
  235. .calledWith(sinon.match.any, 'error adding docs to zip stream')
  236. .should.equal(true)
  237. })
  238. it('should continue with the process', function () {
  239. this.ProjectZipStreamManager.addAllDocsToArchive.called.should.equal(
  240. true
  241. )
  242. this.ProjectZipStreamManager.addAllFilesToArchive.called.should.equal(
  243. true
  244. )
  245. return this.archive.finalize.called.should.equal(true)
  246. })
  247. })
  248. describe('with an error adding files', function () {
  249. beforeEach(function () {
  250. this.ProjectZipStreamManager.addAllDocsToArchive = sinon
  251. .stub()
  252. .callsArg(2)
  253. this.ProjectZipStreamManager.addAllFilesToArchive = sinon
  254. .stub()
  255. .callsArgWith(2, new Error('something went wrong'))
  256. this.archive.finalize = sinon.stub()
  257. return this.ProjectZipStreamManager.createZipStreamForProject(
  258. this.project_id,
  259. this.callback
  260. )
  261. })
  262. it('should log out an error', function () {
  263. return this.logger.error
  264. .calledWith(sinon.match.any, 'error adding files to zip stream')
  265. .should.equal(true)
  266. })
  267. it('should continue with the process', function () {
  268. this.ProjectZipStreamManager.addAllDocsToArchive.called.should.equal(
  269. true
  270. )
  271. this.ProjectZipStreamManager.addAllFilesToArchive.called.should.equal(
  272. true
  273. )
  274. return this.archive.finalize.called.should.equal(true)
  275. })
  276. })
  277. })
  278. describe('addAllDocsToArchive', function () {
  279. beforeEach(function (done) {
  280. this.docs = {
  281. '/main.tex': {
  282. lines: [
  283. '\\documentclass{article}',
  284. '\\begin{document}',
  285. 'Hello world',
  286. '\\end{document}',
  287. ],
  288. },
  289. '/chapters/chapter1.tex': {
  290. lines: ['chapter1', 'content'],
  291. },
  292. }
  293. this.ProjectEntityHandler.getAllDocs = sinon
  294. .stub()
  295. .callsArgWith(1, null, this.docs)
  296. return this.ProjectZipStreamManager.addAllDocsToArchive(
  297. this.project_id,
  298. this.archive,
  299. error => {
  300. this.callback(error)
  301. return done()
  302. }
  303. )
  304. })
  305. it('should get the docs for the project', function () {
  306. return this.ProjectEntityHandler.getAllDocs
  307. .calledWith(this.project_id)
  308. .should.equal(true)
  309. })
  310. it('should add each doc to the archive', function () {
  311. return (() => {
  312. const result = []
  313. for (let path in this.docs) {
  314. const doc = this.docs[path]
  315. path = path.slice(1) // remove "/"
  316. result.push(
  317. this.archive.append
  318. .calledWith(doc.lines.join('\n'), { name: path })
  319. .should.equal(true)
  320. )
  321. }
  322. return result
  323. })()
  324. })
  325. })
  326. describe('addAllFilesToArchive', function () {
  327. beforeEach(function () {
  328. this.files = {
  329. '/image.png': {
  330. _id: 'file-id-1',
  331. },
  332. '/folder/picture.png': {
  333. _id: 'file-id-2',
  334. },
  335. }
  336. this.streams = {
  337. 'file-id-1': new EventEmitter(),
  338. 'file-id-2': new EventEmitter(),
  339. }
  340. this.ProjectEntityHandler.getAllFiles = sinon
  341. .stub()
  342. .callsArgWith(1, null, this.files)
  343. this.FileStoreHandler.getFileStream = (projectId, fileId, ...rest) => {
  344. const obj = rest[0],
  345. callback = rest[1]
  346. return callback(null, this.streams[fileId])
  347. }
  348. sinon.spy(this.FileStoreHandler, 'getFileStream')
  349. this.ProjectZipStreamManager.addAllFilesToArchive(
  350. this.project_id,
  351. this.archive,
  352. this.callback
  353. )
  354. return (() => {
  355. const result = []
  356. for (const path in this.streams) {
  357. const stream = this.streams[path]
  358. result.push(stream.emit('end'))
  359. }
  360. return result
  361. })()
  362. })
  363. it('should get the files for the project', function () {
  364. return this.ProjectEntityHandler.getAllFiles
  365. .calledWith(this.project_id)
  366. .should.equal(true)
  367. })
  368. it('should get a stream for each file', function () {
  369. return (() => {
  370. const result = []
  371. for (const path in this.files) {
  372. const file = this.files[path]
  373. result.push(
  374. this.FileStoreHandler.getFileStream
  375. .calledWith(this.project_id, file._id)
  376. .should.equal(true)
  377. )
  378. }
  379. return result
  380. })()
  381. })
  382. it('should add each file to the archive', function () {
  383. return (() => {
  384. const result = []
  385. for (let path in this.files) {
  386. const file = this.files[path]
  387. path = path.slice(1) // remove "/"
  388. result.push(
  389. this.archive.append
  390. .calledWith(this.streams[file._id], { name: path })
  391. .should.equal(true)
  392. )
  393. }
  394. return result
  395. })()
  396. })
  397. })
  398. })