first
This commit is contained in:
459
tests/public/data-access.test.js
Normal file
459
tests/public/data-access.test.js
Normal file
@@ -0,0 +1,459 @@
|
||||
/**
|
||||
* @fileoverview Tests for public data access MCP tools
|
||||
* Tests email checking, user validation, and public data endpoints
|
||||
*/
|
||||
|
||||
import { describe, test, expect, beforeEach, afterEach } from '@jest/globals';
|
||||
import { mockFactory } from '../mocks/mockFactory.js';
|
||||
|
||||
describe('Public Data Access Tools', () => {
|
||||
let mockEnv;
|
||||
let toolGenerator;
|
||||
|
||||
beforeEach(() => {
|
||||
mockEnv = mockFactory.createMockEnvironment({
|
||||
authTypes: ['public'],
|
||||
enableHttpMocks: true,
|
||||
enableHealthcareMocks: true
|
||||
});
|
||||
|
||||
toolGenerator = mockEnv.toolGenerator;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFactory.resetAllMocks();
|
||||
});
|
||||
|
||||
describe('public_create_checkEmail', () => {
|
||||
test('should check if email is available', async () => {
|
||||
// Setup
|
||||
const toolName = 'public_create_checkEmail';
|
||||
const parameters = {
|
||||
email: 'newuser@test.com'
|
||||
};
|
||||
|
||||
// Mock email availability response
|
||||
mockFactory.httpMocks.mockRequest('POST', '/api/check-email', {
|
||||
status: 200,
|
||||
data: {
|
||||
available: true,
|
||||
email: 'newuser@test.com',
|
||||
message: 'Email is available'
|
||||
}
|
||||
});
|
||||
|
||||
// Execute
|
||||
const result = await toolGenerator.executeTool(toolName, parameters);
|
||||
|
||||
// Assert
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data.available).toBe(true);
|
||||
expect(result.data.email).toBe('newuser@test.com');
|
||||
});
|
||||
|
||||
test('should detect existing email', async () => {
|
||||
// Setup
|
||||
const toolName = 'public_create_checkEmail';
|
||||
const parameters = {
|
||||
email: 'existing@test.com'
|
||||
};
|
||||
|
||||
// Mock existing email response
|
||||
mockFactory.httpMocks.mockRequest('POST', '/api/check-email', {
|
||||
status: 200,
|
||||
data: {
|
||||
available: false,
|
||||
email: 'existing@test.com',
|
||||
message: 'Email is already in use'
|
||||
}
|
||||
});
|
||||
|
||||
// Execute
|
||||
const result = await toolGenerator.executeTool(toolName, parameters);
|
||||
|
||||
// Assert
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data.available).toBe(false);
|
||||
});
|
||||
|
||||
test('should validate email format', async () => {
|
||||
const toolName = 'public_create_checkEmail';
|
||||
const parameters = {
|
||||
email: 'invalid-email-format'
|
||||
};
|
||||
|
||||
await expect(toolGenerator.executeTool(toolName, parameters))
|
||||
.rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('public_create_checkUser', () => {
|
||||
test('should check if provider exists', async () => {
|
||||
// Setup
|
||||
const toolName = 'public_create_checkUser';
|
||||
const parameters = {
|
||||
email: 'provider@test.com'
|
||||
};
|
||||
|
||||
// Mock provider exists response
|
||||
mockFactory.httpMocks.mockRequest('POST', '/api/check-user', {
|
||||
status: 200,
|
||||
data: {
|
||||
exists: true,
|
||||
email: 'provider@test.com',
|
||||
userType: 'provider',
|
||||
message: 'Provider found'
|
||||
}
|
||||
});
|
||||
|
||||
// Execute
|
||||
const result = await toolGenerator.executeTool(toolName, parameters);
|
||||
|
||||
// Assert
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data.exists).toBe(true);
|
||||
expect(result.data.userType).toBe('provider');
|
||||
});
|
||||
|
||||
test('should handle non-existent user', async () => {
|
||||
// Setup
|
||||
const toolName = 'public_create_checkUser';
|
||||
const parameters = {
|
||||
email: 'nonexistent@test.com'
|
||||
};
|
||||
|
||||
// Mock user not found response
|
||||
mockFactory.httpMocks.mockRequest('POST', '/api/check-user', {
|
||||
status: 200,
|
||||
data: {
|
||||
exists: false,
|
||||
email: 'nonexistent@test.com',
|
||||
message: 'User not found'
|
||||
}
|
||||
});
|
||||
|
||||
// Execute
|
||||
const result = await toolGenerator.executeTool(toolName, parameters);
|
||||
|
||||
// Assert
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data.exists).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('public_get_appointmentParticipant', () => {
|
||||
test('should get appointment participants', async () => {
|
||||
// Setup
|
||||
const toolName = 'public_get_appointmentParticipant';
|
||||
const parameters = {
|
||||
appointmentId: 'appointment_123'
|
||||
};
|
||||
|
||||
// Mock appointment participants response
|
||||
mockFactory.httpMocks.mockRequest('GET', '/api/appointment-participants/appointment_123', {
|
||||
status: 200,
|
||||
data: {
|
||||
appointmentId: 'appointment_123',
|
||||
participants: [
|
||||
{
|
||||
id: 'participant_1',
|
||||
name: 'Dr. Smith',
|
||||
role: 'provider',
|
||||
email: 'dr.smith@test.com'
|
||||
},
|
||||
{
|
||||
id: 'participant_2',
|
||||
name: 'John Doe',
|
||||
role: 'patient',
|
||||
email: 'john.doe@test.com'
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
// Execute
|
||||
const result = await toolGenerator.executeTool(toolName, parameters);
|
||||
|
||||
// Assert
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data.participants).toHaveLength(2);
|
||||
expect(result.data.participants[0].role).toBe('provider');
|
||||
expect(result.data.participants[1].role).toBe('patient');
|
||||
});
|
||||
|
||||
test('should handle invalid appointment ID', async () => {
|
||||
// Setup
|
||||
const toolName = 'public_get_appointmentParticipant';
|
||||
const parameters = {
|
||||
appointmentId: 'invalid_appointment'
|
||||
};
|
||||
|
||||
// Mock not found response
|
||||
mockFactory.httpMocks.mockRequest('GET', '/api/appointment-participants/invalid_appointment', null, true, {
|
||||
response: {
|
||||
status: 404,
|
||||
data: { error: 'Appointment not found' }
|
||||
}
|
||||
});
|
||||
|
||||
await expect(toolGenerator.executeTool(toolName, parameters))
|
||||
.rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('public_create_patientavailableSlot', () => {
|
||||
test('should get available appointment slots', async () => {
|
||||
// Setup
|
||||
const toolName = 'public_create_patientavailableSlot';
|
||||
const parameters = {
|
||||
date: '2025-07-15'
|
||||
};
|
||||
|
||||
// Mock available slots response
|
||||
mockFactory.httpMocks.mockRequest('POST', '/api/patient/available-slots/2025-07-15', {
|
||||
status: 200,
|
||||
data: {
|
||||
date: '2025-07-15',
|
||||
availableSlots: [
|
||||
{
|
||||
time: '09:00',
|
||||
duration: 30,
|
||||
providerId: 'provider_123',
|
||||
providerName: 'Dr. Smith'
|
||||
},
|
||||
{
|
||||
time: '10:30',
|
||||
duration: 30,
|
||||
providerId: 'provider_123',
|
||||
providerName: 'Dr. Smith'
|
||||
},
|
||||
{
|
||||
time: '14:00',
|
||||
duration: 60,
|
||||
providerId: 'provider_456',
|
||||
providerName: 'Dr. Johnson'
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
// Execute
|
||||
const result = await toolGenerator.executeTool(toolName, parameters);
|
||||
|
||||
// Assert
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data.availableSlots).toHaveLength(3);
|
||||
expect(result.data.date).toBe('2025-07-15');
|
||||
});
|
||||
|
||||
test('should validate date format', async () => {
|
||||
const toolName = 'public_create_patientavailableSlot';
|
||||
const parameters = {
|
||||
date: 'invalid-date'
|
||||
};
|
||||
|
||||
await expect(toolGenerator.executeTool(toolName, parameters))
|
||||
.rejects.toThrow();
|
||||
});
|
||||
|
||||
test('should handle no available slots', async () => {
|
||||
// Setup
|
||||
const toolName = 'public_create_patientavailableSlot';
|
||||
const parameters = {
|
||||
date: '2025-12-25' // Holiday - no slots
|
||||
};
|
||||
|
||||
// Mock no slots response
|
||||
mockFactory.httpMocks.mockRequest('POST', '/api/patient/available-slots/2025-12-25', {
|
||||
status: 200,
|
||||
data: {
|
||||
date: '2025-12-25',
|
||||
availableSlots: [],
|
||||
message: 'No available slots for this date'
|
||||
}
|
||||
});
|
||||
|
||||
// Execute
|
||||
const result = await toolGenerator.executeTool(toolName, parameters);
|
||||
|
||||
// Assert
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data.availableSlots).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('public_create_patientBookAppointment', () => {
|
||||
test('should successfully book appointment', async () => {
|
||||
// Setup
|
||||
const toolName = 'public_create_patientBookAppointment';
|
||||
const parameters = {
|
||||
patient_id: 'patient_123',
|
||||
start_time: '09:00',
|
||||
end_time: '09:30',
|
||||
practitioner_id: 'provider_456',
|
||||
appointment_date: '2025-07-15',
|
||||
appointment_time: '09:00',
|
||||
notes: 'Annual checkup',
|
||||
order_id: 789
|
||||
};
|
||||
|
||||
// Mock successful booking response
|
||||
mockFactory.httpMocks.mockRequest('POST', '/api/patient-book-appointment', {
|
||||
status: 201,
|
||||
data: {
|
||||
success: true,
|
||||
appointment: {
|
||||
id: 'appointment_789',
|
||||
patientId: 'patient_123',
|
||||
practitionerId: 'provider_456',
|
||||
date: '2025-07-15',
|
||||
time: '09:00',
|
||||
status: 'scheduled'
|
||||
},
|
||||
message: 'Appointment booked successfully'
|
||||
}
|
||||
});
|
||||
|
||||
// Execute
|
||||
const result = await toolGenerator.executeTool(toolName, parameters);
|
||||
|
||||
// Assert
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data.appointment.status).toBe('scheduled');
|
||||
expect(result.data.appointment.date).toBe('2025-07-15');
|
||||
});
|
||||
|
||||
test('should handle scheduling conflicts', async () => {
|
||||
// Setup
|
||||
const toolName = 'public_create_patientBookAppointment';
|
||||
const parameters = {
|
||||
patient_id: 'patient_123',
|
||||
start_time: '09:00',
|
||||
end_time: '09:30',
|
||||
practitioner_id: 'provider_456',
|
||||
appointment_date: '2025-07-15',
|
||||
appointment_time: '09:00'
|
||||
};
|
||||
|
||||
// Mock conflict response
|
||||
mockFactory.httpMocks.mockRequest('POST', '/api/patient-book-appointment', null, true, {
|
||||
response: {
|
||||
status: 409,
|
||||
data: { error: 'Time slot is no longer available' }
|
||||
}
|
||||
});
|
||||
|
||||
await expect(toolGenerator.executeTool(toolName, parameters))
|
||||
.rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Email Verification Tools', () => {
|
||||
describe('public_create_publicManageVerifyEmail', () => {
|
||||
test('should successfully verify email', async () => {
|
||||
// Setup
|
||||
const toolName = 'public_create_publicManageVerifyEmail';
|
||||
const parameters = {
|
||||
token: 'verification_token_123',
|
||||
email: 'user@test.com'
|
||||
};
|
||||
|
||||
// Mock successful verification
|
||||
mockFactory.httpMocks.mockRequest('POST', '/api/public-manage-verify-email', {
|
||||
status: 200,
|
||||
data: {
|
||||
success: true,
|
||||
email: 'user@test.com',
|
||||
verified: true,
|
||||
message: 'Email verified successfully'
|
||||
}
|
||||
});
|
||||
|
||||
// Execute
|
||||
const result = await toolGenerator.executeTool(toolName, parameters);
|
||||
|
||||
// Assert
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data.verified).toBe(true);
|
||||
});
|
||||
|
||||
test('should handle invalid verification token', async () => {
|
||||
// Setup
|
||||
const toolName = 'public_create_publicManageVerifyEmail';
|
||||
const parameters = {
|
||||
token: 'invalid_token',
|
||||
email: 'user@test.com'
|
||||
};
|
||||
|
||||
// Mock invalid token response
|
||||
mockFactory.httpMocks.mockRequest('POST', '/api/public-manage-verify-email', null, true, {
|
||||
response: {
|
||||
status: 400,
|
||||
data: { error: 'Invalid verification token' }
|
||||
}
|
||||
});
|
||||
|
||||
await expect(toolGenerator.executeTool(toolName, parameters))
|
||||
.rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('public_create_publicManageResendVerification', () => {
|
||||
test('should successfully resend verification email', async () => {
|
||||
// Setup
|
||||
const toolName = 'public_create_publicManageResendVerification';
|
||||
const parameters = {
|
||||
email: 'user@test.com'
|
||||
};
|
||||
|
||||
// Mock successful resend
|
||||
mockFactory.httpMocks.mockRequest('POST', '/api/public-manage-resend-verification', {
|
||||
status: 200,
|
||||
data: {
|
||||
success: true,
|
||||
email: 'user@test.com',
|
||||
message: 'Verification email sent'
|
||||
}
|
||||
});
|
||||
|
||||
// Execute
|
||||
const result = await toolGenerator.executeTool(toolName, parameters);
|
||||
|
||||
// Assert
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data.message).toContain('Verification email sent');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Data Access Security Tests', () => {
|
||||
test('should handle rate limiting for email checks', async () => {
|
||||
const toolName = 'public_create_checkEmail';
|
||||
const parameters = {
|
||||
email: 'test@test.com'
|
||||
};
|
||||
|
||||
// Mock rate limit response
|
||||
mockFactory.httpMocks.mockRequest('POST', '/api/check-email', null, true, {
|
||||
response: {
|
||||
status: 429,
|
||||
data: { error: 'Too many requests' }
|
||||
}
|
||||
});
|
||||
|
||||
await expect(toolGenerator.executeTool(toolName, parameters))
|
||||
.rejects.toThrow();
|
||||
});
|
||||
|
||||
test('should sanitize input parameters', async () => {
|
||||
const toolName = 'public_create_checkEmail';
|
||||
const parameters = {
|
||||
email: '<script>alert("xss")</script>@test.com'
|
||||
};
|
||||
|
||||
// Should reject malicious input
|
||||
await expect(toolGenerator.executeTool(toolName, parameters))
|
||||
.rejects.toThrow();
|
||||
});
|
||||
});
|
||||
});
|
352
tests/public/index.test.js
Normal file
352
tests/public/index.test.js
Normal file
@@ -0,0 +1,352 @@
|
||||
/**
|
||||
* @fileoverview Comprehensive test suite for all public MCP tools
|
||||
* Runs all public tool tests and provides coverage reporting
|
||||
*/
|
||||
|
||||
import { describe, test, expect, beforeAll, afterAll } from '@jest/globals';
|
||||
import { mockFactory } from '../mocks/mockFactory.js';
|
||||
|
||||
describe('Public Tools Integration Tests', () => {
|
||||
let mockEnv;
|
||||
let toolGenerator;
|
||||
|
||||
beforeAll(async () => {
|
||||
// Setup comprehensive mock environment
|
||||
mockEnv = mockFactory.createMockEnvironment({
|
||||
authTypes: ['public'],
|
||||
enableHttpMocks: true,
|
||||
enableAuthMocks: true,
|
||||
enableHealthcareMocks: true
|
||||
});
|
||||
|
||||
toolGenerator = mockEnv.toolGenerator;
|
||||
|
||||
// Setup all public endpoint mocks
|
||||
setupPublicEndpointMocks();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
mockFactory.resetAllMocks();
|
||||
});
|
||||
|
||||
/**
|
||||
* Setup mock responses for all public endpoints
|
||||
*/
|
||||
function setupPublicEndpointMocks() {
|
||||
// Login endpoints
|
||||
const loginEndpoints = [
|
||||
{ method: 'POST', path: '/api/login' },
|
||||
{ method: 'POST', path: '/api/frontend/login' },
|
||||
{ method: 'POST', path: '/api/admin/login' },
|
||||
{ method: 'POST', path: '/api/login-partner-api' },
|
||||
{ method: 'POST', path: '/api/affiliate-login-api' },
|
||||
{ method: 'POST', path: '/api/network/login' },
|
||||
{ method: 'POST', path: '/api/patient/login' },
|
||||
{ method: 'POST', path: '/api/patient-login-api' },
|
||||
{ method: 'POST', path: '/api/login-patient' }
|
||||
];
|
||||
|
||||
loginEndpoints.forEach(endpoint => {
|
||||
mockFactory.httpMocks.mockRequest(endpoint.method, endpoint.path, {
|
||||
status: 200,
|
||||
data: {
|
||||
success: true,
|
||||
token: `mock_token_${Date.now()}`,
|
||||
user: { id: 'user_123', email: 'test@example.com' }
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Registration endpoints
|
||||
const registrationEndpoints = [
|
||||
{ method: 'POST', path: '/emr-api/provider-register' },
|
||||
{ method: 'POST', path: '/api/register-patients' },
|
||||
{ method: 'POST', path: '/api/register-patient' },
|
||||
{ method: 'POST', path: '/api/affiliate-register-api' },
|
||||
{ method: 'POST', path: '/api/partner-register-api' },
|
||||
{ method: 'POST', path: '/api/network/register' },
|
||||
{ method: 'POST', path: '/api/emr/provider/register' },
|
||||
{ method: 'POST', path: '/api/patient/register-patient' }
|
||||
];
|
||||
|
||||
registrationEndpoints.forEach(endpoint => {
|
||||
mockFactory.httpMocks.mockRequest(endpoint.method, endpoint.path, {
|
||||
status: 201,
|
||||
data: {
|
||||
success: true,
|
||||
user: { id: 'new_user_123', email: 'newuser@example.com' },
|
||||
message: 'Registration successful'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Password management endpoints
|
||||
const passwordEndpoints = [
|
||||
{ method: 'POST', path: '/api/forgot-password' },
|
||||
{ method: 'POST', path: '/api/frontend/forgot-password' },
|
||||
{ method: 'POST', path: '/api/emr/provider/forgot-password' },
|
||||
{ method: 'POST', path: '/api/password-reset' },
|
||||
{ method: 'POST', path: '/api/frontend/reset-password' },
|
||||
{ method: 'POST', path: '/api/emr/provider/reset-password' },
|
||||
{ method: 'POST', path: '/api/set-password' },
|
||||
{ method: 'POST', path: '/api/emr/set-password' },
|
||||
{ method: 'POST', path: '/api/affiliate/set-password' },
|
||||
{ method: 'POST', path: '/api/reset-password' }
|
||||
];
|
||||
|
||||
passwordEndpoints.forEach(endpoint => {
|
||||
mockFactory.httpMocks.mockRequest(endpoint.method, endpoint.path, {
|
||||
status: 200,
|
||||
data: {
|
||||
success: true,
|
||||
message: 'Password operation successful'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Data access endpoints
|
||||
mockFactory.httpMocks.mockRequest('POST', '/api/check-email', {
|
||||
status: 200,
|
||||
data: { available: true, email: 'test@example.com' }
|
||||
});
|
||||
|
||||
mockFactory.httpMocks.mockRequest('POST', '/api/check-user', {
|
||||
status: 200,
|
||||
data: { exists: true, userType: 'provider' }
|
||||
});
|
||||
|
||||
// Appointment endpoints
|
||||
mockFactory.httpMocks.mockRequest('POST', '/api/patient/available-slots/2025-07-15', {
|
||||
status: 200,
|
||||
data: {
|
||||
date: '2025-07-15',
|
||||
availableSlots: [
|
||||
{ time: '09:00', duration: 30, providerId: 'provider_123' }
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
mockFactory.httpMocks.mockRequest('POST', '/api/patient-book-appointment', {
|
||||
status: 201,
|
||||
data: {
|
||||
success: true,
|
||||
appointment: { id: 'appointment_123', status: 'scheduled' }
|
||||
}
|
||||
});
|
||||
|
||||
// Verification endpoints
|
||||
mockFactory.httpMocks.mockRequest('POST', '/api/public-manage-verify-email', {
|
||||
status: 200,
|
||||
data: { success: true, verified: true }
|
||||
});
|
||||
|
||||
mockFactory.httpMocks.mockRequest('POST', '/api/public-manage-resend-verification', {
|
||||
status: 200,
|
||||
data: { success: true, message: 'Verification email sent' }
|
||||
});
|
||||
}
|
||||
|
||||
describe('Public Tools Coverage Test', () => {
|
||||
test('should have all 77 public tools available', async () => {
|
||||
const allTools = toolGenerator.generateAllTools();
|
||||
const publicTools = allTools.filter(tool => tool.name.startsWith('public_'));
|
||||
|
||||
// Verify we have the expected number of public tools
|
||||
expect(publicTools.length).toBeGreaterThanOrEqual(70); // Allow for some variance
|
||||
|
||||
// Verify tool naming convention
|
||||
publicTools.forEach(tool => {
|
||||
expect(tool.name).toMatch(/^public_[a-zA-Z]+_[a-zA-Z]+/);
|
||||
expect(tool.description).toBeDefined();
|
||||
expect(tool.inputSchema).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
test('should categorize public tools correctly', async () => {
|
||||
const allTools = toolGenerator.generateAllTools();
|
||||
const publicTools = allTools.filter(tool => tool.name.startsWith('public_'));
|
||||
|
||||
const categories = {
|
||||
login: [],
|
||||
registration: [],
|
||||
password: [],
|
||||
verification: [],
|
||||
data: [],
|
||||
appointment: [],
|
||||
other: []
|
||||
};
|
||||
|
||||
publicTools.forEach(tool => {
|
||||
if (tool.name.includes('login') || tool.name.includes('Login')) {
|
||||
categories.login.push(tool);
|
||||
} else if (tool.name.includes('register') || tool.name.includes('Register')) {
|
||||
categories.registration.push(tool);
|
||||
} else if (tool.name.includes('password') || tool.name.includes('Password') || tool.name.includes('setPassword')) {
|
||||
categories.password.push(tool);
|
||||
} else if (tool.name.includes('verify') || tool.name.includes('Verify') || tool.name.includes('verification')) {
|
||||
categories.verification.push(tool);
|
||||
} else if (tool.name.includes('check') || tool.name.includes('Check') || tool.name.includes('get')) {
|
||||
categories.data.push(tool);
|
||||
} else if (tool.name.includes('appointment') || tool.name.includes('Appointment') || tool.name.includes('slot')) {
|
||||
categories.appointment.push(tool);
|
||||
} else {
|
||||
categories.other.push(tool);
|
||||
}
|
||||
});
|
||||
|
||||
// Verify we have tools in each major category
|
||||
expect(categories.login.length).toBeGreaterThan(5);
|
||||
expect(categories.registration.length).toBeGreaterThan(5);
|
||||
expect(categories.password.length).toBeGreaterThan(5);
|
||||
|
||||
console.log('Public Tools Distribution:');
|
||||
Object.entries(categories).forEach(([category, tools]) => {
|
||||
console.log(` ${category}: ${tools.length} tools`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Public Tools Parameter Validation', () => {
|
||||
test('should validate required parameters for login tools', async () => {
|
||||
const loginTools = [
|
||||
'public_create_login',
|
||||
'public_create_frontendlogin',
|
||||
'public_create_adminlogin'
|
||||
];
|
||||
|
||||
for (const toolName of loginTools) {
|
||||
const tool = toolGenerator.getTool(toolName);
|
||||
if (tool) {
|
||||
expect(tool.inputSchema.required).toBeDefined();
|
||||
expect(tool.inputSchema.required.length).toBeGreaterThan(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('should validate required parameters for registration tools', async () => {
|
||||
const registrationTools = [
|
||||
'public_create_emrApiproviderRegister',
|
||||
'public_create_registerPatient',
|
||||
'public_create_affiliateRegisterApi'
|
||||
];
|
||||
|
||||
for (const toolName of registrationTools) {
|
||||
const tool = toolGenerator.getTool(toolName);
|
||||
if (tool) {
|
||||
expect(tool.inputSchema.required).toBeDefined();
|
||||
expect(tool.inputSchema.required.length).toBeGreaterThan(1);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('Public Tools Error Handling', () => {
|
||||
test('should handle network errors gracefully', async () => {
|
||||
// Mock network error for all endpoints
|
||||
mockFactory.httpMocks.setDefaultResponse('POST', null);
|
||||
mockFactory.httpMocks.setDefaultResponse('GET', null);
|
||||
|
||||
const testTool = 'public_create_login';
|
||||
const parameters = { username: 'test', password: 'test' };
|
||||
|
||||
// Should handle network errors without crashing
|
||||
await expect(toolGenerator.executeTool(testTool, parameters))
|
||||
.rejects.toThrow();
|
||||
});
|
||||
|
||||
test('should handle malformed responses', async () => {
|
||||
// Mock malformed response
|
||||
mockFactory.httpMocks.mockRequest('POST', '/api/login', {
|
||||
status: 200,
|
||||
data: 'invalid json response'
|
||||
});
|
||||
|
||||
const testTool = 'public_create_login';
|
||||
const parameters = { username: 'test', password: 'test' };
|
||||
|
||||
await expect(toolGenerator.executeTool(testTool, parameters))
|
||||
.rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Public Tools Security Tests', () => {
|
||||
test('should not expose sensitive information in logs', async () => {
|
||||
const testTool = 'public_create_login';
|
||||
const parameters = {
|
||||
username: 'testuser',
|
||||
password: 'supersecretpassword123!'
|
||||
};
|
||||
|
||||
await toolGenerator.executeTool(testTool, parameters);
|
||||
|
||||
// Check request history doesn't contain password
|
||||
const history = mockFactory.httpMocks.getRequestHistory();
|
||||
const loginRequest = history.find(req => req.url === '/api/login');
|
||||
|
||||
if (loginRequest) {
|
||||
const requestString = JSON.stringify(loginRequest);
|
||||
expect(requestString).not.toContain('supersecretpassword123!');
|
||||
}
|
||||
});
|
||||
|
||||
test('should validate input sanitization', async () => {
|
||||
const testTool = 'public_create_checkEmail';
|
||||
const maliciousEmail = '<script>alert("xss")</script>@test.com';
|
||||
|
||||
await expect(toolGenerator.executeTool(testTool, { email: maliciousEmail }))
|
||||
.rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Public Tools Performance Tests', () => {
|
||||
test('should complete tool execution within reasonable time', async () => {
|
||||
const testTool = 'public_create_checkEmail';
|
||||
const parameters = { email: 'test@example.com' };
|
||||
|
||||
const startTime = Date.now();
|
||||
await toolGenerator.executeTool(testTool, parameters);
|
||||
const endTime = Date.now();
|
||||
|
||||
const executionTime = endTime - startTime;
|
||||
expect(executionTime).toBeLessThan(5000); // Should complete within 5 seconds
|
||||
});
|
||||
|
||||
test('should handle concurrent tool executions', async () => {
|
||||
const testTool = 'public_create_checkEmail';
|
||||
const promises = [];
|
||||
|
||||
// Execute 10 concurrent requests
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const parameters = { email: `test${i}@example.com` };
|
||||
promises.push(toolGenerator.executeTool(testTool, parameters));
|
||||
}
|
||||
|
||||
const results = await Promise.allSettled(promises);
|
||||
|
||||
// All requests should complete (either fulfilled or rejected)
|
||||
expect(results.length).toBe(10);
|
||||
results.forEach(result => {
|
||||
expect(['fulfilled', 'rejected']).toContain(result.status);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Public Tools Integration', () => {
|
||||
test('should maintain consistent response format across tools', async () => {
|
||||
const testTools = [
|
||||
{ name: 'public_create_login', params: { username: 'test', password: 'test' } },
|
||||
{ name: 'public_create_checkEmail', params: { email: 'test@example.com' } },
|
||||
{ name: 'public_create_forgotPassword', params: { email: 'test@example.com' } }
|
||||
];
|
||||
|
||||
for (const { name, params } of testTools) {
|
||||
const result = await toolGenerator.executeTool(name, params);
|
||||
|
||||
// All tools should return consistent structure
|
||||
expect(result).toHaveProperty('success');
|
||||
expect(result).toHaveProperty('data');
|
||||
expect(typeof result.success).toBe('boolean');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
349
tests/public/login.test.js
Normal file
349
tests/public/login.test.js
Normal file
@@ -0,0 +1,349 @@
|
||||
/**
|
||||
* @fileoverview Tests for public login MCP tools
|
||||
* Tests all public authentication and login endpoints
|
||||
*/
|
||||
|
||||
import { describe, test, expect, beforeEach, afterEach } from "@jest/globals";
|
||||
import { mockFactory } from "../mocks/mockFactory.js";
|
||||
import { ToolGenerator } from "../../src/tools/ToolGenerator.js";
|
||||
|
||||
describe("Public Login Tools", () => {
|
||||
let mockEnv;
|
||||
let toolGenerator;
|
||||
|
||||
beforeEach(() => {
|
||||
// Create mock environment
|
||||
mockEnv = mockFactory.createMockEnvironment({
|
||||
authTypes: ["public"],
|
||||
enableHttpMocks: true,
|
||||
enableAuthMocks: true,
|
||||
});
|
||||
|
||||
toolGenerator = mockEnv.toolGenerator;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFactory.resetAllMocks();
|
||||
});
|
||||
|
||||
describe("public_create_login", () => {
|
||||
test("should successfully login with valid credentials", async () => {
|
||||
// Setup
|
||||
const toolName = "public_create_login";
|
||||
const parameters = {
|
||||
username: "validuser",
|
||||
password: "validpassword",
|
||||
};
|
||||
|
||||
// Mock successful login response
|
||||
mockFactory.httpMocks.mockRequest("POST", "/api/login", {
|
||||
status: 200,
|
||||
data: {
|
||||
success: true,
|
||||
token: "mock_login_token_123",
|
||||
user: {
|
||||
id: "user_123",
|
||||
username: "validuser",
|
||||
email: "test@example.com",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Execute
|
||||
const result = await toolGenerator.executeTool(toolName, parameters);
|
||||
|
||||
// Assert
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data).toBeDefined();
|
||||
|
||||
// Verify HTTP request was made
|
||||
const requestHistory = mockFactory.httpMocks.getRequestHistory();
|
||||
const loginRequest = requestHistory.find(
|
||||
(req) => req.method === "POST" && req.url === "/api/login"
|
||||
);
|
||||
expect(loginRequest).toBeDefined();
|
||||
});
|
||||
|
||||
test("should fail with invalid credentials", async () => {
|
||||
// Setup
|
||||
const toolName = "public_create_login";
|
||||
const parameters = {
|
||||
username: "invaliduser",
|
||||
password: "wrongpassword",
|
||||
};
|
||||
|
||||
// Mock failed login response
|
||||
mockFactory.httpMocks.mockRequest("POST", "/api/login", null, true, {
|
||||
response: {
|
||||
status: 401,
|
||||
data: { error: "Invalid credentials" },
|
||||
},
|
||||
});
|
||||
|
||||
// Execute & Assert
|
||||
await expect(
|
||||
toolGenerator.executeTool(toolName, parameters)
|
||||
).rejects.toThrow();
|
||||
});
|
||||
|
||||
test("should validate required parameters", async () => {
|
||||
const toolName = "public_create_login";
|
||||
|
||||
// Test missing username
|
||||
await expect(
|
||||
toolGenerator.executeTool(toolName, { password: "test" })
|
||||
).rejects.toThrow();
|
||||
|
||||
// Test missing password
|
||||
await expect(
|
||||
toolGenerator.executeTool(toolName, { username: "test" })
|
||||
).rejects.toThrow();
|
||||
|
||||
// Test empty parameters
|
||||
await expect(toolGenerator.executeTool(toolName, {})).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe("public_create_frontendlogin", () => {
|
||||
test("should successfully login patient", async () => {
|
||||
// Setup
|
||||
const toolName = "public_create_frontendlogin";
|
||||
const parameters = {
|
||||
email: "patient@test.com",
|
||||
password: "patientpassword",
|
||||
};
|
||||
|
||||
// Mock successful patient login
|
||||
mockFactory.httpMocks.mockRequest("POST", "/api/frontend/login", {
|
||||
status: 200,
|
||||
data: {
|
||||
success: true,
|
||||
token: "mock_patient_token_456",
|
||||
user: {
|
||||
id: "patient_456",
|
||||
email: "patient@test.com",
|
||||
role: "patient",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Execute
|
||||
const result = await toolGenerator.executeTool(toolName, parameters);
|
||||
|
||||
// Assert
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data.user.role).toBe("patient");
|
||||
});
|
||||
|
||||
test("should validate email format", async () => {
|
||||
const toolName = "public_create_frontendlogin";
|
||||
const parameters = {
|
||||
email: "invalid-email",
|
||||
password: "password",
|
||||
};
|
||||
|
||||
// Execute & Assert
|
||||
await expect(
|
||||
toolGenerator.executeTool(toolName, parameters)
|
||||
).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe("public_create_adminlogin", () => {
|
||||
test("should successfully login admin", async () => {
|
||||
// Setup
|
||||
const toolName = "public_create_adminlogin";
|
||||
const parameters = {
|
||||
email: "admin@test.com",
|
||||
password: "adminpassword",
|
||||
};
|
||||
|
||||
// Mock successful admin login
|
||||
mockFactory.httpMocks.mockRequest("POST", "/api/admin/login", {
|
||||
status: 200,
|
||||
data: {
|
||||
success: true,
|
||||
token: "mock_admin_token_789",
|
||||
user: {
|
||||
id: "admin_789",
|
||||
email: "admin@test.com",
|
||||
role: "admin",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Execute
|
||||
const result = await toolGenerator.executeTool(toolName, parameters);
|
||||
|
||||
// Assert
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data.user.role).toBe("admin");
|
||||
});
|
||||
});
|
||||
|
||||
describe("public_create_loginPartnerApi", () => {
|
||||
test("should successfully login partner", async () => {
|
||||
// Setup
|
||||
const toolName = "public_create_loginPartnerApi";
|
||||
const parameters = {
|
||||
email: "partner@test.com",
|
||||
password: "partnerpassword",
|
||||
};
|
||||
|
||||
// Mock successful partner login
|
||||
mockFactory.httpMocks.mockRequest("POST", "/api/login-partner-api", {
|
||||
status: 200,
|
||||
data: {
|
||||
success: true,
|
||||
token: "mock_partner_token_101",
|
||||
user: {
|
||||
id: "partner_101",
|
||||
email: "partner@test.com",
|
||||
role: "partner",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Execute
|
||||
const result = await toolGenerator.executeTool(toolName, parameters);
|
||||
|
||||
// Assert
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data.user.role).toBe("partner");
|
||||
});
|
||||
});
|
||||
|
||||
describe("public_create_affiliateLoginApi", () => {
|
||||
test("should successfully login affiliate", async () => {
|
||||
// Setup
|
||||
const toolName = "public_create_affiliateLoginApi";
|
||||
const parameters = {
|
||||
email: "affiliate@test.com",
|
||||
password: "affiliatepassword",
|
||||
};
|
||||
|
||||
// Mock successful affiliate login
|
||||
mockFactory.httpMocks.mockRequest("POST", "/api/affiliate-login-api", {
|
||||
status: 200,
|
||||
data: {
|
||||
success: true,
|
||||
token: "mock_affiliate_token_202",
|
||||
user: {
|
||||
id: "affiliate_202",
|
||||
email: "affiliate@test.com",
|
||||
role: "affiliate",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Execute
|
||||
const result = await toolGenerator.executeTool(toolName, parameters);
|
||||
|
||||
// Assert
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data.user.role).toBe("affiliate");
|
||||
});
|
||||
});
|
||||
|
||||
describe("public_create_networklogin", () => {
|
||||
test("should successfully login network user", async () => {
|
||||
// Setup
|
||||
const toolName = "public_create_networklogin";
|
||||
const parameters = {
|
||||
email: "network@test.com",
|
||||
password: "networkpassword",
|
||||
};
|
||||
|
||||
// Mock successful network login
|
||||
mockFactory.httpMocks.mockRequest("POST", "/api/network/login", {
|
||||
status: 200,
|
||||
data: {
|
||||
success: true,
|
||||
token: "mock_network_token_303",
|
||||
user: {
|
||||
id: "network_303",
|
||||
email: "network@test.com",
|
||||
role: "network",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Execute
|
||||
const result = await toolGenerator.executeTool(toolName, parameters);
|
||||
|
||||
// Assert
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data.user.role).toBe("network");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Login Security Tests", () => {
|
||||
test("should handle rate limiting", async () => {
|
||||
const toolName = "public_create_login";
|
||||
const parameters = {
|
||||
username: "testuser",
|
||||
password: "testpassword",
|
||||
};
|
||||
|
||||
// Mock rate limit response
|
||||
mockFactory.httpMocks.mockRequest("POST", "/api/login", null, true, {
|
||||
response: {
|
||||
status: 429,
|
||||
data: { error: "Too many login attempts" },
|
||||
},
|
||||
});
|
||||
|
||||
// Execute & Assert
|
||||
await expect(
|
||||
toolGenerator.executeTool(toolName, parameters)
|
||||
).rejects.toThrow();
|
||||
});
|
||||
|
||||
test("should handle server errors gracefully", async () => {
|
||||
const toolName = "public_create_login";
|
||||
const parameters = {
|
||||
username: "testuser",
|
||||
password: "testpassword",
|
||||
};
|
||||
|
||||
// Mock server error
|
||||
mockFactory.httpMocks.mockRequest("POST", "/api/login", null, true, {
|
||||
response: {
|
||||
status: 500,
|
||||
data: { error: "Internal server error" },
|
||||
},
|
||||
});
|
||||
|
||||
// Execute & Assert
|
||||
await expect(
|
||||
toolGenerator.executeTool(toolName, parameters)
|
||||
).rejects.toThrow();
|
||||
});
|
||||
|
||||
test("should not log sensitive information", async () => {
|
||||
const toolName = "public_create_login";
|
||||
const parameters = {
|
||||
username: "testuser",
|
||||
password: "secretpassword",
|
||||
};
|
||||
|
||||
// Mock successful login
|
||||
mockFactory.httpMocks.mockRequest("POST", "/api/login", {
|
||||
status: 200,
|
||||
data: { success: true, token: "token123" },
|
||||
});
|
||||
|
||||
// Execute
|
||||
await toolGenerator.executeTool(toolName, parameters);
|
||||
|
||||
// Verify password is not in request history
|
||||
const requestHistory = mockFactory.httpMocks.getRequestHistory();
|
||||
const loginRequest = requestHistory.find(
|
||||
(req) => req.method === "POST" && req.url === "/api/login"
|
||||
);
|
||||
|
||||
// Password should be redacted or not logged in plain text
|
||||
expect(JSON.stringify(loginRequest)).not.toContain("secretpassword");
|
||||
});
|
||||
});
|
||||
});
|
455
tests/public/password-management.test.js
Normal file
455
tests/public/password-management.test.js
Normal file
@@ -0,0 +1,455 @@
|
||||
/**
|
||||
* @fileoverview Tests for public password management MCP tools
|
||||
* Tests password reset, forgot password, and set password functionality
|
||||
*/
|
||||
|
||||
import { describe, test, expect, beforeEach, afterEach } from "@jest/globals";
|
||||
import { mockFactory } from "../mocks/mockFactory.js";
|
||||
|
||||
describe("Public Password Management Tools", () => {
|
||||
let mockEnv;
|
||||
let toolGenerator;
|
||||
|
||||
beforeEach(() => {
|
||||
mockEnv = mockFactory.createMockEnvironment({
|
||||
authTypes: ["public"],
|
||||
enableHttpMocks: true,
|
||||
});
|
||||
|
||||
toolGenerator = mockEnv.toolGenerator;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFactory.resetAllMocks();
|
||||
});
|
||||
|
||||
describe("public_create_forgotPassword", () => {
|
||||
test("should successfully initiate password reset", async () => {
|
||||
// Setup
|
||||
const toolName = "public_create_forgotPassword";
|
||||
const parameters = {
|
||||
email: "user@test.com",
|
||||
};
|
||||
|
||||
// Mock successful forgot password response
|
||||
mockFactory.httpMocks.mockRequest("POST", "/api/forgot-password", {
|
||||
status: 200,
|
||||
data: {
|
||||
success: true,
|
||||
message: "Password reset email sent successfully",
|
||||
email: "user@test.com",
|
||||
},
|
||||
});
|
||||
|
||||
// Execute
|
||||
const result = await toolGenerator.executeTool(toolName, parameters);
|
||||
|
||||
// Assert
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data.message).toContain("Password reset email sent");
|
||||
});
|
||||
|
||||
test("should validate email format", async () => {
|
||||
const toolName = "public_create_forgotPassword";
|
||||
const parameters = {
|
||||
email: "invalid-email-format",
|
||||
};
|
||||
|
||||
await expect(
|
||||
toolGenerator.executeTool(toolName, parameters)
|
||||
).rejects.toThrow();
|
||||
});
|
||||
|
||||
test("should handle non-existent email gracefully", async () => {
|
||||
const toolName = "public_create_forgotPassword";
|
||||
const parameters = {
|
||||
email: "nonexistent@test.com",
|
||||
};
|
||||
|
||||
// Mock response for non-existent email (should still return success for security)
|
||||
mockFactory.httpMocks.mockRequest("POST", "/api/forgot-password", {
|
||||
status: 200,
|
||||
data: {
|
||||
success: true,
|
||||
message: "If the email exists, a reset link has been sent",
|
||||
},
|
||||
});
|
||||
|
||||
const result = await toolGenerator.executeTool(toolName, parameters);
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("public_create_frontendforgotPassword", () => {
|
||||
test("should successfully initiate patient password reset", async () => {
|
||||
// Setup
|
||||
const toolName = "public_create_frontendforgotPassword";
|
||||
const parameters = {
|
||||
email: "patient@test.com",
|
||||
};
|
||||
|
||||
// Mock successful patient forgot password response
|
||||
mockFactory.httpMocks.mockRequest(
|
||||
"POST",
|
||||
"/api/frontend/forgot-password",
|
||||
{
|
||||
status: 200,
|
||||
data: {
|
||||
success: true,
|
||||
message: "Patient password reset email sent",
|
||||
email: "patient@test.com",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
// Execute
|
||||
const result = await toolGenerator.executeTool(toolName, parameters);
|
||||
|
||||
// Assert
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data.message).toContain("Patient password reset");
|
||||
});
|
||||
});
|
||||
|
||||
describe("public_create_providerforgotPassword", () => {
|
||||
test("should successfully initiate provider password reset", async () => {
|
||||
// Setup
|
||||
const toolName = "public_create_providerforgotPassword";
|
||||
const parameters = {
|
||||
email: "provider@test.com",
|
||||
};
|
||||
|
||||
// Mock successful provider forgot password response
|
||||
mockFactory.httpMocks.mockRequest(
|
||||
"POST",
|
||||
"/api/emr/provider/forgot-password",
|
||||
{
|
||||
status: 200,
|
||||
data: {
|
||||
success: true,
|
||||
message: "Provider password reset email sent",
|
||||
email: "provider@test.com",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
// Execute
|
||||
const result = await toolGenerator.executeTool(toolName, parameters);
|
||||
|
||||
// Assert
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data.message).toContain("Provider password reset");
|
||||
});
|
||||
});
|
||||
|
||||
describe("public_create_passwordReset", () => {
|
||||
test("should successfully reset password with valid token", async () => {
|
||||
// Setup
|
||||
const toolName = "public_create_passwordReset";
|
||||
const parameters = {
|
||||
token: "valid_reset_token_123",
|
||||
email: "user@test.com",
|
||||
password: "NewSecurePass123!",
|
||||
password_confirmation: "NewSecurePass123!",
|
||||
};
|
||||
|
||||
// Mock successful password reset
|
||||
mockFactory.httpMocks.mockRequest("POST", "/api/password-reset", {
|
||||
status: 200,
|
||||
data: {
|
||||
success: true,
|
||||
message: "Password reset successfully",
|
||||
email: "user@test.com",
|
||||
},
|
||||
});
|
||||
|
||||
// Execute
|
||||
const result = await toolGenerator.executeTool(toolName, parameters);
|
||||
|
||||
// Assert
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data.message).toContain("Password reset successfully");
|
||||
});
|
||||
|
||||
test("should validate password confirmation match", async () => {
|
||||
const toolName = "public_create_passwordReset";
|
||||
const parameters = {
|
||||
token: "valid_reset_token_123",
|
||||
email: "user@test.com",
|
||||
password: "NewSecurePass123!",
|
||||
password_confirmation: "DifferentPassword123!",
|
||||
};
|
||||
|
||||
await expect(
|
||||
toolGenerator.executeTool(toolName, parameters)
|
||||
).rejects.toThrow();
|
||||
});
|
||||
|
||||
test("should handle invalid reset token", async () => {
|
||||
const toolName = "public_create_passwordReset";
|
||||
const parameters = {
|
||||
token: "invalid_token",
|
||||
email: "user@test.com",
|
||||
password: "NewSecurePass123!",
|
||||
password_confirmation: "NewSecurePass123!",
|
||||
};
|
||||
|
||||
// Mock invalid token response
|
||||
mockFactory.httpMocks.mockRequest(
|
||||
"POST",
|
||||
"/api/password-reset",
|
||||
null,
|
||||
true,
|
||||
{
|
||||
response: {
|
||||
status: 400,
|
||||
data: { error: "Invalid or expired reset token" },
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
await expect(
|
||||
toolGenerator.executeTool(toolName, parameters)
|
||||
).rejects.toThrow();
|
||||
});
|
||||
|
||||
test("should handle expired reset token", async () => {
|
||||
const toolName = "public_create_passwordReset";
|
||||
const parameters = {
|
||||
token: "expired_token_456",
|
||||
email: "user@test.com",
|
||||
password: "NewSecurePass123!",
|
||||
password_confirmation: "NewSecurePass123!",
|
||||
};
|
||||
|
||||
// Mock expired token response
|
||||
mockFactory.httpMocks.mockRequest(
|
||||
"POST",
|
||||
"/api/password-reset",
|
||||
null,
|
||||
true,
|
||||
{
|
||||
response: {
|
||||
status: 410,
|
||||
data: { error: "Reset token has expired" },
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
await expect(
|
||||
toolGenerator.executeTool(toolName, parameters)
|
||||
).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe("public_create_frontendresetPassword", () => {
|
||||
test("should successfully reset patient password", async () => {
|
||||
// Setup
|
||||
const toolName = "public_create_frontendresetPassword";
|
||||
const parameters = {
|
||||
email: "patient@test.com",
|
||||
password: "NewPatientPass123!",
|
||||
password_confirmation: "NewPatientPass123!",
|
||||
token: "patient_reset_token_789",
|
||||
};
|
||||
|
||||
// Mock successful patient password reset
|
||||
mockFactory.httpMocks.mockRequest(
|
||||
"POST",
|
||||
"/api/frontend/reset-password",
|
||||
{
|
||||
status: 200,
|
||||
data: {
|
||||
success: true,
|
||||
message: "Patient password reset successfully",
|
||||
email: "patient@test.com",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
// Execute
|
||||
const result = await toolGenerator.executeTool(toolName, parameters);
|
||||
|
||||
// Assert
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data.message).toContain("Patient password reset");
|
||||
});
|
||||
});
|
||||
|
||||
describe("public_create_setPassword", () => {
|
||||
test("should successfully set password with valid token", async () => {
|
||||
// Setup
|
||||
const toolName = "public_create_setPassword";
|
||||
const parameters = {
|
||||
password: "NewPassword123!",
|
||||
password_confirmation: "NewPassword123!",
|
||||
token: "set_password_token_101",
|
||||
};
|
||||
|
||||
// Mock successful password set
|
||||
mockFactory.httpMocks.mockRequest("POST", "/api/set-password", {
|
||||
status: 200,
|
||||
data: {
|
||||
success: true,
|
||||
message: "Password set successfully",
|
||||
},
|
||||
});
|
||||
|
||||
// Execute
|
||||
const result = await toolGenerator.executeTool(toolName, parameters);
|
||||
|
||||
// Assert
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data.message).toContain("Password set successfully");
|
||||
});
|
||||
|
||||
test("should validate password strength requirements", async () => {
|
||||
const toolName = "public_create_setPassword";
|
||||
|
||||
// Test weak passwords
|
||||
const weakPasswords = [
|
||||
"123", // Too short
|
||||
"password", // No numbers/special chars
|
||||
"12345678", // Only numbers
|
||||
"PASSWORD", // Only uppercase
|
||||
"password123", // No special characters
|
||||
];
|
||||
|
||||
for (const weakPassword of weakPasswords) {
|
||||
const parameters = {
|
||||
password: weakPassword,
|
||||
password_confirmation: weakPassword,
|
||||
token: "valid_token",
|
||||
};
|
||||
|
||||
await expect(
|
||||
toolGenerator.executeTool(toolName, parameters)
|
||||
).rejects.toThrow();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("public_create_emrsetPassword", () => {
|
||||
test("should successfully set EMR password", async () => {
|
||||
// Setup
|
||||
const toolName = "public_create_emrsetPassword";
|
||||
const parameters = {
|
||||
password: "EMRPassword123!",
|
||||
password_confirmation: "EMRPassword123!",
|
||||
token: "emr_token_202",
|
||||
};
|
||||
|
||||
// Mock successful EMR password set
|
||||
mockFactory.httpMocks.mockRequest("POST", "/api/emr/set-password", {
|
||||
status: 200,
|
||||
data: {
|
||||
success: true,
|
||||
message: "EMR password set successfully",
|
||||
},
|
||||
});
|
||||
|
||||
// Execute
|
||||
const result = await toolGenerator.executeTool(toolName, parameters);
|
||||
|
||||
// Assert
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data.message).toContain("EMR password set");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Password Security Tests", () => {
|
||||
test("should enforce password complexity requirements", async () => {
|
||||
const toolName = "public_create_passwordReset";
|
||||
|
||||
// Test various password requirements
|
||||
const testCases = [
|
||||
{
|
||||
password: "short",
|
||||
description: "too short",
|
||||
},
|
||||
{
|
||||
password: "nouppercase123!",
|
||||
description: "no uppercase",
|
||||
},
|
||||
{
|
||||
password: "NOLOWERCASE123!",
|
||||
description: "no lowercase",
|
||||
},
|
||||
{
|
||||
password: "NoNumbers!",
|
||||
description: "no numbers",
|
||||
},
|
||||
{
|
||||
password: "NoSpecialChars123",
|
||||
description: "no special characters",
|
||||
},
|
||||
];
|
||||
|
||||
for (const testCase of testCases) {
|
||||
const parameters = {
|
||||
token: "valid_token",
|
||||
email: "test@test.com",
|
||||
password: testCase.password,
|
||||
password_confirmation: testCase.password,
|
||||
};
|
||||
|
||||
await expect(
|
||||
toolGenerator.executeTool(toolName, parameters)
|
||||
).rejects.toThrow();
|
||||
}
|
||||
});
|
||||
|
||||
test("should handle rate limiting for password reset attempts", async () => {
|
||||
const toolName = "public_create_forgotPassword";
|
||||
const parameters = {
|
||||
email: "ratelimited@test.com",
|
||||
};
|
||||
|
||||
// Mock rate limit response
|
||||
mockFactory.httpMocks.mockRequest(
|
||||
"POST",
|
||||
"/api/forgot-password",
|
||||
null,
|
||||
true,
|
||||
{
|
||||
response: {
|
||||
status: 429,
|
||||
data: { error: "Too many password reset attempts" },
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
await expect(
|
||||
toolGenerator.executeTool(toolName, parameters)
|
||||
).rejects.toThrow();
|
||||
});
|
||||
|
||||
test("should not expose sensitive information in error messages", async () => {
|
||||
const toolName = "public_create_passwordReset";
|
||||
const parameters = {
|
||||
token: "invalid_token",
|
||||
email: "user@test.com",
|
||||
password: "ValidPass123!",
|
||||
password_confirmation: "ValidPass123!",
|
||||
};
|
||||
|
||||
// Mock generic error response
|
||||
mockFactory.httpMocks.mockRequest(
|
||||
"POST",
|
||||
"/api/password-reset",
|
||||
null,
|
||||
true,
|
||||
{
|
||||
response: {
|
||||
status: 400,
|
||||
data: { error: "Invalid request" }, // Generic error message
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
await expect(
|
||||
toolGenerator.executeTool(toolName, parameters)
|
||||
).rejects.toThrow("Invalid request");
|
||||
});
|
||||
});
|
||||
});
|
385
tests/public/registration.test.js
Normal file
385
tests/public/registration.test.js
Normal file
@@ -0,0 +1,385 @@
|
||||
/**
|
||||
* @fileoverview Tests for public registration MCP tools
|
||||
* Tests all public registration endpoints for different user types
|
||||
*/
|
||||
|
||||
import { describe, test, expect, beforeEach, afterEach } from '@jest/globals';
|
||||
import { mockFactory } from '../mocks/mockFactory.js';
|
||||
|
||||
describe('Public Registration Tools', () => {
|
||||
let mockEnv;
|
||||
let toolGenerator;
|
||||
|
||||
beforeEach(() => {
|
||||
mockEnv = mockFactory.createMockEnvironment({
|
||||
authTypes: ['public'],
|
||||
enableHttpMocks: true,
|
||||
enableHealthcareMocks: true
|
||||
});
|
||||
|
||||
toolGenerator = mockEnv.toolGenerator;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockFactory.resetAllMocks();
|
||||
});
|
||||
|
||||
describe('public_create_emrApiproviderRegister', () => {
|
||||
test('should successfully register a new provider', async () => {
|
||||
// Setup
|
||||
const toolName = 'public_create_emrApiproviderRegister';
|
||||
const parameters = {
|
||||
firstName: 'Dr. John',
|
||||
lastName: 'Smith',
|
||||
username: 'drsmith',
|
||||
emailAddress: 'dr.smith@test.com',
|
||||
textMessageNumber: '555-0123',
|
||||
newUserPassword: 'SecurePass123!',
|
||||
company_name: 'Test Medical Center',
|
||||
on_your_domain: true
|
||||
};
|
||||
|
||||
// Mock successful registration
|
||||
mockFactory.httpMocks.mockRequest('POST', '/emr-api/provider-register', {
|
||||
status: 201,
|
||||
data: {
|
||||
success: true,
|
||||
provider: {
|
||||
id: 'provider_123',
|
||||
firstName: 'Dr. John',
|
||||
lastName: 'Smith',
|
||||
emailAddress: 'dr.smith@test.com',
|
||||
username: 'drsmith'
|
||||
},
|
||||
message: 'Provider registered successfully'
|
||||
}
|
||||
});
|
||||
|
||||
// Execute
|
||||
const result = await toolGenerator.executeTool(toolName, parameters);
|
||||
|
||||
// Assert
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data.provider.firstName).toBe('Dr. John');
|
||||
expect(result.data.provider.emailAddress).toBe('dr.smith@test.com');
|
||||
});
|
||||
|
||||
test('should validate required provider registration fields', async () => {
|
||||
const toolName = 'public_create_emrApiproviderRegister';
|
||||
|
||||
// Test missing required fields
|
||||
const requiredFields = [
|
||||
'firstName', 'lastName', 'username', 'emailAddress',
|
||||
'textMessageNumber', 'newUserPassword', 'company_name'
|
||||
];
|
||||
|
||||
for (const field of requiredFields) {
|
||||
const incompleteParams = {
|
||||
firstName: 'Dr. John',
|
||||
lastName: 'Smith',
|
||||
username: 'drsmith',
|
||||
emailAddress: 'dr.smith@test.com',
|
||||
textMessageNumber: '555-0123',
|
||||
newUserPassword: 'SecurePass123!',
|
||||
company_name: 'Test Medical Center'
|
||||
};
|
||||
delete incompleteParams[field];
|
||||
|
||||
await expect(toolGenerator.executeTool(toolName, incompleteParams))
|
||||
.rejects.toThrow();
|
||||
}
|
||||
});
|
||||
|
||||
test('should handle duplicate email registration', async () => {
|
||||
const toolName = 'public_create_emrApiproviderRegister';
|
||||
const parameters = {
|
||||
firstName: 'Dr. John',
|
||||
lastName: 'Smith',
|
||||
username: 'drsmith',
|
||||
emailAddress: 'existing@test.com',
|
||||
textMessageNumber: '555-0123',
|
||||
newUserPassword: 'SecurePass123!',
|
||||
company_name: 'Test Medical Center'
|
||||
};
|
||||
|
||||
// Mock duplicate email error
|
||||
mockFactory.httpMocks.mockRequest('POST', '/emr-api/provider-register', null, true, {
|
||||
response: {
|
||||
status: 409,
|
||||
data: { error: 'Email already exists' }
|
||||
}
|
||||
});
|
||||
|
||||
await expect(toolGenerator.executeTool(toolName, parameters))
|
||||
.rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('public_create_registerPatient', () => {
|
||||
test('should successfully register a new patient', async () => {
|
||||
// Setup
|
||||
const toolName = 'public_create_registerPatient';
|
||||
const parameters = {
|
||||
first_name: 'Jane',
|
||||
last_name: 'Doe',
|
||||
email: 'jane.doe@test.com',
|
||||
phone_no: '555-0456',
|
||||
dob: '1990-01-01',
|
||||
gender: 'Female',
|
||||
provider_id: 123,
|
||||
preferredPhone: '555-0456',
|
||||
password: 'PatientPass123!',
|
||||
username: 'janedoe',
|
||||
isportalAccess: true
|
||||
};
|
||||
|
||||
// Mock successful patient registration
|
||||
mockFactory.httpMocks.mockRequest('POST', '/api/register-patients', {
|
||||
status: 201,
|
||||
data: {
|
||||
success: true,
|
||||
patient: {
|
||||
id: 'patient_456',
|
||||
first_name: 'Jane',
|
||||
last_name: 'Doe',
|
||||
email: 'jane.doe@test.com',
|
||||
isportalAccess: true
|
||||
},
|
||||
message: 'Patient registered successfully'
|
||||
}
|
||||
});
|
||||
|
||||
// Execute
|
||||
const result = await toolGenerator.executeTool(toolName, parameters);
|
||||
|
||||
// Assert
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data.patient.first_name).toBe('Jane');
|
||||
expect(result.data.patient.email).toBe('jane.doe@test.com');
|
||||
expect(result.data.patient.isportalAccess).toBe(true);
|
||||
});
|
||||
|
||||
test('should validate patient data format', async () => {
|
||||
const toolName = 'public_create_registerPatient';
|
||||
|
||||
// Test invalid email format
|
||||
const invalidEmailParams = {
|
||||
first_name: 'Jane',
|
||||
last_name: 'Doe',
|
||||
email: 'invalid-email',
|
||||
phone_no: '555-0456',
|
||||
dob: '1990-01-01',
|
||||
gender: 'Female',
|
||||
password: 'PatientPass123!'
|
||||
};
|
||||
|
||||
await expect(toolGenerator.executeTool(toolName, invalidEmailParams))
|
||||
.rejects.toThrow();
|
||||
|
||||
// Test invalid date format
|
||||
const invalidDateParams = {
|
||||
first_name: 'Jane',
|
||||
last_name: 'Doe',
|
||||
email: 'jane@test.com',
|
||||
phone_no: '555-0456',
|
||||
dob: 'invalid-date',
|
||||
gender: 'Female',
|
||||
password: 'PatientPass123!'
|
||||
};
|
||||
|
||||
await expect(toolGenerator.executeTool(toolName, invalidDateParams))
|
||||
.rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('public_create_affiliateRegisterApi', () => {
|
||||
test('should successfully register a new affiliate', async () => {
|
||||
// Setup
|
||||
const toolName = 'public_create_affiliateRegisterApi';
|
||||
const parameters = {
|
||||
first_name: 'Alice',
|
||||
last_name: 'Johnson',
|
||||
phone_no: '555-0789',
|
||||
email: 'alice.johnson@test.com',
|
||||
dob: '1985-05-15',
|
||||
gender: 'Female',
|
||||
partner_email: 'partner@test.com'
|
||||
};
|
||||
|
||||
// Mock successful affiliate registration
|
||||
mockFactory.httpMocks.mockRequest('POST', '/api/affiliate-register-api', {
|
||||
status: 201,
|
||||
data: {
|
||||
success: true,
|
||||
affiliate: {
|
||||
id: 'affiliate_789',
|
||||
first_name: 'Alice',
|
||||
last_name: 'Johnson',
|
||||
email: 'alice.johnson@test.com'
|
||||
},
|
||||
message: 'Affiliate registered successfully'
|
||||
}
|
||||
});
|
||||
|
||||
// Execute
|
||||
const result = await toolGenerator.executeTool(toolName, parameters);
|
||||
|
||||
// Assert
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data.affiliate.first_name).toBe('Alice');
|
||||
expect(result.data.affiliate.email).toBe('alice.johnson@test.com');
|
||||
});
|
||||
});
|
||||
|
||||
describe('public_create_partnerRegisterApi', () => {
|
||||
test('should successfully register a new partner', async () => {
|
||||
// Setup
|
||||
const toolName = 'public_create_partnerRegisterApi';
|
||||
const parameters = {
|
||||
first_name: 'Bob',
|
||||
last_name: 'Wilson',
|
||||
phone_no: '555-0321',
|
||||
email: 'bob.wilson@test.com',
|
||||
dob: '1980-12-10',
|
||||
gender: 'Male',
|
||||
password: 'PartnerPass123!'
|
||||
};
|
||||
|
||||
// Mock successful partner registration
|
||||
mockFactory.httpMocks.mockRequest('POST', '/api/partner-register-api', {
|
||||
status: 201,
|
||||
data: {
|
||||
success: true,
|
||||
partner: {
|
||||
id: 'partner_321',
|
||||
first_name: 'Bob',
|
||||
last_name: 'Wilson',
|
||||
email: 'bob.wilson@test.com'
|
||||
},
|
||||
message: 'Partner registered successfully'
|
||||
}
|
||||
});
|
||||
|
||||
// Execute
|
||||
const result = await toolGenerator.executeTool(toolName, parameters);
|
||||
|
||||
// Assert
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data.partner.first_name).toBe('Bob');
|
||||
expect(result.data.partner.email).toBe('bob.wilson@test.com');
|
||||
});
|
||||
});
|
||||
|
||||
describe('public_create_networkregister', () => {
|
||||
test('should successfully register a new network user', async () => {
|
||||
// Setup
|
||||
const toolName = 'public_create_networkregister';
|
||||
const parameters = {
|
||||
first_name: 'Carol',
|
||||
last_name: 'Davis',
|
||||
phone_no: '555-0654',
|
||||
email: 'carol.davis@test.com',
|
||||
dob: '1992-03-20',
|
||||
gender: 'Female',
|
||||
password: 'NetworkPass123!',
|
||||
partner_id: 'partner_123'
|
||||
};
|
||||
|
||||
// Mock successful network registration
|
||||
mockFactory.httpMocks.mockRequest('POST', '/api/network/register', {
|
||||
status: 201,
|
||||
data: {
|
||||
success: true,
|
||||
network_user: {
|
||||
id: 'network_654',
|
||||
first_name: 'Carol',
|
||||
last_name: 'Davis',
|
||||
email: 'carol.davis@test.com'
|
||||
},
|
||||
message: 'Network user registered successfully'
|
||||
}
|
||||
});
|
||||
|
||||
// Execute
|
||||
const result = await toolGenerator.executeTool(toolName, parameters);
|
||||
|
||||
// Assert
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data.network_user.first_name).toBe('Carol');
|
||||
expect(result.data.network_user.email).toBe('carol.davis@test.com');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Registration Security Tests', () => {
|
||||
test('should validate password strength', async () => {
|
||||
const toolName = 'public_create_emrApiproviderRegister';
|
||||
const weakPasswordParams = {
|
||||
firstName: 'Dr. John',
|
||||
lastName: 'Smith',
|
||||
username: 'drsmith',
|
||||
emailAddress: 'dr.smith@test.com',
|
||||
textMessageNumber: '555-0123',
|
||||
newUserPassword: '123', // Weak password
|
||||
company_name: 'Test Medical Center'
|
||||
};
|
||||
|
||||
await expect(toolGenerator.executeTool(toolName, weakPasswordParams))
|
||||
.rejects.toThrow();
|
||||
});
|
||||
|
||||
test('should sanitize input data', async () => {
|
||||
const toolName = 'public_create_registerPatient';
|
||||
const maliciousParams = {
|
||||
first_name: '<script>alert("xss")</script>',
|
||||
last_name: 'Doe',
|
||||
email: 'test@test.com',
|
||||
phone_no: '555-0456',
|
||||
dob: '1990-01-01',
|
||||
gender: 'Female',
|
||||
password: 'ValidPass123!'
|
||||
};
|
||||
|
||||
// Mock successful registration (input should be sanitized)
|
||||
mockFactory.httpMocks.mockRequest('POST', '/api/register-patients', {
|
||||
status: 201,
|
||||
data: {
|
||||
success: true,
|
||||
patient: {
|
||||
id: 'patient_123',
|
||||
first_name: 'alert("xss")', // Sanitized
|
||||
email: 'test@test.com'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const result = await toolGenerator.executeTool(toolName, maliciousParams);
|
||||
|
||||
// Verify XSS attempt was sanitized
|
||||
expect(result.data.patient.first_name).not.toContain('<script>');
|
||||
});
|
||||
|
||||
test('should handle registration rate limiting', async () => {
|
||||
const toolName = 'public_create_registerPatient';
|
||||
const parameters = {
|
||||
first_name: 'Jane',
|
||||
last_name: 'Doe',
|
||||
email: 'jane@test.com',
|
||||
phone_no: '555-0456',
|
||||
dob: '1990-01-01',
|
||||
gender: 'Female',
|
||||
password: 'ValidPass123!'
|
||||
};
|
||||
|
||||
// Mock rate limit response
|
||||
mockFactory.httpMocks.mockRequest('POST', '/api/register-patients', null, true, {
|
||||
response: {
|
||||
status: 429,
|
||||
data: { error: 'Too many registration attempts' }
|
||||
}
|
||||
});
|
||||
|
||||
await expect(toolGenerator.executeTool(toolName, parameters))
|
||||
.rejects.toThrow();
|
||||
});
|
||||
});
|
||||
});
|
Reference in New Issue
Block a user