/** * @fileoverview Tests for authentication error handling and edge cases * Tests authentication failures, token expiration, session management, and security scenarios */ import { describe, test, expect, beforeEach, afterEach } from '@jest/globals'; import { mockFactory } from '../mocks/mockFactory.js'; describe('Authentication Error Handling and Edge Cases', () => { let mockEnv; let toolGenerator; beforeEach(() => { mockEnv = mockFactory.createMockEnvironment({ authTypes: ['provider', 'patient', 'partner'], enableHttpMocks: true, enableAuthMocks: true }); toolGenerator = mockEnv.toolGenerator; }); afterEach(() => { mockFactory.resetAllMocks(); }); describe('Authentication Failure Scenarios', () => { test('should handle invalid credentials gracefully', async () => { const toolName = 'provider_create_emrregisterPatient'; const parameters = { firstName: 'John', lastName: 'Doe', email: 'john@test.com', dateOfBirth: '1990-01-01' }; // Mock authentication failure mockFactory.httpMocks.mockRequest('POST', '/api/emr/register-patients', null, true, { response: { status: 401, data: { error: 'Authentication failed', error_code: 'AUTH_INVALID_CREDENTIALS', message: 'Invalid username or password', retry_allowed: true, lockout_warning: false } } }); try { await toolGenerator.executeTool(toolName, parameters); fail('Expected authentication error'); } catch (error) { expect(error.response.status).toBe(401); expect(error.response.data.error_code).toBe('AUTH_INVALID_CREDENTIALS'); expect(error.response.data.retry_allowed).toBe(true); } }); test('should handle account lockout scenarios', async () => { const toolName = 'public_create_login'; const parameters = { username: 'locked_user', password: 'password' }; // Mock account lockout mockFactory.httpMocks.mockRequest('POST', '/api/login', null, true, { response: { status: 423, data: { error: 'Account locked', error_code: 'AUTH_ACCOUNT_LOCKED', message: 'Account temporarily locked due to multiple failed login attempts', lockout_duration: 900, // 15 minutes in seconds lockout_expiry: new Date(Date.now() + 900000).toISOString(), unlock_methods: ['time_expiry', 'admin_unlock', 'password_reset'], failed_attempts: 5, max_attempts: 5 } } }); try { await toolGenerator.executeTool(toolName, parameters); fail('Expected account lockout error'); } catch (error) { expect(error.response.status).toBe(423); expect(error.response.data.error_code).toBe('AUTH_ACCOUNT_LOCKED'); expect(error.response.data.lockout_duration).toBe(900); expect(error.response.data.unlock_methods).toContain('password_reset'); } }); test('should handle disabled account scenarios', async () => { const toolName = 'patient_create_patientlogin'; const parameters = { email: 'disabled@test.com', password: 'password' }; // Mock disabled account mockFactory.httpMocks.mockRequest('POST', '/api/patient/login', null, true, { response: { status: 403, data: { error: 'Account disabled', error_code: 'AUTH_ACCOUNT_DISABLED', message: 'Account has been disabled by administrator', reason: 'policy_violation', contact_support: true, support_contact: 'support@healthcare.com', appeal_process: true } } }); try { await toolGenerator.executeTool(toolName, parameters); fail('Expected account disabled error'); } catch (error) { expect(error.response.status).toBe(403); expect(error.response.data.error_code).toBe('AUTH_ACCOUNT_DISABLED'); expect(error.response.data.contact_support).toBe(true); expect(error.response.data.appeal_process).toBe(true); } }); }); describe('Token Expiration and Session Management', () => { test('should handle expired authentication tokens', async () => { const toolName = 'provider_create_getPatientInfo'; const parameters = { patientId: 123 }; // Mock expired token mockFactory.httpMocks.mockRequest('POST', '/api/get-patient-info/123', null, true, { response: { status: 401, data: { error: 'Token expired', error_code: 'AUTH_TOKEN_EXPIRED', message: 'Authentication token has expired', expired_at: new Date(Date.now() - 3600000).toISOString(), // 1 hour ago refresh_available: true, refresh_endpoint: '/api/token/refresh', login_required: false } } }); try { await toolGenerator.executeTool(toolName, parameters); fail('Expected token expired error'); } catch (error) { expect(error.response.status).toBe(401); expect(error.response.data.error_code).toBe('AUTH_TOKEN_EXPIRED'); expect(error.response.data.refresh_available).toBe(true); expect(error.response.data.refresh_endpoint).toBe('/api/token/refresh'); } }); test('should handle invalid or malformed tokens', async () => { const toolName = 'provider_create_getPatientInfo'; const parameters = { patientId: 123 }; // Mock invalid token mockFactory.httpMocks.mockRequest('POST', '/api/get-patient-info/123', null, true, { response: { status: 401, data: { error: 'Invalid token', error_code: 'AUTH_TOKEN_INVALID', message: 'Authentication token is invalid or malformed', token_format_error: true, login_required: true, security_incident: false } } }); try { await toolGenerator.executeTool(toolName, parameters); fail('Expected invalid token error'); } catch (error) { expect(error.response.status).toBe(401); expect(error.response.data.error_code).toBe('AUTH_TOKEN_INVALID'); expect(error.response.data.token_format_error).toBe(true); expect(error.response.data.login_required).toBe(true); } }); test('should handle concurrent session conflicts', async () => { const toolName = 'patient_create_patientlogin'; const parameters = { email: 'patient@test.com', password: 'password' }; // Mock concurrent session conflict mockFactory.httpMocks.mockRequest('POST', '/api/patient/login', null, true, { response: { status: 409, data: { error: 'Session conflict', error_code: 'AUTH_SESSION_CONFLICT', message: 'Maximum concurrent sessions exceeded', current_sessions: 3, max_allowed_sessions: 2, active_sessions: [ { session_id: 'session_1', device: 'Desktop Browser', location: 'New York, NY', last_activity: new Date(Date.now() - 300000).toISOString() }, { session_id: 'session_2', device: 'Mobile App', location: 'Boston, MA', last_activity: new Date(Date.now() - 600000).toISOString() } ], options: ['terminate_oldest_session', 'terminate_specific_session', 'upgrade_plan'] } } }); try { await toolGenerator.executeTool(toolName, parameters); fail('Expected session conflict error'); } catch (error) { expect(error.response.status).toBe(409); expect(error.response.data.error_code).toBe('AUTH_SESSION_CONFLICT'); expect(error.response.data.current_sessions).toBe(3); expect(error.response.data.active_sessions).toHaveLength(2); expect(error.response.data.options).toContain('terminate_oldest_session'); } }); test('should handle session timeout scenarios', async () => { const toolName = 'provider_create_addVital'; const parameters = { patientId: 123, provider_id: 456, blood_presssure: '120/80' }; // Mock session timeout mockFactory.httpMocks.mockRequest('POST', '/api/add-vital/123', null, true, { response: { status: 401, data: { error: 'Session timeout', error_code: 'AUTH_SESSION_TIMEOUT', message: 'Session has timed out due to inactivity', timeout_duration: 1800, // 30 minutes last_activity: new Date(Date.now() - 1800000).toISOString(), auto_save_available: true, saved_data: { form_data: parameters, save_timestamp: new Date().toISOString() } } } }); try { await toolGenerator.executeTool(toolName, parameters); fail('Expected session timeout error'); } catch (error) { expect(error.response.status).toBe(401); expect(error.response.data.error_code).toBe('AUTH_SESSION_TIMEOUT'); expect(error.response.data.auto_save_available).toBe(true); expect(error.response.data.saved_data.form_data).toEqual(parameters); } }); }); describe('Permission and Authorization Errors', () => { test('should handle insufficient permissions', async () => { const toolName = 'provider_create_emrupdateProviderProfile'; const parameters = { firstName: 'John', lastName: 'Doe' }; // Mock insufficient permissions mockFactory.httpMocks.mockRequest('POST', '/api/emr/update-provider-profile', null, true, { response: { status: 403, data: { error: 'Insufficient permissions', error_code: 'AUTH_INSUFFICIENT_PERMISSIONS', message: 'User does not have required permissions for this action', required_permissions: ['update:provider_profile', 'admin:user_management'], user_permissions: ['read:patient_data', 'write:patient_data'], missing_permissions: ['update:provider_profile', 'admin:user_management'], request_access_available: true, approval_workflow: 'supervisor_approval' } } }); try { await toolGenerator.executeTool(toolName, parameters); fail('Expected insufficient permissions error'); } catch (error) { expect(error.response.status).toBe(403); expect(error.response.data.error_code).toBe('AUTH_INSUFFICIENT_PERMISSIONS'); expect(error.response.data.missing_permissions).toContain('update:provider_profile'); expect(error.response.data.request_access_available).toBe(true); } }); test('should handle role-based access violations', async () => { const toolName = 'patient_get_providerAnalytics'; const parameters = { provider_id: 'provider_123' }; // Mock role-based access violation mockFactory.httpMocks.mockRequest('GET', '/api/provider-analytics/provider_123', null, true, { response: { status: 403, data: { error: 'Role-based access violation', error_code: 'AUTH_ROLE_VIOLATION', message: 'Patient role cannot access provider analytics', user_role: 'patient', required_role: 'provider', allowed_roles: ['provider', 'admin', 'supervisor'], resource_type: 'provider_analytics', escalation_available: false } } }); try { await toolGenerator.executeTool(toolName, parameters); fail('Expected role violation error'); } catch (error) { expect(error.response.status).toBe(403); expect(error.response.data.error_code).toBe('AUTH_ROLE_VIOLATION'); expect(error.response.data.user_role).toBe('patient'); expect(error.response.data.required_role).toBe('provider'); expect(error.response.data.escalation_available).toBe(false); } }); test('should handle cross-tenant data access violations', async () => { const toolName = 'provider_create_getPatientInfo'; const parameters = { patientId: 999 // Patient from different organization }; // Mock cross-tenant violation mockFactory.httpMocks.mockRequest('POST', '/api/get-patient-info/999', null, true, { response: { status: 403, data: { error: 'Cross-tenant access violation', error_code: 'AUTH_CROSS_TENANT_VIOLATION', message: 'Cannot access patient data from different organization', user_organization: 'org_123', patient_organization: 'org_456', data_sharing_agreement: false, hipaa_violation_risk: true, audit_logged: true } } }); try { await toolGenerator.executeTool(toolName, parameters); fail('Expected cross-tenant violation error'); } catch (error) { expect(error.response.status).toBe(403); expect(error.response.data.error_code).toBe('AUTH_CROSS_TENANT_VIOLATION'); expect(error.response.data.hipaa_violation_risk).toBe(true); expect(error.response.data.audit_logged).toBe(true); } }); }); describe('Security Incident Handling', () => { test('should handle suspicious activity detection', async () => { const toolName = 'provider_create_getPatientInfo'; const parameters = { patientId: 123 }; // Mock suspicious activity detection mockFactory.httpMocks.mockRequest('POST', '/api/get-patient-info/123', null, true, { response: { status: 429, data: { error: 'Suspicious activity detected', error_code: 'SECURITY_SUSPICIOUS_ACTIVITY', message: 'Unusual access patterns detected, temporary restriction applied', detection_triggers: [ 'rapid_successive_requests', 'unusual_access_time', 'geographic_anomaly' ], restriction_duration: 3600, // 1 hour security_review_required: true, incident_id: 'SEC_001', contact_security: true } } }); try { await toolGenerator.executeTool(toolName, parameters); fail('Expected suspicious activity error'); } catch (error) { expect(error.response.status).toBe(429); expect(error.response.data.error_code).toBe('SECURITY_SUSPICIOUS_ACTIVITY'); expect(error.response.data.detection_triggers).toContain('rapid_successive_requests'); expect(error.response.data.security_review_required).toBe(true); } }); test('should handle potential data breach scenarios', async () => { const toolName = 'provider_bulk_patientExport'; const parameters = { patient_ids: Array.from({length: 500}, (_, i) => i + 1), export_format: 'csv' }; // Mock potential breach detection mockFactory.httpMocks.mockRequest('POST', '/api/bulk-export-patients', null, true, { response: { status: 403, data: { error: 'Potential data breach detected', error_code: 'SECURITY_BREACH_RISK', message: 'Large data export request flagged for security review', risk_factors: [ 'bulk_export_threshold_exceeded', 'unusual_user_behavior', 'sensitive_data_included' ], requested_records: 500, threshold: 100, automatic_block: true, incident_reported: true, incident_id: 'BREACH_001', required_approvals: ['security_officer', 'privacy_officer', 'ciso'] } } }); try { await toolGenerator.executeTool(toolName, parameters); fail('Expected breach risk error'); } catch (error) { expect(error.response.status).toBe(403); expect(error.response.data.error_code).toBe('SECURITY_BREACH_RISK'); expect(error.response.data.automatic_block).toBe(true); expect(error.response.data.required_approvals).toContain('security_officer'); } }); test('should handle compromised account scenarios', async () => { const toolName = 'provider_create_updatePassword'; const parameters = { new_password: 'newpassword123' }; // Mock compromised account detection mockFactory.httpMocks.mockRequest('POST', '/api/update-password', null, true, { response: { status: 423, data: { error: 'Account potentially compromised', error_code: 'SECURITY_ACCOUNT_COMPROMISED', message: 'Account locked due to potential security compromise', compromise_indicators: [ 'login_from_suspicious_location', 'password_in_breach_database', 'unusual_activity_pattern' ], immediate_actions_taken: [ 'account_locked', 'sessions_terminated', 'security_team_notified' ], recovery_process: { identity_verification_required: true, password_reset_mandatory: true, security_questions_required: true, admin_approval_needed: true }, incident_id: 'COMP_001' } } }); try { await toolGenerator.executeTool(toolName, parameters); fail('Expected compromised account error'); } catch (error) { expect(error.response.status).toBe(423); expect(error.response.data.error_code).toBe('SECURITY_ACCOUNT_COMPROMISED'); expect(error.response.data.immediate_actions_taken).toContain('account_locked'); expect(error.response.data.recovery_process.identity_verification_required).toBe(true); } }); }); describe('Rate Limiting and Throttling', () => { test('should handle rate limit exceeded scenarios', async () => { const toolName = 'public_create_checkEmail'; const parameters = { email: 'test@test.com' }; // Mock rate limit exceeded mockFactory.httpMocks.mockRequest('POST', '/api/check-email', null, true, { response: { status: 429, data: { error: 'Rate limit exceeded', error_code: 'RATE_LIMIT_EXCEEDED', message: 'Too many requests, please try again later', limit: 100, window: 3600, // 1 hour remaining: 0, reset_time: new Date(Date.now() + 3600000).toISOString(), retry_after: 3600, rate_limit_type: 'user_based' } } }); try { await toolGenerator.executeTool(toolName, parameters); fail('Expected rate limit error'); } catch (error) { expect(error.response.status).toBe(429); expect(error.response.data.error_code).toBe('RATE_LIMIT_EXCEEDED'); expect(error.response.data.limit).toBe(100); expect(error.response.data.remaining).toBe(0); expect(error.response.data.retry_after).toBe(3600); } }); }); });