| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275 |
- import { promisify } from 'node:util'
- import { exec } from 'node:child_process'
- import { expect } from 'chai'
- import { filterOutput } from './helpers/settings.mjs'
- import { db, ObjectId } from '../../../app/src/infrastructure/mongodb.js'
- import fs from 'node:fs/promises'
- const CSV_FILENAME = '/tmp/re_add_deleted_emails.csv'
- async function runScript(commit) {
- const result = await promisify(exec)(
- ['node', 'scripts/re_add_deleted_emails.mjs', commit && '--commit']
- .filter(Boolean)
- .join(' ')
- )
- return {
- ...result,
- stdout: result.stdout.split('\n').filter(filterOutput),
- }
- }
- /**
- * @param {[string, string[]][]} userEmails
- */
- const createUsers = async userEmails =>
- Promise.all(
- userEmails.map(async ([email, emails]) => {
- const _id = new ObjectId()
- await db.users.insertOne({
- _id,
- email,
- emails: emails.map(email => ({ email })),
- features: {},
- })
- return _id
- })
- )
- async function generateCsv(users) {
- const text = 'User ID,Email'
- const userRows = users.map(user => {
- return `${user._id.toString()},${user.email}`
- })
- await fs.writeFile(CSV_FILENAME, [text, ...userRows].join('\n'))
- }
- describe('scripts/re_add_deleted_emails', function () {
- let userIds
- afterEach(async function () {
- try {
- await fs.unlink(CSV_FILENAME)
- } catch (err) {
- // Ignore errors if file doesn't exist
- }
- })
- describe('when user IDs dont match', function () {
- beforeEach(async function () {
- userIds = await createUsers([['mismatch1@xmpl.com', []]])
- await generateCsv([{ _id: new ObjectId(), email: 'mismatch2@xmpl.com' }])
- })
- it('doesnt add new emails', async function () {
- const { stdout } = await runScript(true)
- expect(stdout).to.include('Total emails in the CSV: 1')
- expect(stdout).to.include('Total users in the CSV: 1')
- expect(stdout).to.include('Users not found: 1')
- expect(stdout).to.include('Added emails: 0')
- const updatedUser = await db.users.findOne({ _id: userIds[0] })
- expect(updatedUser.emails).to.have.length(0)
- })
- })
- describe('when email address is invalid', function () {
- beforeEach(async function () {
- userIds = await createUsers([['user@xmpl.com', []]])
- await generateCsv([{ _id: userIds[0], email: 'inv@lid@xmpl.com' }])
- })
- it('throws', async function () {
- await expect(runScript(true)).to.eventually.be.rejectedWith(
- 'invalid email'
- )
- const updatedUser = await db.users.findOne({ _id: userIds[0] })
- expect(updatedUser.emails).to.have.length(0)
- })
- it('throws even without --commit', async function () {
- await expect(runScript(false)).to.eventually.be.rejectedWith(
- 'invalid email'
- )
- const updatedUser = await db.users.findOne({ _id: userIds[0] })
- expect(updatedUser.emails).to.have.length(0)
- })
- })
- describe('when new email is used by another user', function () {
- beforeEach(async function () {
- userIds = await createUsers([
- ['user1@xmpl.com', []],
- ['user2@xmpl.com', ['new-email@xmpl.com']],
- ])
- await generateCsv([{ _id: userIds[0], email: 'new-email@xmpl.com' }])
- })
- it('doesnt add new emails', async function () {
- const { stdout } = await runScript(true)
- expect(stdout).to.include('Total emails in the CSV: 1')
- expect(stdout).to.include('Total users in the CSV: 1')
- expect(stdout).to.include('Users not found: 0')
- expect(stdout).to.include('Already in use: ["new-email@xmpl.com"]')
- expect(stdout).to.include('Added emails: 0')
- const updatedUser = await db.users.findOne({ _id: userIds[0] })
- expect(updatedUser.emails).to.have.length(0)
- })
- })
- describe('when the user has 0 email in the array', function () {
- beforeEach(async function () {
- userIds = await createUsers([['user@xmpl.com', []]])
- })
- it('adds the primary email to the user', async function () {
- await generateCsv([{ _id: userIds[0], email: 'user@xmpl.com' }])
- const { stdout } = await runScript(true)
- expect(stdout).to.include('Total emails in the CSV: 1')
- expect(stdout).to.include('Total users in the CSV: 1')
- expect(stdout).to.include('Users not found: 0')
- expect(stdout).to.include('Added emails: 1')
- const updatedUser = await db.users.findOne({ _id: userIds[0] })
- expect(updatedUser.emails).to.have.length(1)
- expect(updatedUser.emails[0].email).to.equal('user@xmpl.com')
- expect(updatedUser.emails[0].reversedHostname).to.equal('moc.lpmx')
- expect(updatedUser.emails[0].confirmedAt).to.be.an.instanceof(Date)
- expect(updatedUser.emails[0].createdAt).to.be.an.instanceof(Date)
- expect(updatedUser.emails[0].reconfirmedAt).to.be.an.instanceof(Date)
- const auditLogs = await db.userAuditLogEntries
- .find({ userId: userIds[0] })
- .toArray()
- expect(auditLogs).to.have.length(1)
- expect(auditLogs[0].operation).to.equal('add-email')
- expect(auditLogs[0].info).to.deep.include({
- script: true,
- note: 'fix wrongly removed unconfirmed secondary email',
- newSecondaryEmail: 'user@xmpl.com',
- })
- })
- it('adds the secondary email to the user', async function () {
- await generateCsv([{ _id: userIds[0], email: 'new-email@xmpl.com' }])
- const { stdout } = await runScript(true)
- expect(stdout).to.include('Total emails in the CSV: 1')
- expect(stdout).to.include('Total users in the CSV: 1')
- expect(stdout).to.include('Users not found: 0')
- expect(stdout).to.include('Added emails: 1')
- const updatedUser = await db.users.findOne({ _id: userIds[0] })
- expect(updatedUser.emails).to.have.length(1)
- expect(updatedUser.emails[0].email).to.equal('new-email@xmpl.com')
- expect(updatedUser.emails[0].reversedHostname).to.equal('moc.lpmx')
- expect(updatedUser.emails[0].confirmedAt).to.be.an.instanceof(Date)
- expect(updatedUser.emails[0].createdAt).to.be.an.instanceof(Date)
- expect(updatedUser.emails[0].reconfirmedAt).to.be.an.instanceof(Date)
- const auditLogs = await db.userAuditLogEntries
- .find({ userId: userIds[0] })
- .toArray()
- expect(auditLogs).to.have.length(1)
- expect(auditLogs[0].operation).to.equal('add-email')
- expect(auditLogs[0].info).to.deep.include({
- script: true,
- note: 'fix wrongly removed unconfirmed secondary email',
- newSecondaryEmail: 'new-email@xmpl.com',
- })
- })
- it('doesnt add new emails without --commit', async function () {
- await generateCsv([{ _id: userIds[0], email: 'new-email@xmpl.com' }])
- const { stdout } = await runScript(false)
- expect(stdout).to.include('Dry-run, use --commit to apply changes')
- const updatedUser = await db.users.findOne({ _id: userIds[0] })
- expect(updatedUser.emails).to.have.length(0)
- })
- })
- describe('when the user has several emails in the array', function () {
- beforeEach(async function () {
- userIds = await createUsers([
- [
- 'user@xmpl.com',
- ['email1@xmpl.com', 'email2@xmpl.com', 'email3@xmpl.com'],
- ],
- ])
- await generateCsv([{ _id: userIds[0], email: 'new-email@xmpl.com' }])
- })
- it('adds the email to the user', async function () {
- const { stdout } = await runScript(true)
- expect(stdout).to.include('Total emails in the CSV: 1')
- expect(stdout).to.include('Total users in the CSV: 1')
- expect(stdout).to.include('Users not found: 0')
- expect(stdout).to.include('Added emails: 1')
- const updatedUser = await db.users.findOne({ _id: userIds[0] })
- expect(updatedUser.emails).to.have.length(4)
- expect(updatedUser.emails[3].email).to.equal('new-email@xmpl.com')
- expect(updatedUser.emails[3].reversedHostname).to.equal('moc.lpmx')
- expect(updatedUser.emails[3].confirmedAt).to.be.an.instanceof(Date)
- expect(updatedUser.emails[3].createdAt).to.be.an.instanceof(Date)
- expect(updatedUser.emails[3].reconfirmedAt).to.be.an.instanceof(Date)
- })
- it('doesnt add new emails without --commit', async function () {
- const { stdout } = await runScript(false)
- expect(stdout).to.include('Dry-run, use --commit to apply changes')
- const updatedUser = await db.users.findOne({ _id: userIds[0] })
- expect(updatedUser.emails).to.have.length(3)
- })
- })
- describe('all of the above', function () {
- beforeEach(async function () {
- userIds = await createUsers([
- ['user0@xmpl.com', []],
- ['user1@xmpl.com', []],
- ['user2@xmpl.com', ['a@xmpl.com', 'b@xmpl.com', 'c@xmpl.com']],
- ['user3@xmpl.com', ['d@xmpl.com', 'e@xmpl.com', 'f@xmpl.com']],
- ['user4@xmpl.com', ['x@xmpl.com', 'y@xmpl.com', 'z@xmpl.com']],
- ['user5@xmpl.com', ['u@xmpl.com', 'v@xmpl.com', 'w@xmpl.com']],
- ])
- await generateCsv([
- { _id: userIds[1], email: 'new1@xmpl.com' },
- { _id: userIds[1], email: 'user1@xmpl.com' },
- { _id: userIds[1], email: 'new2@xmpl.com' },
- { _id: userIds[2], email: 'new3@xmpl.com' },
- { _id: userIds[2], email: 'new4@xmpl.com' },
- { _id: userIds[2], email: 'user2@xmpl.com' },
- { _id: userIds[2], email: 'user3@xmpl.com' },
- { _id: userIds[3], email: 'd@xmpl.com' },
- { _id: userIds[5], email: 'a@xmpl.com' },
- { _id: new ObjectId(), email: 'a@xmpl.com' },
- ])
- })
- it('updates users', async function () {
- const { stdout } = await runScript(true)
- expect(stdout).to.include('Total emails in the CSV: 10')
- expect(stdout).to.include('Total users in the CSV: 5')
- expect(stdout).to.include('Users not found: 1')
- expect(stdout).to.include(
- 'Already in use: ["user3@xmpl.com","a@xmpl.com"]'
- )
- expect(stdout).to.include('Already OK: ["d@xmpl.com"]')
- expect(stdout).to.include('Primary: ["user1@xmpl.com","user2@xmpl.com"]')
- expect(stdout).to.include(
- 'Secondary: ["new1@xmpl.com","new2@xmpl.com","new3@xmpl.com","new4@xmpl.com"]'
- )
- expect(stdout).to.include(
- 'Added emails: ["new1@xmpl.com","user1@xmpl.com","new2@xmpl.com","new3@xmpl.com","new4@xmpl.com","user2@xmpl.com"]'
- )
- const user0 = await db.users.findOne({ _id: userIds[0] })
- const user1 = await db.users.findOne({ _id: userIds[1] })
- const user2 = await db.users.findOne({ _id: userIds[2] })
- const user3 = await db.users.findOne({ _id: userIds[3] })
- const user4 = await db.users.findOne({ _id: userIds[4] })
- const user5 = await db.users.findOne({ _id: userIds[5] })
- expect(user0.emails).to.have.length(0)
- expect(user1.emails).to.have.length(3) // new1, user1, new2
- expect(user2.emails).to.have.length(6) // a, b, c, new3, new4, user2
- expect(user3.emails).to.have.length(3) // d, e, f
- expect(user4.emails).to.have.length(3) // x, y, z
- expect(user5.emails).to.have.length(3) // u, v, w
- })
- })
- })
|