| 1 | import { describe, it, expect, afterAll, beforeEach, vi } from 'vitest'
|
| 2 | import { sendWelcomeEmail } from '@app/services/mail/welcome'
|
| 3 | import { sendActivateAccountEmail } from '@app/services/mail/sendActivateAccountEmail'
|
| 4 | import { sendResetPasswordEmail } from '@app/services/mail/sendPasswordReset'
|
| 5 | import { mailProcessor } from '@app/lib/queue/processors/mail'
|
| 6 | import { MailJobName } from '@app/lib/queue/types'
|
| 7 | import { buildPrisma, cleanupTestDb, createTestUser } from '../test-helper'
|
| 8 |
|
| 9 | // Intercept all Resend calls
|
| 10 | vi.mock('@app/lib/mail', () => ({
|
| 11 | sendEmail: vi.fn().mockResolvedValue({
|
| 12 | data: { id: 'fake-email-id' },
|
| 13 | error: null
|
| 14 | }),
|
| 15 | }))
|
| 16 |
|
| 17 | import { sendEmail } from '@app/lib/mail'
|
| 18 | const mockedSendEmail = vi.mocked(sendEmail)
|
| 19 |
|
| 20 | const db = buildPrisma()
|
| 21 |
|
| 22 | afterAll(() => db.$disconnect())
|
| 23 | beforeEach(async () => {
|
| 24 | await cleanupTestDb(db)
|
| 25 | mockedSendEmail.mockClear()
|
| 26 | })
|
| 27 |
|
| 28 | describe('sendWelcomeEmail', () => {
|
| 29 | it('sends to the correct address with the activation code', async () => {
|
| 30 | const user = await createTestUser(db, {
|
| 31 | email: 'welcome@example.com',
|
| 32 | firstName: 'Maria'
|
| 33 | })
|
| 34 |
|
| 35 | await sendWelcomeEmail(user.id, '123456')
|
| 36 |
|
| 37 | expect(mockedSendEmail).toHaveBeenCalledOnce()
|
| 38 | expect(mockedSendEmail).toHaveBeenCalledWith(
|
| 39 | expect.objectContaining({
|
| 40 | to: 'welcome@example.com',
|
| 41 | subject: expect.stringContaining('nexo'),
|
| 42 | text: expect.stringContaining('Maria'),
|
| 43 | })
|
| 44 | )
|
| 45 |
|
| 46 | // The code itself is in the body so the user knows what to enter
|
| 47 | const [call] = mockedSendEmail.mock.calls
|
| 48 | expect(call[0].text).toContain('123456')
|
| 49 | expect(call[0].text).toContain('expira em cinco minutos')
|
| 50 | })
|
| 51 | })
|
| 52 |
|
| 53 | describe('sendActivateAccountEmail', () => {
|
| 54 | it('sends to the correct address with the activation code', async () => {
|
| 55 | const user = await createTestUser(db, {
|
| 56 | email: 'activate@example.com',
|
| 57 | firstName: 'João'
|
| 58 | })
|
| 59 |
|
| 60 | await sendActivateAccountEmail(user.id, '654321')
|
| 61 |
|
| 62 | expect(mockedSendEmail).toHaveBeenCalledOnce()
|
| 63 | expect(mockedSendEmail).toHaveBeenCalledWith(
|
| 64 | expect.objectContaining({
|
| 65 | to: 'activate@example.com',
|
| 66 | text: expect.stringContaining('João'),
|
| 67 | })
|
| 68 | )
|
| 69 | const [call] = mockedSendEmail.mock.calls
|
| 70 | expect(call[0].text).toContain('654321')
|
| 71 | expect(call[0].text).toContain('expira em cinco minutos')
|
| 72 | })
|
| 73 | })
|
| 74 |
|
| 75 | describe('sendResetPasswordEmail', () => {
|
| 76 | it('sends to the correct address with the reset token', async () => {
|
| 77 | const user = await createTestUser(db, {
|
| 78 | email: 'reset@example.com',
|
| 79 | firstName: 'Ana'
|
| 80 | })
|
| 81 |
|
| 82 | await sendResetPasswordEmail(user.id, 'token-abc-xyz')
|
| 83 |
|
| 84 | expect(mockedSendEmail).toHaveBeenCalledOnce()
|
| 85 | expect(mockedSendEmail).toHaveBeenCalledWith(
|
| 86 | expect.objectContaining({
|
| 87 | to: 'reset@example.com',
|
| 88 | subject: expect.stringContaining('nexo'),
|
| 89 | text: expect.stringContaining('token-abc-xyz'),
|
| 90 | })
|
| 91 | )
|
| 92 | })
|
| 93 |
|
| 94 | it('renders a clickable deep link in the html body', async () => {
|
| 95 | const user = await createTestUser(db, {
|
| 96 | email: 'reset-html@example.com',
|
| 97 | firstName: 'Ana'
|
| 98 | })
|
| 99 |
|
| 100 | await sendResetPasswordEmail(user.id, 'token-abc-xyz')
|
| 101 |
|
| 102 | const [call] = mockedSendEmail.mock.calls
|
| 103 | expect(call[0].html).toContain(
|
| 104 | 'href="orbit://auth/reset-password?token=token-abc-xyz"'
|
| 105 | )
|
| 106 | })
|
| 107 | })
|
| 108 |
|
| 109 | describe('mailProcessor', () => {
|
| 110 | it('dispatches a password reset email and awaits it', async () => {
|
| 111 | const user = await createTestUser(db, {
|
| 112 | email: 'proc@example.com',
|
| 113 | firstName: 'Lucas'
|
| 114 | })
|
| 115 |
|
| 116 | await mailProcessor({
|
| 117 | name: MailJobName.PasswordReset,
|
| 118 | data: { userId: user.id, token: 'reset-token-123' },
|
| 119 | } as any)
|
| 120 |
|
| 121 | expect(mockedSendEmail).toHaveBeenCalledOnce()
|
| 122 | expect(mockedSendEmail).toHaveBeenCalledWith(
|
| 123 | expect.objectContaining({ to: 'proc@example.com' })
|
| 124 | )
|
| 125 | })
|
| 126 |
|
| 127 | it('throws for unknown job names', async () => {
|
| 128 | await expect(
|
| 129 | mailProcessor({ name: 'mail:unknown', data: {} } as any)
|
| 130 | ).rejects.toThrow('Unknown mail job')
|
| 131 | })
|
| 132 | })
|