Skip to content

Commit

Permalink
test: [TECH-887] improve rules engine unit test coverage (#3462)
Browse files Browse the repository at this point in the history
  • Loading branch information
simonadomnisoru authored Dec 4, 2023
1 parent efa835e commit a47e429
Show file tree
Hide file tree
Showing 11 changed files with 4,522 additions and 406 deletions.
2 changes: 1 addition & 1 deletion packages/rules-engine/src/RulesEngine.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// @flow
import log from 'loglevel';
import { VariableService } from './services/VariableService/VariableService';
import { VariableService } from './services/VariableService';
import { ValueProcessor } from './processors/ValueProcessor';
import { executeExpression } from './services/expressionService';
import { getD2Functions } from './d2Functions';
Expand Down
2 changes: 1 addition & 1 deletion packages/rules-engine/src/helpers/previousValueCheck.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export const getOutputEffectsWithPreviousValueCheck = ({
onProcessValue: (value: any, type: $Values<typeof typeKeys>) => any,
}) =>
outputEffects.reduce((acc, outputEffect) => {
if (formValues && outputEffect.targetDataType) {
if (formValues && Object.keys(formValues).length !== 0 && outputEffect.targetDataType) {
const formValue = formValues[outputEffect.id];
const rawValue = mapByTargetDataTypes[outputEffect.targetDataType]({
dataElementId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,26 +296,23 @@ export function getRulesEffectsProcessor(
formValues,
onProcessValue,
}: {
effects: ?Array<ProgramRuleEffect>,
effects: Array<ProgramRuleEffect>,
dataElements: ?DataElements,
trackedEntityAttributes: ?TrackedEntityAttributes,
formValues?: ?{ [key: string]: any },
onProcessValue: (value: any, type: $Values<typeof typeKeys>) => any,
}): OutputEffects {
if (effects) {
return effects
.filter(({ action }) => mapActionsToProcessor[action])
.flatMap(effect => mapActionsToProcessor[effect.action](
effect,
dataElements,
trackedEntityAttributes,
formValues,
onProcessValue,
))
// when mapActionsToProcessor function returns `null` we filter those value out.
.filter(keepTruthyValues => keepTruthyValues);
}
return [];
return effects
.filter(({ action }) => mapActionsToProcessor[action])
.flatMap(effect => mapActionsToProcessor[effect.action](
effect,
dataElements,
trackedEntityAttributes,
formValues,
onProcessValue,
))
// when mapActionsToProcessor function returns `null` we filter those value out.
.filter(keepTruthyValues => keepTruthyValues);
}

return processRulesEffects;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ export const getStructureEvents = (compareDates: CompareDates) => {
event.eventId !== currentEvent.eventId,
);

const events = [...otherEventsFiltered, currentEvent]
.sort(compareEvents);
const events = Object.keys(currentEvent).length !== 0 ? otherEventsFiltered.concat(currentEvent) : otherEventsFiltered;
const sortedEvents = events.sort(compareEvents);

return createEventsContainer(events);
return createEventsContainer(sortedEvents);
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ Object {
"trackedEntityAttributes": Object {
"lZGmxYbs96q": Object {
"id": "lZGmxYbs96q",
"optionSetId": undefined,
"optionSetId": "optionSet",
"valueType": "DATE",
},
"lZGmxYbs97q": Object {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { variableSourceTypes } from '@dhis2/rules-engine-javascript';
import {
TrackerProgram,
ProgramStage,
Expand All @@ -10,24 +11,27 @@ import {
} from '../../metaData';
import { getApplicableRuleEffectsForTrackerProgram } from '..';

const mockGetProgramRuleEffects = jest.fn().mockImplementation(() => [{
id: 'effectId',
type: 'DISPLAYTEXT',
message: 'display effect',
}]);
const mockGetProgramRuleEffects = jest.fn().mockImplementation(() => [
{
id: 'effectId',
type: 'DISPLAYTEXT',
message: 'display effect',
},
]);

const mockOptionSet = new OptionSet('optionSet1', [new Option('option1', 'opt1')]);
jest.mock('@dhis2/rules-engine-javascript/build/cjs/RulesEngine', () => ({
RulesEngine: jest.fn().mockImplementation(() =>
({ getProgramRuleEffects: (...args) => mockGetProgramRuleEffects(...args) })),
RulesEngine: jest
.fn()
.mockImplementation(() => ({ getProgramRuleEffects: (...args) => mockGetProgramRuleEffects(...args) })),
}));

jest.mock('../../metaDataMemoryStores/constants/constants.store', () => ({
constantsStore: ({ get: () => [{ id: 'constantId1', value: '1' }] }),
constantsStore: { get: () => [{ id: 'constantId1', value: '1' }] },
}));

jest.mock('../../metaDataMemoryStores/optionSets/optionSets.store', () => ({
optionSetStore: ({ get: () => [mockOptionSet] }),
optionSetStore: { get: () => [mockOptionSet] },
}));

describe('getApplicableRuleEffectsForTrackerProgram', () => {
Expand All @@ -46,20 +50,22 @@ describe('getApplicableRuleEffectsForTrackerProgram', () => {
trackedEntityInstanceId: 'vCGpQAWG17I',
};

const otherEvents = [{
da1Id: 'otherEventText',
dueDate: '2021-05-31T09:51:38.134',
enrollmentId: 'vVtmDlsu3me',
enrollmentStatus: 'ACTIVE',
eventDate: '2021-05-31T00:00:00.000',
eventId: 'BxGzDJK3JqN',
orgUnitId: 'DiszpKrYNg8',
orgUnitName: 'Ngelehun CHC',
programId: 'IpHINAT79UW',
programStageId: 'A03MvHHogjR',
status: 'ACTIVE',
trackedEntityInstanceId: 'vCGpQAWG17I',
}];
const otherEvents = [
{
da1Id: 'otherEventText',
dueDate: '2021-05-31T09:51:38.134',
enrollmentId: 'vVtmDlsu3me',
enrollmentStatus: 'ACTIVE',
eventDate: '2021-05-31T00:00:00.000',
eventId: 'BxGzDJK3JqN',
orgUnitId: 'DiszpKrYNg8',
orgUnitName: 'Ngelehun CHC',
programId: 'IpHINAT79UW',
programStageId: 'A03MvHHogjR',
status: 'ACTIVE',
trackedEntityInstanceId: 'vCGpQAWG17I',
},
];

const orgUnit = { id: 'DiszpKrYNg8', code: 'Ngelehun CHC' };

Expand All @@ -72,16 +78,18 @@ describe('getApplicableRuleEffectsForTrackerProgram', () => {
const programStage = new ProgramStage((stage) => {
stage.id = 'st1Id';
stage.name = 'stage1';
stage.programRules = [{
id: 'rule1Id',
name: 'rule1',
displayName: 'rule1',
priority: 1,
condition: 'true',
programId: 'IpHINAT79UW',
programStageId: 'st1Id',
programRuleActions: [],
}];
stage.programRules = [
{
id: 'rule1Id',
name: 'rule1',
displayName: 'rule1',
priority: 1,
condition: 'true',
programId: 'IpHINAT79UW',
programStageId: 'st1Id',
programRuleActions: [],
},
];

stage.stageForm = new RenderFoundation((foundation) => {
const section = new Section((initSection) => {
Expand Down Expand Up @@ -115,6 +123,7 @@ describe('getApplicableRuleEffectsForTrackerProgram', () => {
element3.id = 'lZGmxYbs96q';
element3.name = 'SomeDate';
element3.type = dataElementTypes.DATE;
element3.optionSet = { id: 'optionSet', name: 'optionSet', code: 'optionSet' };
}),
new DataElement((element4) => {
element4.id = 'w75KJ2mc4zz';
Expand All @@ -129,39 +138,43 @@ describe('getApplicableRuleEffectsForTrackerProgram', () => {
displayName: 'Test',
id: 'PUQZWgmQ0jx',
programId: 'IpHINAT79UW',
programRuleVariableSourceType: 'DATAELEMENT_NEWEST_EVENT_PROGRAM',
programRuleVariableSourceType: variableSourceTypes.DATAELEMENT_NEWEST_EVENT_PROGRAM,
useNameForOptionSet: true,
},
{
dataElementId: 'H6uSAMO5WLD',
displayName: 'apgarcomment',
id: 'aKpfPKSRQnv',
programId: 'IpHINAT79UW',
programRuleVariableSourceType: 'DATAELEMENT_NEWEST_EVENT_PROGRAM',
programRuleVariableSourceType: variableSourceTypes.DATAELEMENT_NEWEST_EVENT_PROGRAM,
useNameForOptionSet: true,
},
{
dataElementId: 'a3kGcGDCuk6',
displayName: 'apgarscore',
id: 'g2GooOydipB',
programId: 'IpHINAT79UW',
programRuleVariableSourceType: 'DATAELEMENT_NEWEST_EVENT_PROGRAM',
programRuleVariableSourceType: variableSourceTypes.DATAELEMENT_NEWEST_EVENT_PROGRAM,
useNameForOptionSet: true,
},
];

initProgram.programRules = [{
condition: 'true',
displayName: 'TestRule',
id: 'JJDQxgHuuL2',
programId: 'IpHINAT79UW',
programRuleActions: [{
data: '#{Test}',
id: 'CQaifjkoFEU',
location: 'feedback',
programRuleActionType: 'DISPLAYTEXT',
}],
}];
initProgram.programRules = [
{
condition: 'true',
displayName: 'TestRule',
id: 'JJDQxgHuuL2',
programId: 'IpHINAT79UW',
programRuleActions: [
{
data: '#{Test}',
id: 'CQaifjkoFEU',
location: 'feedback',
programRuleActionType: 'DISPLAYTEXT',
},
],
},
];
});

const attributeValues = {
Expand Down Expand Up @@ -210,16 +223,51 @@ describe('getApplicableRuleEffectsForTrackerProgram', () => {
});

test('Flat result', () => {
const effects = getApplicableRuleEffectsForTrackerProgram(
{
program,
stage: programStage,
orgUnit,
currentEvent,
otherEvents,
attributeValues,
enrollmentData,
},
true,
);

expect(Array.isArray(effects)).toBe(true);
});

test('RulesEngine called without programRules', () => {
const effects = getApplicableRuleEffectsForTrackerProgram({
program: new TrackerProgram((initProgram) => {
initProgram.programRules = [];
}),
stage: new ProgramStage((stage) => {
stage.programRules = [];
}),
orgUnit,
currentEvent,
otherEvents,
attributeValues,
enrollmentData,
});

expect(effects).toStrictEqual([]);
});

test('currentEvent without a programStageId', () => {
const effects = getApplicableRuleEffectsForTrackerProgram({
program,
stage: programStage,
orgUnit,
currentEvent,
currentEvent: {},
otherEvents,
attributeValues,
enrollmentData,
}, true);
});

expect(Array.isArray(effects)).toBe(true);
expect(effects.DISPLAYTEXT).toBeDefined();
});
});
Loading

0 comments on commit a47e429

Please sign in to comment.