Skip to main content
Glama
medication.test.ts13 kB
// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors // SPDX-License-Identifier: Apache-2.0 import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; import { HealthieClient } from './client'; import { HEALTHIE_MEDICATION_CODE_SYSTEM, HEALTHIE_MEDICATION_ID_SYSTEM, HEALTHIE_MEDICATION_ROUTE_CODE_SYSTEM, } from './constants'; import { convertHealthieMedicationToFhir, fetchMedications, parseDosage } from './medication'; import type { HealthieMedicationType } from './medication'; type MockResponse = { json: () => Promise<any>; ok: boolean; status: number; }; describe('fetchMedications', () => { let healthieClient: HealthieClient; const mockBaseUrl = 'https://api.example.com/graphql'; const mockClientSecret = 'test-secret'; let mockFetch: ReturnType<typeof vi.fn>; beforeEach(() => { healthieClient = new HealthieClient(mockBaseUrl, mockClientSecret); // Mock fetch globally mockFetch = vi.fn().mockImplementation((): Promise<MockResponse> => { return Promise.resolve({ json: () => Promise.resolve({}), ok: true, status: 200, }); }); global.fetch = mockFetch as unknown as typeof fetch; }); afterEach(() => { vi.resetAllMocks(); }); test('returns medication data for a patient', async () => { const mockMedications = [ { id: 'med123', name: 'Test Medication', active: true, start_date: '2024-01-01', end_date: '2024-12-31', }, ]; mockFetch.mockImplementationOnce( (): Promise<MockResponse> => Promise.resolve({ json: () => Promise.resolve({ data: { medications: mockMedications } }), ok: true, status: 200, }) ); const result = await fetchMedications(healthieClient, 'patient123'); expect(result).toEqual(mockMedications); // Verify the query includes the patient ID expect(mockFetch).toHaveBeenCalledWith( mockBaseUrl, expect.objectContaining({ body: expect.stringContaining('patient123'), }) ); }); }); describe('convertHealthieMedicationToFhir', () => { const mockMedication: HealthieMedicationType = { id: 'med123', name: 'Test Medication', active: true, dosage: '10 MG', code: 'MED001', comment: 'Take with food', created_at: '2024-01-01T00:00:00Z', mirrored: false, }; test('converts basic medication to FHIR', () => { const result = convertHealthieMedicationToFhir(mockMedication, { display: 'Test Patient', reference: 'Patient/123', }); expect(result).toEqual({ resourceType: 'MedicationRequest', identifier: [{ system: HEALTHIE_MEDICATION_ID_SYSTEM, value: 'med123' }], status: 'active', intent: 'proposal', subject: { display: 'Test Patient', reference: 'Patient/123', }, medicationCodeableConcept: { text: 'Test Medication', coding: [ { system: HEALTHIE_MEDICATION_CODE_SYSTEM, code: 'MED001', display: 'Test Medication', }, ], }, dosageInstruction: [ { doseAndRate: [ { doseQuantity: { value: 10, unit: 'MG', system: 'http://unitsofmeasure.org', }, }, ], }, ], note: [{ text: 'Take with food' }], }); }); test('handles inactive medication', () => { const inactiveMedication: HealthieMedicationType = { ...mockMedication, active: false, }; const result = convertHealthieMedicationToFhir(inactiveMedication, { display: 'Test Patient', reference: 'Patient/123', }); expect(result.status).toBe('unknown'); }); test('handles medication without dosage', () => { const medicationWithoutDosage: HealthieMedicationType = { ...mockMedication, dosage: undefined, }; const result = convertHealthieMedicationToFhir(medicationWithoutDosage, { display: 'John Doe' }); expect(result.dosageInstruction).toBeUndefined(); }); test('handles medication without comment', () => { const medicationWithoutComment: HealthieMedicationType = { ...mockMedication, comment: undefined, }; const result = convertHealthieMedicationToFhir(medicationWithoutComment, { display: 'John Doe' }); expect(result.note).toBeUndefined(); }); test('handles medication without code', () => { const medicationWithoutCode: HealthieMedicationType = { ...mockMedication, code: undefined, }; const result = convertHealthieMedicationToFhir(medicationWithoutCode, { display: 'John Doe' }); expect(result.medicationCodeableConcept?.coding?.[0].code).toBeUndefined(); }); test('handles medication without name', () => { const medicationWithoutName: HealthieMedicationType = { ...mockMedication, name: undefined, }; const result = convertHealthieMedicationToFhir(medicationWithoutName, { display: 'John Doe' }); expect(result.medicationCodeableConcept?.text).toBeUndefined(); expect(result.medicationCodeableConcept?.coding?.[0].display).toBeUndefined(); }); test('handles invalid dosage', () => { const medicationWithInvalidDosage: HealthieMedicationType = { ...mockMedication, dosage: 'invalid dosage', }; const result = convertHealthieMedicationToFhir(medicationWithInvalidDosage, { display: 'John Doe' }); expect(result.dosageInstruction?.[0].doseAndRate?.[0].doseQuantity).toBeUndefined(); }); test('handles medication with route', () => { const medicationWithRoute: HealthieMedicationType = { ...mockMedication, route: 'oral', }; const result = convertHealthieMedicationToFhir(medicationWithRoute, { display: 'Test Patient', reference: 'Patient/123', }); expect(result.dosageInstruction?.[0].route).toEqual({ text: 'oral', coding: [ { system: HEALTHIE_MEDICATION_ROUTE_CODE_SYSTEM, code: 'oral', display: 'oral', }, ], }); }); test('handles medication without route', () => { const medicationWithoutRoute: HealthieMedicationType = { ...mockMedication, route: undefined, }; const result = convertHealthieMedicationToFhir(medicationWithoutRoute, { display: 'Test Patient', reference: 'Patient/123', }); expect(result.dosageInstruction?.[0].route).toBeUndefined(); }); }); describe('parseDosage', () => { test('parses valid dosage strings', () => { expect(parseDosage('10 MG')).toEqual({ value: 10, unit: 'MG', system: 'http://unitsofmeasure.org', }); expect(parseDosage('650 MG')).toEqual({ value: 650, unit: 'MG', system: 'http://unitsofmeasure.org', }); expect(parseDosage('5.5 ML')).toEqual({ value: 5.5, unit: 'ML', system: 'http://unitsofmeasure.org', }); }); test('handles invalid or missing input', () => { expect(parseDosage(undefined)).toBeUndefined(); expect(parseDosage('')).toBeUndefined(); expect(parseDosage('invalid')).toBeUndefined(); expect(parseDosage('10')).toBeUndefined(); expect(parseDosage('MG')).toBeUndefined(); expect(parseDosage('10 mg mg')).toBeUndefined(); }); test('handles decimal variations', () => { // Leading zeros expect(parseDosage('010 MG')).toEqual({ value: 10, unit: 'MG', system: 'http://unitsofmeasure.org', }); // Trailing zeros expect(parseDosage('10.0 MG')).toEqual({ value: 10, unit: 'MG', system: 'http://unitsofmeasure.org', }); expect(parseDosage('10.00 MG')).toEqual({ value: 10, unit: 'MG', system: 'http://unitsofmeasure.org', }); // Many decimal places expect(parseDosage('10.123456 MG')).toEqual({ value: 10.123456, unit: 'MG', system: 'http://unitsofmeasure.org', }); // Very small decimals expect(parseDosage('0.001 MG')).toEqual({ value: 0.001, unit: 'MG', system: 'http://unitsofmeasure.org', }); expect(parseDosage('0.0001 MG')).toEqual({ value: 0.0001, unit: 'MG', system: 'http://unitsofmeasure.org', }); }); test('handles spacing variations', () => { // No space expect(parseDosage('10MG')).toEqual({ value: 10, unit: 'MG', system: 'http://unitsofmeasure.org', }); // Multiple spaces expect(parseDosage('10 MG')).toEqual({ value: 10, unit: 'MG', system: 'http://unitsofmeasure.org', }); // Tab character expect(parseDosage('10\tMG')).toEqual({ value: 10, unit: 'MG', system: 'http://unitsofmeasure.org', }); }); test('handles case variations in units', () => { expect(parseDosage('10 mg')).toEqual({ value: 10, unit: 'mg', system: 'http://unitsofmeasure.org', }); expect(parseDosage('10 Mg')).toEqual({ value: 10, unit: 'Mg', system: 'http://unitsofmeasure.org', }); expect(parseDosage('10 mG')).toEqual({ value: 10, unit: 'mG', system: 'http://unitsofmeasure.org', }); expect(parseDosage('5 ml')).toEqual({ value: 5, unit: 'ml', system: 'http://unitsofmeasure.org', }); expect(parseDosage('5 mL')).toEqual({ value: 5, unit: 'mL', system: 'http://unitsofmeasure.org', }); }); test('handles common medical units', () => { expect(parseDosage('100 mcg')).toEqual({ value: 100, unit: 'mcg', system: 'http://unitsofmeasure.org', }); expect(parseDosage('500 IU')).toEqual({ value: 500, unit: 'IU', system: 'http://unitsofmeasure.org', }); expect(parseDosage('10 units')).toEqual({ value: 10, unit: 'units', system: 'http://unitsofmeasure.org', }); expect(parseDosage('2 tabs')).toEqual({ value: 2, unit: 'tabs', system: 'http://unitsofmeasure.org', }); expect(parseDosage('1 capsule')).toEqual({ value: 1, unit: 'capsule', system: 'http://unitsofmeasure.org', }); }); test('handles large numbers', () => { expect(parseDosage('1000000 MG')).toEqual({ value: 1000000, unit: 'MG', system: 'http://unitsofmeasure.org', }); expect(parseDosage('999999.999 MG')).toEqual({ value: 999999.999, unit: 'MG', system: 'http://unitsofmeasure.org', }); }); test('handles zero values', () => { expect(parseDosage('0 MG')).toEqual({ value: 0, unit: 'MG', system: 'http://unitsofmeasure.org', }); expect(parseDosage('0.0 MG')).toEqual({ value: 0, unit: 'MG', system: 'http://unitsofmeasure.org', }); }); test('handles edge cases that should fail', () => { // Leading decimal point without leading zero - should fail with current regex expect(parseDosage('.5 MG')).toBeUndefined(); // Negative values - should fail with current regex expect(parseDosage('-10 MG')).toBeUndefined(); // Scientific notation - should fail with current regex expect(parseDosage('1e-3 MG')).toBeUndefined(); expect(parseDosage('1E-3 MG')).toBeUndefined(); // Fractional representation - should fail with current regex expect(parseDosage('1/2 MG')).toBeUndefined(); // Multiple decimal points expect(parseDosage('10.5.5 MG')).toBeUndefined(); // Units with numbers expect(parseDosage('10 MG5')).toBeUndefined(); // Units with special characters expect(parseDosage('10 MG-')).toBeUndefined(); expect(parseDosage('10 MG_')).toBeUndefined(); // Leading/trailing whitespace around entire string expect(parseDosage(' 10 MG ')).toBeUndefined(); expect(parseDosage('10 MG ')).toBeUndefined(); expect(parseDosage(' 10 MG')).toBeUndefined(); // Multiple values expect(parseDosage('10 20 MG')).toBeUndefined(); expect(parseDosage('10 MG 20')).toBeUndefined(); }); test('handles problematic but potentially valid inputs', () => { // Very long unit names expect(parseDosage('10 milligrams')).toEqual({ value: 10, unit: 'milligrams', system: 'http://unitsofmeasure.org', }); // Units with mixed case expect(parseDosage('10 InternationalUnits')).toEqual({ value: 10, unit: 'InternationalUnits', system: 'http://unitsofmeasure.org', }); // Single character units expect(parseDosage('10 g')).toEqual({ value: 10, unit: 'g', system: 'http://unitsofmeasure.org', }); // Very long unit abbreviations expect(parseDosage('10 mcgkgmin')).toEqual({ value: 10, unit: 'mcgkgmin', system: 'http://unitsofmeasure.org', }); }); });

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/medplum/medplum'

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