1+ import axios from 'axios'
12import jwt from 'jsonwebtoken'
23import { afterEach , beforeEach , describe , expect , it , vi } from 'vitest'
34
45import appConfig from '@/config/app'
6+ import User from '@/models/user'
57
68import {
79 getAdminTokenUser ,
810 getOrCreateUser ,
911 parseAdminToken ,
12+ sendOnboardingEmail ,
1013 updateLastLogin ,
1114} from '../auth'
1215
@@ -23,6 +26,7 @@ const mocks = vi.hoisted(() => ({
2326 patch : vi . fn ( ( ) => ( {
2427 where : mockPatchWhere ,
2528 } ) ) ,
29+ findById : vi . fn ( ) ,
2630} ) )
2731
2832vi . mock ( '@/models/user' , ( ) => ( {
@@ -32,10 +36,24 @@ vi.mock('@/models/user', () => ({
3236 findOne : mocks . findOne ,
3337 insertAndFetch : mocks . insertAndFetch ,
3438 patch : mocks . patch ,
39+ findById : mocks . findById ,
3540 } ) ) ,
3641 } ,
3742} ) )
3843
44+ vi . mock ( 'axios' , ( ) => ( {
45+ default : {
46+ post : vi . fn ( ) ,
47+ } ,
48+ } ) )
49+ vi . mock ( '@/config/app' , ( ) => ( {
50+ default : {
51+ isProd : false ,
52+ onboardingEmailWebhookUrl : 'https://test-webhook.com' ,
53+ adminJwtSecretKey : 'test-secret-key' ,
54+ } ,
55+ } ) )
56+
3957describe ( 'Auth helpers' , ( ) => {
4058 afterEach ( ( ) => {
4159 vi . restoreAllMocks ( )
@@ -202,4 +220,107 @@ describe('Auth helpers', () => {
202220 )
203221 } )
204222 } )
223+
224+ describe ( 'sendOnboardingEmail' , ( ) => {
225+ beforeEach ( ( ) => {
226+ vi . clearAllMocks ( )
227+ } )
228+
229+ afterEach ( ( ) => {
230+ vi . restoreAllMocks ( )
231+ } )
232+
233+ it ( 'throws error with no user id' , async ( ) => {
234+ await expect ( sendOnboardingEmail ( '' ) ) . rejects . toThrowError (
235+ 'User id required' ,
236+ )
237+ } )
238+
239+ it ( 'throws error with non-existent user id' , async ( ) => {
240+ mocks . findById . mockResolvedValueOnce ( null )
241+ await expect ( sendOnboardingEmail ( 'non-existent-id' ) ) . rejects . toThrowError (
242+ 'User not found' ,
243+ )
244+ } )
245+
246+ it ( 'does not send email if user has logged in before' , async ( ) => {
247+ const mockUser = {
248+ id : 'test-id' ,
249+ 250+ lastLoginAt : new Date ( ) ,
251+ createdAt : new Date ( ) ,
252+ }
253+
254+ mocks . findById . mockResolvedValueOnce ( mockUser )
255+
256+ await sendOnboardingEmail ( mockUser . id )
257+ expect ( axios . post ) . not . toHaveBeenCalled ( )
258+ } )
259+
260+ it ( 'does not send email if user was created before release date' , async ( ) => {
261+ const mockUser = {
262+ id : 'test-id' ,
263+ 264+ createdAt : new Date ( '2024-01-01' ) , // Before release date
265+ }
266+
267+ mocks . findById . mockResolvedValueOnce ( mockUser )
268+
269+ await sendOnboardingEmail ( mockUser . id )
270+ expect ( axios . post ) . not . toHaveBeenCalled ( )
271+ } )
272+
273+ it ( 'does not send email in non-prod environment' , async ( ) => {
274+ const mockUser = {
275+ id : 'test-id' ,
276+ 277+ createdAt : new Date ( '2025-03-01' ) , // After release date
278+ }
279+
280+ mocks . findById . mockResolvedValueOnce ( mockUser )
281+
282+ await sendOnboardingEmail ( mockUser . id )
283+ expect ( axios . post ) . not . toHaveBeenCalled ( )
284+ } )
285+
286+ it ( 'sends email in prod for eligible users' , async ( ) => {
287+ appConfig . isProd = true
288+
289+ const mockUser = {
290+ id : 'test-id' ,
291+ 292+ lastLoginAt : null ,
293+ createdAt : new Date ( '2025-03-01' ) , // After release date
294+ } as unknown as User
295+
296+ mocks . findById . mockResolvedValueOnce ( mockUser )
297+ vi . mocked ( axios . post ) . mockResolvedValueOnce ( { data : { } } )
298+
299+ await sendOnboardingEmail ( mockUser . id )
300+
301+ expect ( axios . post ) . toHaveBeenCalledWith (
302+ appConfig . onboardingEmailWebhookUrl ,
303+ {
304+ email : mockUser . email ,
305+ } ,
306+ )
307+ } )
308+
309+ it ( 'does not send email if webhook URL is not configured' , async ( ) => {
310+ vi . mocked ( appConfig ) . isProd = true
311+ vi . mocked ( appConfig ) . onboardingEmailWebhookUrl = ''
312+
313+ const mockUser = {
314+ id : 'test-id' ,
315+ 316+ lastLoginAt : null ,
317+ createdAt : new Date ( '2025-03-01' ) , // After release date
318+ } as unknown as User
319+
320+ mocks . findById . mockResolvedValueOnce ( mockUser )
321+
322+ await sendOnboardingEmail ( mockUser . id )
323+ expect ( axios . post ) . not . toHaveBeenCalled ( )
324+ } )
325+ } )
205326} )
0 commit comments