/** * @fileoverview Tests for API and network error handling * Tests network failures, API errors, timeout scenarios, and resilience patterns */ import { describe, test, expect, beforeEach, afterEach } from '@jest/globals'; import { mockFactory } from '../mocks/mockFactory.js'; describe('API and Network Error Handling Tests', () => { let mockEnv; let toolGenerator; beforeEach(() => { mockEnv = mockFactory.createMockEnvironment({ authTypes: ['provider', 'patient'], enableHttpMocks: true, enableAuthMocks: true }); toolGenerator = mockEnv.toolGenerator; // Setup authentication mockFactory.authMocks.setMockCredentials('provider', { username: 'test_provider', password: 'test_password' }); }); afterEach(() => { mockFactory.resetAllMocks(); }); describe('Network Connectivity Issues', () => { test('should handle network timeout errors', async () => { const toolName = 'provider_create_getPatientInfo'; const parameters = { patientId: 123 }; // Mock network timeout mockFactory.httpMocks.mockRequest('POST', '/api/get-patient-info/123', null, true, { code: 'ECONNABORTED', message: 'timeout of 5000ms exceeded', config: { timeout: 5000, url: '/api/get-patient-info/123', method: 'post' } }); try { await toolGenerator.executeTool(toolName, parameters); fail('Expected timeout error'); } catch (error) { expect(error.code).toBe('ECONNABORTED'); expect(error.message).toContain('timeout'); } }); test('should handle connection refused errors', async () => { const toolName = 'provider_create_emrregisterPatient'; const parameters = { firstName: 'John', lastName: 'Doe', email: 'john@test.com', dateOfBirth: '1990-01-01' }; // Mock connection refused mockFactory.httpMocks.mockRequest('POST', '/api/emr/register-patients', null, true, { code: 'ECONNREFUSED', message: 'connect ECONNREFUSED 127.0.0.1:80', errno: -61, syscall: 'connect', address: '127.0.0.1', port: 80 }); try { await toolGenerator.executeTool(toolName, parameters); fail('Expected connection refused error'); } catch (error) { expect(error.code).toBe('ECONNREFUSED'); expect(error.message).toContain('ECONNREFUSED'); } }); test('should handle DNS resolution failures', async () => { const toolName = 'provider_create_addVital'; const parameters = { patientId: 123, provider_id: 456, blood_presssure: '120/80' }; // Mock DNS failure mockFactory.httpMocks.mockRequest('POST', '/api/add-vital/123', null, true, { code: 'ENOTFOUND', message: 'getaddrinfo ENOTFOUND api.healthcare.com', errno: -3008, syscall: 'getaddrinfo', hostname: 'api.healthcare.com' }); try { await toolGenerator.executeTool(toolName, parameters); fail('Expected DNS error'); } catch (error) { expect(error.code).toBe('ENOTFOUND'); expect(error.message).toContain('ENOTFOUND'); } }); test('should handle SSL/TLS certificate errors', async () => { const toolName = 'provider_create_prescriptionstore'; const parameters = { patient_id: 'patient_123', medication_data: { medication_name: 'Lisinopril', strength: '10mg' } }; // Mock SSL certificate error mockFactory.httpMocks.mockRequest('POST', '/api/emr/prescription/store/patient_123', null, true, { code: 'CERT_UNTRUSTED', message: 'certificate verify failed: self signed certificate', errno: -67, syscall: 'connect' }); try { await toolGenerator.executeTool(toolName, parameters); fail('Expected SSL certificate error'); } catch (error) { expect(error.code).toBe('CERT_UNTRUSTED'); expect(error.message).toContain('certificate verify failed'); } }); }); describe('HTTP Status Code Errors', () => { test('should handle 400 Bad Request errors', async () => { const toolName = 'provider_create_emrregisterPatient'; const parameters = { firstName: 'John', lastName: 'Doe', email: 'invalid-email', // Invalid email format dateOfBirth: '1990-01-01' }; // Mock bad request mockFactory.httpMocks.mockRequest('POST', '/api/emr/register-patients', null, true, { response: { status: 400, data: { error: 'Bad Request', error_code: 'VALIDATION_ERROR', message: 'Request validation failed', validation_errors: [ { field: 'email', message: 'Invalid email format', code: 'INVALID_EMAIL' } ], request_id: 'req_123' } } }); try { await toolGenerator.executeTool(toolName, parameters); fail('Expected bad request error'); } catch (error) { expect(error.response.status).toBe(400); expect(error.response.data.error_code).toBe('VALIDATION_ERROR'); expect(error.response.data.validation_errors[0].field).toBe('email'); } }); test('should handle 404 Not Found errors', async () => { const toolName = 'provider_create_getPatientInfo'; const parameters = { patientId: 999999 // Non-existent patient }; // Mock not found mockFactory.httpMocks.mockRequest('POST', '/api/get-patient-info/999999', null, true, { response: { status: 404, data: { error: 'Not Found', error_code: 'PATIENT_NOT_FOUND', message: 'Patient with ID 999999 not found', resource_type: 'patient', resource_id: 999999, suggestions: [ 'Verify patient ID is correct', 'Check if patient exists in system', 'Contact system administrator' ] } } }); try { await toolGenerator.executeTool(toolName, parameters); fail('Expected not found error'); } catch (error) { expect(error.response.status).toBe(404); expect(error.response.data.error_code).toBe('PATIENT_NOT_FOUND'); expect(error.response.data.suggestions).toContain('Verify patient ID is correct'); } }); test('should handle 409 Conflict errors', async () => { const toolName = 'provider_create_emrregisterPatient'; const parameters = { firstName: 'John', lastName: 'Doe', email: 'existing@test.com', // Email already exists dateOfBirth: '1990-01-01' }; // Mock conflict mockFactory.httpMocks.mockRequest('POST', '/api/emr/register-patients', null, true, { response: { status: 409, data: { error: 'Conflict', error_code: 'PATIENT_ALREADY_EXISTS', message: 'Patient with email existing@test.com already exists', conflicting_resource: { type: 'patient', id: 'patient_456', email: 'existing@test.com' }, resolution_options: [ 'use_existing_patient', 'update_existing_patient', 'use_different_email' ] } } }); try { await toolGenerator.executeTool(toolName, parameters); fail('Expected conflict error'); } catch (error) { expect(error.response.status).toBe(409); expect(error.response.data.error_code).toBe('PATIENT_ALREADY_EXISTS'); expect(error.response.data.resolution_options).toContain('use_existing_patient'); } }); test('should handle 422 Unprocessable Entity errors', async () => { const toolName = 'provider_create_addVital'; const parameters = { patientId: 123, provider_id: 456, blood_presssure: '300/200', // Invalid vital signs temperature: 150 // Invalid temperature }; // Mock unprocessable entity mockFactory.httpMocks.mockRequest('POST', '/api/add-vital/123', null, true, { response: { status: 422, data: { error: 'Unprocessable Entity', error_code: 'INVALID_VITAL_SIGNS', message: 'Vital signs values are outside acceptable ranges', validation_errors: [ { field: 'blood_presssure', value: '300/200', message: 'Blood pressure values are dangerously high', acceptable_range: '80/50 - 200/120' }, { field: 'temperature', value: 150, message: 'Temperature value is not physiologically possible', acceptable_range: '95.0 - 110.0 °F' } ], clinical_review_required: true } } }); try { await toolGenerator.executeTool(toolName, parameters); fail('Expected unprocessable entity error'); } catch (error) { expect(error.response.status).toBe(422); expect(error.response.data.error_code).toBe('INVALID_VITAL_SIGNS'); expect(error.response.data.clinical_review_required).toBe(true); } }); test('should handle 500 Internal Server Error', async () => { const toolName = 'provider_create_medicalRecordscreate'; const parameters = { patient_id: 'patient_123', record_type: 'progress_note', diagnosis: 'Test diagnosis' }; // Mock internal server error mockFactory.httpMocks.mockRequest('POST', '/api/emr/medical-records/create', null, true, { response: { status: 500, data: { error: 'Internal Server Error', error_code: 'SERVER_ERROR', message: 'An unexpected error occurred while processing your request', incident_id: 'INC_001', timestamp: new Date().toISOString(), support_contact: 'support@healthcare.com', retry_recommended: true, retry_after: 300 // 5 minutes } } }); try { await toolGenerator.executeTool(toolName, parameters); fail('Expected internal server error'); } catch (error) { expect(error.response.status).toBe(500); expect(error.response.data.error_code).toBe('SERVER_ERROR'); expect(error.response.data.retry_recommended).toBe(true); expect(error.response.data.incident_id).toBe('INC_001'); } }); test('should handle 502 Bad Gateway errors', async () => { const toolName = 'provider_create_labscreate'; const parameters = { lab_data: { test_type: 'CBC', patient_id: 'patient_123' } }; // Mock bad gateway mockFactory.httpMocks.mockRequest('POST', '/api/labs/create', null, true, { response: { status: 502, data: { error: 'Bad Gateway', error_code: 'UPSTREAM_SERVICE_ERROR', message: 'Lab service is temporarily unavailable', upstream_service: 'lab_integration_service', service_status: 'degraded', estimated_recovery: '15 minutes', alternative_actions: [ 'retry_later', 'use_manual_lab_order', 'contact_lab_directly' ] } } }); try { await toolGenerator.executeTool(toolName, parameters); fail('Expected bad gateway error'); } catch (error) { expect(error.response.status).toBe(502); expect(error.response.data.error_code).toBe('UPSTREAM_SERVICE_ERROR'); expect(error.response.data.alternative_actions).toContain('retry_later'); } }); test('should handle 503 Service Unavailable errors', async () => { const toolName = 'provider_create_emrcreateAppointment'; const parameters = { patient_id: 'patient_123', practitioner_id: 'provider_456', appointment_date: '2025-07-15', appointment_time: '10:00' }; // Mock service unavailable mockFactory.httpMocks.mockRequest('POST', '/api/emr/create-appointment', null, true, { response: { status: 503, data: { error: 'Service Unavailable', error_code: 'MAINTENANCE_MODE', message: 'System is temporarily unavailable for scheduled maintenance', maintenance_window: { start: '2025-07-09T02:00:00Z', end: '2025-07-09T04:00:00Z', duration: '2 hours' }, retry_after: 7200, // 2 hours emergency_contact: 'emergency@healthcare.com', status_page: 'https://status.healthcare.com' } } }); try { await toolGenerator.executeTool(toolName, parameters); fail('Expected service unavailable error'); } catch (error) { expect(error.response.status).toBe(503); expect(error.response.data.error_code).toBe('MAINTENANCE_MODE'); expect(error.response.data.retry_after).toBe(7200); expect(error.response.data.emergency_contact).toBeDefined(); } }); }); describe('Data Validation and Format Errors', () => { test('should handle malformed JSON responses', async () => { const toolName = 'provider_create_getPatientInfo'; const parameters = { patientId: 123 }; // Mock malformed JSON mockFactory.httpMocks.mockRequest('POST', '/api/get-patient-info/123', { status: 200, data: 'invalid json response {malformed' }); try { await toolGenerator.executeTool(toolName, parameters); fail('Expected JSON parse error'); } catch (error) { expect(error.message).toContain('JSON'); } }); test('should handle missing required response fields', async () => { const toolName = 'provider_create_emrregisterPatient'; const parameters = { firstName: 'John', lastName: 'Doe', email: 'john@test.com', dateOfBirth: '1990-01-01' }; // Mock incomplete response mockFactory.httpMocks.mockRequest('POST', '/api/emr/register-patients', { status: 201, data: { // Missing success field and patient data message: 'Patient registered' } }); try { await toolGenerator.executeTool(toolName, parameters); fail('Expected incomplete response error'); } catch (error) { expect(error.message).toContain('incomplete'); } }); test('should handle unexpected response structure', async () => { const toolName = 'provider_create_addVital'; const parameters = { patientId: 123, provider_id: 456, blood_presssure: '120/80' }; // Mock unexpected response structure mockFactory.httpMocks.mockRequest('POST', '/api/add-vital/123', { status: 200, data: { unexpected_field: 'value', different_structure: true // Missing expected fields } }); try { await toolGenerator.executeTool(toolName, parameters); fail('Expected unexpected response error'); } catch (error) { expect(error.message).toContain('unexpected'); } }); }); describe('Retry and Resilience Patterns', () => { test('should implement exponential backoff for retries', async () => { const toolName = 'provider_create_getPatientInfo'; const parameters = { patientId: 123 }; // Mock temporary failure followed by success let attemptCount = 0; mockFactory.httpMocks.mockRequest('POST', '/api/get-patient-info/123', () => { attemptCount++; if (attemptCount < 3) { throw { response: { status: 503, data: { error: 'Service temporarily unavailable' } } }; } return { status: 200, data: { success: true, patient: mockFactory.healthcareMocks.createHIPAACompliantData('patient', 'provider') } }; }); const result = await toolGenerator.executeTool(toolName, parameters); expect(result.success).toBe(true); expect(attemptCount).toBe(3); // Should have retried twice before success }); test('should handle circuit breaker pattern', async () => { const toolName = 'provider_create_labscreate'; const parameters = { lab_data: { test_type: 'CBC', patient_id: 'patient_123' } }; // Mock circuit breaker open mockFactory.httpMocks.mockRequest('POST', '/api/labs/create', null, true, { response: { status: 503, data: { error: 'Circuit breaker open', error_code: 'CIRCUIT_BREAKER_OPEN', message: 'Lab service circuit breaker is open due to repeated failures', circuit_state: 'open', failure_count: 10, failure_threshold: 5, next_attempt_allowed: new Date(Date.now() + 300000).toISOString(), // 5 minutes fallback_available: true, fallback_action: 'manual_lab_order' } } }); try { await toolGenerator.executeTool(toolName, parameters); fail('Expected circuit breaker error'); } catch (error) { expect(error.response.status).toBe(503); expect(error.response.data.error_code).toBe('CIRCUIT_BREAKER_OPEN'); expect(error.response.data.fallback_available).toBe(true); } }); test('should handle graceful degradation', async () => { const toolName = 'provider_create_emrcreateAppointment'; const parameters = { patient_id: 'patient_123', practitioner_id: 'provider_456', appointment_date: '2025-07-15', appointment_time: '10:00' }; // Mock degraded service response mockFactory.httpMocks.mockRequest('POST', '/api/emr/create-appointment', { status: 200, data: { success: true, appointment: mockFactory.healthcareMocks.generateMockAppointment(), service_status: 'degraded', degraded_features: [ 'email_notifications', 'calendar_sync', 'reminder_service' ], available_features: [ 'appointment_creation', 'basic_scheduling', 'patient_notification' ], estimated_full_service_restoration: '2025-07-09T16:00:00Z' } }); const result = await toolGenerator.executeTool(toolName, parameters); expect(result.success).toBe(true); expect(result.data.service_status).toBe('degraded'); expect(result.data.degraded_features).toContain('email_notifications'); expect(result.data.available_features).toContain('appointment_creation'); }); }); });