ServerCEScriptsTests.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698
  1. const { execSync } = require('child_process')
  2. const fs = require('fs')
  3. const Settings = require('@overleaf/settings')
  4. const { expect } = require('chai')
  5. const { db } = require('../../../../../app/src/infrastructure/mongodb')
  6. const User = require('../../../../../test/acceptance/src/helpers/User').promises
  7. /**
  8. * @param {string} cmd
  9. * @return {string}
  10. */
  11. function run(cmd) {
  12. // https://nodejs.org/docs/latest-v12.x/api/child_process.html#child_process_child_process_execsync_command_options
  13. // > stderr by default will be output to the parent process' stderr
  14. // > unless stdio is specified.
  15. // https://nodejs.org/docs/latest-v12.x/api/child_process.html#child_process_options_stdio
  16. // Pipe stdin from /dev/null, store stdout, pipe stderr to /dev/null.
  17. return execSync(cmd, {
  18. stdio: ['ignore', 'pipe', 'pipe'],
  19. }).toString()
  20. }
  21. function runAndExpectError(cmd, errorMessages) {
  22. try {
  23. run(cmd)
  24. } catch (error) {
  25. expect(error.status).to.equal(1)
  26. if (errorMessages) {
  27. errorMessages.forEach(errorMessage =>
  28. expect(error.stderr.toString()).to.include(errorMessage)
  29. )
  30. }
  31. return
  32. }
  33. expect.fail('command should have failed')
  34. }
  35. async function getUser(email) {
  36. return db.users.findOne({ email }, { projection: { _id: 0, isAdmin: 1 } })
  37. }
  38. describe('ServerCEScripts', function () {
  39. describe('check-mongodb', function () {
  40. it('should exit with code 0 on success', function () {
  41. run('node modules/server-ce-scripts/scripts/check-mongodb')
  42. })
  43. it('should exit with code 1 on error', function () {
  44. try {
  45. run(
  46. 'MONGO_SERVER_SELECTION_TIMEOUT=1' +
  47. 'MONGO_CONNECTION_STRING=mongodb://127.0.0.1:4242 ' +
  48. 'node modules/server-ce-scripts/scripts/check-mongodb'
  49. )
  50. } catch (e) {
  51. expect(e.status).to.equal(1)
  52. return
  53. }
  54. expect.fail('command should have failed')
  55. })
  56. })
  57. describe('check-redis', function () {
  58. it('should exit with code 0 on success', function () {
  59. run('node modules/server-ce-scripts/scripts/check-redis')
  60. })
  61. it('should exit with code 1 on error', function () {
  62. try {
  63. run('REDIS_PORT=42 node modules/server-ce-scripts/scripts/check-redis')
  64. } catch (e) {
  65. expect(e.status).to.equal(1)
  66. return
  67. }
  68. expect.fail('command should have failed')
  69. })
  70. })
  71. describe('create-user', function () {
  72. it('should exit with code 0 on success', function () {
  73. const out = run(
  74. 'node modules/server-ce-scripts/scripts/create-user --email=foo@bar.com'
  75. )
  76. expect(out).to.include('/user/activate?token=')
  77. })
  78. it('should create a regular user by default', async function () {
  79. run(
  80. 'node modules/server-ce-scripts/scripts/create-user --email=foo@bar.com'
  81. )
  82. expect(await getUser('foo@bar.com')).to.deep.equal({ isAdmin: false })
  83. })
  84. it('should create an admin user with --admin flag', async function () {
  85. run(
  86. 'node modules/server-ce-scripts/scripts/create-user --admin --email=foo@bar.com'
  87. )
  88. expect(await getUser('foo@bar.com')).to.deep.equal({ isAdmin: true })
  89. })
  90. it('should exit with code 1 on missing email', function () {
  91. try {
  92. run('node modules/server-ce-scripts/scripts/create-user')
  93. } catch (e) {
  94. expect(e.status).to.equal(1)
  95. return
  96. }
  97. expect.fail('command should have failed')
  98. })
  99. })
  100. describe('delete-user', function () {
  101. let user
  102. beforeEach(async function () {
  103. user = new User()
  104. await user.login()
  105. })
  106. it('should log missing user', function () {
  107. const email = 'does-not-exist@example.com'
  108. const out = run(
  109. 'node modules/server-ce-scripts/scripts/delete-user --email=' + email
  110. )
  111. expect(out).to.include('not in database, potentially already deleted')
  112. })
  113. it('should exit with code 0 on success', function () {
  114. const email = user.email
  115. run('node modules/server-ce-scripts/scripts/delete-user --email=' + email)
  116. })
  117. it('should have deleted the user on success', async function () {
  118. const email = user.email
  119. run('node modules/server-ce-scripts/scripts/delete-user --email=' + email)
  120. const dbEntry = await user.get()
  121. expect(dbEntry).to.not.exist
  122. const softDeletedEntry = await db.deletedUsers.findOne({
  123. 'user.email': email,
  124. })
  125. expect(softDeletedEntry).to.exist
  126. expect(softDeletedEntry.deleterData.deleterIpAddress).to.equal('0.0.0.0')
  127. })
  128. it('should exit with code 1 on missing email', function () {
  129. try {
  130. run('node modules/server-ce-scripts/scripts/delete-user')
  131. } catch (e) {
  132. expect(e.status).to.equal(1)
  133. return
  134. }
  135. expect.fail('command should have failed')
  136. })
  137. })
  138. describe('migrate-user-emails', function () {
  139. let usersToMigrate
  140. let otherUsers
  141. let csv
  142. let csvfail
  143. beforeEach(async function () {
  144. // set up some users to migrate and others to leave alone
  145. usersToMigrate = []
  146. otherUsers = []
  147. for (let i = 0; i < 2; i++) {
  148. const user = new User()
  149. await user.login()
  150. usersToMigrate.push(user)
  151. }
  152. for (let i = 0; i < 2; i++) {
  153. const user = new User()
  154. await user.login()
  155. otherUsers.push(user)
  156. }
  157. // write the migration csv to a temporary file
  158. const id = usersToMigrate[0]._id
  159. csv = `/tmp/migration-${id}.csv`
  160. const rows = []
  161. for (const user of usersToMigrate) {
  162. rows.push(`${user.email},new-${user.email}`)
  163. }
  164. fs.writeFileSync(csv, rows.join('\n'))
  165. // also write a csv with a user that doesn't exist
  166. csvfail = `/tmp/migration-fail-${id}.csv`
  167. fs.writeFileSync(
  168. csvfail,
  169. [
  170. 'nouser@example.com,nouser@other.example.com',
  171. ...rows,
  172. 'foo@example.com,bar@example.com',
  173. ].join('\n')
  174. )
  175. })
  176. afterEach(function () {
  177. // clean up the temporary files
  178. fs.unlinkSync(csv)
  179. fs.unlinkSync(csvfail)
  180. })
  181. it('should do a dry run by default', async function () {
  182. run(
  183. `node modules/server-ce-scripts/scripts/migrate-user-emails.js ${csv}`
  184. )
  185. for (const user of usersToMigrate) {
  186. const dbEntry = await user.get()
  187. expect(dbEntry.email).to.equal(user.email)
  188. }
  189. for (const user of otherUsers) {
  190. const dbEntry = await user.get()
  191. expect(dbEntry.email).to.equal(user.email)
  192. }
  193. })
  194. it('should exit with code 0 when successfully migrating user emails', function () {
  195. run(
  196. `node modules/server-ce-scripts/scripts/migrate-user-emails.js --commit ${csv}`
  197. )
  198. })
  199. it('should migrate the user emails with the --commit option', async function () {
  200. run(
  201. `node modules/server-ce-scripts/scripts/migrate-user-emails.js --commit ${csv}`
  202. )
  203. for (const user of usersToMigrate) {
  204. const dbEntry = await user.get()
  205. expect(dbEntry.email).to.equal(`new-${user.email}`)
  206. expect(dbEntry.emails).to.have.lengthOf(1)
  207. expect(dbEntry.emails[0].email).to.equal(`new-${user.email}`)
  208. expect(dbEntry.emails[0].reversedHostname).to.equal('moc.elpmaxe')
  209. expect(dbEntry.emails[0].createdAt).to.eql(user.emails[0].createdAt)
  210. }
  211. })
  212. it('should leave other user emails unchanged', async function () {
  213. run(
  214. `node modules/server-ce-scripts/scripts/migrate-user-emails.js --commit ${csv}`
  215. )
  216. for (const user of otherUsers) {
  217. const dbEntry = await user.get()
  218. expect(dbEntry.email).to.equal(user.email)
  219. }
  220. })
  221. it('should exit with code 1 when there are failures migrating user emails', function () {
  222. try {
  223. run(
  224. `node modules/server-ce-scripts/scripts/migrate-user-emails.js --commit ${csvfail}`
  225. )
  226. } catch (e) {
  227. expect(e.status).to.equal(1)
  228. return
  229. }
  230. expect.fail('command should have failed')
  231. })
  232. it('should migrate other users when there are failures with the --continue option', async function () {
  233. try {
  234. run(
  235. `node modules/server-ce-scripts/scripts/migrate-user-emails.js --commit ${csvfail}`
  236. )
  237. } catch (e) {
  238. expect(e.status).to.equal(1)
  239. run(
  240. `node modules/server-ce-scripts/scripts/migrate-user-emails.js --commit --continue ${csvfail}`
  241. )
  242. for (const user of usersToMigrate) {
  243. const dbEntry = await user.get()
  244. expect(dbEntry.email).to.equal(`new-${user.email}`)
  245. expect(dbEntry.emails).to.have.lengthOf(1)
  246. expect(dbEntry.emails[0].email).to.equal(`new-${user.email}`)
  247. expect(dbEntry.emails[0].reversedHostname).to.equal('moc.elpmaxe')
  248. expect(dbEntry.emails[0].createdAt).to.eql(user.emails[0].createdAt)
  249. }
  250. return
  251. }
  252. expect.fail('command should have failed')
  253. })
  254. })
  255. describe('rename-tag', function () {
  256. let user
  257. beforeEach(async function () {
  258. user = new User()
  259. await user.login()
  260. })
  261. async function createTag(name) {
  262. await user.doRequest('POST', { url: '/tag', json: { name } })
  263. }
  264. async function getTagNames() {
  265. const { body } = await user.doRequest('GET', { url: '/tag', json: true })
  266. return body.map(tag => tag.name)
  267. }
  268. it('should rename a tag', async function () {
  269. const oldName = 'before'
  270. const newName = 'after'
  271. await createTag(oldName)
  272. expect(await getTagNames()).to.deep.equal([oldName])
  273. run(
  274. `node modules/server-ce-scripts/scripts/rename-tag --user-id=${user.id} --old-name=${oldName} --new-name=${newName}`
  275. )
  276. expect(await getTagNames()).to.deep.equal([newName])
  277. })
  278. })
  279. describe('change-compile-timeout', function () {
  280. let userA, userB
  281. beforeEach('login', async function () {
  282. userA = new User()
  283. await userA.login()
  284. userB = new User()
  285. await userB.login()
  286. })
  287. async function getCompileTimeout(user) {
  288. const { compileTimeout } = await user.getFeatures()
  289. return compileTimeout
  290. }
  291. let userATimeout, userBTimeout
  292. beforeEach('fetch current state', async function () {
  293. userATimeout = await getCompileTimeout(userA)
  294. userBTimeout = await getCompileTimeout(userB)
  295. })
  296. describe('happy path', function () {
  297. let newUserATimeout
  298. beforeEach('run script on user a', function () {
  299. newUserATimeout = userATimeout - 1
  300. run(
  301. `node modules/server-ce-scripts/scripts/change-compile-timeout --user-id=${userA.id} --compile-timeout=${newUserATimeout}`
  302. )
  303. })
  304. it('should change the timeout for user a', async function () {
  305. const actual = await getCompileTimeout(userA)
  306. expect(actual).to.not.equal(userATimeout)
  307. expect(actual).to.equal(newUserATimeout)
  308. })
  309. it('should leave the timeout for user b as is', async function () {
  310. expect(await getCompileTimeout(userB)).to.equal(userBTimeout)
  311. })
  312. })
  313. describe('bad options', function () {
  314. it('should reject zero timeout', async function () {
  315. try {
  316. run(
  317. `node modules/server-ce-scripts/scripts/change-compile-timeout --user-id=${userA.id} --compile-timeout=0`
  318. )
  319. expect.fail('should error out')
  320. } catch (err) {
  321. expect(err.stderr.toString()).to.include('positive number of seconds')
  322. }
  323. expect(await getCompileTimeout(userA)).to.equal(userATimeout)
  324. expect(await getCompileTimeout(userB)).to.equal(userBTimeout)
  325. })
  326. it('should reject a 20min timeout', async function () {
  327. try {
  328. run(
  329. `node modules/server-ce-scripts/scripts/change-compile-timeout --user-id=${userA.id} --compile-timeout=1200`
  330. )
  331. expect.fail('should error out')
  332. } catch (err) {
  333. expect(err.stderr.toString()).to.include('below 10 minutes')
  334. }
  335. expect(await getCompileTimeout(userA)).to.equal(userATimeout)
  336. expect(await getCompileTimeout(userB)).to.equal(userBTimeout)
  337. })
  338. })
  339. })
  340. describe('upgrade-user-features', function () {
  341. let userLatest, userSP1, userCustomTimeoutLower, userCustomTimeoutHigher
  342. beforeEach('create users', async function () {
  343. userLatest = new User()
  344. userSP1 = new User()
  345. userCustomTimeoutLower = new User()
  346. userCustomTimeoutHigher = new User()
  347. await Promise.all([
  348. userLatest.ensureUserExists(),
  349. userSP1.ensureUserExists(),
  350. userCustomTimeoutLower.ensureUserExists(),
  351. userCustomTimeoutHigher.ensureUserExists(),
  352. ])
  353. })
  354. const serverPro1Features = {
  355. collaborators: -1,
  356. dropbox: true,
  357. versioning: true,
  358. compileTimeout: 180,
  359. compileGroup: 'standard',
  360. references: true,
  361. trackChanges: true,
  362. }
  363. beforeEach('downgrade userSP1', async function () {
  364. await userSP1.mongoUpdate({ $set: { features: serverPro1Features } })
  365. })
  366. beforeEach('downgrade userCustomTimeoutLower', async function () {
  367. run(
  368. `node modules/server-ce-scripts/scripts/change-compile-timeout --user-id=${userCustomTimeoutLower.id} --compile-timeout=42`
  369. )
  370. })
  371. beforeEach('upgrade userCustomTimeoutHigher', async function () {
  372. run(
  373. `node modules/server-ce-scripts/scripts/change-compile-timeout --user-id=${userCustomTimeoutHigher.id} --compile-timeout=360`
  374. )
  375. })
  376. async function getFeatures() {
  377. return [
  378. await userLatest.getFeatures(),
  379. await userSP1.getFeatures(),
  380. await userCustomTimeoutLower.getFeatures(),
  381. await userCustomTimeoutHigher.getFeatures(),
  382. ]
  383. }
  384. let initialFeatures
  385. beforeEach('collect initial features', async function () {
  386. initialFeatures = await getFeatures()
  387. })
  388. it('should have prepared the right features', async function () {
  389. expect(initialFeatures).to.deep.equal([
  390. Settings.defaultFeatures,
  391. serverPro1Features,
  392. Object.assign({}, Settings.defaultFeatures, {
  393. compileTimeout: 42,
  394. }),
  395. Object.assign({}, Settings.defaultFeatures, {
  396. compileTimeout: 360,
  397. }),
  398. ])
  399. })
  400. describe('dry-run', function () {
  401. let output
  402. beforeEach('run script', function () {
  403. output = run(
  404. `node modules/server-ce-scripts/scripts/upgrade-user-features`
  405. )
  406. })
  407. it('should update SP1 features', function () {
  408. expect(output).to.include(userSP1.id)
  409. })
  410. it('should update lowerTimeout features', function () {
  411. expect(output).to.include(userCustomTimeoutLower.id)
  412. })
  413. it('should not update latest features', function () {
  414. expect(output).to.not.include(userLatest.id)
  415. })
  416. it('should not update higherTimeout features', function () {
  417. expect(output).to.not.include(userCustomTimeoutHigher.id)
  418. })
  419. it('should not change any features in the db', async function () {
  420. expect(await getFeatures()).to.deep.equal(initialFeatures)
  421. })
  422. })
  423. describe('live run', function () {
  424. let output
  425. beforeEach('run script', function () {
  426. output = run(
  427. `node modules/server-ce-scripts/scripts/upgrade-user-features --dry-run=false`
  428. )
  429. })
  430. it('should update SP1 features', function () {
  431. expect(output).to.include(userSP1.id)
  432. })
  433. it('should update lowerTimeout features', function () {
  434. expect(output).to.include(userCustomTimeoutLower.id)
  435. })
  436. it('should not update latest features', function () {
  437. expect(output).to.not.include(userLatest.id)
  438. })
  439. it('should not update higherTimeout features', function () {
  440. expect(output).to.not.include(userCustomTimeoutHigher.id)
  441. })
  442. it('should update features in the db', async function () {
  443. expect(await getFeatures()).to.deep.equal([
  444. Settings.defaultFeatures,
  445. Settings.defaultFeatures,
  446. Settings.defaultFeatures,
  447. Object.assign({}, Settings.defaultFeatures, {
  448. compileTimeout: 360,
  449. }),
  450. ])
  451. })
  452. })
  453. })
  454. describe('check-texlive-images', function () {
  455. const TEST_TL_IMAGE = 'sharelatex/texlive:2023'
  456. const TEST_TL_IMAGE_LIST =
  457. 'sharelatex/texlive:2021,sharelatex/texlive:2022,sharelatex/texlive:2023'
  458. let output
  459. function buildCheckTexLiveCmd({
  460. SANDBOXED_COMPILES,
  461. TEX_LIVE_DOCKER_IMAGE,
  462. ALL_TEX_LIVE_DOCKER_IMAGES,
  463. OVERLEAF_IS_SERVER_PRO = true,
  464. }) {
  465. let cmd = `SANDBOXED_COMPILES=${SANDBOXED_COMPILES ? 'true' : 'false'}`
  466. if (TEX_LIVE_DOCKER_IMAGE) {
  467. cmd += ` TEX_LIVE_DOCKER_IMAGE='${TEX_LIVE_DOCKER_IMAGE}'`
  468. }
  469. if (ALL_TEX_LIVE_DOCKER_IMAGES) {
  470. cmd += ` ALL_TEX_LIVE_DOCKER_IMAGES='${ALL_TEX_LIVE_DOCKER_IMAGES}'`
  471. }
  472. if (OVERLEAF_IS_SERVER_PRO === true) {
  473. cmd += ` OVERLEAF_IS_SERVER_PRO=${OVERLEAF_IS_SERVER_PRO}`
  474. }
  475. return (
  476. cmd + ' node modules/server-ce-scripts/scripts/check-texlive-images'
  477. )
  478. }
  479. beforeEach(async function () {
  480. const user = new User()
  481. await user.ensureUserExists()
  482. await user.login()
  483. await user.createProject('test-project')
  484. })
  485. describe('when running in CE', function () {
  486. beforeEach('run script', function () {
  487. output = run(buildCheckTexLiveCmd({ OVERLEAF_IS_SERVER_PRO: false }))
  488. })
  489. it('should skip checks', function () {
  490. expect(output).to.include(
  491. 'Running Overleaf Community Edition, skipping TexLive checks'
  492. )
  493. })
  494. })
  495. describe('when sandboxed compiles are disabled', function () {
  496. beforeEach('run script', function () {
  497. output = run(buildCheckTexLiveCmd({ SANDBOXED_COMPILES: false }))
  498. })
  499. it('should skip checks', function () {
  500. expect(output).to.include(
  501. 'Sandboxed compiles disabled, skipping TexLive checks'
  502. )
  503. })
  504. })
  505. describe('when texlive configuration is incorrect', function () {
  506. it('should fail when TEX_LIVE_DOCKER_IMAGE is not set', function () {
  507. runAndExpectError(
  508. buildCheckTexLiveCmd({
  509. SANDBOXED_COMPILES: true,
  510. ALL_TEX_LIVE_DOCKER_IMAGES: TEST_TL_IMAGE_LIST,
  511. }),
  512. [
  513. 'Sandboxed compiles require TEX_LIVE_DOCKER_IMAGE and ALL_TEX_LIVE_DOCKER_IMAGES being set',
  514. ]
  515. )
  516. })
  517. it('should fail when ALL_TEX_LIVE_DOCKER_IMAGES is not set', function () {
  518. runAndExpectError(
  519. buildCheckTexLiveCmd({
  520. SANDBOXED_COMPILES: true,
  521. TEX_LIVE_DOCKER_IMAGE: TEST_TL_IMAGE,
  522. }),
  523. [
  524. 'Sandboxed compiles require TEX_LIVE_DOCKER_IMAGE and ALL_TEX_LIVE_DOCKER_IMAGES being set',
  525. ]
  526. )
  527. })
  528. it('should fail when TEX_LIVE_DOCKER_IMAGE is not defined in ALL_TEX_LIVE_DOCKER_IMAGES', function () {
  529. runAndExpectError(
  530. buildCheckTexLiveCmd({
  531. SANDBOXED_COMPILES: true,
  532. TEX_LIVE_DOCKER_IMAGE: 'tl-1',
  533. ALL_TEX_LIVE_DOCKER_IMAGES: 'tl-2,tl-3',
  534. }),
  535. [
  536. 'TEX_LIVE_DOCKER_IMAGE must be included in ALL_TEX_LIVE_DOCKER_IMAGES',
  537. ]
  538. )
  539. })
  540. })
  541. describe(`when projects don't have 'imageName' set`, function () {
  542. beforeEach(async function () {
  543. await db.projects.updateMany({}, { $unset: { imageName: 1 } })
  544. })
  545. it('should fail and suggest running backfilling scripts', function () {
  546. runAndExpectError(
  547. buildCheckTexLiveCmd({
  548. SANDBOXED_COMPILES: true,
  549. TEX_LIVE_DOCKER_IMAGE: TEST_TL_IMAGE,
  550. ALL_TEX_LIVE_DOCKER_IMAGES: TEST_TL_IMAGE_LIST,
  551. }),
  552. [
  553. `'project.imageName' is not set for some projects`,
  554. `Set SKIP_TEX_LIVE_CHECK=true in config/variables.env, restart the instance and run 'bin/run-script scripts/backfill_project_image_name.js' to initialise TexLive image in existing projects`,
  555. ]
  556. )
  557. })
  558. })
  559. describe(`when projects have a null 'imageName'`, function () {
  560. beforeEach(async function () {
  561. await db.projects.updateMany({}, { $set: { imageName: null } })
  562. })
  563. it('should fail and suggest running backfilling scripts', function () {
  564. runAndExpectError(
  565. buildCheckTexLiveCmd({
  566. SANDBOXED_COMPILES: true,
  567. TEX_LIVE_DOCKER_IMAGE: TEST_TL_IMAGE,
  568. ALL_TEX_LIVE_DOCKER_IMAGES: TEST_TL_IMAGE_LIST,
  569. }),
  570. [
  571. `'project.imageName' is not set for some projects`,
  572. `Set SKIP_TEX_LIVE_CHECK=true in config/variables.env, restart the instance and run 'bin/run-script scripts/backfill_project_image_name.js' to initialise TexLive image in existing projects`,
  573. ]
  574. )
  575. })
  576. })
  577. describe('when TexLive ALL_TEX_LIVE_DOCKER_IMAGES are upgraded and used images are no longer available', function () {
  578. it('should suggest running a fixing script', async function () {
  579. await db.projects.updateMany({}, { $set: { imageName: TEST_TL_IMAGE } })
  580. runAndExpectError(
  581. buildCheckTexLiveCmd({
  582. SANDBOXED_COMPILES: true,
  583. TEX_LIVE_DOCKER_IMAGE: 'tl-1',
  584. ALL_TEX_LIVE_DOCKER_IMAGES: 'tl-1,tl-2',
  585. }),
  586. [
  587. `Set SKIP_TEX_LIVE_CHECK=true in config/variables.env, restart the instance and run 'bin/run-script scripts/update_project_image_name.js <dangling_image> <new_image>' to update projects to a new image`,
  588. ]
  589. )
  590. })
  591. })
  592. describe('success scenarios', function () {
  593. beforeEach(async function () {
  594. await db.projects.updateMany({}, { $set: { imageName: TEST_TL_IMAGE } })
  595. })
  596. it('should succeed when there are no changes to the TexLive images', function () {
  597. const output = run(
  598. buildCheckTexLiveCmd({
  599. SANDBOXED_COMPILES: true,
  600. TEX_LIVE_DOCKER_IMAGE: TEST_TL_IMAGE,
  601. ALL_TEX_LIVE_DOCKER_IMAGES: TEST_TL_IMAGE_LIST,
  602. })
  603. )
  604. expect(output).to.include('Done.')
  605. })
  606. it('should succeed when there are valid changes to the TexLive images', function () {
  607. const output = run(
  608. buildCheckTexLiveCmd({
  609. SANDBOXED_COMPILES: true,
  610. TEX_LIVE_DOCKER_IMAGE: 'new-image',
  611. ALL_TEX_LIVE_DOCKER_IMAGES: TEST_TL_IMAGE_LIST + ',new-image',
  612. })
  613. )
  614. expect(output).to.include('Done.')
  615. })
  616. })
  617. })
  618. })