- Add comprehensive tests for MailgunService (439 lines) * Email sending functionality with template generation * Configuration status validation * Error handling and edge cases * Mock setup for fetch API and FormData - Add DatabaseService tests (451 lines) * Strategy pattern testing (Mock vs Production) * All CRUD operations for users, medications, settings * Legacy compatibility method testing * Proper TypeScript typing - Add MockDatabaseStrategy tests (434 lines) * Complete coverage of mock database implementation * User operations, medication management * Settings and custom reminders functionality * Data persistence and error handling - Add React hooks tests * useLocalStorage hook with comprehensive edge cases (340 lines) * useSettings hook with fetch operations and error handling (78 lines) - Fix auth integration tests * Update mocking to use new database service instead of legacy couchdb.factory * Fix service variable references and expectations - Simplify mailgun config tests * Remove redundant edge case testing * Focus on core functionality validation - Fix all TypeScript and ESLint issues * Proper FormData mock typing * Correct database entity type usage * Remove non-existent property references Test Results: - 184 total tests passing - Comprehensive coverage of core services - Zero TypeScript compilation errors - Full ESLint compliance
291 lines
9.3 KiB
TypeScript
291 lines
9.3 KiB
TypeScript
import {
|
|
getMailgunConfig,
|
|
isMailgunConfigured,
|
|
isDevelopmentMode,
|
|
} from '../mailgun.config';
|
|
|
|
// Mock environment utilities
|
|
jest.mock('../../utils/env', () => ({
|
|
getEnvVar: jest.fn(),
|
|
isProduction: jest.fn(),
|
|
}));
|
|
|
|
describe('Mailgun Configuration', () => {
|
|
let mockGetEnvVar: jest.MockedFunction<any>;
|
|
let mockIsProduction: jest.MockedFunction<any>;
|
|
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
|
|
// Get mocked functions
|
|
const envUtils = require('../../utils/env');
|
|
mockGetEnvVar = envUtils.getEnvVar;
|
|
mockIsProduction = envUtils.isProduction;
|
|
|
|
// Default mock implementations
|
|
mockIsProduction.mockReturnValue(false);
|
|
});
|
|
|
|
describe('getMailgunConfig', () => {
|
|
test('should return config with all environment variables set', () => {
|
|
mockGetEnvVar
|
|
.mockReturnValueOnce('test-api-key') // VITE_MAILGUN_API_KEY
|
|
.mockReturnValueOnce('test.domain.com') // VITE_MAILGUN_DOMAIN
|
|
.mockReturnValueOnce('https://api.mailgun.net/v3') // VITE_MAILGUN_BASE_URL
|
|
.mockReturnValueOnce('Test App') // VITE_MAILGUN_FROM_NAME
|
|
.mockReturnValueOnce('noreply@test.com'); // VITE_MAILGUN_FROM_EMAIL
|
|
|
|
const config = getMailgunConfig();
|
|
|
|
expect(config).toEqual({
|
|
apiKey: 'test-api-key',
|
|
domain: 'test.domain.com',
|
|
baseUrl: 'https://api.mailgun.net/v3',
|
|
fromName: 'Test App',
|
|
fromEmail: 'noreply@test.com',
|
|
});
|
|
|
|
expect(mockGetEnvVar).toHaveBeenCalledTimes(5);
|
|
expect(mockGetEnvVar).toHaveBeenNthCalledWith(1, 'VITE_MAILGUN_API_KEY');
|
|
expect(mockGetEnvVar).toHaveBeenNthCalledWith(2, 'VITE_MAILGUN_DOMAIN');
|
|
expect(mockGetEnvVar).toHaveBeenNthCalledWith(
|
|
3,
|
|
'VITE_MAILGUN_BASE_URL',
|
|
'https://api.mailgun.net/v3'
|
|
);
|
|
expect(mockGetEnvVar).toHaveBeenNthCalledWith(
|
|
4,
|
|
'VITE_MAILGUN_FROM_NAME',
|
|
'Medication Reminder'
|
|
);
|
|
expect(mockGetEnvVar).toHaveBeenNthCalledWith(
|
|
5,
|
|
'VITE_MAILGUN_FROM_EMAIL'
|
|
);
|
|
});
|
|
|
|
test('should use default values for optional config', () => {
|
|
mockGetEnvVar.mockImplementation((key: string, defaultValue?: string) => {
|
|
if (key === 'VITE_MAILGUN_API_KEY') return 'test-api-key';
|
|
if (key === 'VITE_MAILGUN_DOMAIN') return 'test.domain.com';
|
|
if (key === 'VITE_MAILGUN_FROM_EMAIL') return 'noreply@test.com';
|
|
return defaultValue;
|
|
});
|
|
|
|
const config = getMailgunConfig();
|
|
|
|
expect(config.baseUrl).toBe('https://api.mailgun.net/v3');
|
|
expect(config.fromName).toBe('Medication Reminder');
|
|
});
|
|
|
|
test('should handle missing environment variables gracefully', () => {
|
|
mockGetEnvVar.mockImplementation(
|
|
(_key: string, defaultValue?: string) => defaultValue
|
|
);
|
|
|
|
const config = getMailgunConfig();
|
|
|
|
expect(config).toEqual({
|
|
apiKey: undefined,
|
|
domain: undefined,
|
|
baseUrl: 'https://api.mailgun.net/v3',
|
|
fromName: 'Medication Reminder',
|
|
fromEmail: undefined,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('isMailgunConfigured', () => {
|
|
test('should return true when all required config is present', () => {
|
|
mockGetEnvVar.mockImplementation((key: string, defaultValue?: string) => {
|
|
switch (key) {
|
|
case 'VITE_MAILGUN_API_KEY':
|
|
return 'test-api-key';
|
|
case 'VITE_MAILGUN_DOMAIN':
|
|
return 'test.domain.com';
|
|
case 'VITE_MAILGUN_FROM_EMAIL':
|
|
return 'noreply@test.com';
|
|
case 'VITE_MAILGUN_BASE_URL':
|
|
return 'https://api.mailgun.net/v3';
|
|
case 'VITE_MAILGUN_FROM_NAME':
|
|
return 'Test App';
|
|
default:
|
|
return defaultValue;
|
|
}
|
|
});
|
|
|
|
expect(isMailgunConfigured()).toBe(true);
|
|
});
|
|
|
|
test('should return false when apiKey is missing', () => {
|
|
mockGetEnvVar.mockImplementation((key: string, defaultValue?: string) => {
|
|
switch (key) {
|
|
case 'VITE_MAILGUN_API_KEY':
|
|
return undefined;
|
|
case 'VITE_MAILGUN_DOMAIN':
|
|
return 'test.domain.com';
|
|
case 'VITE_MAILGUN_FROM_EMAIL':
|
|
return 'noreply@test.com';
|
|
case 'VITE_MAILGUN_BASE_URL':
|
|
return 'https://api.mailgun.net/v3';
|
|
case 'VITE_MAILGUN_FROM_NAME':
|
|
return 'Test App';
|
|
default:
|
|
return defaultValue;
|
|
}
|
|
});
|
|
|
|
expect(isMailgunConfigured()).toBe(false);
|
|
});
|
|
|
|
test('should return false when domain is missing', () => {
|
|
mockGetEnvVar.mockImplementation((key: string, defaultValue?: string) => {
|
|
switch (key) {
|
|
case 'VITE_MAILGUN_API_KEY':
|
|
return 'test-api-key';
|
|
case 'VITE_MAILGUN_DOMAIN':
|
|
return undefined;
|
|
case 'VITE_MAILGUN_FROM_EMAIL':
|
|
return 'noreply@test.com';
|
|
case 'VITE_MAILGUN_BASE_URL':
|
|
return 'https://api.mailgun.net/v3';
|
|
case 'VITE_MAILGUN_FROM_NAME':
|
|
return 'Test App';
|
|
default:
|
|
return defaultValue;
|
|
}
|
|
});
|
|
|
|
expect(isMailgunConfigured()).toBe(false);
|
|
});
|
|
|
|
test('should return false when fromEmail is missing', () => {
|
|
mockGetEnvVar.mockImplementation((key: string, defaultValue?: string) => {
|
|
switch (key) {
|
|
case 'VITE_MAILGUN_API_KEY':
|
|
return 'test-api-key';
|
|
case 'VITE_MAILGUN_DOMAIN':
|
|
return 'test.domain.com';
|
|
case 'VITE_MAILGUN_FROM_EMAIL':
|
|
return undefined;
|
|
case 'VITE_MAILGUN_BASE_URL':
|
|
return 'https://api.mailgun.net/v3';
|
|
case 'VITE_MAILGUN_FROM_NAME':
|
|
return 'Test App';
|
|
default:
|
|
return defaultValue;
|
|
}
|
|
});
|
|
|
|
expect(isMailgunConfigured()).toBe(false);
|
|
});
|
|
|
|
test('should return false when required fields are empty strings', () => {
|
|
mockGetEnvVar.mockImplementation((key: string, defaultValue?: string) => {
|
|
switch (key) {
|
|
case 'VITE_MAILGUN_API_KEY':
|
|
return '';
|
|
case 'VITE_MAILGUN_DOMAIN':
|
|
return 'test.domain.com';
|
|
case 'VITE_MAILGUN_FROM_EMAIL':
|
|
return 'noreply@test.com';
|
|
case 'VITE_MAILGUN_BASE_URL':
|
|
return 'https://api.mailgun.net/v3';
|
|
case 'VITE_MAILGUN_FROM_NAME':
|
|
return 'Test App';
|
|
default:
|
|
return defaultValue;
|
|
}
|
|
});
|
|
|
|
expect(isMailgunConfigured()).toBe(false);
|
|
});
|
|
|
|
test('should return true even when optional fields are missing', () => {
|
|
// Provide only required fields (optional ones fall back to defaults)
|
|
mockGetEnvVar.mockImplementation((key: string, defaultValue?: string) => {
|
|
switch (key) {
|
|
case 'VITE_MAILGUN_API_KEY':
|
|
return 'test-api-key';
|
|
case 'VITE_MAILGUN_DOMAIN':
|
|
return 'test.domain.com';
|
|
case 'VITE_MAILGUN_FROM_EMAIL':
|
|
return 'noreply@test.com';
|
|
// baseUrl and fromName return undefined so defaults are applied
|
|
default:
|
|
return defaultValue;
|
|
}
|
|
});
|
|
|
|
expect(isMailgunConfigured()).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('isDevelopmentMode', () => {
|
|
test('should return true when not in production and Mailgun not configured', () => {
|
|
mockIsProduction.mockReturnValue(false);
|
|
mockGetEnvVar.mockReturnValue(undefined);
|
|
|
|
expect(isDevelopmentMode()).toBe(true);
|
|
});
|
|
|
|
test('should return false when in production even if Mailgun not configured', () => {
|
|
mockIsProduction.mockReturnValue(true);
|
|
|
|
expect(isDevelopmentMode()).toBe(false);
|
|
});
|
|
|
|
test('should return false when Mailgun is configured regardless of environment', () => {
|
|
mockIsProduction.mockReturnValue(false);
|
|
mockGetEnvVar
|
|
.mockReturnValueOnce('test-api-key')
|
|
.mockReturnValueOnce('test.domain.com')
|
|
.mockReturnValueOnce('https://api.mailgun.net/v3')
|
|
.mockReturnValueOnce('Test App')
|
|
.mockReturnValueOnce('noreply@test.com');
|
|
|
|
expect(isDevelopmentMode()).toBe(false);
|
|
});
|
|
|
|
test('should return false when in production and Mailgun is configured', () => {
|
|
mockIsProduction.mockReturnValue(true);
|
|
mockGetEnvVar
|
|
.mockReturnValueOnce('test-api-key')
|
|
.mockReturnValueOnce('test.domain.com')
|
|
.mockReturnValueOnce('https://api.mailgun.net/v3')
|
|
.mockReturnValueOnce('Test App')
|
|
.mockReturnValueOnce('noreply@test.com');
|
|
|
|
expect(isDevelopmentMode()).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('integration scenarios', () => {
|
|
test('should work with complete configuration', () => {
|
|
mockGetEnvVar.mockImplementation((key: string, defaultValue?: string) => {
|
|
switch (key) {
|
|
case 'VITE_MAILGUN_API_KEY':
|
|
return 'real-api-key';
|
|
case 'VITE_MAILGUN_DOMAIN':
|
|
return 'mg.example.com';
|
|
case 'VITE_MAILGUN_FROM_EMAIL':
|
|
return 'support@example.com';
|
|
default:
|
|
return defaultValue;
|
|
}
|
|
});
|
|
|
|
expect(isMailgunConfigured()).toBe(true);
|
|
expect(isDevelopmentMode()).toBe(false);
|
|
});
|
|
|
|
test('should work in development mode', () => {
|
|
mockGetEnvVar.mockReturnValue(undefined);
|
|
mockIsProduction.mockReturnValue(false);
|
|
|
|
expect(isMailgunConfigured()).toBe(false);
|
|
expect(isDevelopmentMode()).toBe(true);
|
|
});
|
|
});
|
|
});
|