ContentCacheManagerTests.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. const fs = require('fs')
  2. const Path = require('path')
  3. const { expect } = require('chai')
  4. const MODULE_PATH = '../../../app/js/ContentCacheManager'
  5. describe('ContentCacheManager', function () {
  6. let contentDir, pdfPath
  7. let ContentCacheManager, files, Settings
  8. before(function () {
  9. Settings = require('@overleaf/settings')
  10. ContentCacheManager = require(MODULE_PATH)
  11. })
  12. let contentRanges, newContentRanges, reclaimed
  13. async function run(filePath, size) {
  14. const result = await ContentCacheManager.promises.update(
  15. contentDir,
  16. filePath,
  17. size
  18. )
  19. let newlyReclaimed
  20. ;[contentRanges, newContentRanges, newlyReclaimed] = result
  21. reclaimed += newlyReclaimed
  22. const fileNames = await fs.promises.readdir(contentDir)
  23. files = {}
  24. for (const fileName of fileNames) {
  25. const path = Path.join(contentDir, fileName)
  26. files[path] = await fs.promises.readFile(path)
  27. }
  28. }
  29. before(function () {
  30. contentDir =
  31. '/overleaf/services/clsi/output/602cee6f6460fca0ba7921e6/content/1797a7f48f9-5abc1998509dea1f'
  32. pdfPath =
  33. '/overleaf/services/clsi/output/602cee6f6460fca0ba7921e6/generated-files/1797a7f48ea-8ac6805139f43351/output.pdf'
  34. reclaimed = 0
  35. Settings.pdfCachingMinChunkSize = 1024
  36. })
  37. before(async function () {
  38. await fs.promises.rmdir(contentDir, { recursive: true })
  39. await fs.promises.mkdir(contentDir, { recursive: true })
  40. await fs.promises.mkdir(Path.dirname(pdfPath), { recursive: true })
  41. })
  42. describe('minimal', function () {
  43. const PATH_MINIMAL = 'test/acceptance/fixtures/minimal.pdf'
  44. const OBJECT_ID_1 = '9 0 '
  45. const HASH_LARGE =
  46. 'd7cfc73ad2fba4578a437517923e3714927bbf35e63ea88bd93c7a8076cf1fcd'
  47. const OBJECT_ID_2 = '10 0 '
  48. const HASH_SMALL =
  49. '896749b8343851b0dc385f71616916a7ba0434fcfb56d1fc7e27cd139eaa2f71'
  50. function getChunkPath(hash) {
  51. return Path.join('test/unit/js/snapshots/minimalCompile/chunks', hash)
  52. }
  53. let MINIMAL_SIZE, RANGE_1, RANGE_2, h1, h2, START_1, START_2, END_1, END_2
  54. before(async function () {
  55. await fs.promises.copyFile(PATH_MINIMAL, pdfPath)
  56. const MINIMAL = await fs.promises.readFile(PATH_MINIMAL)
  57. MINIMAL_SIZE = (await fs.promises.stat(PATH_MINIMAL)).size
  58. RANGE_1 = await fs.promises.readFile(getChunkPath(HASH_LARGE))
  59. RANGE_2 = await fs.promises.readFile(getChunkPath(HASH_SMALL))
  60. h1 = HASH_LARGE
  61. h2 = HASH_SMALL
  62. START_1 = MINIMAL.indexOf(RANGE_1)
  63. END_1 = START_1 + RANGE_1.byteLength
  64. START_2 = MINIMAL.indexOf(RANGE_2)
  65. END_2 = START_2 + RANGE_2.byteLength
  66. })
  67. async function runWithMinimal() {
  68. await run(pdfPath, MINIMAL_SIZE)
  69. }
  70. describe('with two ranges qualifying', function () {
  71. before(function () {
  72. Settings.pdfCachingMinChunkSize = 500
  73. })
  74. before(async function () {
  75. await runWithMinimal()
  76. })
  77. it('should produce two ranges', function () {
  78. expect(contentRanges).to.have.length(2)
  79. })
  80. it('should find the correct offsets', function () {
  81. expect(contentRanges).to.deep.equal([
  82. {
  83. objectId: OBJECT_ID_1,
  84. start: START_1,
  85. end: END_1,
  86. hash: h1,
  87. },
  88. {
  89. objectId: OBJECT_ID_2,
  90. start: START_2,
  91. end: END_2,
  92. hash: h2,
  93. },
  94. ])
  95. })
  96. it('should store the contents', function () {
  97. expect(files).to.deep.equal({
  98. [Path.join(contentDir, h1)]: RANGE_1,
  99. [Path.join(contentDir, h2)]: RANGE_2,
  100. [Path.join(contentDir, '.state.v0.json')]: Buffer.from(
  101. JSON.stringify({
  102. hashAge: [
  103. [h1, 0],
  104. [h2, 0],
  105. ],
  106. hashSize: [
  107. [h1, RANGE_1.byteLength],
  108. [h2, RANGE_2.byteLength],
  109. ],
  110. })
  111. ),
  112. })
  113. })
  114. it('should mark all ranges as new', function () {
  115. expect(contentRanges).to.deep.equal(newContentRanges)
  116. })
  117. describe('when re-running with one range too small', function () {
  118. before(function () {
  119. Settings.pdfCachingMinChunkSize = 1024
  120. })
  121. before(async function () {
  122. await runWithMinimal()
  123. })
  124. it('should produce one range', function () {
  125. expect(contentRanges).to.have.length(1)
  126. })
  127. it('should find the correct offsets', function () {
  128. expect(contentRanges).to.deep.equal([
  129. {
  130. objectId: OBJECT_ID_1,
  131. start: START_1,
  132. end: END_1,
  133. hash: h1,
  134. },
  135. ])
  136. })
  137. it('should update the age of the 2nd range', function () {
  138. expect(files).to.deep.equal({
  139. [Path.join(contentDir, h1)]: RANGE_1,
  140. [Path.join(contentDir, h2)]: RANGE_2,
  141. [Path.join(contentDir, '.state.v0.json')]: Buffer.from(
  142. JSON.stringify({
  143. hashAge: [
  144. [h1, 0],
  145. [h2, 1],
  146. ],
  147. hashSize: [
  148. [h1, RANGE_1.byteLength],
  149. [h2, RANGE_2.byteLength],
  150. ],
  151. })
  152. ),
  153. })
  154. })
  155. it('should find no new ranges', function () {
  156. expect(newContentRanges).to.deep.equal([])
  157. })
  158. describe('when re-running 5 more times', function () {
  159. for (let i = 0; i < 5; i++) {
  160. before(async function () {
  161. await runWithMinimal()
  162. })
  163. }
  164. it('should still produce one range', function () {
  165. expect(contentRanges).to.have.length(1)
  166. })
  167. it('should still find the correct offsets', function () {
  168. expect(contentRanges).to.deep.equal([
  169. {
  170. objectId: OBJECT_ID_1,
  171. start: START_1,
  172. end: END_1,
  173. hash: h1,
  174. },
  175. ])
  176. })
  177. it('should delete the 2nd range', function () {
  178. expect(files).to.deep.equal({
  179. [Path.join(contentDir, h1)]: RANGE_1,
  180. [Path.join(contentDir, '.state.v0.json')]: Buffer.from(
  181. JSON.stringify({
  182. hashAge: [[h1, 0]],
  183. hashSize: [[h1, RANGE_1.byteLength]],
  184. })
  185. ),
  186. })
  187. })
  188. it('should find no new ranges', function () {
  189. expect(newContentRanges).to.deep.equal([])
  190. })
  191. it('should yield the reclaimed space', function () {
  192. expect(reclaimed).to.equal(RANGE_2.byteLength)
  193. })
  194. })
  195. })
  196. })
  197. })
  198. })