MigrateUserFeatureTimeoutTests.js 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. const { expect } = require('chai')
  2. const { promisify } = require('node:util')
  3. const { exec } = require('node:child_process')
  4. const logger = require('@overleaf/logger')
  5. const { db } = require('../../../app/src/infrastructure/mongodb')
  6. async function runScript(args = []) {
  7. try {
  8. return await promisify(exec)(
  9. ['node', 'scripts/migration_compile_timeout_60s_to_20s.js', ...args].join(
  10. ' '
  11. )
  12. )
  13. } catch (error) {
  14. logger.error({ error }, 'script failed')
  15. throw error
  16. }
  17. }
  18. async function runFixupScript(args = []) {
  19. try {
  20. return await promisify(exec)(
  21. [
  22. 'node',
  23. 'scripts/migration_compile_timeout_60s_to_20s_fixup_features_updated_at.js',
  24. ...args,
  25. ].join(' ')
  26. )
  27. } catch (error) {
  28. logger.error({ error }, 'script failed')
  29. throw error
  30. }
  31. }
  32. describe('MigrateUserFeatureTimeoutTests', function () {
  33. describe('initial script', function () {
  34. const usersInput = {
  35. noFeatures: {},
  36. noFeatureTimeout: { features: {} },
  37. timeout10s: {
  38. features: { compileTimeout: 10, other: 'val1' },
  39. bar: '1',
  40. featuresUpdatedAt: new Date('2020-01-01'),
  41. },
  42. timeout20s: { features: { compileTimeout: 20, other: 'val2' }, bar: '2' },
  43. timeout30s: {
  44. features: { compileTimeout: 30, other: 'val3' },
  45. bar: '3',
  46. featuresUpdatedAt: new Date('2025-01-01'),
  47. },
  48. timeout60s: {
  49. features: { compileTimeout: 60, other: 'val4' },
  50. bar: '4',
  51. featuresUpdatedAt: new Date(),
  52. },
  53. timeout120s: {
  54. features: { compileTimeout: 120, other: 'val5' },
  55. bar: '5',
  56. },
  57. timeout180s: {
  58. features: { compileTimeout: 180, other: 'val6' },
  59. bar: '6',
  60. featuresUpdatedAt: new Date('2020-01-01'),
  61. },
  62. }
  63. const usersKeys = Object.keys(usersInput)
  64. const userIds = {}
  65. beforeEach('insert users', async function () {
  66. const usersInsertedValues = await db.users.insertMany(
  67. usersKeys.map(key => ({
  68. ...usersInput[key],
  69. email: `${key}@example.com`,
  70. }))
  71. )
  72. usersKeys.forEach(
  73. (key, index) => (userIds[key] = usersInsertedValues.insertedIds[index])
  74. )
  75. })
  76. afterEach('clear users', async function () {
  77. await db.users.deleteMany({})
  78. })
  79. it('gives correct counts in dry mode', async function () {
  80. const users = await db.users.find().toArray()
  81. expect(users).to.have.lengthOf(usersKeys.length)
  82. const result = await runScript([])
  83. expect(result.stderr).to.contain(
  84. 'Doing dry run. Add --commit to commit changes'
  85. )
  86. expect(result.stdout).to.contain(
  87. 'Found 3 users with compileTimeout <= 60s && != 20s'
  88. )
  89. expect(result.stdout).to.contain(
  90. 'Found 1 users with compileTimeout == 20s'
  91. )
  92. expect(result.stdout).not.to.contain('Updated')
  93. const usersAfter = await db.users.find().toArray()
  94. expect(usersAfter).to.deep.equal(users)
  95. })
  96. it("updates users compileTimeout when '--commit' is set", async function () {
  97. const users = await db.users.find().toArray()
  98. expect(users).to.have.lengthOf(usersKeys.length)
  99. const result = await runScript(['--commit'])
  100. expect(result.stdout).to.contain(
  101. 'Found 3 users with compileTimeout <= 60s && != 20s'
  102. )
  103. expect(result.stdout).to.contain(
  104. 'Found 1 users with compileTimeout == 20s'
  105. )
  106. expect(result.stdout).to.contain('Updated 3 records')
  107. const usersAfter = await db.users.find().toArray()
  108. expect(
  109. usersAfter.map(({ featuresUpdatedAt, ...rest }) => rest)
  110. ).to.deep.equal([
  111. { _id: userIds.noFeatures, email: 'noFeatures@example.com' },
  112. {
  113. _id: userIds.noFeatureTimeout,
  114. email: 'noFeatureTimeout@example.com',
  115. features: {},
  116. },
  117. {
  118. _id: userIds.timeout10s,
  119. email: 'timeout10s@example.com',
  120. features: { compileTimeout: 20, other: 'val1' },
  121. bar: '1',
  122. },
  123. {
  124. _id: userIds.timeout20s,
  125. email: 'timeout20s@example.com',
  126. features: { compileTimeout: 20, other: 'val2' },
  127. bar: '2',
  128. },
  129. {
  130. _id: userIds.timeout30s,
  131. email: 'timeout30s@example.com',
  132. features: { compileTimeout: 20, other: 'val3' },
  133. bar: '3',
  134. },
  135. {
  136. _id: userIds.timeout60s,
  137. email: 'timeout60s@example.com',
  138. features: { compileTimeout: 20, other: 'val4' },
  139. bar: '4',
  140. },
  141. {
  142. _id: userIds.timeout120s,
  143. email: 'timeout120s@example.com',
  144. features: { compileTimeout: 120, other: 'val5' },
  145. bar: '5',
  146. },
  147. {
  148. _id: userIds.timeout180s,
  149. email: 'timeout180s@example.com',
  150. features: { compileTimeout: 180, other: 'val6' },
  151. bar: '6',
  152. },
  153. ])
  154. expect(usersAfter[0].featuresUpdatedAt).to.be.undefined
  155. expect(usersAfter[1].featuresUpdatedAt).to.be.undefined
  156. expect(usersAfter[2].featuresUpdatedAt).to.be.instanceOf(Date)
  157. expect(usersAfter[3].featuresUpdatedAt).to.be.undefined // was already 20s
  158. expect(usersAfter[4].featuresUpdatedAt).to.be.instanceOf(Date)
  159. expect(usersAfter[5].featuresUpdatedAt).to.be.instanceOf(Date)
  160. expect(usersAfter[6].featuresUpdatedAt).to.be.undefined
  161. const result2 = await runScript([])
  162. expect(result2.stdout).to.contain(
  163. 'Found 0 users with compileTimeout <= 60s && != 20s'
  164. )
  165. expect(result2.stdout).to.contain(
  166. 'Found 4 users with compileTimeout == 20s'
  167. )
  168. })
  169. })
  170. describe('fixup script', function () {
  171. const usersInput = {
  172. timeout20s1: {
  173. features: { compileTimeout: 20 },
  174. },
  175. timeout20s2: {
  176. features: { compileTimeout: 20 },
  177. featuresUpdatedAt: new Date('2023-01-01'),
  178. },
  179. timeout20s3: {
  180. features: { compileTimeout: 20 },
  181. featuresUpdatedAt: new Date('2025-01-01'),
  182. },
  183. timeout240s1: {
  184. features: { compileTimeout: 240 },
  185. },
  186. timeout240s2: {
  187. features: { compileTimeout: 240 },
  188. featuresUpdatedAt: new Date('2023-01-01'),
  189. },
  190. timeout240s3: {
  191. features: { compileTimeout: 240 },
  192. featuresUpdatedAt: new Date('2025-01-01'),
  193. },
  194. }
  195. const usersKeys = Object.keys(usersInput)
  196. const userIds = {}
  197. beforeEach('insert users', async function () {
  198. const usersInsertedValues = await db.users.insertMany(
  199. usersKeys.map(key => ({
  200. ...usersInput[key],
  201. email: `${key}@example.com`,
  202. }))
  203. )
  204. usersKeys.forEach(
  205. (key, index) => (userIds[key] = usersInsertedValues.insertedIds[index])
  206. )
  207. })
  208. afterEach('clear users', async function () {
  209. await db.users.deleteMany({})
  210. })
  211. it('gives correct counts in dry mode', async function () {
  212. const users = await db.users.find().toArray()
  213. expect(users).to.have.lengthOf(usersKeys.length)
  214. const result = await runFixupScript([])
  215. expect(result.stderr).to.contain(
  216. 'Doing dry run. Add --commit to commit changes'
  217. )
  218. expect(result.stdout).to.contain(
  219. 'Found 2 users needing their featuresUpdatedAt updated'
  220. )
  221. expect(result.stdout).not.to.contain('Updated 2 records')
  222. const usersAfter = await db.users.find().toArray()
  223. expect(usersAfter).to.deep.equal(users)
  224. })
  225. it("updates users featuresUpdatedAt when '--commit' is set", async function () {
  226. const FEATURES_UPDATED_AT = new Date('2024-04-16T12:41:00Z')
  227. const users = await db.users.find().toArray()
  228. expect(users).to.have.lengthOf(usersKeys.length)
  229. const result = await runFixupScript(['--commit'])
  230. expect(result.stdout).to.contain(
  231. 'Found 2 users needing their featuresUpdatedAt updated'
  232. )
  233. expect(result.stdout).to.contain('Updated 2 records')
  234. const usersAfter = await db.users.find().toArray()
  235. expect(usersAfter).to.deep.equal([
  236. {
  237. _id: userIds.timeout20s1,
  238. email: 'timeout20s1@example.com',
  239. features: { compileTimeout: 20 },
  240. featuresUpdatedAt: FEATURES_UPDATED_AT,
  241. },
  242. {
  243. _id: userIds.timeout20s2,
  244. email: 'timeout20s2@example.com',
  245. features: { compileTimeout: 20 },
  246. featuresUpdatedAt: FEATURES_UPDATED_AT,
  247. },
  248. {
  249. _id: userIds.timeout20s3,
  250. email: 'timeout20s3@example.com',
  251. features: { compileTimeout: 20 },
  252. featuresUpdatedAt: new Date('2025-01-01'),
  253. },
  254. {
  255. _id: userIds.timeout240s1,
  256. email: 'timeout240s1@example.com',
  257. features: { compileTimeout: 240 },
  258. },
  259. {
  260. _id: userIds.timeout240s2,
  261. email: 'timeout240s2@example.com',
  262. features: { compileTimeout: 240 },
  263. featuresUpdatedAt: new Date('2023-01-01'),
  264. },
  265. {
  266. _id: userIds.timeout240s3,
  267. email: 'timeout240s3@example.com',
  268. features: { compileTimeout: 240 },
  269. featuresUpdatedAt: new Date('2025-01-01'),
  270. },
  271. ])
  272. const result2 = await runFixupScript([])
  273. expect(result2.stdout).to.contain(
  274. 'Found 0 users needing their featuresUpdatedAt updated'
  275. )
  276. })
  277. })
  278. })