496 lines
15 KiB
JavaScript
496 lines
15 KiB
JavaScript
/**
|
|
* @fileoverview Tests for patient portal and authentication MCP tools
|
|
* Tests patient login, portal access, and patient-specific authentication
|
|
*/
|
|
|
|
import { describe, test, expect, beforeEach, afterEach } from '@jest/globals';
|
|
import { mockFactory } from '../mocks/mockFactory.js';
|
|
|
|
describe('Patient Portal and Authentication 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_create_patientlogin', () => {
|
|
test('should successfully login patient', async () => {
|
|
// Setup
|
|
const toolName = 'patient_create_patientlogin';
|
|
const parameters = {
|
|
email: 'patient@test.com',
|
|
password: 'patientpassword'
|
|
};
|
|
|
|
// Mock successful patient login
|
|
mockFactory.httpMocks.mockRequest('POST', '/api/patient/login', {
|
|
status: 200,
|
|
data: {
|
|
success: true,
|
|
token: 'patient_token_456',
|
|
user: {
|
|
id: 'patient_456',
|
|
email: 'patient@test.com',
|
|
role: 'patient',
|
|
firstName: 'John',
|
|
lastName: 'Doe',
|
|
portalAccess: true
|
|
},
|
|
permissions: ['read:own_data', 'write:own_data'],
|
|
message: 'Patient login successful'
|
|
}
|
|
});
|
|
|
|
// Execute
|
|
const result = await toolGenerator.executeTool(toolName, parameters);
|
|
|
|
// Assert
|
|
expect(result.success).toBe(true);
|
|
expect(result.data.user.role).toBe('patient');
|
|
expect(result.data.user.portalAccess).toBe(true);
|
|
expect(result.data.permissions).toContain('read:own_data');
|
|
});
|
|
|
|
test('should handle invalid patient credentials', async () => {
|
|
const toolName = 'patient_create_patientlogin';
|
|
const parameters = {
|
|
email: 'patient@test.com',
|
|
password: 'wrongpassword'
|
|
};
|
|
|
|
// Mock authentication failure
|
|
mockFactory.httpMocks.mockRequest('POST', '/api/patient/login', null, true, {
|
|
response: {
|
|
status: 401,
|
|
data: { error: 'Invalid patient credentials' }
|
|
}
|
|
});
|
|
|
|
await expect(toolGenerator.executeTool(toolName, parameters))
|
|
.rejects.toThrow();
|
|
});
|
|
|
|
test('should handle disabled portal access', async () => {
|
|
const toolName = 'patient_create_patientlogin';
|
|
const parameters = {
|
|
email: 'disabled@test.com',
|
|
password: 'password'
|
|
};
|
|
|
|
// Mock disabled portal access
|
|
mockFactory.httpMocks.mockRequest('POST', '/api/patient/login', null, true, {
|
|
response: {
|
|
status: 403,
|
|
data: { error: 'Portal access is disabled for this patient' }
|
|
}
|
|
});
|
|
|
|
await expect(toolGenerator.executeTool(toolName, parameters))
|
|
.rejects.toThrow();
|
|
});
|
|
});
|
|
|
|
describe('patient_create_patientLoginApi', () => {
|
|
test('should successfully login via API', async () => {
|
|
// Setup
|
|
const toolName = 'patient_create_patientLoginApi';
|
|
const parameters = {
|
|
email: 'patient@test.com',
|
|
password: 'patientpassword'
|
|
};
|
|
|
|
// Mock successful API login
|
|
mockFactory.httpMocks.mockRequest('POST', '/api/patient-login-api', {
|
|
status: 200,
|
|
data: {
|
|
success: true,
|
|
token: 'patient_api_token_789',
|
|
user: {
|
|
id: 'patient_789',
|
|
email: 'patient@test.com',
|
|
role: 'patient'
|
|
},
|
|
apiAccess: true,
|
|
tokenExpiry: new Date(Date.now() + 3600000).toISOString()
|
|
}
|
|
});
|
|
|
|
// Execute
|
|
const result = await toolGenerator.executeTool(toolName, parameters);
|
|
|
|
// Assert
|
|
expect(result.success).toBe(true);
|
|
expect(result.data.apiAccess).toBe(true);
|
|
expect(result.data.tokenExpiry).toBeDefined();
|
|
});
|
|
});
|
|
|
|
describe('patient_create_loginPatient', () => {
|
|
test('should successfully login patient with alternative endpoint', async () => {
|
|
// Setup
|
|
const toolName = 'patient_create_loginPatient';
|
|
const parameters = {
|
|
email: 'patient@test.com',
|
|
password: 'patientpassword'
|
|
};
|
|
|
|
// Mock successful login
|
|
mockFactory.httpMocks.mockRequest('POST', '/api/login-patient', {
|
|
status: 200,
|
|
data: {
|
|
success: true,
|
|
token: 'patient_login_token_101',
|
|
patient: {
|
|
id: 'patient_101',
|
|
email: 'patient@test.com',
|
|
firstName: 'Jane',
|
|
lastName: 'Smith',
|
|
dateOfBirth: '1985-03-15',
|
|
portalEnabled: true
|
|
}
|
|
}
|
|
});
|
|
|
|
// Execute
|
|
const result = await toolGenerator.executeTool(toolName, parameters);
|
|
|
|
// Assert
|
|
expect(result.success).toBe(true);
|
|
expect(result.data.patient.portalEnabled).toBe(true);
|
|
expect(result.data.patient.firstName).toBe('Jane');
|
|
});
|
|
});
|
|
|
|
describe('Patient Authentication Security Tests', () => {
|
|
test('should validate email format for patient login', async () => {
|
|
const toolName = 'patient_create_patientlogin';
|
|
const parameters = {
|
|
email: 'invalid-email-format',
|
|
password: 'password'
|
|
};
|
|
|
|
await expect(toolGenerator.executeTool(toolName, parameters))
|
|
.rejects.toThrow();
|
|
});
|
|
|
|
test('should handle account lockout after failed attempts', async () => {
|
|
const toolName = 'patient_create_patientlogin';
|
|
const parameters = {
|
|
email: 'locked@test.com',
|
|
password: 'password'
|
|
};
|
|
|
|
// Mock account lockout
|
|
mockFactory.httpMocks.mockRequest('POST', '/api/patient/login', null, true, {
|
|
response: {
|
|
status: 423,
|
|
data: {
|
|
error: 'Account temporarily locked due to multiple failed login attempts',
|
|
lockoutExpiry: new Date(Date.now() + 900000).toISOString() // 15 minutes
|
|
}
|
|
}
|
|
});
|
|
|
|
await expect(toolGenerator.executeTool(toolName, parameters))
|
|
.rejects.toThrow();
|
|
});
|
|
|
|
test('should enforce password complexity for patient accounts', async () => {
|
|
const toolName = 'patient_create_patientlogin';
|
|
const parameters = {
|
|
email: 'patient@test.com',
|
|
password: '123' // Weak password
|
|
};
|
|
|
|
// Mock weak password rejection
|
|
mockFactory.httpMocks.mockRequest('POST', '/api/patient/login', null, true, {
|
|
response: {
|
|
status: 400,
|
|
data: { error: 'Password does not meet security requirements' }
|
|
}
|
|
});
|
|
|
|
await expect(toolGenerator.executeTool(toolName, parameters))
|
|
.rejects.toThrow();
|
|
});
|
|
|
|
test('should audit patient login activities for HIPAA compliance', async () => {
|
|
const toolName = 'patient_create_patientlogin';
|
|
const parameters = {
|
|
email: 'patient@test.com',
|
|
password: 'validpassword'
|
|
};
|
|
|
|
// Mock login with audit trail
|
|
mockFactory.httpMocks.mockRequest('POST', '/api/patient/login', {
|
|
status: 200,
|
|
data: {
|
|
success: true,
|
|
token: 'patient_token_audit',
|
|
user: {
|
|
id: 'patient_audit',
|
|
email: 'patient@test.com',
|
|
role: 'patient'
|
|
},
|
|
auditTrail: {
|
|
loginTime: new Date().toISOString(),
|
|
ipAddress: '127.0.0.1',
|
|
userAgent: 'Jest Test Suite',
|
|
sessionId: 'session_123',
|
|
hipaaCompliant: true
|
|
}
|
|
}
|
|
});
|
|
|
|
const result = await toolGenerator.executeTool(toolName, parameters);
|
|
|
|
expect(result.data.auditTrail).toBeDefined();
|
|
expect(result.data.auditTrail.hipaaCompliant).toBe(true);
|
|
expect(result.data.auditTrail.sessionId).toBeDefined();
|
|
});
|
|
|
|
test('should handle concurrent login sessions', async () => {
|
|
const toolName = 'patient_create_patientlogin';
|
|
const parameters = {
|
|
email: 'patient@test.com',
|
|
password: 'password'
|
|
};
|
|
|
|
// Mock concurrent session handling
|
|
mockFactory.httpMocks.mockRequest('POST', '/api/patient/login', {
|
|
status: 200,
|
|
data: {
|
|
success: true,
|
|
token: 'new_session_token',
|
|
user: {
|
|
id: 'patient_concurrent',
|
|
email: 'patient@test.com',
|
|
role: 'patient'
|
|
},
|
|
sessionInfo: {
|
|
currentSessions: 2,
|
|
maxAllowedSessions: 3,
|
|
previousSessionTerminated: false
|
|
}
|
|
}
|
|
});
|
|
|
|
const result = await toolGenerator.executeTool(toolName, parameters);
|
|
|
|
expect(result.data.sessionInfo.currentSessions).toBe(2);
|
|
expect(result.data.sessionInfo.maxAllowedSessions).toBe(3);
|
|
});
|
|
|
|
test('should validate patient portal access permissions', async () => {
|
|
const toolName = 'patient_create_patientlogin';
|
|
const parameters = {
|
|
email: 'restricted@test.com',
|
|
password: 'password'
|
|
};
|
|
|
|
// Mock restricted portal access
|
|
mockFactory.httpMocks.mockRequest('POST', '/api/patient/login', {
|
|
status: 200,
|
|
data: {
|
|
success: true,
|
|
token: 'restricted_token',
|
|
user: {
|
|
id: 'patient_restricted',
|
|
email: 'restricted@test.com',
|
|
role: 'patient',
|
|
portalAccess: false
|
|
},
|
|
restrictions: {
|
|
reason: 'Account under review',
|
|
contactSupport: true
|
|
}
|
|
}
|
|
});
|
|
|
|
const result = await toolGenerator.executeTool(toolName, parameters);
|
|
|
|
expect(result.data.user.portalAccess).toBe(false);
|
|
expect(result.data.restrictions.reason).toBe('Account under review');
|
|
});
|
|
|
|
test('should handle two-factor authentication for patient accounts', async () => {
|
|
const toolName = 'patient_create_patientlogin';
|
|
const parameters = {
|
|
email: 'patient2fa@test.com',
|
|
password: 'password',
|
|
twoFactorCode: '123456'
|
|
};
|
|
|
|
// Mock 2FA verification
|
|
mockFactory.httpMocks.mockRequest('POST', '/api/patient/login', {
|
|
status: 200,
|
|
data: {
|
|
success: true,
|
|
token: 'patient_2fa_token',
|
|
user: {
|
|
id: 'patient_2fa',
|
|
email: 'patient2fa@test.com',
|
|
role: 'patient',
|
|
twoFactorEnabled: true
|
|
},
|
|
twoFactorVerified: true
|
|
}
|
|
});
|
|
|
|
const result = await toolGenerator.executeTool(toolName, parameters);
|
|
|
|
expect(result.data.user.twoFactorEnabled).toBe(true);
|
|
expect(result.data.twoFactorVerified).toBe(true);
|
|
});
|
|
|
|
test('should handle expired patient accounts', async () => {
|
|
const toolName = 'patient_create_patientlogin';
|
|
const parameters = {
|
|
email: 'expired@test.com',
|
|
password: 'password'
|
|
};
|
|
|
|
// Mock expired account
|
|
mockFactory.httpMocks.mockRequest('POST', '/api/patient/login', null, true, {
|
|
response: {
|
|
status: 403,
|
|
data: {
|
|
error: 'Patient account has expired',
|
|
expirationDate: '2024-12-31',
|
|
renewalRequired: true
|
|
}
|
|
}
|
|
});
|
|
|
|
await expect(toolGenerator.executeTool(toolName, parameters))
|
|
.rejects.toThrow();
|
|
});
|
|
|
|
test('should validate patient data access scope', async () => {
|
|
const toolName = 'patient_create_patientlogin';
|
|
const parameters = {
|
|
email: 'patient@test.com',
|
|
password: 'password'
|
|
};
|
|
|
|
// Mock login with data access scope
|
|
const mockPatient = mockFactory.healthcareMocks.createHIPAACompliantData('patient', 'patient');
|
|
|
|
mockFactory.httpMocks.mockRequest('POST', '/api/patient/login', {
|
|
status: 200,
|
|
data: {
|
|
success: true,
|
|
token: 'patient_scope_token',
|
|
user: mockPatient,
|
|
dataAccessScope: {
|
|
ownDataOnly: true,
|
|
medicalRecords: true,
|
|
prescriptions: true,
|
|
appointments: true,
|
|
billing: false // Limited billing access
|
|
}
|
|
}
|
|
});
|
|
|
|
const result = await toolGenerator.executeTool(toolName, parameters);
|
|
|
|
expect(result.data.dataAccessScope.ownDataOnly).toBe(true);
|
|
expect(result.data.dataAccessScope.medicalRecords).toBe(true);
|
|
expect(result.data.dataAccessScope.billing).toBe(false);
|
|
});
|
|
|
|
test('should handle patient consent verification', async () => {
|
|
const toolName = 'patient_create_patientlogin';
|
|
const parameters = {
|
|
email: 'patient@test.com',
|
|
password: 'password'
|
|
};
|
|
|
|
// Mock login with consent verification
|
|
mockFactory.httpMocks.mockRequest('POST', '/api/patient/login', {
|
|
status: 200,
|
|
data: {
|
|
success: true,
|
|
token: 'patient_consent_token',
|
|
user: {
|
|
id: 'patient_consent',
|
|
email: 'patient@test.com',
|
|
role: 'patient'
|
|
},
|
|
consentStatus: {
|
|
hipaaConsent: true,
|
|
dataProcessingConsent: true,
|
|
marketingConsent: false,
|
|
lastUpdated: '2025-01-01'
|
|
}
|
|
}
|
|
});
|
|
|
|
const result = await toolGenerator.executeTool(toolName, parameters);
|
|
|
|
expect(result.data.consentStatus.hipaaConsent).toBe(true);
|
|
expect(result.data.consentStatus.dataProcessingConsent).toBe(true);
|
|
expect(result.data.consentStatus.marketingConsent).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('Patient Portal Feature Access Tests', () => {
|
|
test('should validate patient portal feature availability', async () => {
|
|
const toolName = 'patient_create_patientlogin';
|
|
const parameters = {
|
|
email: 'patient@test.com',
|
|
password: 'password'
|
|
};
|
|
|
|
// Mock login with feature access
|
|
mockFactory.httpMocks.mockRequest('POST', '/api/patient/login', {
|
|
status: 200,
|
|
data: {
|
|
success: true,
|
|
token: 'patient_features_token',
|
|
user: {
|
|
id: 'patient_features',
|
|
email: 'patient@test.com',
|
|
role: 'patient'
|
|
},
|
|
portalFeatures: {
|
|
viewMedicalRecords: true,
|
|
scheduleAppointments: true,
|
|
viewPrescriptions: true,
|
|
messaging: true,
|
|
billing: false,
|
|
labResults: true,
|
|
telehealth: true
|
|
}
|
|
}
|
|
});
|
|
|
|
const result = await toolGenerator.executeTool(toolName, parameters);
|
|
|
|
expect(result.data.portalFeatures.viewMedicalRecords).toBe(true);
|
|
expect(result.data.portalFeatures.scheduleAppointments).toBe(true);
|
|
expect(result.data.portalFeatures.billing).toBe(false);
|
|
});
|
|
});
|
|
});
|