353 lines
12 KiB
JavaScript
353 lines
12 KiB
JavaScript
/**
|
|
* @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');
|
|
}
|
|
});
|
|
});
|
|
});
|