feat(mail): clarify mailgun configuration feedback
This commit is contained in:
@@ -1,15 +1,17 @@
|
||||
// Mock the mailgun config before any imports
|
||||
const mockGetMailgunConfig = jest.fn().mockReturnValue({
|
||||
apiKey: 'test-api-key',
|
||||
domain: 'test.mailgun.org',
|
||||
baseUrl: 'https://api.mailgun.net/v3',
|
||||
fromName: 'Test App',
|
||||
fromEmail: 'test@example.com',
|
||||
});
|
||||
jest.mock('../mailgun.config', () => {
|
||||
const defaultConfig = {
|
||||
apiKey: 'test-api-key',
|
||||
domain: 'test.mailgun.org',
|
||||
baseUrl: 'https://api.mailgun.net/v3',
|
||||
fromName: 'Test App',
|
||||
fromEmail: 'test@example.com',
|
||||
};
|
||||
|
||||
jest.mock('../mailgun.config', () => ({
|
||||
getMailgunConfig: mockGetMailgunConfig,
|
||||
}));
|
||||
return {
|
||||
getMailgunConfig: jest.fn(() => defaultConfig),
|
||||
};
|
||||
});
|
||||
|
||||
// Mock the app config
|
||||
jest.mock('../../config/unified.config', () => ({
|
||||
@@ -18,6 +20,7 @@ jest.mock('../../config/unified.config', () => ({
|
||||
baseUrl: 'http://localhost:3000',
|
||||
},
|
||||
},
|
||||
getAppConfig: jest.fn(() => ({ baseUrl: 'http://localhost:3000' })),
|
||||
}));
|
||||
|
||||
// Mock global fetch and related APIs
|
||||
@@ -32,10 +35,28 @@ global.btoa = jest
|
||||
|
||||
// Import the service after mocks are set up
|
||||
import { MailgunService } from '../mailgun.service';
|
||||
import { getMailgunConfig } from '../mailgun.config';
|
||||
import { logger } from '../logging';
|
||||
|
||||
const mockGetMailgunConfig = getMailgunConfig as jest.MockedFunction<
|
||||
typeof getMailgunConfig
|
||||
>;
|
||||
|
||||
mockGetMailgunConfig.mockReturnValue({
|
||||
apiKey: 'test-api-key',
|
||||
domain: 'test.mailgun.org',
|
||||
baseUrl: 'https://api.mailgun.net/v3',
|
||||
fromName: 'Test App',
|
||||
fromEmail: 'test@example.com',
|
||||
});
|
||||
|
||||
describe('MailgunService', () => {
|
||||
let mockFetch: jest.MockedFunction<typeof fetch>;
|
||||
let mockFormData: jest.MockedFunction<any>;
|
||||
let warnSpy: jest.SpyInstance;
|
||||
let infoSpy: jest.SpyInstance;
|
||||
let errorSpy: jest.SpyInstance;
|
||||
let debugSpy: jest.SpyInstance;
|
||||
|
||||
const mockConfig = {
|
||||
apiKey: 'test-api-key',
|
||||
@@ -47,10 +68,13 @@ describe('MailgunService', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
console.warn = jest.fn();
|
||||
console.error = jest.fn();
|
||||
mockFetch = fetch as jest.MockedFunction<typeof fetch>;
|
||||
mockFormData = MockFormData;
|
||||
|
||||
warnSpy = jest.spyOn(logger, 'warn').mockImplementation(() => undefined);
|
||||
infoSpy = jest.spyOn(logger, 'info').mockImplementation(() => undefined);
|
||||
errorSpy = jest.spyOn(logger, 'error').mockImplementation(() => undefined);
|
||||
debugSpy = jest.spyOn(logger, 'debug').mockImplementation(() => undefined);
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
@@ -67,11 +91,28 @@ describe('MailgunService', () => {
|
||||
|
||||
new MailgunService();
|
||||
|
||||
expect(console.warn).toHaveBeenCalledWith(
|
||||
'📧 Mailgun Service: Running in development mode (emails will be logged only)'
|
||||
expect(warnSpy).toHaveBeenCalledWith(
|
||||
'Mailgun running in development mode; emails will not be delivered',
|
||||
'MAILGUN',
|
||||
{
|
||||
missingFields: [
|
||||
'VITE_MAILGUN_API_KEY',
|
||||
'VITE_MAILGUN_DOMAIN',
|
||||
'VITE_MAILGUN_FROM_EMAIL',
|
||||
],
|
||||
domain: undefined,
|
||||
}
|
||||
);
|
||||
expect(console.warn).toHaveBeenCalledWith(
|
||||
'💡 To enable real emails, configure Mailgun credentials in .env.local'
|
||||
expect(infoSpy).toHaveBeenCalledWith(
|
||||
'To enable email delivery, configure Mailgun environment variables',
|
||||
'MAILGUN',
|
||||
{
|
||||
requiredVariables: [
|
||||
'VITE_MAILGUN_API_KEY',
|
||||
'VITE_MAILGUN_DOMAIN',
|
||||
'VITE_MAILGUN_FROM_EMAIL',
|
||||
],
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -80,10 +121,15 @@ describe('MailgunService', () => {
|
||||
|
||||
new MailgunService();
|
||||
|
||||
expect(console.warn).toHaveBeenCalledWith(
|
||||
'📧 Mailgun Service: Configured for production with domain:',
|
||||
'test.mailgun.org'
|
||||
expect(infoSpy).toHaveBeenCalledWith(
|
||||
'Mailgun configured for delivery',
|
||||
'MAILGUN',
|
||||
{
|
||||
domain: 'test.mailgun.org',
|
||||
fromEmail: 'test@example.com',
|
||||
}
|
||||
);
|
||||
expect(warnSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -122,8 +168,9 @@ describe('MailgunService', () => {
|
||||
}),
|
||||
})
|
||||
);
|
||||
expect(console.warn).toHaveBeenCalledWith(
|
||||
'📧 Email sent successfully via Mailgun:',
|
||||
expect(infoSpy).toHaveBeenCalledWith(
|
||||
'Email sent via Mailgun',
|
||||
'MAILGUN',
|
||||
{
|
||||
to: 'test@example.com',
|
||||
subject: 'Test Subject',
|
||||
@@ -148,8 +195,10 @@ describe('MailgunService', () => {
|
||||
const result = await service.sendEmail('test@example.com', template);
|
||||
|
||||
expect(result).toBe(false);
|
||||
expect(console.error).toHaveBeenCalledWith(
|
||||
'Email sending failed:',
|
||||
expect(errorSpy).toHaveBeenCalledWith(
|
||||
'Mailgun email send failed',
|
||||
'MAILGUN',
|
||||
{ domain: 'test.mailgun.org' },
|
||||
expect.any(Error)
|
||||
);
|
||||
});
|
||||
@@ -165,8 +214,10 @@ describe('MailgunService', () => {
|
||||
const result = await service.sendEmail('test@example.com', template);
|
||||
|
||||
expect(result).toBe(false);
|
||||
expect(console.error).toHaveBeenCalledWith(
|
||||
'Email sending failed:',
|
||||
expect(errorSpy).toHaveBeenCalledWith(
|
||||
'Mailgun email send failed',
|
||||
'MAILGUN',
|
||||
{ domain: 'test.mailgun.org' },
|
||||
expect.any(Error)
|
||||
);
|
||||
});
|
||||
@@ -237,6 +288,56 @@ describe('MailgunService', () => {
|
||||
expect.anything()
|
||||
);
|
||||
});
|
||||
|
||||
test('logs preview and skips send when configuration is missing', async () => {
|
||||
const unconfiguredConfig = {
|
||||
apiKey: undefined,
|
||||
domain: undefined,
|
||||
baseUrl: 'https://api.mailgun.net/v3',
|
||||
fromName: 'Test App',
|
||||
fromEmail: undefined,
|
||||
};
|
||||
|
||||
mockGetMailgunConfig.mockReturnValue(unconfiguredConfig);
|
||||
const unconfiguredService = new MailgunService();
|
||||
|
||||
const template = {
|
||||
subject: 'Test Subject',
|
||||
html: '<p>Test HTML</p>',
|
||||
};
|
||||
|
||||
const result = await unconfiguredService.sendEmail(
|
||||
'test@example.com',
|
||||
template
|
||||
);
|
||||
|
||||
expect(result).toBe(false);
|
||||
expect(mockFetch).not.toHaveBeenCalled();
|
||||
expect(warnSpy).toHaveBeenCalledWith(
|
||||
'Skipping email send; Mailgun is not configured',
|
||||
'MAILGUN',
|
||||
expect.objectContaining({
|
||||
to: 'test@example.com',
|
||||
missingFields: [
|
||||
'VITE_MAILGUN_API_KEY',
|
||||
'VITE_MAILGUN_DOMAIN',
|
||||
'VITE_MAILGUN_FROM_EMAIL',
|
||||
],
|
||||
preview: true,
|
||||
})
|
||||
);
|
||||
expect(debugSpy).toHaveBeenCalledWith(
|
||||
'Mailgun email preview',
|
||||
'MAILGUN',
|
||||
expect.objectContaining({
|
||||
to: 'test@example.com',
|
||||
subject: 'Test Subject',
|
||||
html: '<p>Test HTML</p>',
|
||||
})
|
||||
);
|
||||
|
||||
mockGetMailgunConfig.mockReturnValue(mockConfig);
|
||||
});
|
||||
});
|
||||
|
||||
describe('sendVerificationEmail', () => {
|
||||
@@ -360,6 +461,7 @@ describe('MailgunService', () => {
|
||||
mode: 'production',
|
||||
domain: 'test.mailgun.org',
|
||||
fromEmail: 'test@example.com',
|
||||
missingFields: [],
|
||||
});
|
||||
});
|
||||
|
||||
@@ -382,6 +484,11 @@ describe('MailgunService', () => {
|
||||
mode: 'development',
|
||||
domain: undefined,
|
||||
fromEmail: undefined,
|
||||
missingFields: [
|
||||
'VITE_MAILGUN_API_KEY',
|
||||
'VITE_MAILGUN_DOMAIN',
|
||||
'VITE_MAILGUN_FROM_EMAIL',
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
@@ -404,6 +511,13 @@ describe('MailgunService', () => {
|
||||
mode: 'development',
|
||||
domain: '',
|
||||
fromEmail: '',
|
||||
missingFields: [
|
||||
'VITE_MAILGUN_API_KEY',
|
||||
'VITE_MAILGUN_DOMAIN',
|
||||
'VITE_MAILGUN_FROM_EMAIL',
|
||||
'VITE_MAILGUN_BASE_URL',
|
||||
'VITE_MAILGUN_FROM_NAME',
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user