358 lines
9.3 KiB
JavaScript
358 lines
9.3 KiB
JavaScript
/**
|
|
* @fileoverview Test setup and configuration for Laravel Healthcare MCP Server
|
|
* Global test setup, custom matchers, and environment configuration
|
|
*/
|
|
|
|
import { jest } from "@jest/globals";
|
|
|
|
// Global test timeout for healthcare operations
|
|
jest.setTimeout(30000);
|
|
|
|
// Global test constants
|
|
global.testConstants = {
|
|
AUTH_TYPES: {
|
|
PUBLIC: "public",
|
|
PROVIDER: "provider",
|
|
PATIENT: "patient",
|
|
PARTNER: "partner",
|
|
AFFILIATE: "affiliate",
|
|
NETWORK: "network",
|
|
},
|
|
API_BASE_URL: "https://test-api.healthcare.com",
|
|
TIMEOUT: 5000,
|
|
RETRY_ATTEMPTS: 2,
|
|
};
|
|
|
|
// Mock console methods to reduce noise in tests
|
|
const originalConsole = { ...console };
|
|
|
|
beforeAll(() => {
|
|
// Suppress console output during tests unless verbose mode
|
|
if (!process.env.VERBOSE_TESTS) {
|
|
console.log = jest.fn();
|
|
console.info = jest.fn();
|
|
console.warn = jest.fn();
|
|
console.error = jest.fn();
|
|
}
|
|
});
|
|
|
|
afterAll(() => {
|
|
// Restore console methods
|
|
if (!process.env.VERBOSE_TESTS) {
|
|
console.log = originalConsole.log;
|
|
console.info = originalConsole.info;
|
|
console.warn = originalConsole.warn;
|
|
console.error = originalConsole.error;
|
|
}
|
|
});
|
|
|
|
// Global error handler for unhandled promises
|
|
process.on("unhandledRejection", (reason, promise) => {
|
|
console.error("Unhandled Rejection at:", promise, "reason:", reason);
|
|
});
|
|
|
|
// Custom matchers for healthcare testing
|
|
expect.extend({
|
|
/**
|
|
* Check if response contains HIPAA-compliant data structure
|
|
*/
|
|
toBeHIPAACompliant(received) {
|
|
const pass =
|
|
received &&
|
|
received.hipaaMetadata &&
|
|
received.hipaaMetadata.dataClassification === "PHI" &&
|
|
received.hipaaMetadata.encryptionStatus === "encrypted";
|
|
|
|
if (pass) {
|
|
return {
|
|
message: () => `Expected data not to be HIPAA compliant`,
|
|
pass: true,
|
|
};
|
|
} else {
|
|
return {
|
|
message: () =>
|
|
`Expected data to be HIPAA compliant with proper metadata`,
|
|
pass: false,
|
|
};
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Check if response contains valid audit trail
|
|
*/
|
|
toHaveAuditTrail(received) {
|
|
const pass =
|
|
received &&
|
|
received.auditTrail &&
|
|
received.auditTrail.accessedBy &&
|
|
received.auditTrail.accessTime &&
|
|
received.auditTrail.action;
|
|
|
|
if (pass) {
|
|
return {
|
|
message: () => `Expected response not to have audit trail`,
|
|
pass: true,
|
|
};
|
|
} else {
|
|
return {
|
|
message: () =>
|
|
`Expected response to have valid audit trail with accessedBy, accessTime, and action`,
|
|
pass: false,
|
|
};
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Check if error response has proper healthcare error structure
|
|
*/
|
|
toBeHealthcareError(received) {
|
|
const pass =
|
|
received && received.error && received.error_code && received.message;
|
|
|
|
if (pass) {
|
|
return {
|
|
message: () => `Expected not to be a healthcare error`,
|
|
pass: true,
|
|
};
|
|
} else {
|
|
return {
|
|
message: () =>
|
|
`Expected to be a healthcare error with error, error_code, and message fields`,
|
|
pass: false,
|
|
};
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Check if prescription has drug interaction warnings
|
|
*/
|
|
toHaveDrugInteractionWarnings(received) {
|
|
const pass =
|
|
received &&
|
|
received.clinical_alerts &&
|
|
Array.isArray(received.clinical_alerts) &&
|
|
received.clinical_alerts.some(
|
|
(alert) => alert.type === "drug_interaction"
|
|
);
|
|
|
|
if (pass) {
|
|
return {
|
|
message: () => `Expected not to have drug interaction warnings`,
|
|
pass: true,
|
|
};
|
|
} else {
|
|
return {
|
|
message: () =>
|
|
`Expected to have drug interaction warnings in clinical_alerts`,
|
|
pass: false,
|
|
};
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Check if medical coding is valid
|
|
*/
|
|
toHaveValidMedicalCoding(received) {
|
|
const pass =
|
|
received &&
|
|
received.coding_validation &&
|
|
received.coding_validation.coding_accuracy >= 90 &&
|
|
received.coding_validation.billing_compliance === true;
|
|
|
|
if (pass) {
|
|
return {
|
|
message: () => `Expected not to have valid medical coding`,
|
|
pass: true,
|
|
};
|
|
} else {
|
|
return {
|
|
message: () =>
|
|
`Expected to have valid medical coding with accuracy >= 90% and billing compliance`,
|
|
pass: false,
|
|
};
|
|
}
|
|
},
|
|
});
|
|
|
|
// Global test utilities
|
|
global.testUtils = {
|
|
/**
|
|
* Wait for a specified amount of time
|
|
*/
|
|
wait: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
|
|
/**
|
|
* Generate random test data
|
|
*/
|
|
generateRandomString: (length = 10) => {
|
|
const chars =
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
let result = "";
|
|
for (let i = 0; i < length; i++) {
|
|
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
}
|
|
return result;
|
|
},
|
|
|
|
/**
|
|
* Generate random email
|
|
*/
|
|
generateRandomEmail: () => {
|
|
const username = global.testUtils.generateRandomString(8);
|
|
return `${username}@test.com`;
|
|
},
|
|
|
|
/**
|
|
* Generate random phone number
|
|
*/
|
|
generateRandomPhone: () => {
|
|
const areaCode = Math.floor(Math.random() * 900) + 100;
|
|
const exchange = Math.floor(Math.random() * 900) + 100;
|
|
const number = Math.floor(Math.random() * 9000) + 1000;
|
|
return `${areaCode}-${exchange}-${number}`;
|
|
},
|
|
|
|
/**
|
|
* Generate random date
|
|
*/
|
|
generateRandomDate: (startYear = 1950, endYear = 2000) => {
|
|
const start = new Date(startYear, 0, 1);
|
|
const end = new Date(endYear, 11, 31);
|
|
const randomTime =
|
|
start.getTime() + Math.random() * (end.getTime() - start.getTime());
|
|
return new Date(randomTime).toISOString().split("T")[0];
|
|
},
|
|
|
|
/**
|
|
* Validate email format
|
|
*/
|
|
isValidEmail: (email) => {
|
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
return emailRegex.test(email);
|
|
},
|
|
|
|
/**
|
|
* Validate phone format
|
|
*/
|
|
isValidPhone: (phone) => {
|
|
const phoneRegex = /^\d{3}-\d{3}-\d{4}$/;
|
|
return phoneRegex.test(phone);
|
|
},
|
|
|
|
/**
|
|
* Validate date format (YYYY-MM-DD)
|
|
*/
|
|
isValidDate: (date) => {
|
|
const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
|
|
return dateRegex.test(date) && !isNaN(Date.parse(date));
|
|
},
|
|
|
|
/**
|
|
* Create mock patient data
|
|
*/
|
|
createMockPatientData: () => {
|
|
return {
|
|
id: `patient_${global.testUtils.generateRandomString(8)}`,
|
|
firstName: "John",
|
|
lastName: "Doe",
|
|
email: global.testUtils.generateRandomEmail(),
|
|
dateOfBirth: global.testUtils.generateRandomDate(1950, 2000),
|
|
phone: global.testUtils.generateRandomPhone(),
|
|
address: "123 Main St",
|
|
city: "Anytown",
|
|
state: "CA",
|
|
zipcode: "12345",
|
|
genderIdentity: "Male",
|
|
status: "active",
|
|
isportalAccess: true,
|
|
hipaaMetadata: {
|
|
dataClassification: "PHI",
|
|
encryptionStatus: "encrypted",
|
|
accessLevel: "restricted",
|
|
},
|
|
auditTrail: {
|
|
accessedBy: "test_provider",
|
|
accessTime: new Date().toISOString(),
|
|
action: "read",
|
|
},
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Create mock provider data
|
|
*/
|
|
createMockProviderData: () => {
|
|
return {
|
|
id: `provider_${global.testUtils.generateRandomString(8)}`,
|
|
firstName: "Dr. Jane",
|
|
lastName: "Smith",
|
|
email: global.testUtils.generateRandomEmail(),
|
|
phone: global.testUtils.generateRandomPhone(),
|
|
specialty: "Internal Medicine",
|
|
licenseNumber: `LIC${global.testUtils.generateRandomString(6)}`,
|
|
npiNumber: `NPI${global.testUtils.generateRandomString(10)}`,
|
|
status: "active",
|
|
accessRights: {
|
|
admin: false,
|
|
practitioner: true,
|
|
patientPortal: true,
|
|
},
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Create mock appointment data
|
|
*/
|
|
createMockAppointmentData: () => {
|
|
return {
|
|
id: `appointment_${global.testUtils.generateRandomString(8)}`,
|
|
patient_id: `patient_${global.testUtils.generateRandomString(8)}`,
|
|
practitioner_id: `provider_${global.testUtils.generateRandomString(8)}`,
|
|
appointment_date: global.testUtils.generateRandomDate(2025, 2025),
|
|
appointment_time: "10:00",
|
|
status: "scheduled",
|
|
type: "consultation",
|
|
duration: 30,
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Create mock prescription data
|
|
*/
|
|
createMockPrescriptionData: () => {
|
|
return {
|
|
id: `prescription_${global.testUtils.generateRandomString(8)}`,
|
|
patient_id: `patient_${global.testUtils.generateRandomString(8)}`,
|
|
provider_id: `provider_${global.testUtils.generateRandomString(8)}`,
|
|
medication_name: "Lisinopril",
|
|
strength: "10mg",
|
|
dosage: "10mg daily",
|
|
quantity: 30,
|
|
refills: 2,
|
|
status: "active",
|
|
};
|
|
},
|
|
};
|
|
|
|
// Healthcare-specific test constants
|
|
global.healthcareConstants = {
|
|
VALID_ICD10_CODES: ["E11.9", "I10", "Z00.00", "M79.3"],
|
|
VALID_CPT_CODES: ["99213", "93000", "36415", "80053"],
|
|
COMMON_MEDICATIONS: ["Lisinopril", "Metformin", "Atorvastatin", "Amlodipine"],
|
|
DRUG_INTERACTIONS: {
|
|
Warfarin: ["Aspirin", "Ibuprofen", "Amiodarone"],
|
|
Digoxin: ["Amiodarone", "Verapamil", "Quinidine"],
|
|
},
|
|
VITAL_SIGN_RANGES: {
|
|
systolic_bp: { min: 80, max: 200 },
|
|
diastolic_bp: { min: 50, max: 120 },
|
|
heart_rate: { min: 40, max: 150 },
|
|
temperature: { min: 95.0, max: 110.0 },
|
|
respiratory_rate: { min: 8, max: 30 },
|
|
oxygen_saturation: { min: 85, max: 100 },
|
|
},
|
|
};
|
|
|
|
// Export for use in other test files (commented out since these are globals)
|
|
// export { testConstants, testUtils, healthcareConstants };
|