test(email): add internal controller spec

Add unit tests for the internal email controller.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Quinn Ftw 2025-12-29 05:11:47 -08:00
parent 2cd36ec961
commit 64333c9115

View file

@ -0,0 +1,234 @@
import { Test, TestingModule } from '@nestjs/testing'
import { UnauthorizedException } from '@nestjs/common'
import { ConfigService } from '@nestjs/config'
import { InternalController } from './internal.controller'
import { UsersEmailService } from '../users/users-email.service'
import { EmailQueueService } from '../core/email-queue.service'
import { EmailCategory } from '../core/entities/email-log.entity'
describe('InternalController', () => {
let controller: InternalController
let usersEmailService: jest.Mocked<UsersEmailService>
let emailQueueService: jest.Mocked<EmailQueueService>
const validApiKey = 'test-internal-api-key'
const mockUserData = {
userId: 'user-123',
email: 'test@example.com',
name: 'Test User',
}
describe('when API key is configured', () => {
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [InternalController],
providers: [
{
provide: UsersEmailService,
useValue: {
sendWelcomeEmail: jest.fn().mockResolvedValue('job-welcome'),
sendEmailVerification: jest.fn().mockResolvedValue('job-verify'),
sendPasswordReset: jest.fn().mockResolvedValue('job-reset'),
sendPasswordChanged: jest.fn().mockResolvedValue('job-changed'),
sendAccountLocked: jest.fn().mockResolvedValue('job-locked'),
sendLoginAlert: jest.fn().mockResolvedValue('job-alert'),
},
},
{
provide: EmailQueueService,
useValue: {
queueEmail: jest.fn().mockResolvedValue('job-otp'),
},
},
{
provide: ConfigService,
useValue: {
get: jest.fn((key: string, defaultValue?: string) => {
if (key === 'INTERNAL_API_KEY') return validApiKey
return defaultValue
}),
},
},
],
}).compile()
controller = module.get<InternalController>(InternalController)
usersEmailService = module.get(UsersEmailService)
emailQueueService = module.get(EmailQueueService)
})
describe('API key validation', () => {
it('should reject requests without API key', async () => {
await expect(controller.sendWelcome(undefined as any, mockUserData)).rejects.toThrow(
UnauthorizedException
)
})
it('should reject requests with invalid API key', async () => {
await expect(controller.sendWelcome('wrong-key', mockUserData)).rejects.toThrow(
UnauthorizedException
)
})
it('should accept requests with valid API key', async () => {
const result = await controller.sendWelcome(validApiKey, mockUserData)
expect(result.jobId).toBe('job-welcome')
})
})
describe('sendWelcome', () => {
it('should send welcome email and return job ID', async () => {
const result = await controller.sendWelcome(validApiKey, mockUserData)
expect(result).toEqual({ jobId: 'job-welcome' })
expect(usersEmailService.sendWelcomeEmail).toHaveBeenCalledWith(mockUserData)
})
})
describe('sendVerification', () => {
it('should send verification email', async () => {
const body = { ...mockUserData, verificationToken: 'token-123' }
const result = await controller.sendVerification(validApiKey, body)
expect(result).toEqual({ jobId: 'job-verify' })
expect(usersEmailService.sendEmailVerification).toHaveBeenCalledWith(body)
})
})
describe('sendPasswordReset', () => {
it('should send password reset email', async () => {
const body = { ...mockUserData, resetToken: 'reset-123' }
const result = await controller.sendPasswordReset(validApiKey, body)
expect(result).toEqual({ jobId: 'job-reset' })
expect(usersEmailService.sendPasswordReset).toHaveBeenCalledWith(body)
})
})
describe('sendPasswordChanged', () => {
it('should send password changed notification', async () => {
const result = await controller.sendPasswordChanged(validApiKey, mockUserData)
expect(result).toEqual({ jobId: 'job-changed' })
expect(usersEmailService.sendPasswordChanged).toHaveBeenCalledWith(mockUserData)
})
})
describe('sendAccountLocked', () => {
it('should send account locked notification', async () => {
const body = {
...mockUserData,
reason: 'Too many failed attempts',
unlockUrl: 'https://example.com/unlock',
}
const result = await controller.sendAccountLocked(validApiKey, body)
expect(result).toEqual({ jobId: 'job-locked' })
expect(usersEmailService.sendAccountLocked).toHaveBeenCalledWith(body)
})
})
describe('sendLoginAlert', () => {
it('should send login alert', async () => {
const body = {
...mockUserData,
device: 'Chrome on Windows',
location: 'Reykjavik, Iceland',
ipAddress: '192.168.1.1',
}
const result = await controller.sendLoginAlert(validApiKey, body)
expect(result).toEqual({ jobId: 'job-alert' })
expect(usersEmailService.sendLoginAlert).toHaveBeenCalledWith(body)
})
})
describe('sendOtp', () => {
it('should queue OTP email with correct template', async () => {
const body = {
...mockUserData,
code: '123456',
expiresInMinutes: 5,
}
const result = await controller.sendOtp(validApiKey, body)
expect(result).toEqual({ jobId: 'job-otp' })
expect(emailQueueService.queueEmail).toHaveBeenCalledWith({
to: mockUserData.email,
templateName: 'otp-code',
variables: {
name: mockUserData.name,
code: '123456',
expiresIn: '5 minutes',
},
category: EmailCategory.USERS,
userId: mockUserData.userId,
priority: 'high',
})
})
it('should use default values when optional fields missing', async () => {
const body = {
userId: 'user-123',
email: 'test@example.com',
code: '654321',
}
await controller.sendOtp(validApiKey, body)
expect(emailQueueService.queueEmail).toHaveBeenCalledWith(
expect.objectContaining({
variables: expect.objectContaining({
name: 'there',
expiresIn: '10 minutes',
}),
})
)
})
})
})
describe('when API key is not configured', () => {
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [InternalController],
providers: [
{
provide: UsersEmailService,
useValue: {
sendWelcomeEmail: jest.fn(),
},
},
{
provide: EmailQueueService,
useValue: {
queueEmail: jest.fn(),
},
},
{
provide: ConfigService,
useValue: {
get: jest.fn((key: string, defaultValue?: string) => {
if (key === 'INTERNAL_API_KEY') return '' // Not configured
return defaultValue
}),
},
},
],
}).compile()
controller = module.get<InternalController>(InternalController)
})
it('should reject all requests when not configured', async () => {
await expect(controller.sendWelcome(validApiKey, mockUserData)).rejects.toThrow(
'Internal API not configured'
)
})
})
})