Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(#142): adding tests to increase coverage #143

Merged
merged 3 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions mediator/src/controllers/cht.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export async function createPatient(chtPatientDoc: any) {

export async function updatePatientIds(chtFormDoc: any) {
// first, get the existing patient from fhir server
const response = await getFHIRPatientResource(chtFormDoc.external_id);
const response = await getFHIRPatientResource(chtFormDoc.doc.external_id);

if (response.status != 200) {
return { status: 500, data: { message: `FHIR responded with ${response.status}`} };
Expand All @@ -37,10 +37,10 @@ export async function updatePatientIds(chtFormDoc: any) {
}

const fhirPatient = response.data.entry[0].resource;
addId(fhirPatient, chtPatientIdentifierType, chtFormDoc.patient_id);
addId(fhirPatient, chtPatientIdentifierType, chtFormDoc.doc.patient_id);

// now, we need to get the actual patient doc from cht...
const patient_uuid = await getPatientUUIDFromSourceId(chtFormDoc._id);
const patient_uuid = await getPatientUUIDFromSourceId(chtFormDoc.doc._id);
if (patient_uuid){
addId(fhirPatient, chtDocumentIdentifierType, patient_uuid);
return updateFhirResource({ ...fhirPatient, resourceType: 'Patient' });
Expand Down
178 changes: 178 additions & 0 deletions mediator/src/controllers/tests/cht.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import {
createPatient,
updatePatientIds,
createEncounter
} from '../cht'
import {
ChtPatientFactory,
ChtSMSPatientFactory,
ChtPatientIdsFactory,
ChtPregnancyForm
} from '../../middlewares/schemas/tests/cht-request-factories';
import {
PatientFactory,
EncounterFactory,
ObservationFactory
} from '../../middlewares/schemas/tests/fhir-resource-factories';
import {
chtDocumentIdentifierType,
chtPatientIdentifierType
} from '../../mappers/cht';

import * as fhir from '../../utils/fhir';
import * as cht from '../../utils/cht';

import axios from 'axios';
import { randomUUID } from 'crypto';

jest.mock('axios');

describe('CHT outgoing document controllers', () => {
describe('createPatient', () => {
it('creates a FHIR Patient from CHT patient doc', async () => {
jest.spyOn(fhir, 'updateFhirResource').mockResolvedValueOnce({
witash marked this conversation as resolved.
Show resolved Hide resolved
data: {},
status: 200,
});

const data = ChtPatientFactory.build();

const res = await createPatient(data);

expect(res.status).toBe(200);

// assert that the create resource has the right identifier and type
expect(fhir.updateFhirResource).toHaveBeenCalledWith(
expect.objectContaining({
resourceType: 'Patient',
identifier: expect.arrayContaining([
expect.objectContaining({
type: chtDocumentIdentifierType,
value: data.doc._id
})
]),
})
);
});

it('creates a FHIR Patient from an SMS form using source id', async () => {
jest.spyOn(fhir, 'updateFhirResource').mockResolvedValueOnce({
data: {},
status: 200,
});

let sourceId = randomUUID();
jest.spyOn(cht, 'getPatientUUIDFromSourceId').mockResolvedValueOnce(sourceId);

const data = ChtSMSPatientFactory.build();

const res = await createPatient(data);

expect(res.status).toBe(200);

// assert that the createid resource has the right identifier and type
expect(fhir.updateFhirResource).toHaveBeenCalledWith(
expect.objectContaining({
resourceType: 'Patient',
identifier: expect.arrayContaining([
expect.objectContaining({
type: chtDocumentIdentifierType,
value: sourceId
})
]),
})
);
});
});

describe('updatePatientIds', () => {
it('updates patient ids', async () => {
const existingPatient = PatientFactory.build();
jest.spyOn(fhir, 'getFHIRPatientResource').mockResolvedValue({
data: { total: 1, entry: [ { resource: existingPatient } ] },
status: 200,
});
jest.spyOn(fhir, 'updateFhirResource').mockResolvedValueOnce({
data: {},
status: 200,
});

let sourceId = randomUUID();
jest.spyOn(cht, 'getPatientUUIDFromSourceId').mockResolvedValueOnce(sourceId);

const data = ChtPatientIdsFactory.build();

const res = await updatePatientIds(data);

expect(res.status).toBe(200);

// assert that the created resource has the right identifier and type
expect(fhir.updateFhirResource).toHaveBeenCalledWith(
expect.objectContaining({
id: existingPatient.id,
identifier: expect.arrayContaining([
expect.objectContaining({
type: chtDocumentIdentifierType,
value: sourceId
})
]),
})
);
});
});

describe('createEncounter', () => {
it('creates FHIR Encounter from CHT form', async () => {
jest.spyOn(fhir, 'getFHIRPatientResource').mockResolvedValueOnce({
data: { total: 1, entry: [ { resource: PatientFactory.build() } ] },
status: 200,
});
// observations use createFhirResource
jest.spyOn(fhir, 'createFhirResource').mockResolvedValueOnce({
data: {},
status: 200,
});
// encounter uses updatedFhirResource
jest.spyOn(fhir, 'updateFhirResource').mockResolvedValueOnce({
data: {},
status: 200,
});

const data = ChtPregnancyForm.build();

const res = await createEncounter(data);

expect(res.status).toBe(200);

// assert that the encounter was created
expect(fhir.updateFhirResource).toHaveBeenCalledWith(
expect.objectContaining({
resourceType: 'Encounter',
identifier: expect.arrayContaining([
expect.objectContaining({
type: chtDocumentIdentifierType,
value: data.id
})
]),
})
);

// assert that at least one observation was created with the right codes
expect(fhir.createFhirResource).toHaveBeenCalledWith(
expect.objectContaining({
resourceType: 'Observation',
code: {
coding: expect.arrayContaining([{
code: data.observations[0].code
}])
},
valueCodeableConcept: {
coding: expect.arrayContaining([{
code: data.observations[0].valueCode
}])
}
})
);
});
});
});
4 changes: 4 additions & 0 deletions mediator/src/controllers/tests/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
deleteFhirSubscription,
createFHIRSubscriptionResource,
} from '../../utils/fhir';
import { queryCht } from '../../utils/cht';

jest.mock('../../utils/fhir');
jest.mock('../../utils/cht');
Expand All @@ -24,3 +25,6 @@ export const mockCreateFHIRSubscriptionResource =
export const mockCreateChtRecord = createChtFollowUpRecord as jest.MockedFn<
typeof createChtFollowUpRecord
>;
export const mockQueryCht = queryCht as jest.MockedFn<
typeof queryCht
>;
4 changes: 2 additions & 2 deletions mediator/src/mappers/openmrs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const openMRSIdentifierType: fhir4.CodeableConcept = {

export const openMRSSource = 'openmrs';

const visitNoteType: fhir4.CodeableConcept = {
export const visitNoteType: fhir4.CodeableConcept = {
text: "Visit Note",
coding: [{
system: "http://fhir.openmrs.org/code-system/encounter-type",
Expand All @@ -23,7 +23,7 @@ const visitNoteType: fhir4.CodeableConcept = {
}]
}

const visitType: fhir4.CodeableConcept = {
export const visitType: fhir4.CodeableConcept = {
text: "Home Visit",
coding: [{
system: "http://fhir.openmrs.org/code-system/visit-type",
Expand Down
4 changes: 4 additions & 0 deletions mediator/src/middlewares/schemas/encounter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,8 @@ export const EncounterSchema = joi.object({
type: joi.array().length(1).required(),
subject: joi.required(),
participant: joi.array().length(1).required(),
period: joi.object({
start: joi.string(),
end: joi.string()
})
});
18 changes: 18 additions & 0 deletions mediator/src/middlewares/schemas/tests/cht-request-factories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,24 @@ export const ChtPatientDoc = Factory.define('chtPatientDoc')
.attr('sex', 'female')
.attr('patient_id', randomUUID());

export const ChtSMSPatientFactory = Factory.define('chtPatient')
.attr('doc', () => ChtSMSPatientDoc.build())

export const ChtSMSPatientDoc = Factory.define('chtPatientDoc')
.attr('_id', randomUUID())
.attr('name', 'John Doe')
.attr('phone', '+9770000000')
.attr('date_of_birth', '2000-01-01')
.attr('sex', 'female')
.attr('source_id', randomUUID());

export const ChtPatientIdsFactory = Factory.define('chtPatientIds')
.attr('doc', () => ChtPatientIdsDoc.build())

export const ChtPatientIdsDoc = Factory.define('chtPatientIds')
.attr('external_id', randomUUID())
.attr('patient_uuid', randomUUID());

export const ChtPregnancyForm = Factory.define('chtPregnancyDoc')
.attr('patient_uuid', randomUUID())
.attr('reported_date', Date.now())
Expand Down
24 changes: 19 additions & 5 deletions mediator/src/middlewares/schemas/tests/fhir-resource-factories.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { randomUUID } from 'crypto';
import { Factory } from 'rosie';
import { VALID_CODE, VALID_SYSTEM } from '../endpoint';
import { chtDocumentIdentifierType } from '../../../mappers/cht';

const identifier = [
{
type: {
text: 'CHT Document Identifier'
},
type: chtDocumentIdentifierType,
system: 'cht',
value: randomUUID(),
},
Expand All @@ -28,11 +27,15 @@ export const EncounterFactory = Factory.define('encounter')
.attr('resourceType', 'Encounter')
.attr('id', randomUUID())
.attr('identifier', identifier)
.attr('status', 'planned')
.attr('status', 'finished')
.attr('class', 'outpatient')
.attr('type', [{ text: 'Community health worker visit' }])
.attr('subject', { reference: 'Patient/3' })
.attr('participant', [{ type: [{ text: 'Community health worker' }] }]);
.attr('participant', [{ type: [{ text: 'Community health worker' }] }])
.attr('period', {
start: new Date(new Date().getTime() - 60 * 60 * 1000).toISOString(),
end: new Date(new Date().getTime() - 50 * 60 * 1000).toISOString()
})

export const EndpointFactory = Factory.define('endpoint')
.attr('connectionType', { system: VALID_SYSTEM, code: VALID_CODE })
Expand All @@ -56,3 +59,14 @@ export const ServiceRequestFactory = Factory.define('serviceRequest')
.attr('intent', 'order')
.attr('subject', SubjectFactory.build())
.attr('requester', RequesterFactory.build());

export const ObservationFactory = Factory.define('Observation')
.attr('resourceType', 'Observation')
.attr('id', () => randomUUID())
.attr('encounter', () => { reference: 'Encounter/' + randomUUID() })
.attr('code', {
coding: [{ code: 'DANGER_SIGNS' }],
})
.attr('valueCodeableConcept', {
coding: [{ code: 'HIGH_BLOOD_PRESSURE' }]
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { randomUUID } from 'crypto';
import { Factory } from 'rosie';
import { visitNoteType, visitType } from '../../../mappers/openmrs';

// creates an openmrs patient with the special address extension
export const OpenMRSPatientFactory = Factory.define('openMRSFhirPatient')
.attr('resourceType', 'Patient')
.attr('id', () => randomUUID()) // Assign a random UUID for the patient
.attr('address', ['addressKey', 'addressValue'], (addressKey, addressValue) => [
{
extension: [{
extension: [
{
url: `http://fhir.openmrs.org/ext/address#${addressKey}`,
valueString: addressValue
}
]
}]
}
]);

// creates an openmrs encounter with visit type
export const OpenMRSVisitFactory = Factory.define('openMRSVisit')
.attr('resourceType', 'Encounter')
.attr('id', () => randomUUID()) // Assign a random UUID for the patient
.attr('type', visitType);

// creates an openmrs encounter with visit note type
export const OpenMRSVisitNoteFactory = Factory.define('openMRSVisit')
.attr('resourceType', 'Encounter')
.attr('id', () => randomUUID()) // Assign a random UUID for the patient
.attr('type', visitNoteType);
2 changes: 1 addition & 1 deletion mediator/src/routes/cht.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ router.post(

router.post(
'/patient_ids',
requestHandler((req) => updatePatientIds(req.body.doc))
requestHandler((req) => updatePatientIds(req.body))
);

router.post(
Expand Down
Loading
Loading