/** * @fileoverview Tests for patient data management and portal operations MCP tools * Tests patient profile updates, medical record access, and portal-specific operations */ import { describe, test, expect, beforeEach, afterEach } from '@jest/globals'; import { mockFactory } from '../mocks/mockFactory.js'; describe('Patient Data Management and Portal Operations Tools', () => { let mockEnv; let toolGenerator; let mockToken; beforeEach(() => { mockEnv = mockFactory.createMockEnvironment({ authTypes: ['patient'], enableHttpMocks: true, enableAuthMocks: true, enableHealthcareMocks: true }); toolGenerator = mockEnv.toolGenerator; // Setup patient authentication mockToken = 'patient_token_123'; mockFactory.authMocks.setMockCredentials('patient', { username: 'test_patient', password: 'test_password' }); }); afterEach(() => { mockFactory.resetAllMocks(); }); describe('patient_get_patientProfile', () => { test('should successfully retrieve patient profile', async () => { // Setup const toolName = 'patient_get_patientProfile'; const parameters = { patient_id: 'patient_123' }; // Mock patient profile response const mockPatient = mockFactory.healthcareMocks.createHIPAACompliantData('patient', 'patient', { id: 'patient_123', firstName: 'John', lastName: 'Doe', email: 'john.doe@test.com', dateOfBirth: '1990-01-01', phone: '555-0123', address: '123 Main St', city: 'Test City', state: 'TS', zipcode: '12345' }); mockFactory.httpMocks.mockRequest('GET', '/api/patient/profile/patient_123', { status: 200, data: { success: true, patient: mockPatient, lastUpdated: new Date().toISOString() } }); // Execute const result = await toolGenerator.executeTool(toolName, parameters); // Assert expect(result.success).toBe(true); expect(result.data.patient.id).toBe('patient_123'); expect(result.data.patient.firstName).toBe('John'); expect(result.data.patient.hipaaMetadata).toBeDefined(); }); test('should handle unauthorized access to other patient profiles', async () => { const toolName = 'patient_get_patientProfile'; const parameters = { patient_id: 'other_patient_456' // Different patient }; // Mock unauthorized access mockFactory.httpMocks.mockRequest('GET', '/api/patient/profile/other_patient_456', null, true, { response: { status: 403, data: { error: 'Unauthorized access to patient profile' } } }); await expect(toolGenerator.executeTool(toolName, parameters)) .rejects.toThrow(); }); }); describe('patient_put_updatePatientProfile', () => { test('should successfully update patient profile', async () => { // Setup const toolName = 'patient_put_updatePatientProfile'; const parameters = { patient_id: 'patient_123', firstName: 'John', lastName: 'Smith', // Changed last name email: 'john.smith@test.com', phone: '555-0124', address: '456 New St', city: 'New City', state: 'NC', zipcode: '54321', emergencyContact: { name: 'Jane Smith', relationship: 'Spouse', phone: '555-0125' } }; // Mock successful profile update mockFactory.httpMocks.mockRequest('PUT', '/api/patient/profile/patient_123', { status: 200, data: { success: true, patient: { id: 'patient_123', firstName: 'John', lastName: 'Smith', email: 'john.smith@test.com', phone: '555-0124', address: '456 New St', updatedAt: new Date().toISOString() }, message: 'Patient profile updated successfully' } }); // Execute const result = await toolGenerator.executeTool(toolName, parameters); // Assert expect(result.success).toBe(true); expect(result.data.patient.lastName).toBe('Smith'); expect(result.data.patient.email).toBe('john.smith@test.com'); }); test('should validate patient profile update data', async () => { const toolName = 'patient_put_updatePatientProfile'; // Test invalid email format const invalidEmailParams = { patient_id: 'patient_123', email: 'invalid-email-format' }; await expect(toolGenerator.executeTool(toolName, invalidEmailParams)) .rejects.toThrow(); // Test invalid phone format const invalidPhoneParams = { patient_id: 'patient_123', phone: 'invalid-phone' }; await expect(toolGenerator.executeTool(toolName, invalidPhoneParams)) .rejects.toThrow(); }); test('should handle concurrent profile updates', async () => { const toolName = 'patient_put_updatePatientProfile'; const parameters = { patient_id: 'patient_123', firstName: 'John', lastName: 'Updated' }; // Mock concurrent update conflict mockFactory.httpMocks.mockRequest('PUT', '/api/patient/profile/patient_123', null, true, { response: { status: 409, data: { error: 'Profile was updated by another session', lastModified: new Date().toISOString() } } }); await expect(toolGenerator.executeTool(toolName, parameters)) .rejects.toThrow(); }); }); describe('patient_get_medicalRecords', () => { test('should successfully retrieve patient medical records', async () => { // Setup const toolName = 'patient_get_medicalRecords'; const parameters = { patient_id: 'patient_123', limit: 10, offset: 0 }; // Mock medical records response const mockRecords = [ mockFactory.healthcareMocks.generateMockMedicalRecord({ patientId: 'patient_123', type: 'progress_note', date: '2025-07-01' }), mockFactory.healthcareMocks.generateMockMedicalRecord({ patientId: 'patient_123', type: 'lab_result', date: '2025-06-15' }) ]; mockFactory.httpMocks.mockRequest('GET', '/api/patient/medical-records/patient_123', { status: 200, data: { success: true, medical_records: mockRecords, total_count: 2, page: 1, per_page: 10 } }); // Execute const result = await toolGenerator.executeTool(toolName, parameters); // Assert expect(result.success).toBe(true); expect(result.data.medical_records).toHaveLength(2); expect(result.data.medical_records[0].patientId).toBe('patient_123'); expect(result.data.total_count).toBe(2); }); test('should filter medical records by date range', async () => { const toolName = 'patient_get_medicalRecords'; const parameters = { patient_id: 'patient_123', start_date: '2025-06-01', end_date: '2025-06-30' }; // Mock filtered records mockFactory.httpMocks.mockRequest('GET', '/api/patient/medical-records/patient_123', { status: 200, data: { success: true, medical_records: [ mockFactory.healthcareMocks.generateMockMedicalRecord({ patientId: 'patient_123', date: '2025-06-15' }) ], filters_applied: { start_date: '2025-06-01', end_date: '2025-06-30' } } }); const result = await toolGenerator.executeTool(toolName, parameters); expect(result.data.filters_applied.start_date).toBe('2025-06-01'); expect(result.data.filters_applied.end_date).toBe('2025-06-30'); }); }); describe('patient_get_prescriptions', () => { test('should successfully retrieve patient prescriptions', async () => { // Setup const toolName = 'patient_get_prescriptions'; const parameters = { patient_id: 'patient_123' }; // Mock prescriptions response const mockPrescriptions = [ mockFactory.healthcareMocks.generateMockPrescription({ patientId: 'patient_123', medication: { name: 'Lisinopril', strength: '10mg' }, status: 'active' }), mockFactory.healthcareMocks.generateMockPrescription({ patientId: 'patient_123', medication: { name: 'Metformin', strength: '500mg' }, status: 'active' }) ]; mockFactory.httpMocks.mockRequest('GET', '/api/patient/prescriptions/patient_123', { status: 200, data: { success: true, prescriptions: mockPrescriptions, active_count: 2, total_count: 2 } }); // Execute const result = await toolGenerator.executeTool(toolName, parameters); // Assert expect(result.success).toBe(true); expect(result.data.prescriptions).toHaveLength(2); expect(result.data.active_count).toBe(2); expect(result.data.prescriptions[0].medication.name).toBe('Lisinopril'); }); test('should filter prescriptions by status', async () => { const toolName = 'patient_get_prescriptions'; const parameters = { patient_id: 'patient_123', status: 'active' }; // Mock filtered prescriptions mockFactory.httpMocks.mockRequest('GET', '/api/patient/prescriptions/patient_123', { status: 200, data: { success: true, prescriptions: [ mockFactory.healthcareMocks.generateMockPrescription({ status: 'active' }) ], filter: { status: 'active' } } }); const result = await toolGenerator.executeTool(toolName, parameters); expect(result.data.filter.status).toBe('active'); }); }); describe('patient_get_appointments', () => { test('should successfully retrieve patient appointments', async () => { // Setup const toolName = 'patient_get_appointments'; const parameters = { patient_id: 'patient_123' }; // Mock appointments response const mockAppointments = [ mockFactory.healthcareMocks.generateMockAppointment({ patientId: 'patient_123', date: '2025-07-15', time: '10:00', status: 'scheduled' }), mockFactory.healthcareMocks.generateMockAppointment({ patientId: 'patient_123', date: '2025-07-20', time: '14:00', status: 'scheduled' }) ]; mockFactory.httpMocks.mockRequest('GET', '/api/patient/appointments/patient_123', { status: 200, data: { success: true, appointments: mockAppointments, upcoming_count: 2, total_count: 2 } }); // Execute const result = await toolGenerator.executeTool(toolName, parameters); // Assert expect(result.success).toBe(true); expect(result.data.appointments).toHaveLength(2); expect(result.data.upcoming_count).toBe(2); expect(result.data.appointments[0].status).toBe('scheduled'); }); }); describe('patient_post_scheduleAppointment', () => { test('should successfully schedule new appointment', async () => { // Setup const toolName = 'patient_post_scheduleAppointment'; const parameters = { patient_id: 'patient_123', provider_id: 'provider_456', appointment_date: '2025-07-25', appointment_time: '11:00', reason: 'Follow-up consultation', notes: 'Patient requested follow-up' }; // Mock successful appointment scheduling const mockAppointment = mockFactory.healthcareMocks.generateMockAppointment({ patientId: 'patient_123', providerId: 'provider_456', date: '2025-07-25', time: '11:00', reason: 'Follow-up consultation' }); mockFactory.httpMocks.mockRequest('POST', '/api/patient/schedule-appointment', { status: 201, data: { success: true, appointment: mockAppointment, confirmation_number: 'CONF123456', message: 'Appointment scheduled successfully' } }); // Execute const result = await toolGenerator.executeTool(toolName, parameters); // Assert expect(result.success).toBe(true); expect(result.data.appointment.patientId).toBe('patient_123'); expect(result.data.confirmation_number).toBe('CONF123456'); }); test('should handle scheduling conflicts', async () => { const toolName = 'patient_post_scheduleAppointment'; const parameters = { patient_id: 'patient_123', provider_id: 'provider_456', appointment_date: '2025-07-25', appointment_time: '11:00' }; // Mock scheduling conflict mockFactory.httpMocks.mockRequest('POST', '/api/patient/schedule-appointment', null, true, { response: { status: 409, data: { error: 'Time slot is no longer available', alternative_slots: [ { date: '2025-07-25', time: '12:00' }, { date: '2025-07-26', time: '11:00' } ] } } }); await expect(toolGenerator.executeTool(toolName, parameters)) .rejects.toThrow(); }); }); describe('patient_put_cancelAppointment', () => { test('should successfully cancel appointment', async () => { // Setup const toolName = 'patient_put_cancelAppointment'; const parameters = { appointment_id: 'appointment_123', cancellation_reason: 'Schedule conflict' }; // Mock successful cancellation mockFactory.httpMocks.mockRequest('PUT', '/api/patient/cancel-appointment/appointment_123', { status: 200, data: { success: true, appointment: { id: 'appointment_123', status: 'cancelled', cancellation_reason: 'Schedule conflict', cancelled_at: new Date().toISOString(), cancelled_by: 'patient_123' }, message: 'Appointment cancelled successfully' } }); // Execute const result = await toolGenerator.executeTool(toolName, parameters); // Assert expect(result.success).toBe(true); expect(result.data.appointment.status).toBe('cancelled'); expect(result.data.appointment.cancellation_reason).toBe('Schedule conflict'); }); test('should handle cancellation policy violations', async () => { const toolName = 'patient_put_cancelAppointment'; const parameters = { appointment_id: 'appointment_123' }; // Mock cancellation policy violation mockFactory.httpMocks.mockRequest('PUT', '/api/patient/cancel-appointment/appointment_123', null, true, { response: { status: 400, data: { error: 'Cancellation not allowed within 24 hours of appointment', policy: { minimum_notice_hours: 24, appointment_time: '2025-07-10 10:00:00' } } } }); await expect(toolGenerator.executeTool(toolName, parameters)) .rejects.toThrow(); }); }); describe('Patient Data Security and Privacy Tests', () => { test('should enforce patient data access restrictions', async () => { const toolName = 'patient_get_medicalRecords'; const parameters = { patient_id: 'other_patient_456' // Trying to access another patient's records }; // Mock unauthorized access mockFactory.httpMocks.mockRequest('GET', '/api/patient/medical-records/other_patient_456', null, true, { response: { status: 403, data: { error: 'Access denied: Can only view own medical records' } } }); await expect(toolGenerator.executeTool(toolName, parameters)) .rejects.toThrow(); }); test('should audit patient data access for HIPAA compliance', async () => { const toolName = 'patient_get_medicalRecords'; const parameters = { patient_id: 'patient_123' }; // Mock response with audit trail mockFactory.httpMocks.mockRequest('GET', '/api/patient/medical-records/patient_123', { status: 200, data: { success: true, medical_records: [mockFactory.healthcareMocks.generateMockMedicalRecord()], auditTrail: { accessedBy: 'patient_123', accessTime: new Date().toISOString(), accessType: 'patient_portal', ipAddress: '127.0.0.1', sessionId: 'session_123' } } }); const result = await toolGenerator.executeTool(toolName, parameters); expect(result.data.auditTrail).toBeDefined(); expect(result.data.auditTrail.accessType).toBe('patient_portal'); expect(result.data.auditTrail.accessedBy).toBe('patient_123'); }); test('should validate patient consent for data access', async () => { const toolName = 'patient_get_prescriptions'; const parameters = { patient_id: 'patient_123' }; // Mock consent verification mockFactory.httpMocks.mockRequest('GET', '/api/patient/prescriptions/patient_123', { status: 200, data: { success: true, prescriptions: [mockFactory.healthcareMocks.generateMockPrescription()], consentVerified: true, consentDetails: { dataAccessConsent: true, lastConsentUpdate: '2025-01-01', consentVersion: '2.0' } } }); const result = await toolGenerator.executeTool(toolName, parameters); expect(result.data.consentVerified).toBe(true); expect(result.data.consentDetails.dataAccessConsent).toBe(true); }); test('should handle data retention policies', async () => { const toolName = 'patient_get_medicalRecords'; const parameters = { patient_id: 'patient_123', include_archived: false }; // Mock data retention filtering mockFactory.httpMocks.mockRequest('GET', '/api/patient/medical-records/patient_123', { status: 200, data: { success: true, medical_records: [mockFactory.healthcareMocks.generateMockMedicalRecord()], retention_info: { active_records: 5, archived_records: 2, retention_period_years: 7, next_archive_date: '2026-01-01' } } }); const result = await toolGenerator.executeTool(toolName, parameters); expect(result.data.retention_info.retention_period_years).toBe(7); expect(result.data.retention_info.active_records).toBe(5); }); }); });