diff --git a/src/email-digest/email-digest.service.spec.ts b/src/email-digest/email-digest.service.spec.ts index 7134080e..e1cfdf02 100644 --- a/src/email-digest/email-digest.service.spec.ts +++ b/src/email-digest/email-digest.service.spec.ts @@ -1,3 +1,11 @@ +jest.mock('@prisma/client', () => ({ + ...jest.requireActual('@prisma/client'), + DigestFrequency: { + DAILY: 'DAILY', + WEEKLY: 'WEEKLY', + }, +})); + import { EmailDigestService } from './email-digest.service'; import { PrismaService } from '../database/prisma.service'; import { EmailService } from '../email/email.service'; @@ -6,18 +14,38 @@ import { ConfigService } from '@nestjs/config'; describe('EmailDigestService', () => { let service: EmailDigestService; let prisma: jest.Mocked>; + let emailService: { sendEmail: jest.Mock }; + let configService: { get: jest.Mock }; beforeEach(() => { prisma = { digestPreference: { - upsert: jest.fn().mockResolvedValue({ userId: 'u1', enabled: true, frequency: 'DAILY', unsubscribeToken: 'tok' }), + upsert: jest.fn().mockResolvedValue({ + userId: 'u1', + enabled: true, + frequency: 'DAILY', + unsubscribeToken: 'tok', + }), findUnique: jest.fn().mockResolvedValue(null), } as any, + notification: { + findMany: jest.fn().mockResolvedValue([ + { + title: 'New property update', + message: 'A property has new activity', + type: 'INFO', + createdAt: new Date('2026-08-31T12:00:00.000Z'), + }, + ]), + } as any, }; + emailService = { sendEmail: jest.fn().mockResolvedValue(undefined) }; + configService = { get: jest.fn().mockReturnValue('https://api.propchain.example/api') }; + service = new EmailDigestService( prisma as unknown as PrismaService, - { sendEmail: jest.fn() } as unknown as EmailService, - { get: jest.fn().mockReturnValue('') } as unknown as ConfigService, + emailService as unknown as EmailService, + configService as unknown as ConfigService, ); }); @@ -29,4 +57,32 @@ describe('EmailDigestService', () => { ); expect(result.userId).toBe('u1'); }); -}); \ No newline at end of file + + it('uses configured API_URL for digest unsubscribe links', async () => { + await service['sendDigestForUser']( + { id: 'u1', email: 'user@example.com', firstName: 'User' }, + new Date('2026-08-30T12:00:00.000Z'), + 'token-123', + ); + + expect(emailService.sendEmail).toHaveBeenCalledWith( + expect.objectContaining({ + html: expect.stringContaining( + 'https://api.propchain.example/api/email-digest/unsubscribe?token=token-123', + ), + }), + ); + }); + + it('fails when API_URL is missing for digest unsubscribe links', async () => { + configService.get.mockReturnValue(undefined); + + await expect( + service['sendDigestForUser']( + { id: 'u1', email: 'user@example.com', firstName: 'User' }, + new Date('2026-08-30T12:00:00.000Z'), + 'token-123', + ), + ).rejects.toThrow('API_URL environment variable is not set'); + }); +}); diff --git a/src/email-digest/email-digest.service.ts b/src/email-digest/email-digest.service.ts index beca649f..99c5cd9b 100644 --- a/src/email-digest/email-digest.service.ts +++ b/src/email-digest/email-digest.service.ts @@ -92,7 +92,13 @@ export class EmailDigestService { if (notifications.length === 0) return; - const apiUrl = this.configService.get('API_URL', 'http://localhost:3000/api'); + const apiUrl = this.configService.get('API_URL'); + if (!apiUrl) { + throw new Error( + 'API_URL environment variable is not set. Cannot generate digest unsubscribe link.', + ); + } + const unsubscribeUrl = `${apiUrl}/email-digest/unsubscribe?token=${unsubscribeToken}`; const html = this.buildDigestHtml(user.firstName, notifications, unsubscribeUrl);