RemoveEmailsWithCommasScriptTest.mjs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. import { promisify } from 'node:util'
  2. import { exec } from 'node:child_process'
  3. import { expect } from 'chai'
  4. import { filterOutput } from './helpers/settings.mjs'
  5. import { db, ObjectId } from '../../../app/src/infrastructure/mongodb.js'
  6. import fs from 'node:fs/promises'
  7. const CSV_FILENAME = '/tmp/emails-with-commas.csv'
  8. async function runScript(commit) {
  9. const result = await promisify(exec)(
  10. ['node', 'scripts/remove_emails_with_commas.mjs', commit && '--commit']
  11. .filter(Boolean)
  12. .join(' ')
  13. )
  14. return {
  15. ...result,
  16. stdout: result.stdout.split('\n').filter(filterOutput),
  17. }
  18. }
  19. function createUser(email, emails) {
  20. return {
  21. _id: new ObjectId(),
  22. email,
  23. emails,
  24. }
  25. }
  26. describe('scripts/remove_emails_with_commas', function () {
  27. let user, unchangedUser
  28. beforeEach(async function () {
  29. await fs.writeFile(
  30. CSV_FILENAME,
  31. '"user,email@test.com"\n"user,another@test.com"\n'
  32. )
  33. })
  34. afterEach(async function () {
  35. try {
  36. await fs.unlink(CSV_FILENAME)
  37. } catch (err) {
  38. // Ignore errors if file doesn't exist
  39. }
  40. })
  41. describe('when removing email addresses with commas', function () {
  42. beforeEach(async function () {
  43. user = createUser('user,email@test.com', [
  44. {
  45. email: 'user,email@test.com',
  46. createdAt: new Date(),
  47. reversedHostname: 'moc.tset',
  48. },
  49. ])
  50. await db.users.insertOne(user)
  51. unchangedUser = createUser('john.doe@example.com', [
  52. {
  53. email: 'john.doe@example.com',
  54. createdAt: new Date(),
  55. reversedHostname: 'moc.elpmaxe',
  56. },
  57. ])
  58. await db.users.insertOne(unchangedUser)
  59. })
  60. afterEach(async function () {
  61. await db.users.deleteOne({ _id: user._id })
  62. })
  63. it('should replace emails with commas with encoded support emails', async function () {
  64. const r = await runScript(true)
  65. expect(r.stdout).to.include(
  66. 'user,email@test.com -> support+user_2cemail_40test.com@overleaf.com'
  67. )
  68. expect(r.stdout).to.include('Updated users: 1')
  69. const updatedUser = await db.users.findOne({ _id: user._id })
  70. expect(updatedUser.email).to.equal(
  71. 'support+user_2cemail_40test.com@overleaf.com'
  72. )
  73. expect(updatedUser.emails).to.have.length(1)
  74. expect(updatedUser.emails[0].email).to.equal(
  75. 'support+user_2cemail_40test.com@overleaf.com'
  76. )
  77. expect(updatedUser.emails[0].reversedHostname).to.equal('moc.faelrevo')
  78. const unchanged = await db.users.findOne({ _id: unchangedUser._id })
  79. expect(unchanged.emails).to.have.length(1)
  80. expect(unchanged.email).to.equal('john.doe@example.com')
  81. expect(unchanged.emails[0].email).to.equal('john.doe@example.com')
  82. })
  83. it('should not modify anything in dry run mode', async function () {
  84. const r = await runScript(false)
  85. expect(r.stdout).to.include(
  86. 'user,email@test.com -> support+user_2cemail_40test.com@overleaf.com'
  87. )
  88. expect(r.stdout).to.include(
  89. 'Note: this was a dry-run. No changes were made.'
  90. )
  91. const updatedUser = await db.users.findOne({ _id: user._id })
  92. expect(updatedUser.email).to.equal('user,email@test.com')
  93. expect(updatedUser.emails).to.have.length(1)
  94. expect(updatedUser.emails[0].email).to.equal('user,email@test.com')
  95. })
  96. })
  97. describe('when handling multiple email replacements', function () {
  98. beforeEach(async function () {
  99. user = createUser('user,email@test.com', [
  100. {
  101. email: 'user,email@test.com',
  102. createdAt: new Date(),
  103. reversedHostname: 'moc.tset',
  104. },
  105. {
  106. email: 'normal@test.com',
  107. createdAt: new Date(),
  108. reversedHostname: 'moc.tset',
  109. },
  110. ])
  111. await db.users.insertOne(user)
  112. })
  113. afterEach(async function () {
  114. await db.users.deleteOne({ _id: user._id })
  115. })
  116. it('should only replace primary email with comma and keep other emails', async function () {
  117. const r = await runScript(true)
  118. expect(r.stdout).to.include(
  119. 'user,email@test.com -> support+user_2cemail_40test.com@overleaf.com'
  120. )
  121. expect(r.stdout).to.include('Updated users: 1')
  122. const updatedUser = await db.users.findOne({ _id: user._id })
  123. expect(updatedUser.email).to.equal(
  124. 'support+user_2cemail_40test.com@overleaf.com'
  125. )
  126. expect(updatedUser.emails).to.have.length(2)
  127. expect(updatedUser.emails[0].email).to.equal('normal@test.com')
  128. expect(updatedUser.emails[1].email).to.equal(
  129. 'support+user_2cemail_40test.com@overleaf.com'
  130. )
  131. })
  132. })
  133. describe('when handling special characters in emails', function () {
  134. beforeEach(async function () {
  135. await fs.writeFile(
  136. CSV_FILENAME,
  137. '"user,email@test.com"\n",<user@test.com>"\n"user_special@test.co,"\n'
  138. )
  139. user = createUser('user,email@test.com', [
  140. {
  141. email: 'user,email@test.com',
  142. createdAt: new Date(),
  143. reversedHostname: 'moc.tset',
  144. },
  145. ])
  146. await db.users.insertOne(user)
  147. const user2 = createUser('user<>@test.com', [
  148. {
  149. email: 'user<>@test.com',
  150. createdAt: new Date(),
  151. reversedHostname: 'moc.tset',
  152. },
  153. ])
  154. await db.users.insertOne(user2)
  155. })
  156. afterEach(async function () {
  157. await db.users.deleteMany({
  158. email: {
  159. $in: [
  160. 'support+user_2cemail_40test.com@overleaf.com',
  161. 'support+user_60_62_40test.com@overleaf.com',
  162. ],
  163. },
  164. })
  165. })
  166. it('should correctly encode various special characters', async function () {
  167. const r = await runScript(true)
  168. expect(r.stdout).to.include(
  169. 'user,email@test.com -> support+user_2cemail_40test.com@overleaf.com'
  170. )
  171. expect(r.stdout).to.include(
  172. ',<user@test.com> -> support+_2c_60user_40test.com_62@overleaf.com'
  173. )
  174. const updatedUser1 = await db.users.findOne({ _id: user._id })
  175. expect(updatedUser1.email).to.equal(
  176. 'support+user_2cemail_40test.com@overleaf.com'
  177. )
  178. })
  179. })
  180. describe('when user does not exist', function () {
  181. beforeEach(async function () {
  182. await fs.writeFile(CSV_FILENAME, '"nonexistent,email@test.com"\n')
  183. })
  184. it('should handle missing users gracefully', async function () {
  185. const r = await runScript(true)
  186. expect(r.stdout).to.include(
  187. 'User not found for email: nonexistent,email@test.com'
  188. )
  189. expect(r.stdout).to.include('Updated users: 0')
  190. })
  191. })
  192. })