ContentCacheManagerTests.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. const Path = require('path')
  2. const crypto = require('crypto')
  3. const { Readable } = require('stream')
  4. const SandboxedModule = require('sandboxed-module')
  5. const sinon = require('sinon')
  6. const { expect } = require('chai')
  7. const MODULE_PATH = '../../../app/js/ContentCacheManager'
  8. class FakeFile {
  9. constructor() {
  10. this.closed = false
  11. this.contents = []
  12. }
  13. async write(blob) {
  14. this.contents.push(blob)
  15. return this
  16. }
  17. async close() {
  18. this.closed = true
  19. return this
  20. }
  21. toJSON() {
  22. return {
  23. contents: Buffer.concat(this.contents).toString(),
  24. closed: this.closed
  25. }
  26. }
  27. }
  28. function hash(blob) {
  29. const hash = crypto.createHash('sha256')
  30. hash.update(blob)
  31. return hash.digest('hex')
  32. }
  33. describe('ContentCacheManager', function () {
  34. let contentDir, pdfPath
  35. let ContentCacheManager, fs, files, Settings
  36. function load() {
  37. ContentCacheManager = SandboxedModule.require(MODULE_PATH, {
  38. requires: {
  39. fs,
  40. 'settings-sharelatex': Settings
  41. }
  42. })
  43. }
  44. let contentRanges, newContentRanges
  45. function run(filePath, done) {
  46. ContentCacheManager.update(contentDir, filePath, (err, ranges) => {
  47. if (err) return done(err)
  48. ;[contentRanges, newContentRanges] = ranges
  49. done()
  50. })
  51. }
  52. beforeEach(function () {
  53. contentDir =
  54. '/app/output/602cee6f6460fca0ba7921e6/content/1797a7f48f9-5abc1998509dea1f'
  55. pdfPath =
  56. '/app/output/602cee6f6460fca0ba7921e6/generated-files/1797a7f48ea-8ac6805139f43351/output.pdf'
  57. Settings = {
  58. pdfCachingMinChunkSize: 1024,
  59. enablePdfCachingDark: false
  60. }
  61. files = {}
  62. fs = {
  63. createReadStream: sinon.stub().returns(Readable.from([])),
  64. promises: {
  65. async open(name) {
  66. files[name] = new FakeFile()
  67. return files[name]
  68. },
  69. async stat(name) {
  70. if (!files[name]) {
  71. throw new Error()
  72. }
  73. },
  74. rename: sinon.stub().resolves(),
  75. unlink: sinon.stub().resolves()
  76. }
  77. }
  78. })
  79. describe('with a small minChunkSize', function () {
  80. beforeEach(function () {
  81. Settings.pdfCachingMinChunkSize = 1
  82. load()
  83. })
  84. describe('when the ranges are split across chunks', function () {
  85. const RANGE_1 = 'stream123endstream'
  86. const RANGE_2 = 'stream(|)endstream'
  87. const RANGE_3 = 'stream!$%endstream'
  88. beforeEach(function (done) {
  89. fs.createReadStream
  90. .withArgs(pdfPath)
  91. .returns(
  92. Readable.from([
  93. Buffer.from('abcstr'),
  94. Buffer.from('eam123endstreamABC'),
  95. Buffer.from('str'),
  96. Buffer.from('eam(|'),
  97. Buffer.from(')end'),
  98. Buffer.from('stream-_~stream!$%endstream')
  99. ])
  100. )
  101. run(pdfPath, done)
  102. })
  103. it('should produce three ranges', function () {
  104. expect(contentRanges).to.have.length(3)
  105. })
  106. it('should find the correct offsets', function () {
  107. expect(contentRanges).to.deep.equal([
  108. {
  109. start: 3,
  110. end: 21,
  111. hash: hash(RANGE_1)
  112. },
  113. {
  114. start: 24,
  115. end: 42,
  116. hash: hash(RANGE_2)
  117. },
  118. {
  119. start: 45,
  120. end: 63,
  121. hash: hash(RANGE_3)
  122. }
  123. ])
  124. })
  125. it('should store the contents', function () {
  126. expect(JSON.parse(JSON.stringify(files))).to.deep.equal({
  127. [Path.join(contentDir, hash(RANGE_1))]: {
  128. contents: RANGE_1,
  129. closed: true
  130. },
  131. [Path.join(contentDir, hash(RANGE_2))]: {
  132. contents: RANGE_2,
  133. closed: true
  134. },
  135. [Path.join(contentDir, hash(RANGE_3))]: {
  136. contents: RANGE_3,
  137. closed: true
  138. }
  139. })
  140. })
  141. it('should mark all ranges as new', function () {
  142. expect(contentRanges).to.deep.equal(newContentRanges)
  143. })
  144. })
  145. })
  146. })