ProjectZipStreamManagerTests.js 14 KB

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