Files
smoothschedule/frontend/src/api/__tests__/platformEmailAddresses.test.ts
poduck 8dc2248f1f feat: Add comprehensive test suite and misc improvements
- Add frontend unit tests with Vitest for components, hooks, pages, and utilities
- Add backend tests for webhooks, notifications, middleware, and edge cases
- Add ForgotPassword, NotFound, and ResetPassword pages
- Add migration for orphaned staff resources conversion
- Add coverage directory to gitignore (generated reports)
- Various bug fixes and improvements from previous work

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 02:36:46 -05:00

1233 lines
39 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest';
// Mock apiClient
vi.mock('../client', () => ({
default: {
get: vi.fn(),
post: vi.fn(),
patch: vi.fn(),
delete: vi.fn(),
},
}));
import {
getPlatformEmailAddresses,
getPlatformEmailAddress,
createPlatformEmailAddress,
updatePlatformEmailAddress,
deletePlatformEmailAddress,
removeLocalPlatformEmailAddress,
syncPlatformEmailAddress,
testImapConnection,
testSmtpConnection,
setAsDefault,
testMailServerConnection,
getMailServerAccounts,
getAvailableDomains,
getAssignableUsers,
importFromMailServer,
type PlatformEmailAddress,
type PlatformEmailAddressListItem,
type PlatformEmailAddressCreate,
type PlatformEmailAddressUpdate,
type AssignedUser,
type AssignableUser,
type EmailDomain,
type TestConnectionResponse,
type SyncResponse,
type MailServerAccountsResponse,
type ImportFromMailServerResponse,
} from '../platformEmailAddresses';
import apiClient from '../client';
describe('platformEmailAddresses API', () => {
beforeEach(() => {
vi.clearAllMocks();
});
// ============================================================================
// List and Retrieve
// ============================================================================
describe('getPlatformEmailAddresses', () => {
it('fetches all platform email addresses from API', async () => {
const mockAddresses: PlatformEmailAddressListItem[] = [
{
id: 1,
display_name: 'Support Team',
sender_name: 'Support',
effective_sender_name: 'Support',
local_part: 'support',
domain: 'talova.net',
email_address: 'support@talova.net',
color: '#3B82F6',
assigned_user: {
id: 10,
email: 'john@example.com',
first_name: 'John',
last_name: 'Doe',
full_name: 'John Doe',
},
is_active: true,
is_default: true,
mail_server_synced: true,
last_check_at: '2025-01-15T10:00:00Z',
emails_processed_count: 150,
created_at: '2025-01-01T00:00:00Z',
updated_at: '2025-01-15T00:00:00Z',
},
{
id: 2,
display_name: 'Sales Team',
sender_name: 'Sales',
effective_sender_name: 'Sales',
local_part: 'sales',
domain: 'talova.net',
email_address: 'sales@talova.net',
color: '#10B981',
assigned_user: null,
is_active: true,
is_default: false,
mail_server_synced: false,
emails_processed_count: 0,
created_at: '2025-01-10T00:00:00Z',
updated_at: '2025-01-10T00:00:00Z',
},
];
vi.mocked(apiClient.get).mockResolvedValue({ data: mockAddresses });
const result = await getPlatformEmailAddresses();
expect(apiClient.get).toHaveBeenCalledWith('/platform/email-addresses/');
expect(result).toEqual(mockAddresses);
expect(result).toHaveLength(2);
expect(result[0].is_default).toBe(true);
expect(result[0].assigned_user?.full_name).toBe('John Doe');
expect(result[1].assigned_user).toBeNull();
});
it('returns empty array when no email addresses exist', async () => {
vi.mocked(apiClient.get).mockResolvedValue({ data: [] });
const result = await getPlatformEmailAddresses();
expect(apiClient.get).toHaveBeenCalledWith('/platform/email-addresses/');
expect(result).toEqual([]);
expect(result).toHaveLength(0);
});
it('handles email addresses with no assigned users', async () => {
const mockAddresses: PlatformEmailAddressListItem[] = [
{
id: 1,
display_name: 'Unassigned',
sender_name: '',
effective_sender_name: 'Unassigned',
local_part: 'info',
domain: 'talova.net',
email_address: 'info@talova.net',
color: '#6B7280',
assigned_user: null,
is_active: false,
is_default: false,
mail_server_synced: false,
emails_processed_count: 0,
created_at: '2025-01-01T00:00:00Z',
updated_at: '2025-01-01T00:00:00Z',
},
];
vi.mocked(apiClient.get).mockResolvedValue({ data: mockAddresses });
const result = await getPlatformEmailAddresses();
expect(result[0].assigned_user).toBeNull();
expect(result[0].is_active).toBe(false);
});
});
describe('getPlatformEmailAddress', () => {
it('fetches a specific platform email address by ID', async () => {
const mockAddress: PlatformEmailAddress = {
id: 1,
display_name: 'Support Team',
sender_name: 'Support',
effective_sender_name: 'Support',
local_part: 'support',
domain: 'talova.net',
email_address: 'support@talova.net',
color: '#3B82F6',
is_active: true,
is_default: true,
mail_server_synced: true,
last_sync_error: undefined,
last_synced_at: '2025-01-15T09:00:00Z',
last_check_at: '2025-01-15T10:00:00Z',
emails_processed_count: 150,
created_at: '2025-01-01T00:00:00Z',
updated_at: '2025-01-15T00:00:00Z',
imap_settings: {
host: 'mail.talova.net',
port: 993,
use_ssl: true,
username: 'support@talova.net',
folder: 'INBOX',
},
smtp_settings: {
host: 'mail.talova.net',
port: 587,
use_tls: true,
use_ssl: false,
username: 'support@talova.net',
},
};
vi.mocked(apiClient.get).mockResolvedValue({ data: mockAddress });
const result = await getPlatformEmailAddress(1);
expect(apiClient.get).toHaveBeenCalledWith('/platform/email-addresses/1/');
expect(result).toEqual(mockAddress);
expect(result.imap_settings?.host).toBe('mail.talova.net');
expect(result.smtp_settings?.port).toBe(587);
});
it('fetches email address without IMAP/SMTP settings', async () => {
const mockAddress: PlatformEmailAddress = {
id: 2,
display_name: 'Info',
sender_name: '',
effective_sender_name: 'Info',
local_part: 'info',
domain: 'talova.net',
email_address: 'info@talova.net',
color: '#6B7280',
is_active: true,
is_default: false,
mail_server_synced: false,
emails_processed_count: 0,
created_at: '2025-01-10T00:00:00Z',
updated_at: '2025-01-10T00:00:00Z',
};
vi.mocked(apiClient.get).mockResolvedValue({ data: mockAddress });
const result = await getPlatformEmailAddress(2);
expect(apiClient.get).toHaveBeenCalledWith('/platform/email-addresses/2/');
expect(result.imap_settings).toBeUndefined();
expect(result.smtp_settings).toBeUndefined();
});
it('fetches email address with sync error', async () => {
const mockAddress: PlatformEmailAddress = {
id: 3,
display_name: 'Failed Sync',
sender_name: '',
effective_sender_name: 'Failed Sync',
local_part: 'test',
domain: 'talova.net',
email_address: 'test@talova.net',
color: '#EF4444',
is_active: true,
is_default: false,
mail_server_synced: false,
last_sync_error: 'Connection timeout',
last_synced_at: '2025-01-14T12:00:00Z',
emails_processed_count: 0,
created_at: '2025-01-14T00:00:00Z',
updated_at: '2025-01-14T12:00:00Z',
};
vi.mocked(apiClient.get).mockResolvedValue({ data: mockAddress });
const result = await getPlatformEmailAddress(3);
expect(result.last_sync_error).toBe('Connection timeout');
expect(result.mail_server_synced).toBe(false);
});
it('handles different email address IDs correctly', async () => {
const mockAddress: PlatformEmailAddress = {
id: 999,
display_name: 'Test',
sender_name: '',
effective_sender_name: 'Test',
local_part: 'test',
domain: 'example.com',
email_address: 'test@example.com',
color: '#000000',
is_active: true,
is_default: false,
mail_server_synced: false,
emails_processed_count: 0,
created_at: '2025-01-01T00:00:00Z',
updated_at: '2025-01-01T00:00:00Z',
};
vi.mocked(apiClient.get).mockResolvedValue({ data: mockAddress });
await getPlatformEmailAddress(999);
expect(apiClient.get).toHaveBeenCalledWith('/platform/email-addresses/999/');
});
});
// ============================================================================
// Create and Update
// ============================================================================
describe('createPlatformEmailAddress', () => {
it('creates a new platform email address with all fields', async () => {
const createData: PlatformEmailAddressCreate = {
display_name: 'New Support',
sender_name: 'Support Team',
assigned_user_id: 10,
local_part: 'support',
domain: 'talova.net',
color: '#3B82F6',
password: 'secure-password-123',
is_active: true,
is_default: false,
};
const mockResponse: PlatformEmailAddress = {
id: 5,
display_name: 'New Support',
sender_name: 'Support Team',
effective_sender_name: 'Support Team',
local_part: 'support',
domain: 'talova.net',
email_address: 'support@talova.net',
color: '#3B82F6',
is_active: true,
is_default: false,
mail_server_synced: false,
emails_processed_count: 0,
created_at: '2025-01-15T14:00:00Z',
updated_at: '2025-01-15T14:00:00Z',
};
vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });
const result = await createPlatformEmailAddress(createData);
expect(apiClient.post).toHaveBeenCalledWith('/platform/email-addresses/', createData);
expect(result).toEqual(mockResponse);
expect(result.id).toBe(5);
expect(result.email_address).toBe('support@talova.net');
});
it('creates email address without optional sender_name', async () => {
const createData: PlatformEmailAddressCreate = {
display_name: 'Info',
local_part: 'info',
domain: 'talova.net',
color: '#6B7280',
password: 'password123',
is_active: true,
is_default: false,
};
const mockResponse: PlatformEmailAddress = {
id: 6,
display_name: 'Info',
sender_name: '',
effective_sender_name: 'Info',
local_part: 'info',
domain: 'talova.net',
email_address: 'info@talova.net',
color: '#6B7280',
is_active: true,
is_default: false,
mail_server_synced: false,
emails_processed_count: 0,
created_at: '2025-01-15T15:00:00Z',
updated_at: '2025-01-15T15:00:00Z',
};
vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });
const result = await createPlatformEmailAddress(createData);
expect(result.sender_name).toBe('');
expect(result.effective_sender_name).toBe('Info');
});
it('creates email address without assigned user', async () => {
const createData: PlatformEmailAddressCreate = {
display_name: 'Unassigned',
assigned_user_id: null,
local_part: 'unassigned',
domain: 'talova.net',
color: '#9CA3AF',
password: 'pass123',
is_active: false,
is_default: false,
};
const mockResponse: PlatformEmailAddress = {
id: 7,
display_name: 'Unassigned',
sender_name: '',
effective_sender_name: 'Unassigned',
local_part: 'unassigned',
domain: 'talova.net',
email_address: 'unassigned@talova.net',
color: '#9CA3AF',
is_active: false,
is_default: false,
mail_server_synced: false,
emails_processed_count: 0,
created_at: '2025-01-15T16:00:00Z',
updated_at: '2025-01-15T16:00:00Z',
};
vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });
const result = await createPlatformEmailAddress(createData);
expect(result.is_active).toBe(false);
});
it('creates email address with is_default set to true', async () => {
const createData: PlatformEmailAddressCreate = {
display_name: 'Default Email',
local_part: 'default',
domain: 'talova.net',
color: '#F59E0B',
password: 'secure-pass',
is_active: true,
is_default: true,
};
const mockResponse: PlatformEmailAddress = {
id: 8,
display_name: 'Default Email',
sender_name: '',
effective_sender_name: 'Default Email',
local_part: 'default',
domain: 'talova.net',
email_address: 'default@talova.net',
color: '#F59E0B',
is_active: true,
is_default: true,
mail_server_synced: true,
emails_processed_count: 0,
created_at: '2025-01-15T17:00:00Z',
updated_at: '2025-01-15T17:00:00Z',
};
vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });
const result = await createPlatformEmailAddress(createData);
expect(result.is_default).toBe(true);
});
});
describe('updatePlatformEmailAddress', () => {
it('updates email address with full data', async () => {
const updateData: PlatformEmailAddressUpdate = {
display_name: 'Updated Support',
sender_name: 'Updated Sender',
assigned_user_id: 20,
color: '#EF4444',
password: 'new-password-456',
is_active: false,
is_default: true,
};
const mockResponse: PlatformEmailAddress = {
id: 1,
display_name: 'Updated Support',
sender_name: 'Updated Sender',
effective_sender_name: 'Updated Sender',
local_part: 'support',
domain: 'talova.net',
email_address: 'support@talova.net',
color: '#EF4444',
is_active: false,
is_default: true,
mail_server_synced: true,
emails_processed_count: 150,
created_at: '2025-01-01T00:00:00Z',
updated_at: '2025-01-15T18:00:00Z',
};
vi.mocked(apiClient.patch).mockResolvedValue({ data: mockResponse });
const result = await updatePlatformEmailAddress(1, updateData);
expect(apiClient.patch).toHaveBeenCalledWith('/platform/email-addresses/1/', updateData);
expect(result).toEqual(mockResponse);
expect(result.display_name).toBe('Updated Support');
expect(result.is_active).toBe(false);
expect(result.is_default).toBe(true);
});
it('updates email address with partial data - only display_name', async () => {
const updateData: PlatformEmailAddressUpdate = {
display_name: 'New Display Name',
};
const mockResponse: PlatformEmailAddress = {
id: 2,
display_name: 'New Display Name',
sender_name: 'Sales',
effective_sender_name: 'Sales',
local_part: 'sales',
domain: 'talova.net',
email_address: 'sales@talova.net',
color: '#10B981',
is_active: true,
is_default: false,
mail_server_synced: true,
emails_processed_count: 50,
created_at: '2025-01-10T00:00:00Z',
updated_at: '2025-01-15T18:30:00Z',
};
vi.mocked(apiClient.patch).mockResolvedValue({ data: mockResponse });
const result = await updatePlatformEmailAddress(2, updateData);
expect(apiClient.patch).toHaveBeenCalledWith('/platform/email-addresses/2/', updateData);
expect(result.display_name).toBe('New Display Name');
});
it('updates only color', async () => {
const updateData: PlatformEmailAddressUpdate = {
color: '#8B5CF6',
};
const mockResponse: PlatformEmailAddress = {
id: 3,
display_name: 'Info',
sender_name: '',
effective_sender_name: 'Info',
local_part: 'info',
domain: 'talova.net',
email_address: 'info@talova.net',
color: '#8B5CF6',
is_active: true,
is_default: false,
mail_server_synced: false,
emails_processed_count: 0,
created_at: '2025-01-14T00:00:00Z',
updated_at: '2025-01-15T19:00:00Z',
};
vi.mocked(apiClient.patch).mockResolvedValue({ data: mockResponse });
const result = await updatePlatformEmailAddress(3, updateData);
expect(result.color).toBe('#8B5CF6');
});
it('updates only is_active status', async () => {
const updateData: PlatformEmailAddressUpdate = {
is_active: false,
};
const mockResponse: PlatformEmailAddress = {
id: 4,
display_name: 'Test',
sender_name: '',
effective_sender_name: 'Test',
local_part: 'test',
domain: 'talova.net',
email_address: 'test@talova.net',
color: '#6B7280',
is_active: false,
is_default: false,
mail_server_synced: true,
emails_processed_count: 25,
created_at: '2025-01-12T00:00:00Z',
updated_at: '2025-01-15T19:30:00Z',
};
vi.mocked(apiClient.patch).mockResolvedValue({ data: mockResponse });
const result = await updatePlatformEmailAddress(4, updateData);
expect(result.is_active).toBe(false);
});
it('updates assigned user to null (unassign)', async () => {
const updateData: PlatformEmailAddressUpdate = {
assigned_user_id: null,
};
const mockResponse: PlatformEmailAddress = {
id: 5,
display_name: 'Unassigned Now',
sender_name: '',
effective_sender_name: 'Unassigned Now',
local_part: 'unassigned',
domain: 'talova.net',
email_address: 'unassigned@talova.net',
color: '#9CA3AF',
is_active: true,
is_default: false,
mail_server_synced: true,
emails_processed_count: 10,
created_at: '2025-01-13T00:00:00Z',
updated_at: '2025-01-15T20:00:00Z',
};
vi.mocked(apiClient.patch).mockResolvedValue({ data: mockResponse });
await updatePlatformEmailAddress(5, updateData);
expect(apiClient.patch).toHaveBeenCalledWith('/platform/email-addresses/5/', updateData);
});
it('updates password only', async () => {
const updateData: PlatformEmailAddressUpdate = {
password: 'new-secure-password',
};
const mockResponse: PlatformEmailAddress = {
id: 6,
display_name: 'Password Updated',
sender_name: '',
effective_sender_name: 'Password Updated',
local_part: 'passupdate',
domain: 'talova.net',
email_address: 'passupdate@talova.net',
color: '#F59E0B',
is_active: true,
is_default: false,
mail_server_synced: true,
emails_processed_count: 5,
created_at: '2025-01-14T00:00:00Z',
updated_at: '2025-01-15T20:30:00Z',
};
vi.mocked(apiClient.patch).mockResolvedValue({ data: mockResponse });
await updatePlatformEmailAddress(6, updateData);
expect(apiClient.patch).toHaveBeenCalledWith('/platform/email-addresses/6/', {
password: 'new-secure-password',
});
});
});
// ============================================================================
// Delete Operations
// ============================================================================
describe('deletePlatformEmailAddress', () => {
it('deletes a platform email address by ID', async () => {
vi.mocked(apiClient.delete).mockResolvedValue({});
await deletePlatformEmailAddress(1);
expect(apiClient.delete).toHaveBeenCalledWith('/platform/email-addresses/1/');
});
it('returns void on successful deletion', async () => {
vi.mocked(apiClient.delete).mockResolvedValue({});
const result = await deletePlatformEmailAddress(42);
expect(result).toBeUndefined();
});
it('handles deletion with different IDs', async () => {
vi.mocked(apiClient.delete).mockResolvedValue({});
await deletePlatformEmailAddress(999);
expect(apiClient.delete).toHaveBeenCalledWith('/platform/email-addresses/999/');
});
});
describe('removeLocalPlatformEmailAddress', () => {
it('removes email address from database only', async () => {
const mockResponse = {
success: true,
message: 'Email address removed from database. Mail server account retained.',
};
vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });
const result = await removeLocalPlatformEmailAddress(5);
expect(apiClient.post).toHaveBeenCalledWith('/platform/email-addresses/5/remove_local/');
expect(result).toEqual(mockResponse);
expect(result.success).toBe(true);
expect(result.message).toContain('Mail server account retained');
});
it('handles removal failure', async () => {
const mockResponse = {
success: false,
message: 'Failed to remove email address',
};
vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });
const result = await removeLocalPlatformEmailAddress(10);
expect(result.success).toBe(false);
});
it('removes with different email address IDs', async () => {
const mockResponse = {
success: true,
message: 'Removed successfully',
};
vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });
await removeLocalPlatformEmailAddress(123);
expect(apiClient.post).toHaveBeenCalledWith('/platform/email-addresses/123/remove_local/');
});
});
// ============================================================================
// Sync Operations
// ============================================================================
describe('syncPlatformEmailAddress', () => {
it('syncs email address to mail server successfully', async () => {
const mockResponse: SyncResponse = {
success: true,
message: 'Email address synced successfully',
mail_server_synced: true,
last_synced_at: '2025-01-15T21:00:00Z',
};
vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });
const result = await syncPlatformEmailAddress(1);
expect(apiClient.post).toHaveBeenCalledWith('/platform/email-addresses/1/sync/');
expect(result).toEqual(mockResponse);
expect(result.success).toBe(true);
expect(result.mail_server_synced).toBe(true);
expect(result.last_synced_at).toBe('2025-01-15T21:00:00Z');
});
it('handles sync failure with error message', async () => {
const mockResponse: SyncResponse = {
success: false,
message: 'Connection to mail server failed',
mail_server_synced: false,
last_sync_error: 'Connection timeout',
};
vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });
const result = await syncPlatformEmailAddress(2);
expect(apiClient.post).toHaveBeenCalledWith('/platform/email-addresses/2/sync/');
expect(result.success).toBe(false);
expect(result.last_sync_error).toBe('Connection timeout');
});
it('syncs with different email address IDs', async () => {
const mockResponse: SyncResponse = {
success: true,
message: 'Synced',
mail_server_synced: true,
};
vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });
await syncPlatformEmailAddress(999);
expect(apiClient.post).toHaveBeenCalledWith('/platform/email-addresses/999/sync/');
});
});
// ============================================================================
// Connection Testing
// ============================================================================
describe('testImapConnection', () => {
it('tests IMAP connection successfully', async () => {
const mockResponse: TestConnectionResponse = {
success: true,
message: 'IMAP connection successful',
};
vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });
const result = await testImapConnection(1);
expect(apiClient.post).toHaveBeenCalledWith('/platform/email-addresses/1/test_imap/');
expect(result).toEqual(mockResponse);
expect(result.success).toBe(true);
expect(result.message).toContain('successful');
});
it('handles IMAP connection failure', async () => {
const mockResponse: TestConnectionResponse = {
success: false,
message: 'IMAP connection failed: Invalid credentials',
};
vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });
const result = await testImapConnection(2);
expect(apiClient.post).toHaveBeenCalledWith('/platform/email-addresses/2/test_imap/');
expect(result.success).toBe(false);
expect(result.message).toContain('Invalid credentials');
});
it('tests IMAP with different IDs', async () => {
const mockResponse: TestConnectionResponse = {
success: true,
message: 'Connected',
};
vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });
await testImapConnection(50);
expect(apiClient.post).toHaveBeenCalledWith('/platform/email-addresses/50/test_imap/');
});
});
describe('testSmtpConnection', () => {
it('tests SMTP connection successfully', async () => {
const mockResponse: TestConnectionResponse = {
success: true,
message: 'SMTP connection successful',
};
vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });
const result = await testSmtpConnection(1);
expect(apiClient.post).toHaveBeenCalledWith('/platform/email-addresses/1/test_smtp/');
expect(result).toEqual(mockResponse);
expect(result.success).toBe(true);
expect(result.message).toContain('successful');
});
it('handles SMTP connection failure', async () => {
const mockResponse: TestConnectionResponse = {
success: false,
message: 'SMTP connection failed: Connection refused',
};
vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });
const result = await testSmtpConnection(2);
expect(apiClient.post).toHaveBeenCalledWith('/platform/email-addresses/2/test_smtp/');
expect(result.success).toBe(false);
expect(result.message).toContain('Connection refused');
});
it('tests SMTP with different IDs', async () => {
const mockResponse: TestConnectionResponse = {
success: true,
message: 'OK',
};
vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });
await testSmtpConnection(75);
expect(apiClient.post).toHaveBeenCalledWith('/platform/email-addresses/75/test_smtp/');
});
});
describe('testMailServerConnection', () => {
it('tests mail server SSH connection successfully', async () => {
const mockResponse: TestConnectionResponse = {
success: true,
message: 'SSH connection to mail server successful',
};
vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });
const result = await testMailServerConnection();
expect(apiClient.post).toHaveBeenCalledWith('/platform/email-addresses/test_mail_server/');
expect(result).toEqual(mockResponse);
expect(result.success).toBe(true);
});
it('handles mail server connection failure', async () => {
const mockResponse: TestConnectionResponse = {
success: false,
message: 'SSH connection failed: Authentication error',
};
vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });
const result = await testMailServerConnection();
expect(apiClient.post).toHaveBeenCalledWith('/platform/email-addresses/test_mail_server/');
expect(result.success).toBe(false);
expect(result.message).toContain('Authentication error');
});
});
// ============================================================================
// Set as Default
// ============================================================================
describe('setAsDefault', () => {
it('sets an email address as default', async () => {
const mockResponse = {
success: true,
message: 'Email address set as default',
};
vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });
const result = await setAsDefault(3);
expect(apiClient.post).toHaveBeenCalledWith('/platform/email-addresses/3/set_as_default/');
expect(result).toEqual(mockResponse);
expect(result.success).toBe(true);
expect(result.message).toContain('default');
});
it('handles setting default with different IDs', async () => {
const mockResponse = {
success: true,
message: 'Default updated',
};
vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });
await setAsDefault(100);
expect(apiClient.post).toHaveBeenCalledWith('/platform/email-addresses/100/set_as_default/');
});
it('handles failure to set as default', async () => {
const mockResponse = {
success: false,
message: 'Email address must be synced before setting as default',
};
vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });
const result = await setAsDefault(5);
expect(result.success).toBe(false);
expect(result.message).toContain('must be synced');
});
});
// ============================================================================
// Mail Server Accounts
// ============================================================================
describe('getMailServerAccounts', () => {
it('fetches all email accounts from mail server', async () => {
const mockResponse: MailServerAccountsResponse = {
success: true,
accounts: [
{
email: 'support@talova.net',
raw_line: 'support@talova.net:encrypted-password-hash',
},
{
email: 'sales@talova.net',
raw_line: 'sales@talova.net:encrypted-password-hash',
},
{
email: 'info@talova.net',
raw_line: 'info@talova.net:encrypted-password-hash',
},
],
count: 3,
};
vi.mocked(apiClient.get).mockResolvedValue({ data: mockResponse });
const result = await getMailServerAccounts();
expect(apiClient.get).toHaveBeenCalledWith('/platform/email-addresses/mail_server_accounts/');
expect(result).toEqual(mockResponse);
expect(result.success).toBe(true);
expect(result.count).toBe(3);
expect(result.accounts).toHaveLength(3);
expect(result.accounts[0].email).toBe('support@talova.net');
});
it('handles empty mail server accounts', async () => {
const mockResponse: MailServerAccountsResponse = {
success: true,
accounts: [],
count: 0,
};
vi.mocked(apiClient.get).mockResolvedValue({ data: mockResponse });
const result = await getMailServerAccounts();
expect(result.accounts).toEqual([]);
expect(result.count).toBe(0);
});
it('handles mail server connection failure', async () => {
const mockResponse: MailServerAccountsResponse = {
success: false,
accounts: [],
count: 0,
};
vi.mocked(apiClient.get).mockResolvedValue({ data: mockResponse });
const result = await getMailServerAccounts();
expect(result.success).toBe(false);
});
});
// ============================================================================
// Available Domains and Users
// ============================================================================
describe('getAvailableDomains', () => {
it('fetches available email domains', async () => {
const mockResponse = {
domains: [
{
value: 'talova.net',
label: 'talova.net',
},
{
value: 'example.com',
label: 'example.com',
},
],
};
vi.mocked(apiClient.get).mockResolvedValue({ data: mockResponse });
const result = await getAvailableDomains();
expect(apiClient.get).toHaveBeenCalledWith('/platform/email-addresses/available_domains/');
expect(result).toEqual(mockResponse);
expect(result.domains).toHaveLength(2);
expect(result.domains[0].value).toBe('talova.net');
});
it('handles single domain', async () => {
const mockResponse = {
domains: [
{
value: 'talova.net',
label: 'talova.net',
},
],
};
vi.mocked(apiClient.get).mockResolvedValue({ data: mockResponse });
const result = await getAvailableDomains();
expect(result.domains).toHaveLength(1);
});
it('handles empty domains list', async () => {
const mockResponse = {
domains: [],
};
vi.mocked(apiClient.get).mockResolvedValue({ data: mockResponse });
const result = await getAvailableDomains();
expect(result.domains).toEqual([]);
});
});
describe('getAssignableUsers', () => {
it('fetches assignable platform users', async () => {
const mockResponse = {
users: [
{
id: 1,
email: 'admin@platform.com',
first_name: 'Admin',
last_name: 'User',
full_name: 'Admin User',
role: 'superuser',
},
{
id: 2,
email: 'support@platform.com',
first_name: 'Support',
last_name: 'Agent',
full_name: 'Support Agent',
role: 'platform_support',
},
{
id: 3,
email: 'manager@platform.com',
first_name: 'Platform',
last_name: 'Manager',
full_name: 'Platform Manager',
role: 'platform_manager',
},
],
};
vi.mocked(apiClient.get).mockResolvedValue({ data: mockResponse });
const result = await getAssignableUsers();
expect(apiClient.get).toHaveBeenCalledWith('/platform/email-addresses/assignable_users/');
expect(result).toEqual(mockResponse);
expect(result.users).toHaveLength(3);
expect(result.users[0].full_name).toBe('Admin User');
expect(result.users[1].role).toBe('platform_support');
});
it('handles empty users list', async () => {
const mockResponse = {
users: [],
};
vi.mocked(apiClient.get).mockResolvedValue({ data: mockResponse });
const result = await getAssignableUsers();
expect(result.users).toEqual([]);
});
it('handles users with different roles', async () => {
const mockResponse = {
users: [
{
id: 10,
email: 'user@platform.com',
first_name: 'Test',
last_name: 'User',
full_name: 'Test User',
role: 'platform_manager',
},
],
};
vi.mocked(apiClient.get).mockResolvedValue({ data: mockResponse });
const result = await getAssignableUsers();
expect(result.users[0].role).toBe('platform_manager');
});
});
// ============================================================================
// Import from Mail Server
// ============================================================================
describe('importFromMailServer', () => {
it('imports email addresses from mail server successfully', async () => {
const mockResponse: ImportFromMailServerResponse = {
success: true,
imported: [
{
id: 10,
email: 'support@talova.net',
display_name: 'Support',
},
{
id: 11,
email: 'sales@talova.net',
display_name: 'Sales',
},
],
imported_count: 2,
skipped: [
{
email: 'info@talova.net',
reason: 'Already exists in database',
},
],
skipped_count: 1,
message: 'Imported 2 email addresses, skipped 1',
};
vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });
const result = await importFromMailServer();
expect(apiClient.post).toHaveBeenCalledWith('/platform/email-addresses/import_from_mail_server/');
expect(result).toEqual(mockResponse);
expect(result.success).toBe(true);
expect(result.imported_count).toBe(2);
expect(result.skipped_count).toBe(1);
expect(result.imported).toHaveLength(2);
expect(result.skipped).toHaveLength(1);
expect(result.imported[0].email).toBe('support@talova.net');
expect(result.skipped[0].reason).toContain('Already exists');
});
it('handles import with no new addresses', async () => {
const mockResponse: ImportFromMailServerResponse = {
success: true,
imported: [],
imported_count: 0,
skipped: [
{
email: 'existing@talova.net',
reason: 'Already exists',
},
],
skipped_count: 1,
message: 'No new email addresses to import',
};
vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });
const result = await importFromMailServer();
expect(result.imported_count).toBe(0);
expect(result.skipped_count).toBe(1);
expect(result.message).toContain('No new');
});
it('handles import with all addresses imported', async () => {
const mockResponse: ImportFromMailServerResponse = {
success: true,
imported: [
{
id: 20,
email: 'new1@talova.net',
display_name: 'New1',
},
{
id: 21,
email: 'new2@talova.net',
display_name: 'New2',
},
{
id: 22,
email: 'new3@talova.net',
display_name: 'New3',
},
],
imported_count: 3,
skipped: [],
skipped_count: 0,
message: 'Successfully imported 3 email addresses',
};
vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });
const result = await importFromMailServer();
expect(result.imported_count).toBe(3);
expect(result.skipped_count).toBe(0);
expect(result.skipped).toEqual([]);
});
it('handles import failure', async () => {
const mockResponse: ImportFromMailServerResponse = {
success: false,
imported: [],
imported_count: 0,
skipped: [],
skipped_count: 0,
message: 'Failed to connect to mail server',
};
vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });
const result = await importFromMailServer();
expect(result.success).toBe(false);
expect(result.message).toContain('Failed to connect');
});
it('handles import with validation errors', async () => {
const mockResponse: ImportFromMailServerResponse = {
success: true,
imported: [],
imported_count: 0,
skipped: [
{
email: 'invalid@email',
reason: 'Invalid email format',
},
{
email: 'missing-domain',
reason: 'Missing domain',
},
],
skipped_count: 2,
message: 'Skipped 2 invalid email addresses',
};
vi.mocked(apiClient.post).mockResolvedValue({ data: mockResponse });
const result = await importFromMailServer();
expect(result.skipped_count).toBe(2);
expect(result.skipped[0].reason).toContain('Invalid');
expect(result.skipped[1].reason).toContain('Missing');
});
});
});