This commit is contained in:
2025-05-23 19:03:44 +00:00
parent 7d28d23bbd
commit 1b141ec8f3
101 changed files with 30736 additions and 374 deletions

View File

@ -54,9 +54,9 @@ tap.test('Base error classes should set properties correctly', async () => {
expect(platformError.severity).toEqual(ErrorSeverity.MEDIUM);
expect(platformError.category).toEqual(ErrorCategory.OPERATION);
expect(platformError.recoverability).toEqual(ErrorRecoverability.MAYBE_RECOVERABLE);
expect(platformError.context.component).toEqual(context.component);
expect(platformError.context.operation).toEqual(context.operation);
expect(platformError.context.data.foo).toEqual('bar');
expect(platformError.context?.component).toEqual(context.component);
expect(platformError.context?.operation).toEqual(context.operation);
expect(platformError.context?.data?.foo).toEqual('bar');
expect(platformError.name).toEqual('PlatformError');
// Test ValidationError
@ -75,32 +75,54 @@ tap.test('Base error classes should set properties correctly', async () => {
expect(resourceError.category).toEqual(ErrorCategory.RESOURCE);
});
// Test 7: Error withRetry() method
tap.test('PlatformError withRetry creates new instance with retry info', async () => {
const originalError = new EmailSendError('Send failed', {
data: { someData: true }
});
const retryError = originalError.withRetry(3, 1, 1000);
// Verify it's a new instance
expect(retryError === originalError).toEqual(false);
expect(retryError).toBeInstanceOf(EmailSendError);
// Verify original data is preserved
expect(retryError.context?.data?.someData).toEqual(true);
// Verify retry info is added
expect(retryError.context?.retry?.maxRetries).toEqual(3);
expect(retryError.context?.retry?.currentRetry).toEqual(1);
expect(retryError.context?.retry?.retryDelay).toEqual(1000);
expect(retryError.context?.retry?.nextRetryAt).toBeTypeofNumber();
});
// Test email error classes
tap.test('Email error classes should be properly constructed', async () => {
// Test EmailServiceError
const emailServiceError = new EmailServiceError('Email service error', {
component: 'EmailService',
operation: 'sendEmail'
});
expect(emailServiceError.code).toEqual('EMAIL_SERVICE_ERROR');
expect(emailServiceError.name).toEqual('EmailServiceError');
try {
// Test EmailServiceError
const emailServiceError = new EmailServiceError('Email service error', {
component: 'EmailService',
operation: 'sendEmail'
});
expect(emailServiceError.code).toEqual('EMAIL_SERVICE_ERROR');
expect(emailServiceError.name).toEqual('EmailServiceError');
// Test EmailTemplateError
const templateError = new EmailTemplateError('Template not found: welcome_email', {
data: { templateId: 'welcome_email' }
});
expect(templateError.code).toEqual('EMAIL_TEMPLATE_ERROR');
expect(templateError.context.data.templateId).toEqual('welcome_email');
expect(templateError.context.data?.templateId).toEqual('welcome_email');
// Test EmailSendError with permanent flag
const permanentError = EmailSendError.permanent(
'Invalid recipient',
'user@example.com',
{ data: { details: 'DNS not found' } }
'Invalid recipient: user@example.com',
{ data: { details: 'DNS not found', recipient: 'user@example.com' } }
);
expect(permanentError.code).toEqual('EMAIL_SEND_ERROR');
expect(permanentError.isPermanent()).toEqual(true);
expect(permanentError.context.data.permanent).toEqual(true);
expect(permanentError.context.data?.permanent).toEqual(true);
// Test EmailSendError with temporary flag and retry
const tempError = EmailSendError.temporary(
@ -111,36 +133,42 @@ tap.test('Email error classes should be properly constructed', async () => {
{ data: { server: 'smtp.example.com' } }
);
expect(tempError.isPermanent()).toEqual(false);
expect(tempError.context.data.permanent).toEqual(false);
expect(tempError.context.retry.maxRetries).toEqual(3);
expect(tempError.context.data?.permanent).toEqual(false);
expect(tempError.context.retry?.maxRetries).toEqual(3);
expect(tempError.shouldRetry()).toEqual(true);
} catch (error) {
console.error('Test failed with error:', error);
throw error;
}
});
// Test MTA error classes
tap.test('MTA error classes should be properly constructed', async () => {
// Test MtaConnectionError
const dnsError = MtaConnectionError.dnsError('mail.example.com', new Error('DNS lookup failed'));
expect(dnsError.code).toEqual('MTA_CONNECTION_ERROR');
expect(dnsError.category).toEqual(ErrorCategory.CONNECTIVITY);
expect(dnsError.context.data.hostname).toEqual('mail.example.com');
try {
// Test MtaConnectionError
const dnsError = MtaConnectionError.dnsError('mail.example.com', new Error('DNS lookup failed'));
expect(dnsError.code).toEqual('MTA_CONNECTION_ERROR');
expect(dnsError.category).toEqual(ErrorCategory.CONNECTIVITY);
expect(dnsError.context.data?.hostname).toEqual('mail.example.com');
// Test MtaTimeoutError via MtaConnectionError.timeout
const timeoutError = MtaConnectionError.timeout('mail.example.com', 25, 30000);
expect(timeoutError.code).toEqual('MTA_CONNECTION_ERROR');
expect(timeoutError.context.data.timeout).toEqual(30000);
expect(timeoutError.context.data?.timeout).toEqual(30000);
// Test MtaAuthenticationError
const authError = MtaAuthenticationError.invalidCredentials('mail.example.com', 'user@example.com');
expect(authError.code).toEqual('MTA_AUTHENTICATION_ERROR');
expect(authError.category).toEqual(ErrorCategory.AUTHENTICATION);
expect(authError.context.data.username).toEqual('user@example.com');
expect(authError.context.data?.username).toEqual('user@example.com');
// Test MtaDeliveryError
const permDeliveryError = MtaDeliveryError.permanent(
'User unknown',
'nonexistent@example.com',
'550',
'550 5.1.1 User unknown'
'550 5.1.1 User unknown',
{}
);
expect(permDeliveryError.code).toEqual('MTA_DELIVERY_ERROR');
expect(permDeliveryError.isPermanent()).toEqual(true);
@ -159,8 +187,12 @@ tap.test('MTA error classes should be properly constructed', async () => {
);
expect(tempDeliveryError.isPermanent()).toEqual(false);
expect(tempDeliveryError.shouldRetry()).toEqual(true);
expect(tempDeliveryError.context.retry.currentRetry).toEqual(1);
expect(tempDeliveryError.context.retry.maxRetries).toEqual(3);
expect(tempDeliveryError.context.retry?.currentRetry).toEqual(1);
expect(tempDeliveryError.context.retry?.maxRetries).toEqual(3);
} catch (error) {
console.error('MTA test failed with error:', error);
throw error;
}
});
// Test error handler utility
@ -187,13 +219,13 @@ tap.test('ErrorHandler should properly handle and format errors', async () => {
expect(platformError).toBeInstanceOf(PlatformError);
expect(platformError.code).toEqual('PLATFORM_OPERATION_ERROR');
expect(platformError.context.component).toEqual('TestHandler');
expect(platformError.context?.component).toEqual('TestHandler');
// Test formatting error for API response
const formattedError = ErrorHandler.formatErrorForResponse(platformError, true);
expect(formattedError.code).toEqual('PLATFORM_OPERATION_ERROR');
expect(formattedError.message).toEqual('An unexpected error occurred.');
expect(formattedError.details.rawMessage).toEqual('Something went wrong');
expect(formattedError.details?.rawMessage).toEqual('Something went wrong');
// Test executing a function with error handling
let executed = false;
@ -303,15 +335,25 @@ tap.test('Error retry utilities should work correctly', async () => {
});
// Helper function that will reject first n times, then resolve
async function flaky(failTimes: number, result: any = 'success'): Promise<any> {
if (flaky.counter < failTimes) {
flaky.counter++;
throw new Error(`Flaky failure ${flaky.counter}`);
}
return result;
interface FlakyFunction {
(failTimes: number, result?: any): Promise<any>;
counter: number;
reset: () => void;
}
flaky.counter = 0;
flaky.reset = () => { flaky.counter = 0; };
const flaky: FlakyFunction = Object.assign(
async function (failTimes: number, result: any = 'success'): Promise<any> {
if (flaky.counter < failTimes) {
flaky.counter++;
throw new Error(`Flaky failure ${flaky.counter}`);
}
return result;
},
{
counter: 0,
reset: () => { flaky.counter = 0; }
}
);
// Test error wrapping and retry combination
tap.test('Error handling can be combined with retry for robust operations', async () => {
@ -326,20 +368,16 @@ tap.test('Error handling can be combined with retry for robust operations', asyn
);
// Execute with retry
try {
const result = await errors.retry(
wrapped,
{
maxRetries: 3,
initialDelay: 10,
}
);
expect(result).toEqual('wrapped success');
expect(flaky.counter).toEqual(2);
} catch (error) {
// Should not reach here
expect(false).toEqual(true);
}
const result = await errors.retry(
wrapped,
{
maxRetries: 3,
initialDelay: 10,
retryableErrors: [/Flaky failure/]
}
);
expect(result).toEqual('wrapped success');
expect(flaky.counter).toEqual(2);
// Reset and test failure case
flaky.reset();
@ -361,7 +399,7 @@ tap.test('Error handling can be combined with retry for robust operations', asyn
});
tap.test('stop', async () => {
// This is a placeholder test to ensure we call tap.stopForcefully()
await tap.stopForcefully();
});
export default tap.stopForcefully();
export default tap.start();