fix(email): gate resend wrappers behind ALLOW_DEV_TEST_ENDPOINTS
All five resend.js send wrappers (registration, cancellation, waitlist, series pass, welcome) dispatched live in dev. Add a skipEmailInDev guard mirroring the gate in pre-registrants/invite.post.js so dev runs and e2e don't fire real Resend sends. Also add the monthly-onboarding Slack-timing line to the welcome email. Unit-tested.
This commit is contained in:
parent
a9312c423b
commit
dac423afcd
2 changed files with 118 additions and 0 deletions
85
tests/server/utils/resend.test.js
Normal file
85
tests/server/utils/resend.test.js
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
||||
|
||||
import {
|
||||
sendEventRegistrationEmail,
|
||||
sendEventCancellationEmail,
|
||||
sendWaitlistNotificationEmail,
|
||||
sendSeriesPassConfirmation,
|
||||
sendWelcomeEmail,
|
||||
} from '../../../server/utils/resend.js'
|
||||
|
||||
// Hoisted spy so the resend mock and the assertions share one reference.
|
||||
const { sendSpy } = vi.hoisted(() => ({ sendSpy: vi.fn() }))
|
||||
|
||||
vi.mock('resend', () => ({
|
||||
Resend: class {
|
||||
constructor() {
|
||||
this.emails = { send: sendSpy }
|
||||
}
|
||||
},
|
||||
}))
|
||||
|
||||
const eventData = {
|
||||
title: 'Co-op Basics',
|
||||
startDate: '2026-06-01T17:00:00.000Z',
|
||||
endDate: '2026-06-01T18:00:00.000Z',
|
||||
location: 'Online',
|
||||
slug: 'co-op-basics',
|
||||
}
|
||||
|
||||
const registration = { email: 'reg@example.com', name: 'Reg', ticketType: 'member', amountPaid: 0 }
|
||||
|
||||
const seriesPassOptions = {
|
||||
to: 'pass@example.com',
|
||||
name: 'Pass Holder',
|
||||
series: { title: 'Workshop Series' },
|
||||
ticket: { type: 'member', price: 0, currency: 'CAD', isFree: true },
|
||||
events: [eventData],
|
||||
paymentId: null,
|
||||
}
|
||||
|
||||
const member = { email: 'welcome@example.com', name: 'New Member', circle: 'Community' }
|
||||
|
||||
describe('resend email wrappers — ALLOW_DEV_TEST_ENDPOINTS gate', () => {
|
||||
beforeEach(() => {
|
||||
sendSpy.mockReset()
|
||||
sendSpy.mockResolvedValue({ data: { id: 'email_1' }, error: null })
|
||||
})
|
||||
afterEach(() => {
|
||||
delete process.env.ALLOW_DEV_TEST_ENDPOINTS
|
||||
})
|
||||
|
||||
describe('when ALLOW_DEV_TEST_ENDPOINTS=true', () => {
|
||||
beforeEach(() => {
|
||||
process.env.ALLOW_DEV_TEST_ENDPOINTS = 'true'
|
||||
})
|
||||
|
||||
const cases = [
|
||||
['registration', () => sendEventRegistrationEmail(registration, eventData)],
|
||||
['cancellation', () => sendEventCancellationEmail(registration, eventData)],
|
||||
['waitlist', () => sendWaitlistNotificationEmail(registration, eventData)],
|
||||
['series pass', () => sendSeriesPassConfirmation(seriesPassOptions)],
|
||||
['welcome', () => sendWelcomeEmail(member)],
|
||||
]
|
||||
|
||||
it.each(cases)('skips the live send for %s', async (_label, call) => {
|
||||
const result = await call()
|
||||
expect(result).toEqual({ success: true, skipped: true })
|
||||
expect(sendSpy).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the gate is off', () => {
|
||||
it('dispatches a live send and returns success', async () => {
|
||||
const result = await sendWelcomeEmail(member)
|
||||
expect(sendSpy).toHaveBeenCalledTimes(1)
|
||||
expect(result).toEqual({ success: true, data: { id: 'email_1' } })
|
||||
})
|
||||
|
||||
it('includes the monthly-onboarding Slack-timing line in the welcome email', async () => {
|
||||
await sendWelcomeEmail(member)
|
||||
const sent = sendSpy.mock.calls[0][0]
|
||||
expect(sent.text).toContain('monthly onboarding waves')
|
||||
})
|
||||
})
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue