update
This commit is contained in:
@ -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();
|
Reference in New Issue
Block a user