Skip to main content
Glama
address.test.ts12.4 kB
// SPDX-FileCopyrightText: Copyright Orangebot, Inc. and Medplum contributors // SPDX-License-Identifier: Apache-2.0 import type { WithId } from '@medplum/core'; import { Operator } from '@medplum/core'; import type { Address, InsurancePlan, Location, Patient, Resource, ResourceType } from '@medplum/fhirtypes'; import { randomUUID } from 'crypto'; import type { PoolClient } from 'pg'; import { initAppServices, shutdownApp } from '../../app'; import { loadTestConfig } from '../../config/loader'; import { getLogger } from '../../logger'; import { withTestContext } from '../../test.setup'; import { getSystemRepo } from '../repo'; import type { AddressTableRow } from './address'; import { AddressTable } from './address'; describe('Address Lookup Table', () => { const systemRepo = getSystemRepo(); beforeAll(async () => { const config = await loadTestConfig(); await initAppServices(config); }); afterAll(async () => { await shutdownApp(); }); test('Patient resource with address', () => withTestContext(async () => { const addressLine = randomUUID(); const addressCity = randomUUID(); const postalCode = randomUUID(); const patient = await systemRepo.createResource<Patient>({ resourceType: 'Patient', name: [{ given: ['Alice'], family: 'Smith' }], address: [ { use: 'home', line: [addressLine], city: addressCity, state: 'CA', postalCode, country: 'US', }, ], }); const searchResult1 = await systemRepo.search({ resourceType: 'Patient', filters: [ { code: 'address', operator: Operator.CONTAINS, value: addressLine, }, ], }); expect(searchResult1.entry?.length).toStrictEqual(1); expect(searchResult1.entry?.[0]?.resource?.id).toStrictEqual(patient.id); const searchResult2 = await systemRepo.search({ resourceType: 'Patient', filters: [ { code: 'address-city', operator: Operator.EQUALS, value: addressCity, }, ], }); expect(searchResult2.entry?.length).toStrictEqual(1); expect(searchResult2.entry?.[0]?.resource?.id).toStrictEqual(patient.id); const searchResult3 = await systemRepo.search({ resourceType: 'Patient', filters: [ { code: 'address-postalcode', operator: Operator.EQUALS, value: postalCode, }, ], }); expect(searchResult3.entry?.length).toStrictEqual(1); expect(searchResult3.entry?.[0]?.resource?.id).toStrictEqual(patient.id); })); test('Multiple addresses', () => withTestContext(async () => { const address = randomUUID(); const other = randomUUID(); const patient = await systemRepo.createResource<Patient>({ resourceType: 'Patient', name: [{ given: ['Alice'], family: 'Smith' }], address: [ { use: 'home', line: [address] }, { use: 'home', line: [other] }, ], }); const searchResult = await systemRepo.search({ resourceType: 'Patient', filters: [ { code: 'address', operator: Operator.EQUALS, value: address, }, ], }); expect(searchResult.entry?.length).toStrictEqual(1); expect(searchResult.entry?.[0]?.resource?.id).toStrictEqual(patient.id); const searchResult2 = await systemRepo.search({ resourceType: 'Patient', filters: [ { code: 'address', operator: Operator.EQUALS, value: other, }, ], }); expect(searchResult2.entry?.length).toStrictEqual(1); expect(searchResult2.entry?.[0]?.resource?.id).toStrictEqual(patient.id); })); test.each([ [ 'Patient' as ResourceType, (address: string): Patient => ({ resourceType: 'Patient', name: [{ given: ['Alice'], family: 'Smith' }], address: [{ use: 'home', line: [address] }], }), ], [ 'InsurancePlan' as ResourceType, (address: string): InsurancePlan => ({ resourceType: 'InsurancePlan', name: 'Test Insurance Plan', contact: [{ address: { use: 'home', line: [address] } }], }), ], [ 'Location' as ResourceType, (address: string): Location => ({ resourceType: 'Location', address: { use: 'home', line: [address] }, }), ], ])('Update %s address', (resourceType, buildResource) => withTestContext(async () => { const address1 = randomUUID(); const address2 = randomUUID(); const resource1 = await systemRepo.createResource(buildResource(address1)); const bundle2 = await systemRepo.search({ resourceType, filters: [ { code: 'address', operator: Operator.EQUALS, value: address1, }, ], }); expect(bundle2.entry?.length).toStrictEqual(1); expect(bundle2.entry?.[0]?.resource?.id).toStrictEqual(resource1.id); const bundle3 = await systemRepo.search({ resourceType, filters: [ { code: 'address', operator: Operator.EQUALS, value: address2, }, ], }); expect(bundle3.entry?.length).toStrictEqual(0); await systemRepo.updateResource({ ...resource1, ...buildResource(address2), } as Resource); const bundle5 = await systemRepo.search({ resourceType, filters: [ { code: 'address', operator: Operator.EQUALS, value: address1, }, ], }); expect(bundle5.entry?.length).toStrictEqual(0); const bundle6 = await systemRepo.search({ resourceType, filters: [ { code: 'address', operator: Operator.EQUALS, value: address2, }, ], }); expect(bundle6.entry?.length).toStrictEqual(1); expect(bundle6.entry?.[0]?.resource?.id).toStrictEqual(resource1.id); }) ); test('Purges related resource type', async () => { const db = { query: jest.fn().mockReturnValue({ rowCount: 0, rows: [] }) } as unknown as PoolClient; const table = new AddressTable(); await table.purgeValuesBefore(db, 'Patient', '2024-01-01T00:00:00Z'); expect(db.query).toHaveBeenCalled(); }); test('Does not purge unrelated resource type', async () => { const db = { query: jest.fn() } as unknown as PoolClient; const table = new AddressTable(); await table.purgeValuesBefore(db, 'AuditEvent', '2024-01-01T00:00:00Z'); expect(db.query).not.toHaveBeenCalled(); }); test('extractValues defensive against nullish and empty string values', () => { const table = new AddressTable(); const r1: WithId<Patient> = { resourceType: 'Patient', id: '1', address: undefined, }; const result1: any[] = []; table.extractValues(result1, r1); expect(result1).toStrictEqual([]); const r2: WithId<Patient> = { resourceType: 'Patient', id: '2', address: [ {}, null, undefined, { use: 'work', line: ['Line 1'], city: 'City 1', country: 'Country 1', postalCode: 'Postal 1', state: 'State 1', }, { line: ['Line 1', 'Line 2'], city: '' }, // empty city should be ignored { line: ['Line 1', 'Line 2'] }, { use: 'home' }, ] as unknown as Address[], }; const result2: any[] = []; table.extractValues(result2, r2); expect(result2).toStrictEqual([ { resourceId: '2', address: 'Line 1, City 1, State 1, Postal 1', city: 'City 1', country: 'Country 1', postalCode: 'Postal 1', state: 'State 1', use: 'work', }, { address: 'Line 1, Line 2', city: undefined, country: undefined, postalCode: undefined, resourceId: '2', state: undefined, use: undefined, }, { address: undefined, city: undefined, country: undefined, postalCode: undefined, resourceId: '2', state: undefined, use: 'home', }, ]); const r3: WithId<InsurancePlan> = { resourceType: 'InsurancePlan', id: '3', contact: [ { address: { use: 'work', line: ['Line 1'], city: 'City 1', country: 'Country 1', postalCode: 'Postal 1', state: 'State 1', }, }, { address: { use: 'work', line: ['Line 1'], city: 'City 1', country: 'Country 1', postalCode: 'Postal 1', state: 'State 1', }, }, ], }; const result3: any[] = []; table.extractValues(result3, r3); expect(result3).toStrictEqual([ { resourceId: '3', address: 'Line 1, City 1, State 1, Postal 1', city: 'City 1', country: 'Country 1', postalCode: 'Postal 1', state: 'State 1', use: 'work', }, ]); }); test('extractValues multiple patients with identical address', () => { const table = new AddressTable(); const r1: WithId<Patient> = { resourceType: 'Patient', id: '1', name: [{ given: ['Alice'], family: 'Smith' }], address: [ { line: ['500 Jefferson Street'], city: 'San Francisco', state: 'CA', postalCode: '94109', }, { line: ['500 Jefferson Street'], city: 'San Francisco', state: 'CA', postalCode: '94109', }, ], }; const r2: WithId<Patient> = { resourceType: 'Patient', id: '2', name: [{ given: ['Bob'], family: 'Smith' }], address: [ { line: ['500 Jefferson Street'], city: 'San Francisco', state: 'CA', postalCode: '94109', }, { line: ['500 Jefferson Street'], city: 'San Francisco', state: 'CA', postalCode: '94109', }, ] as unknown as Address[], }; const result: AddressTableRow[] = []; table.extractValues(result, r1); table.extractValues(result, r2); expect(result).toStrictEqual([ { resourceId: '1', address: '500 Jefferson Street, San Francisco, CA, 94109', city: 'San Francisco', country: undefined, postalCode: '94109', state: 'CA', use: undefined, }, { resourceId: '2', address: '500 Jefferson Street, San Francisco, CA, 94109', city: 'San Francisco', country: undefined, postalCode: '94109', state: 'CA', use: undefined, }, ]); }); test('Errors logged and rethrown', async () => { const db = { query: jest.fn().mockReturnValue({ rowCount: 0, rows: [] }) } as unknown as PoolClient; const table = new AddressTable(); const r1: WithId<Patient> = { resourceType: 'Patient', id: '1', name: [{ given: ['Alice'], family: 'Smith' }], address: [ { line: ['500 Jefferson Street'], city: 'San Francisco', state: 'CA', postalCode: '94109', }, ] as unknown as Address[], }; const extractValuesSpy = jest.spyOn(table, 'extractValues').mockImplementation(() => { throw new Error('test error'); }); const logger = getLogger(); const errorSpy = jest.spyOn(logger, 'error').mockImplementation(() => {}); await expect(async () => table.batchIndexResources(db, [r1], false)).rejects.toThrow('test error'); expect(extractValuesSpy).toHaveBeenCalledTimes(1); expect(errorSpy).toHaveBeenCalledWith('Error extracting values for resource', { resource: 'Patient/1', err: expect.any(Error), }); errorSpy.mockClear(); }); });

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