SimpleLatexFileTests.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. import Client from './helpers/Client.js'
  2. import { fetchNothing, fetchString } from '@overleaf/fetch-utils'
  3. import ClsiApp from './helpers/ClsiApp.js'
  4. import { expect } from 'chai'
  5. import Settings from '@overleaf/settings'
  6. describe('Simple LaTeX file', function () {
  7. const content = `\
  8. \\documentclass{article}
  9. \\begin{document}
  10. Hello world
  11. \\end{document}\
  12. `
  13. const scenarios = [
  14. {
  15. description: 'simple file',
  16. request: {
  17. resources: [{ path: 'main.tex', content }],
  18. options: {
  19. compileGroup: 'simple-latex-file',
  20. },
  21. },
  22. },
  23. {
  24. description: 'clsi-perf request',
  25. request: {
  26. resources: [{ path: 'main.tex', content }],
  27. options: {
  28. enablePdfCaching: false,
  29. metricsPath: 'clsi-perf',
  30. metricsMethod: 'memoir-manual',
  31. compileGroup: 'clsi-perf', // only used by tests, not by the service
  32. },
  33. },
  34. },
  35. ]
  36. for (const scenario of scenarios) {
  37. describe(scenario.description, function () {
  38. before(async function () {
  39. this.project_id = Client.randomId()
  40. this.request = scenario.request
  41. await ClsiApp.ensureRunning()
  42. try {
  43. this.body = await Client.compile(this.project_id, this.request)
  44. } catch (error) {
  45. this.error = error
  46. }
  47. })
  48. it('should return the PDF', function () {
  49. const pdf = Client.getOutputFile(this.body, 'pdf')
  50. pdf.type.should.equal('pdf')
  51. })
  52. it('should return the log', function () {
  53. const log = Client.getOutputFile(this.body, 'log')
  54. log.type.should.equal('log')
  55. })
  56. it('should provide the pdf for download', async function () {
  57. const pdf = Client.getOutputFile(this.body, 'pdf')
  58. const response = await fetchNothing(pdf.url)
  59. response.status.should.equal(200)
  60. })
  61. it('should provide the log for download', async function () {
  62. const log = Client.getOutputFile(this.body, 'pdf')
  63. const response = await fetchNothing(log.url)
  64. response.status.should.equal(200)
  65. })
  66. if (scenario.description === 'clsi-perf request') {
  67. it('should only emit e2e compile time metric', async function () {
  68. const metrics = await fetchString(`${Settings.apis.clsi.url}/metrics`)
  69. const byPath = `path="${scenario.request.options.metricsPath}"`
  70. const byMethod = `method="${scenario.request.options.metricsMethod}"`
  71. const byVariant = `variant="${scenario.request.options.metricsMethod}"`
  72. const byGroup = `group="${scenario.request.options.compileGroup}"`
  73. expect(metrics).to.not.include(byPath)
  74. expect(metrics).to.not.include(byMethod)
  75. expect(metrics).to.not.include(byGroup)
  76. expect(metrics).to.include(byVariant)
  77. expect(metrics.match(new RegExp(byVariant, 'g'))).to.have.lengthOf(1)
  78. })
  79. } else {
  80. it('should shard metrics by compileGroup', async function () {
  81. const metrics = await fetchString(`${Settings.apis.clsi.url}/metrics`)
  82. const byGroup = `group="${scenario.request.options.compileGroup}"`
  83. expect(metrics).to.include(byGroup)
  84. expect(metrics.match(new RegExp(byGroup, 'g'))).to.have.lengthOf(134)
  85. })
  86. }
  87. it('should return only the expected keys for stats and timings', function () {
  88. const { stats, timings } = this.body.compile
  89. let pdfCachingStats = []
  90. let pdfCachingTimings = []
  91. if (scenario.request.options.enablePdfCaching !== false) {
  92. pdfCachingStats = [
  93. 'pdf-caching-total-ranges-size',
  94. 'pdf-caching-reclaimed-space',
  95. 'pdf-caching-new-ranges-size',
  96. 'pdf-caching-n-ranges',
  97. 'pdf-caching-n-new-ranges',
  98. ]
  99. pdfCachingTimings = [
  100. 'compute-pdf-caching',
  101. 'pdf-caching-overhead-delete-stale-hashes',
  102. ]
  103. }
  104. // Note: chai's all.keys assertion rejects extra keys
  105. stats.should.have.all.keys(
  106. 'isInitialCompile',
  107. 'latexmk-errors',
  108. 'latex-runs',
  109. 'latex-runs-with-errors',
  110. 'latex-runs-1',
  111. 'latex-runs-with-errors-1',
  112. 'pdf-size',
  113. ...pdfCachingStats
  114. )
  115. timings.should.have.all.keys(
  116. 'sync',
  117. 'compile',
  118. 'output',
  119. 'compileE2E',
  120. ...pdfCachingTimings
  121. )
  122. })
  123. })
  124. }
  125. describe('document with shell commands', function () {
  126. before(async function () {
  127. this.project_id = Client.randomId()
  128. this.request = {
  129. resources: [
  130. {
  131. path: 'main.tex',
  132. content: `\
  133. \\documentclass{article}
  134. \\begin{document}
  135. Testing system calls:
  136. \\immediate\\write18{/bin/date > date.txt}
  137. The current date from system is: \\input{date.txt}
  138. The current date from popen is: \\input{"|date"}
  139. \\end{document}\
  140. `,
  141. },
  142. ],
  143. }
  144. await ClsiApp.ensureRunning()
  145. })
  146. it('should compile successfully', async function () {
  147. const body = await Client.compile(this.project_id, this.request)
  148. expect(body).to.exist
  149. expect(body.compile?.status, 'compile status').to.equal('success')
  150. })
  151. it('should return the PDF', async function () {
  152. const body = await Client.compile(this.project_id, this.request)
  153. const pdf = Client.getOutputFile(body, 'pdf')
  154. expect(pdf, 'pdf file not produced').to.exist
  155. expect(pdf.type, 'invalid pdf file').to.equal('pdf')
  156. })
  157. })
  158. })