- 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>
1233 lines
39 KiB
TypeScript
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');
|
|
});
|
|
});
|
|
});
|