ServerCEScriptsTests.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. const Settings = require('@overleaf/settings')
  2. const { execSync } = require('child_process')
  3. const { expect } = require('chai')
  4. const { db } = require('../../../../../app/src/infrastructure/mongodb')
  5. const User = require('../../../../../test/acceptance/src/helpers/User').promises
  6. /**
  7. * @param {string} cmd
  8. * @return {string}
  9. */
  10. function run(cmd) {
  11. // https://nodejs.org/docs/latest-v12.x/api/child_process.html#child_process_child_process_execsync_command_options
  12. // > stderr by default will be output to the parent process' stderr
  13. // > unless stdio is specified.
  14. // https://nodejs.org/docs/latest-v12.x/api/child_process.html#child_process_options_stdio
  15. // Pipe stdin from /dev/null, store stdout, pipe stderr to /dev/null.
  16. return execSync(cmd, {
  17. stdio: ['ignore', 'pipe', 'pipe'],
  18. }).toString()
  19. }
  20. async function getUser(email) {
  21. return db.users.findOne({ email }, { projection: { _id: 0, isAdmin: 1 } })
  22. }
  23. describe('ServerCEScripts', function () {
  24. describe('check-mongodb', function () {
  25. it('should exit with code 0 on success', function () {
  26. run('node modules/server-ce-scripts/scripts/check-mongodb')
  27. })
  28. it('should exit with code 1 on error', function () {
  29. try {
  30. run(
  31. 'MONGO_SERVER_SELECTION_TIMEOUT=1' +
  32. 'MONGO_CONNECTION_STRING=mongodb://localhost:4242 ' +
  33. 'node modules/server-ce-scripts/scripts/check-mongodb'
  34. )
  35. } catch (e) {
  36. expect(e.status).to.equal(1)
  37. return
  38. }
  39. expect.fail('command should have failed')
  40. })
  41. })
  42. describe('check-redis', function () {
  43. it('should exit with code 0 on success', function () {
  44. run('node modules/server-ce-scripts/scripts/check-redis')
  45. })
  46. it('should exit with code 1 on error', function () {
  47. try {
  48. run('REDIS_PORT=42 node modules/server-ce-scripts/scripts/check-redis')
  49. } catch (e) {
  50. expect(e.status).to.equal(1)
  51. return
  52. }
  53. expect.fail('command should have failed')
  54. })
  55. })
  56. describe('create-user', function () {
  57. it('should exit with code 0 on success', function () {
  58. const out = run(
  59. 'node modules/server-ce-scripts/scripts/create-user --email=foo@bar.com'
  60. )
  61. expect(out).to.include('/user/activate?token=')
  62. })
  63. it('should create a regular user by default', async function () {
  64. run(
  65. 'node modules/server-ce-scripts/scripts/create-user --email=foo@bar.com'
  66. )
  67. expect(await getUser('foo@bar.com')).to.deep.equal({ isAdmin: false })
  68. })
  69. it('should create an admin user with --admin flag', async function () {
  70. run(
  71. 'node modules/server-ce-scripts/scripts/create-user --admin --email=foo@bar.com'
  72. )
  73. expect(await getUser('foo@bar.com')).to.deep.equal({ isAdmin: true })
  74. })
  75. it('should exit with code 1 on missing email', function () {
  76. try {
  77. run('node modules/server-ce-scripts/scripts/create-user')
  78. } catch (e) {
  79. expect(e.status).to.equal(1)
  80. return
  81. }
  82. expect.fail('command should have failed')
  83. })
  84. })
  85. describe('delete-user', function () {
  86. let user
  87. beforeEach(async function () {
  88. user = new User()
  89. await user.login()
  90. })
  91. it('should log missing user', function () {
  92. const email = 'does-not-exist@example.com'
  93. const out = run(
  94. 'node modules/server-ce-scripts/scripts/delete-user --email=' + email
  95. )
  96. expect(out).to.include('not in database, potentially already deleted')
  97. })
  98. it('should exit with code 0 on success', function () {
  99. const email = user.email
  100. run('node modules/server-ce-scripts/scripts/delete-user --email=' + email)
  101. })
  102. it('should have deleted the user on success', async function () {
  103. const email = user.email
  104. run('node modules/server-ce-scripts/scripts/delete-user --email=' + email)
  105. const dbEntry = await user.get()
  106. expect(dbEntry).to.not.exist
  107. })
  108. it('should exit with code 1 on missing email', function () {
  109. try {
  110. run('node modules/server-ce-scripts/scripts/delete-user')
  111. } catch (e) {
  112. expect(e.status).to.equal(1)
  113. return
  114. }
  115. expect.fail('command should have failed')
  116. })
  117. })
  118. describe('rename-tag', function () {
  119. let user
  120. beforeEach(async function () {
  121. user = new User()
  122. await user.login()
  123. })
  124. async function createTag(name) {
  125. await user.doRequest('POST', { url: '/tag', json: { name } })
  126. }
  127. async function getTagNames() {
  128. const { body } = await user.doRequest('GET', { url: '/tag', json: true })
  129. return body.map(tag => tag.name)
  130. }
  131. it('should rename a tag', async function () {
  132. const oldName = 'before'
  133. const newName = 'after'
  134. await createTag(oldName)
  135. expect(await getTagNames()).to.deep.equal([oldName])
  136. run(
  137. `node modules/server-ce-scripts/scripts/rename-tag --user-id=${user.id} --old-name=${oldName} --new-name=${newName}`
  138. )
  139. expect(await getTagNames()).to.deep.equal([newName])
  140. })
  141. })
  142. describe('change-compile-timeout', function () {
  143. let userA, userB
  144. beforeEach('login', async function () {
  145. userA = new User()
  146. await userA.login()
  147. userB = new User()
  148. await userB.login()
  149. })
  150. async function getCompileTimeout(user) {
  151. const { compileTimeout } = await user.getFeatures()
  152. return compileTimeout
  153. }
  154. let userATimeout, userBTimeout
  155. beforeEach('fetch current state', async function () {
  156. userATimeout = await getCompileTimeout(userA)
  157. userBTimeout = await getCompileTimeout(userB)
  158. })
  159. describe('happy path', function () {
  160. let newUserATimeout
  161. beforeEach('run script on user a', function () {
  162. newUserATimeout = userATimeout - 1
  163. run(
  164. `node modules/server-ce-scripts/scripts/change-compile-timeout --user-id=${userA.id} --compile-timeout=${newUserATimeout}`
  165. )
  166. })
  167. it('should change the timeout for user a', async function () {
  168. const actual = await getCompileTimeout(userA)
  169. expect(actual).to.not.equal(userATimeout)
  170. expect(actual).to.equal(newUserATimeout)
  171. })
  172. it('should leave the timeout for user b as is', async function () {
  173. expect(await getCompileTimeout(userB)).to.equal(userBTimeout)
  174. })
  175. })
  176. describe('bad options', function () {
  177. it('should reject zero timeout', async function () {
  178. try {
  179. run(
  180. `node modules/server-ce-scripts/scripts/change-compile-timeout --user-id=${userA.id} --compile-timeout=0`
  181. )
  182. expect.fail('should error out')
  183. } catch (err) {
  184. expect(err.stderr.toString()).to.include('positive number of seconds')
  185. }
  186. expect(await getCompileTimeout(userA)).to.equal(userATimeout)
  187. expect(await getCompileTimeout(userB)).to.equal(userBTimeout)
  188. })
  189. it('should reject a 20min timeout', async function () {
  190. try {
  191. run(
  192. `node modules/server-ce-scripts/scripts/change-compile-timeout --user-id=${userA.id} --compile-timeout=1200`
  193. )
  194. expect.fail('should error out')
  195. } catch (err) {
  196. expect(err.stderr.toString()).to.include('below 10 minutes')
  197. }
  198. expect(await getCompileTimeout(userA)).to.equal(userATimeout)
  199. expect(await getCompileTimeout(userB)).to.equal(userBTimeout)
  200. })
  201. })
  202. })
  203. describe('upgrade-user-features', function () {
  204. let userLatest, userSP1, userCustomTimeoutLower, userCustomTimeoutHigher
  205. beforeEach('create users', async function () {
  206. userLatest = new User()
  207. userSP1 = new User()
  208. userCustomTimeoutLower = new User()
  209. userCustomTimeoutHigher = new User()
  210. await Promise.all([
  211. userLatest.ensureUserExists(),
  212. userSP1.ensureUserExists(),
  213. userCustomTimeoutLower.ensureUserExists(),
  214. userCustomTimeoutHigher.ensureUserExists(),
  215. ])
  216. })
  217. const serverPro1Features = {
  218. collaborators: -1,
  219. dropbox: true,
  220. versioning: true,
  221. compileTimeout: 180,
  222. compileGroup: 'standard',
  223. references: true,
  224. templates: true,
  225. trackChanges: true,
  226. }
  227. beforeEach('downgrade userSP1', async function () {
  228. await userSP1.mongoUpdate({ $set: { features: serverPro1Features } })
  229. })
  230. beforeEach('downgrade userCustomTimeoutLower', async function () {
  231. run(
  232. `node modules/server-ce-scripts/scripts/change-compile-timeout --user-id=${userCustomTimeoutLower.id} --compile-timeout=42`
  233. )
  234. })
  235. beforeEach('upgrade userCustomTimeoutHigher', async function () {
  236. run(
  237. `node modules/server-ce-scripts/scripts/change-compile-timeout --user-id=${userCustomTimeoutHigher.id} --compile-timeout=360`
  238. )
  239. })
  240. async function getFeatures() {
  241. return [
  242. await userLatest.getFeatures(),
  243. await userSP1.getFeatures(),
  244. await userCustomTimeoutLower.getFeatures(),
  245. await userCustomTimeoutHigher.getFeatures(),
  246. ]
  247. }
  248. let initialFeatures
  249. beforeEach('collect initial features', async function () {
  250. initialFeatures = await getFeatures()
  251. })
  252. it('should have prepared the right features', async function () {
  253. expect(initialFeatures).to.deep.equal([
  254. Settings.defaultFeatures,
  255. serverPro1Features,
  256. Object.assign({}, Settings.defaultFeatures, {
  257. compileTimeout: 42,
  258. }),
  259. Object.assign({}, Settings.defaultFeatures, {
  260. compileTimeout: 360,
  261. }),
  262. ])
  263. })
  264. describe('dry-run', function () {
  265. let output
  266. beforeEach('run script', function () {
  267. output = run(
  268. `node modules/server-ce-scripts/scripts/upgrade-user-features`
  269. )
  270. })
  271. it('should update SP1 features', function () {
  272. expect(output).to.include(userSP1.id)
  273. })
  274. it('should update lowerTimeout features', function () {
  275. expect(output).to.include(userCustomTimeoutLower.id)
  276. })
  277. it('should not update latest features', function () {
  278. expect(output).to.not.include(userLatest.id)
  279. })
  280. it('should not update higherTimeout features', function () {
  281. expect(output).to.not.include(userCustomTimeoutHigher.id)
  282. })
  283. it('should not change any features in the db', async function () {
  284. expect(await getFeatures()).to.deep.equal(initialFeatures)
  285. })
  286. })
  287. describe('live run', function () {
  288. let output
  289. beforeEach('run script', function () {
  290. output = run(
  291. `node modules/server-ce-scripts/scripts/upgrade-user-features --dry-run=false`
  292. )
  293. })
  294. it('should update SP1 features', function () {
  295. expect(output).to.include(userSP1.id)
  296. })
  297. it('should update lowerTimeout features', function () {
  298. expect(output).to.include(userCustomTimeoutLower.id)
  299. })
  300. it('should not update latest features', function () {
  301. expect(output).to.not.include(userLatest.id)
  302. })
  303. it('should not update higherTimeout features', function () {
  304. expect(output).to.not.include(userCustomTimeoutHigher.id)
  305. })
  306. it('should update features in the db', async function () {
  307. expect(await getFeatures()).to.deep.equal([
  308. Settings.defaultFeatures,
  309. Settings.defaultFeatures,
  310. Settings.defaultFeatures,
  311. Object.assign({}, Settings.defaultFeatures, {
  312. compileTimeout: 360,
  313. }),
  314. ])
  315. })
  316. })
  317. })
  318. })