Skip to main content
Glama
medicationRequest.integration.test.ts18.9 kB
import { medplum, ensureAuthenticated } from '../../src/config/medplumClient'; import { createMedicationRequest, CreateMedicationRequestArgs, getMedicationRequestById, updateMedicationRequest, UpdateMedicationRequestArgs, searchMedicationRequests, SearchMedicationRequestsArgs } from '../../src/tools/medicationRequestUtils'; import { createPatient, CreatePatientArgs } from '../../src/tools/patientUtils'; import { createPractitioner, CreatePractitionerArgs } from '../../src/tools/practitionerUtils'; import { createEncounter, CreateEncounterArgs } from '../../src/tools/encounterUtils'; import { Patient, Practitioner, Encounter, MedicationRequest, CodeableConcept, Dosage } from '@medplum/fhirtypes'; import { randomUUID } from 'crypto'; const rxnormSystem = 'http://www.nlm.nih.gov/research/umls/rxnorm'; describe('MedicationRequest Tool Integration Tests', () => { let testPatient: Patient | null; let testPractitioner: Practitioner | null; let testEncounter: Encounter | null; beforeAll(async () => { await ensureAuthenticated(); const patientArgs: CreatePatientArgs = { firstName: 'MedReqTest', lastName: `Patient-${randomUUID().substring(0, 8)}`, birthDate: '1975-05-25', gender: 'female', }; testPatient = await createPatient(patientArgs); if (!testPatient || !testPatient.id) throw new Error('Test patient creation failed for MedicationRequest tests'); console.log(`Created test patient for MedicationRequest tests: ${testPatient.id}`); const practitionerArgs: CreatePractitionerArgs = { givenName: 'MedReqTest', familyName: `Doctor-${randomUUID().substring(0, 8)}`, }; testPractitioner = await createPractitioner(practitionerArgs); if (!testPractitioner || !testPractitioner.id) throw new Error('Test practitioner creation failed for MedicationRequest tests'); console.log(`Created test practitioner for MedicationRequest tests: ${testPractitioner.id}`); const encounterArgs: CreateEncounterArgs = { status: 'finished', classCode: 'AMB', patientId: testPatient.id, practitionerIds: [testPractitioner.id], identifierValue: `MEDREQ-ENC-${randomUUID().substring(0,8)}` }; testEncounter = await createEncounter(encounterArgs); if (!testEncounter || !testEncounter.id) throw new Error('Test encounter creation failed for MedicationRequest tests'); console.log(`Created test encounter for MedicationRequest tests: ${testEncounter.id}`); }); describe('createMedicationRequest', () => { it('should create a new medication request successfully', async () => { if (!testPatient?.id || !testEncounter?.id || !testPractitioner?.id) { throw new Error('Test entities not initialized for createMedicationRequest test'); } const medReqArgs: CreateMedicationRequestArgs = { status: 'active', intent: 'order', medicationCodeableConcept: { coding: [{ system: rxnormSystem, code: '834060', // Lisinopril 10 MG Oral Tablet display: 'Lisinopril 10 MG Oral Tablet' }], text: 'Lisinopril 10 MG Oral Tablet' }, subjectId: testPatient.id, encounterId: testEncounter.id, authoredOn: new Date().toISOString(), requesterId: testPractitioner.id, dosageInstruction: [{ text: 'Take one tablet by mouth daily' }], identifier: { value: `medreq-create-${randomUUID().substring(0,8)}`, system: 'urn:custom:medreq'} }; const newMedReq = await createMedicationRequest(medReqArgs); expect(newMedReq).toBeDefined(); expect(newMedReq.resourceType).toBe('MedicationRequest'); expect(newMedReq.id).toBeDefined(); expect(newMedReq.status).toBe('active'); expect(newMedReq.intent).toBe('order'); expect(newMedReq.medicationCodeableConcept?.coding?.[0]?.code).toBe('834060'); expect(newMedReq.subject?.reference).toBe(`Patient/${testPatient.id}`); expect(newMedReq.encounter?.reference).toBe(`Encounter/${testEncounter.id}`); expect(newMedReq.requester?.reference).toBe(`Practitioner/${testPractitioner.id}`); expect(newMedReq.dosageInstruction?.[0]?.text).toBe('Take one tablet by mouth daily'); expect(newMedReq.identifier?.[0]?.value).toBe(medReqArgs.identifier?.value); console.log(`Created MedicationRequest (Lisinopril): ${newMedReq.id}`); }); it('should throw an error if status is missing', async () => { const medReqArgs = { intent: 'order', medicationCodeableConcept: { text: 'Aspirin' }, subjectId: testPatient!.id! } as unknown as CreateMedicationRequestArgs; await expect(createMedicationRequest(medReqArgs)).rejects.toThrow('MedicationRequest status is required.'); }); it('should throw an error if intent is missing', async () => { const medReqArgs = { status: 'active', medicationCodeableConcept: { text: 'Aspirin' }, subjectId: testPatient!.id! } as unknown as CreateMedicationRequestArgs; await expect(createMedicationRequest(medReqArgs)).rejects.toThrow('MedicationRequest intent is required.'); }); it('should throw an error if medication is missing', async () => { const medReqArgs = { status: 'active', intent: 'order', subjectId: testPatient!.id! } as unknown as CreateMedicationRequestArgs; await expect(createMedicationRequest(medReqArgs)).rejects.toThrow('Medication (medicationCodeableConcept or medicationReference) is required.'); }); it('should throw an error if subjectId is missing', async () => { const medReqArgs = { status: 'active', intent: 'order', medicationCodeableConcept: { text: 'Aspirin' } } as unknown as CreateMedicationRequestArgs; await expect(createMedicationRequest(medReqArgs)).rejects.toThrow('Subject (Patient ID) is required to create a MedicationRequest.'); }); }); describe('getMedicationRequestById', () => { let createdMedReq: MedicationRequest | null; beforeAll(async () => { if (!testPatient?.id) throw new Error('Test patient not initialized for getMedicationRequestById tests'); const medReqArgs: CreateMedicationRequestArgs = { status: 'active', intent: 'proposal', medicationCodeableConcept: { text: 'Amoxicillin 250mg tablet' }, subjectId: testPatient.id, identifier: { value: `medreq-get-${randomUUID().substring(0,8)}`} }; createdMedReq = await createMedicationRequest(medReqArgs); if (!createdMedReq || !createdMedReq.id) throw new Error('Test MedicationRequest creation failed for get tests'); console.log(`Created test MedicationRequest for getMedicationRequestById tests: ${createdMedReq.id}`); }); it('should retrieve an existing medication request successfully', async () => { expect(createdMedReq?.id).toBeDefined(); const fetchedMedReq = await getMedicationRequestById({ medicationRequestId: createdMedReq!.id! }); expect(fetchedMedReq).toBeDefined(); expect(fetchedMedReq!.id).toBe(createdMedReq!.id); expect(fetchedMedReq!.status).toBe('active'); expect(fetchedMedReq!.medicationCodeableConcept?.text).toBe('Amoxicillin 250mg tablet'); }); it('should return null for a non-existent medication request ID', async () => { const nonExistentId = randomUUID(); const fetchedMedReq = await getMedicationRequestById({ medicationRequestId: nonExistentId }); expect(fetchedMedReq).toBeNull(); }); it('should throw an error if medication request ID is not provided', async () => { // @ts-ignore: Testing invalid input await expect(getMedicationRequestById({})).rejects.toThrow('MedicationRequest ID is required'); await expect(getMedicationRequestById({ medicationRequestId: '' })).rejects.toThrow('MedicationRequest ID is required'); }); }); describe('updateMedicationRequest', () => { let medReqToUpdate: MedicationRequest | null; const initialStatus = 'draft'; const updatedStatus = 'active'; beforeEach(async () => { if (!testPatient?.id) throw new Error('Test patient not initialized for updateMedicationRequest tests'); const medReqArgs: CreateMedicationRequestArgs = { status: initialStatus, intent: 'plan', medicationCodeableConcept: { coding: [{ system: rxnormSystem, code: '313782', display: 'Acetaminophen 325 MG Oral Tablet' }], text: 'Acetaminophen 325 MG Oral Tablet' }, subjectId: testPatient.id, identifier: { value: `medreq-update-${randomUUID().substring(0,8)}`} }; medReqToUpdate = await createMedicationRequest(medReqArgs); if (!medReqToUpdate || !medReqToUpdate.id) throw new Error('Test MedicationRequest creation failed for update tests'); console.log(`Created MedicationRequest for update test: ${medReqToUpdate.id}, status: ${medReqToUpdate.status}`); }); it('should update an existing medication request successfully', async () => { expect(medReqToUpdate?.id).toBeDefined(); const newDosageInstruction: Dosage[] = [{ sequence: 1, text: 'Take 1 tablet every 4-6 hours as needed for pain', timing: { repeat: { frequency: 1, period: 6, periodUnit: 'h' } }, route: { coding: [{ system: 'http://snomed.info/sct', code: '26643006', display: 'Oral route' }] } }]; const updates: UpdateMedicationRequestArgs = { status: updatedStatus, intent: 'order', // Change intent authoredOn: new Date().toISOString(), dosageInstruction: newDosageInstruction, note: 'Patient advised to not exceed 4g/day.' }; const updatedMedReq = await updateMedicationRequest(medReqToUpdate!.id!, updates); expect(updatedMedReq).toBeDefined(); expect(updatedMedReq.id).toBe(medReqToUpdate!.id); expect(updatedMedReq.status).toBe(updatedStatus); expect(updatedMedReq.intent).toBe('order'); expect(updatedMedReq.authoredOn).toBeDefined(); expect(updatedMedReq.dosageInstruction?.[0]?.text).toBe(newDosageInstruction[0].text); expect(updatedMedReq.note?.[0]?.text).toBe('Patient advised to not exceed 4g/day.'); console.log(`Updated MedicationRequest: ${updatedMedReq.id}, new status: ${updatedMedReq.status}`); }); it('should clear optional fields when null is provided', async () => { expect(medReqToUpdate?.id).toBeDefined(); const initialEncounterId = testEncounter!.id!; // First, add an encounter to it let tempUpdatedMedReq = await updateMedicationRequest(medReqToUpdate!.id!, { encounterId: initialEncounterId, note: 'Initial note' }); expect(tempUpdatedMedReq.encounter?.reference).toBe(`Encounter/${initialEncounterId}`); expect(tempUpdatedMedReq.note).toBeDefined(); const updatesToClear: UpdateMedicationRequestArgs = { encounterId: null, note: null, dosageInstruction: null }; const clearedMedReq = await updateMedicationRequest(medReqToUpdate!.id!, updatesToClear); expect(clearedMedReq.encounter).toBeUndefined(); expect(clearedMedReq.note).toBeUndefined(); expect(clearedMedReq.dosageInstruction).toBeUndefined(); }); it('should throw an error if medication request ID is not provided', async () => { await expect(updateMedicationRequest('', { status: 'active' })).rejects.toThrow('MedicationRequest ID is required'); }); it('should throw an error if updates object is empty', async () => { expect(medReqToUpdate?.id).toBeDefined(); // @ts-ignore: Testing invalid input await expect(updateMedicationRequest(medReqToUpdate!.id!, {})).rejects.toThrow('Updates object cannot be empty'); }); }); describe('searchMedicationRequests', () => { let medReq1: MedicationRequest | null, medReq2: MedicationRequest | null, medReq3_other_patient: MedicationRequest | null; const medCode1 = '197360'; // Atorvastatin 10mg tablet (Lipitor) const medText1 = 'Atorvastatin 10mg tablet'; const medCode2 = '866924'; // Metformin 500 MG Extended Release Oral Tablet const medText2 = 'Metformin 500 MG ER Oral Tablet'; let searchPatientId: string; let searchPractitionerId: string; let otherPatientId: string; let medReq1Identifier: string; beforeAll(async () => { if (!testPatient?.id || !testPractitioner?.id) { throw new Error('Test entities not initialized for searchMedicationRequests tests'); } searchPatientId = testPatient.id; searchPractitionerId = testPractitioner.id; medReq1Identifier = `medreq-search-${randomUUID().substring(0,8)}`; // Create another patient for exclusivity tests const otherPatientArgs: CreatePatientArgs = { firstName: 'OtherMed', lastName: 'Patient', birthDate: '1990-01-01', gender: 'other' }; const tempOtherPatient = await createPatient(otherPatientArgs); if (!tempOtherPatient || !tempOtherPatient.id) throw new Error('Creation of other patient failed for search tests'); otherPatientId = tempOtherPatient.id; medReq1 = await createMedicationRequest({ status: 'active', intent: 'order', medicationCodeableConcept: { coding: [{ system: rxnormSystem, code: medCode1, display: medText1 }], text: medText1 }, subjectId: searchPatientId, requesterId: searchPractitionerId, authoredOn: '2023-01-15T10:00:00Z', identifier: {value: medReq1Identifier, system: 'test-medreq-system'} }); medReq2 = await createMedicationRequest({ status: 'completed', intent: 'order', medicationCodeableConcept: { coding: [{ system: rxnormSystem, code: medCode2, display: medText2 }], text: medText2 }, subjectId: searchPatientId, requesterId: searchPractitionerId, // Same requester authoredOn: '2023-02-20T11:00:00Z' }); medReq3_other_patient = await createMedicationRequest({ status: 'active', intent: 'order', medicationCodeableConcept: { coding: [{ system: rxnormSystem, code: medCode1, display: medText1 }], text: medText1 }, // Same med as medReq1 subjectId: otherPatientId, // Different patient requesterId: searchPractitionerId, authoredOn: '2023-03-10T09:00:00Z' }); if (!medReq1 || !medReq2 || !medReq3_other_patient) throw new Error('Failed to create medication requests for search tests'); console.log(`Created medication requests for search tests: ${medReq1.id}, ${medReq2.id}, ${medReq3_other_patient.id}`); }); it('should find medication requests by patient ID', async () => { const results = await searchMedicationRequests({ patientId: searchPatientId }); expect(results.length).toBeGreaterThanOrEqual(2); const resultIds = results.map(r => r.id); expect(resultIds).toContain(medReq1!.id); expect(resultIds).toContain(medReq2!.id); expect(resultIds).not.toContain(medReq3_other_patient!.id); }); it('should find medication requests by status for a specific patient', async () => { const resultsActive = await searchMedicationRequests({ patientId: searchPatientId, status: 'active' }); expect(resultsActive.some(r => r.id === medReq1!.id)).toBe(true); expect(resultsActive.every(r => r.status === 'active' && r.subject?.reference === `Patient/${searchPatientId}`)).toBe(true); const resultsCompleted = await searchMedicationRequests({ patientId: searchPatientId, status: 'completed' }); expect(resultsCompleted.some(r => r.id === medReq2!.id)).toBe(true); expect(resultsCompleted.every(r => r.status === 'completed' && r.subject?.reference === `Patient/${searchPatientId}`)).toBe(true); }); it('should find medication requests by intent', async () => { const results = await searchMedicationRequests({ patientId: searchPatientId, intent: 'order' }); expect(results.length).toBeGreaterThanOrEqual(2); // Both medReq1 and medReq2 are 'order' expect(results.every(r => r.intent === 'order')).toBe(true); }); it('should find medication requests by medication code', async () => { // Allow time for search indexing await new Promise(resolve => setTimeout(resolve, 200)); // Test that code search works const resultsByCodeOnly = await searchMedicationRequests({ code: medCode1 }); expect(resultsByCodeOnly.length).toBeGreaterThan(0); expect(resultsByCodeOnly.every(r => r.medicationCodeableConcept?.coding?.some(c => c.code === medCode1))).toBe(true); }); it('should find medication requests by authoredOn date', async () => { // Using a range for the whole day to make the test more robust const resultsRange = await searchMedicationRequests({ patientId: searchPatientId, authoredon: 'ge2023-01-15T00:00:00Z&authoredon=le2023-01-15T23:59:59Z' }); expect(resultsRange.some(r => r.id === medReq1!.id)).toBe(true); }); it('should find medication requests by requester', async () => { const results = await searchMedicationRequests({ requester: `Practitioner/${searchPractitionerId}` }); expect(results.length).toBeGreaterThanOrEqual(3); // All 3 created MRs have this requester const resultIds = results.map(r => r.id); expect(resultIds).toContain(medReq1!.id); expect(resultIds).toContain(medReq2!.id); expect(resultIds).toContain(medReq3_other_patient!.id); }); it('should find medication request by identifier', async () => { const results = await searchMedicationRequests({ identifier: `${medReq1!.identifier![0].system}|${medReq1Identifier}` }); expect(results.length).toBeGreaterThanOrEqual(1); expect(results.some(o => o.id === medReq1!.id)).toBe(true); const resultsShort = await searchMedicationRequests({ identifier: medReq1Identifier }); expect(resultsShort.length).toBeGreaterThanOrEqual(1); expect(resultsShort.some(o => o.id === medReq1!.id)).toBe(true); }); it('should return an empty array for criteria that match no medication requests', async () => { const results = await searchMedicationRequests({ patientId: searchPatientId, code: 'NON-EXISTENT-CODE' }); expect(results.length).toBe(0); }); it('should warn and return results or empty for search with no criteria', async () => { const consoleWarnSpy = jest.spyOn(console, 'warn'); const results = await searchMedicationRequests({}); expect(consoleWarnSpy).toHaveBeenCalledWith(expect.stringContaining('MedicationRequest search called with no specific criteria')); expect(results).toBeInstanceOf(Array); consoleWarnSpy.mockRestore(); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/rkirkendall/medplum-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server