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

MAT-7801: handle possible null expression result type when building expressionToReturnTypeMap #31

Merged
merged 1 commit into from
Jan 8, 2025
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
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>gov.cms.madie</groupId>
<artifactId>madie-translator-commons</artifactId>
<version>2.0.2-SNAPSHOT</version>
<version>2.0.3-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>17</java.version>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -398,12 +398,13 @@ private void collectReturnTypeMap() {
this.allNamesToReturnTypeMap.put(libraryName + "-" + libraryVersion, new HashMap<>());

for (ExpressionDef expression : statements.getDef()) {
String resultType =
expression.getResultType() == null ? null : expression.getResultType().toString();
this.allNamesToReturnTypeMap
.get(libraryName + "-" + libraryVersion)
.put(expression.getName(), expression.getResultType().toString());
this.nameToReturnTypeMap.put(expression.getName(), expression.getResultType().toString());
this.expressionToReturnTypeMap.put(
expression.getName(), expression.getResultType().toString());
.put(expression.getName(), resultType);
this.nameToReturnTypeMap.put(expression.getName(), resultType);
this.expressionToReturnTypeMap.put(expression.getName(), resultType);
}

if (parameters != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,29 @@
package gov.cms.madie.cql_elm_translator.utils.cql;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import lombok.extern.slf4j.Slf4j;
import org.cqframework.cql.cql2elm.CqlTranslator;
import org.cqframework.cql.cql2elm.model.CompiledLibrary;
import org.cqframework.cql.cql2elm.model.ResolvedIdentifierContext;
import org.hl7.cql.model.DataType;
import org.hl7.elm.r1.IncludeDef;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
Expand All @@ -26,13 +37,16 @@
import org.hl7.elm.r1.ExpressionDef;

@ExtendWith(MockitoExtension.class)
@Slf4j
class CQLToolsTest {

@Mock private CqlTranslator cqlTranslator;
@Mock private CompiledLibrary compiledLibrary;
@Mock private Library library;
@Mock private ResolvedIdentifierContext element;
@Mock private Statements statements;
@Mock private Library.Includes includes;
@Mock private Map<String, CompiledLibrary> compiledLibraryMap;

VersionedIdentifier versionedIdentifier = new VersionedIdentifier();

Expand All @@ -59,4 +73,66 @@ void testGenerate() {
fail();
}
}

@Test
void testItHandlesNullExpressionResultType() {
versionedIdentifier.setId("local");
String cqlData = ResourceUtils.getData("/tooling_test_qicore6.cql");
doReturn(statements).when(library).getStatements();
doReturn(compiledLibrary).when(cqlTranslator).getTranslatedLibrary();
doReturn(library).when(compiledLibrary).getLibrary();
doReturn(versionedIdentifier).when(compiledLibrary).getIdentifier();
doReturn(element).when(compiledLibrary).resolve(any(String.class));
doReturn(includes).when(library).getIncludes();

ExpressionDef expression1 = mock(ExpressionDef.class);
DataType booleanDataType = mock(DataType.class);
doReturn(booleanDataType).when(expression1).getResultType();
doReturn("expression1").when(expression1).getName();
doReturn("System.Boolean").when(booleanDataType).toString();

ExpressionDef expression2 = mock(ExpressionDef.class);
doReturn("expression2").when(expression2).getName();
doReturn(null).when(expression2).getResultType();

List<ExpressionDef> expressionDefs = List.of(expression1, expression2);

doReturn(expressionDefs).when(statements).getDef();

Set<String> parentExpressions = new HashSet<>();
IncludeDef includeDef1 = mock(IncludeDef.class);
List<IncludeDef> includeDefs = List.of(includeDef1);
doReturn(includeDefs).when(includes).getDef();
doReturn("SupplementalDataElements").when(includeDef1).getPath();
CompiledLibrary sdeCompiledLib = mock(CompiledLibrary.class);
Library sdeLib = mock(Library.class);
doReturn(sdeLib).when(sdeCompiledLib).getLibrary();
VersionedIdentifier sdeLibVersionedIdentifier = new VersionedIdentifier();
sdeLibVersionedIdentifier.setId("SupplementalDataElements");
doReturn(sdeLibVersionedIdentifier).when(sdeCompiledLib).getIdentifier();

Library.Statements statements1 = mock(Library.Statements.class);
doReturn(statements1).when(sdeLib).getStatements();
Library.Parameters parameters1 = mock(Library.Parameters.class);
doReturn(parameters1).when(sdeLib).getParameters();

doReturn(sdeCompiledLib).when(compiledLibraryMap).get(anyString());

CQLTools cqlTools =
new CQLTools(cqlData, null, parentExpressions, cqlTranslator, compiledLibraryMap);
assertNotNull(cqlTools);
try {
cqlTools.generate();
Set<DefinitionContent> contents = cqlTools.getDefinitionContents();
assertNotNull(contents);
Map<String, String> expressionToReturnTypeMap = cqlTools.getExpressionToReturnTypeMap();
assertThat(expressionToReturnTypeMap, is(notNullValue()));
assertThat(expressionToReturnTypeMap.containsKey("expression1"), is(true));
assertThat(expressionToReturnTypeMap.containsKey("expression2"), is(true));
assertThat(expressionToReturnTypeMap.get("expression1"), is(equalTo("System.Boolean")));
assertThat(expressionToReturnTypeMap.get("expression2"), is(nullValue()));
} catch (IOException e) {
fail();
}
}
}
61 changes: 61 additions & 0 deletions src/test/resources/tooling_test_qicore6.cql
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
library QM61 version '0.0.000'

using QICore version '6.0.0'

include QICoreCommon version '3.0.000' called QICoreCommon
include FHIRHelpers version '4.4.000' called FHIRHelpers
include SupplementalDataElements version '4.3.000' called SDE
include Hospice version '7.0.000' called Hospice
include Status version '2.0.000' called Status

codesystem "LOINC": 'http://loinc.org'
codesystem "SNOMEDCT": 'http://snomed.info/sct'

valueset "Clinical Oral Evaluation": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.125.12.1003'
valueset "Dental Caries": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.125.12.1004'
valueset "Discharged to Health Care Facility for Hospice Care": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.117.1.7.1.207'
valueset "Discharged to Home for Hospice Care": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.117.1.7.1.209'
valueset "Encounter Inpatient": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.666.5.307'

code "Discharge to healthcare facility for hospice care (procedure)": '428371000124100' from "SNOMEDCT" display 'Discharge to healthcare facility for hospice care (procedure)'
code "Discharge to home for hospice care (procedure)": '428361000124107' from "SNOMEDCT" display 'Discharge to home for hospice care (procedure)'
code "Hospice care [Minimum Data Set]": '45755-6' from "LOINC" display 'Hospice care [Minimum Data Set]'
code "Yes (qualifier value)": '373066001' from "SNOMEDCT" display 'Yes (qualifier value)'

parameter "Measurement Period" Interval<DateTime>

context Patient


define "Initial Population":
AgeInYearsAt(date from start of "Measurement Period")in Interval[1, 20]
and exists ( "Qualifying Encounters" )

define "Qualifying Encounters":
(([Encounter: "Clinical Oral Evaluation"]).isEncounterPerformed()) ValidEncounter
where ValidEncounter.period.toInterval() during day of "Measurement Period"

define "Denominator":
"Initial Population"

define "Denominator Exclusions":
Hospice."Has Hospice Services"

define "Numerator":
exists ["QICore Condition Problems Health Concerns": "Dental Caries"] DentalCaries
where DentalCaries.prevalenceInterval() overlaps "Measurement Period"

define "SDE Ethnicity":
SDE."SDE Ethnicity"

define "SDE Payer":
SDE."SDE Payer"

define "SDE Race":
SDE."SDE Race"

define "SDE Sex":
SDE."SDE Sex"

define "SDE Eth":
SDE."SDE Eth"
Loading