first
This commit is contained in:
357
tests/setup.js
Normal file
357
tests/setup.js
Normal file
@@ -0,0 +1,357 @@
|
||||
/**
|
||||
* @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 };
|
Reference in New Issue
Block a user