diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java index 6e2a2c84846..101da940c23 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/validation/InstanceValidator.java @@ -2639,9 +2639,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat List answers = new ArrayList(); element.getNamedChildren("answer", answers); if (inProgress) - warning(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), (answers.size() > 0) || !qItem.getRequired(), "No response answer found for required item "+qItem.getLinkId()); + warning(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), isAnswerRequirementFulfilled(qItem, answers), "No response answer found for required item "+qItem.getLinkId()); else - rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), (answers.size() > 0) || !qItem.getRequired(), "No response answer found for required item "+qItem.getLinkId()); + rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), isAnswerRequirementFulfilled(qItem, answers), "No response answer found for required item "+qItem.getLinkId()); if (answers.size() > 1) rule(errors, IssueType.INVALID, answers.get(1).line(), answers.get(1).col(), stack.getLiteralPath(), qItem.getRepeats(), "Only one response answer item with this linkId allowed"); @@ -2729,6 +2729,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } } +private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, List answers) { + return !answers.isEmpty() || !qItem.getRequired() || qItem.getType() == QuestionnaireItemType.GROUP; +} + private void validateQuestionannaireResponseItem(Questionnaire qsrc, QuestionnaireItemComponent qItem, List errors, List elements, NodeStack stack, boolean inProgress, Element questionnaireResponseRoot) { if (elements.size() > 1) rule(errors, IssueType.INVALID, elements.get(1).line(), elements.get(1).col(), stack.getLiteralPath(), qItem.getRepeats(), "Only one response item with this linkId allowed - " + qItem.getLinkId()); diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireResponseValidatorDstu3Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireResponseValidatorDstu3Test.java index 463bec25368..7a535344c0e 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireResponseValidatorDstu3Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/QuestionnaireResponseValidatorDstu3Test.java @@ -22,6 +22,7 @@ import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseItemC import org.hl7.fhir.dstu3.model.QuestionnaireResponse.QuestionnaireResponseStatus; import org.junit.AfterClass; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import java.util.ArrayList; @@ -40,17 +41,23 @@ import static org.mockito.Mockito.reset; import static org.mockito.Mockito.when; public class QuestionnaireResponseValidatorDstu3Test { + private static final String QUESTIONNAIRE_URL = "http://example.com/Questionnaire/q1"; public static final IdType ID_ICC_QUESTIONNAIRE_SETUP = new IdType("Questionnaire/profile"); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(QuestionnaireResponseValidatorDstu3Test.class); private static final String CODE_ICC_SCHOOLTYPE_PT = "PT"; private static final IdType ID_VS_SCHOOLTYPE = new IdType("ValueSet/schooltype"); private static final String SYSTEMURI_ICC_SCHOOLTYPE = "http://ehealthinnovation/icc/ns/schooltype"; private static DefaultProfileValidationSupport myDefaultValidationSupport = new DefaultProfileValidationSupport(); - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static FhirContext ourCtx; private FhirInstanceValidator myInstanceVal; private FhirValidator myVal; private IValidationSupport myValSupport; + @BeforeClass + public static void beforeClass() { + ourCtx = FhirContext.forDstu3(); + } + @Before public void before() { myValSupport = mock(IValidationSupport.class); @@ -122,7 +129,7 @@ public class QuestionnaireResponseValidatorDstu3Test { answerValues[11] = new Coding().setSystem("http://codesystems.com/system").setCode("code0"); answerValues[12] = new StringType("some value"); answerValues[13] = new Attachment().setData("some data".getBytes()).setContentType("txt"); - answerValues[14] = new Reference("http://example.com/Questionnaire/q1"); + answerValues[14] = new Reference(QUESTIONNAIRE_URL); answerValues[15] = new Quantity(42); for (int i = 0; i < itemCnt; i++) { @@ -132,7 +139,7 @@ public class QuestionnaireResponseValidatorDstu3Test { reset(myValSupport); Questionnaire q = new Questionnaire(); when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), - eq("http://example.com/Questionnaire/q1"))).thenReturn(q); + eq(QUESTIONNAIRE_URL))).thenReturn(q); when(myValSupport.fetchCodeSystem(any(FhirContext.class), eq("http://codesystems.com/system"))).thenReturn(codeSystem); when(myValSupport.fetchResource(any(FhirContext.class), eq(ValueSet.class), eq("http://somevalueset"))).thenReturn(options); myInstanceVal.flushCaches(); @@ -149,7 +156,7 @@ public class QuestionnaireResponseValidatorDstu3Test { QuestionnaireResponse qa = new QuestionnaireResponse(); qa.setStatus(QuestionnaireResponseStatus.INPROGRESS); - qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL); qa.addItem().setLinkId(linkId).addAnswer().setValue(answerValues[i]); ValidationResult errors = myVal.validateWithResult(qa); @@ -165,7 +172,7 @@ public class QuestionnaireResponseValidatorDstu3Test { QuestionnaireResponse qa = new QuestionnaireResponse(); qa.setStatus(QuestionnaireResponseStatus.COMPLETED); - qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL); qa.addItem().setLinkId("link0").addAnswer().setValue(new StringType("FOO")); when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(qa.getQuestionnaire().getReference()))).thenReturn(q); @@ -178,11 +185,11 @@ public class QuestionnaireResponseValidatorDstu3Test { @Test public void testCodedAnswer() { - String questionnaireRef = "http://example.com/Questionnaire/q1"; + String questionnaireRef = QUESTIONNAIRE_URL; Questionnaire q = new Questionnaire(); q.addItem().setLinkId("link0").setRequired(false).setType(QuestionnaireItemType.CHOICE).setOptions(new Reference("http://somevalueset")); - when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq("http://example.com/Questionnaire/q1"))).thenReturn(q); + when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(QUESTIONNAIRE_URL))).thenReturn(q); CodeSystem codeSystem = new CodeSystem(); codeSystem.setContent(CodeSystemContentMode.COMPLETE); @@ -246,7 +253,7 @@ public class QuestionnaireResponseValidatorDstu3Test { QuestionnaireResponse qa = new QuestionnaireResponse(); qa.setStatus(QuestionnaireResponseStatus.COMPLETED); - qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL); QuestionnaireResponseItemComponent qaGroup = qa.addItem(); qaGroup.addItem().setLinkId("link0").addAnswer().setValue(new StringType("FOO")); @@ -256,6 +263,43 @@ public class QuestionnaireResponseValidatorDstu3Test { ourLog.info(errors.toString()); assertThat(errors.toString(), containsString("No LinkId, so can't be validated")); } + + @Test + public void testMissingAnswerInNestedStructureIsReported() throws Exception { + Questionnaire q = new Questionnaire(); + q.addItem().setType(QuestionnaireItemType.GROUP).setRequired(true) + .addItem().setType(QuestionnaireItemType.GROUP).setRequired(true) + .addItem().setType(QuestionnaireItemType.BOOLEAN).setLinkId("link0").setRequired(true); + + QuestionnaireResponse qa = new QuestionnaireResponse(); + qa.setStatus(QuestionnaireResponseStatus.COMPLETED); + qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL); + + when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(qa.getQuestionnaire().getReference()))).thenReturn(q); + + ValidationResult errors = myVal.validateWithResult(qa); + + assertThat(errors.toString(), Matchers.not(containsString("No issues"))); + } + + @Test + public void testGroupMarkedAsRequiredIsOk() throws Exception { + Questionnaire q = new Questionnaire(); + q.addItem().setType(QuestionnaireItemType.GROUP).setRequired(true).setLinkId("link1") + .addItem().setType(QuestionnaireItemType.BOOLEAN).setLinkId("link0").setRequired(true); + + QuestionnaireResponse qa = new QuestionnaireResponse(); + qa.setStatus(QuestionnaireResponseStatus.COMPLETED); + qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL); + qa.addItem().setLinkId("link1") + .addItem().setLinkId("link0").addAnswer().setValue(new BooleanType(true)); + + when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(qa.getQuestionnaire().getReference()))).thenReturn(q); + + ValidationResult errors = myVal.validateWithResult(qa); + + assertThat(errors.toString(), containsString("No issues")); + } @Test public void testItemWithNoType() { @@ -266,7 +310,7 @@ public class QuestionnaireResponseValidatorDstu3Test { QuestionnaireResponse qa = new QuestionnaireResponse(); qa.setStatus(QuestionnaireResponseStatus.COMPLETED); - qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL); QuestionnaireResponseItemComponent qaGroup = qa.addItem().setLinkId("link0"); qaGroup.addItem().setLinkId("link1").addAnswer().setValue(new StringType("FOO")); @@ -287,7 +331,7 @@ public class QuestionnaireResponseValidatorDstu3Test { QuestionnaireResponse qa = new QuestionnaireResponse(); qa.setStatus(QuestionnaireResponseStatus.COMPLETED); - qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL); qa.addItem().setLinkId("link1").addAnswer().setValue(new StringType("FOO")); String reference = qa.getQuestionnaire().getReference(); @@ -316,8 +360,7 @@ public class QuestionnaireResponseValidatorDstu3Test { QuestionnaireResponse qa = new QuestionnaireResponse(); qa.setStatus(QuestionnaireResponseStatus.COMPLETED); - qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); - qa.addItem().setLinkId("link0").addAnswer().setValue(new StringType("FOO")); + qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL); String reference = qa.getQuestionnaire().getReference(); when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(reference))).thenReturn(q); @@ -345,7 +388,7 @@ public class QuestionnaireResponseValidatorDstu3Test { QuestionnaireResponse qa = new QuestionnaireResponse(); qa.setStatus(QuestionnaireResponseStatus.COMPLETED); - qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL); String reference = qa.getQuestionnaire().getReference(); when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(reference))).thenReturn(q); @@ -372,7 +415,7 @@ public class QuestionnaireResponseValidatorDstu3Test { QuestionnaireResponse qa = new QuestionnaireResponse(); qa.setStatus(QuestionnaireResponseStatus.COMPLETED); - qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL); qa.addItem().setLinkId("link0").addAnswer().setValue(new Quantity().setValue(1L)); String reference = qa.getQuestionnaire().getReference(); @@ -400,7 +443,7 @@ public class QuestionnaireResponseValidatorDstu3Test { QuestionnaireResponse qa = new QuestionnaireResponse(); qa.setStatus(QuestionnaireResponseStatus.COMPLETED); - qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL); qa.addItem().setLinkId("link0").addAnswer().setValue(new Quantity().setValue(1L)); String reference = qa.getQuestionnaire().getReference(); @@ -428,7 +471,7 @@ public class QuestionnaireResponseValidatorDstu3Test { QuestionnaireResponse qa = new QuestionnaireResponse(); qa.setStatus(QuestionnaireResponseStatus.COMPLETED); - qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL); qa.addItem().setLinkId("link0").addAnswer().setValue(new StringType("FOO")); qa.addItem().setLinkId("link1").addAnswer().setValue(new StringType("BAR")); @@ -458,7 +501,7 @@ public class QuestionnaireResponseValidatorDstu3Test { QuestionnaireResponse qa = new QuestionnaireResponse(); qa.setStatus(QuestionnaireResponseStatus.COMPLETED); - qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL); // link1 should be disabled, because the enableWhen enables it when link0 doesn't haven an answer qa.addItem().setLinkId("link0").addAnswer().setValue(new StringType("FOO")); @@ -479,7 +522,7 @@ public class QuestionnaireResponseValidatorDstu3Test { QuestionnaireResponse qr = new QuestionnaireResponse(); qr.setStatus(QuestionnaireResponseStatus.COMPLETED); - qr.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qr.getQuestionnaire().setReference(QUESTIONNAIRE_URL); qr.addItem().setLinkId("link2").addAnswer().setValue(new StringType("FOO")); @@ -499,7 +542,7 @@ public class QuestionnaireResponseValidatorDstu3Test { QuestionnaireResponse qr = new QuestionnaireResponse(); qr.setStatus(QuestionnaireResponseStatus.COMPLETED); - qr.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qr.getQuestionnaire().setReference(QUESTIONNAIRE_URL); qr.addItem().setLinkId("link0").setText("Text"); qr.addItem().setLinkId("link1").addAnswer().setValue(new StringType("Answer")); String reference = qr.getQuestionnaire().getReference(); @@ -514,7 +557,7 @@ public class QuestionnaireResponseValidatorDstu3Test { @Test public void testEmbeddedItemInChoice() { - String questionnaireRef = "http://example.com/Questionnaire/q1"; + String questionnaireRef = QUESTIONNAIRE_URL; String valueSetRef = "http://somevalueset"; String codeSystemUrl = "http://codesystems.com/system"; String codeValue = "code0"; @@ -570,7 +613,7 @@ public class QuestionnaireResponseValidatorDstu3Test { @Test public void testEmbeddedItemInOpenChoice() { - String questionnaireRef = "http://example.com/Questionnaire/q1"; + String questionnaireRef = QUESTIONNAIRE_URL; String valueSetRef = "http://somevalueset"; String codeSystemUrl = "http://codesystems.com/system"; String codeValue = "code0"; @@ -626,7 +669,7 @@ public class QuestionnaireResponseValidatorDstu3Test { @Test public void testEmbeddedItemInString() { - String questionnaireRef = "http://example.com/Questionnaire/q1"; + String questionnaireRef = QUESTIONNAIRE_URL; // create the questionnaire QuestionnaireItemComponent item1 = new QuestionnaireItemComponent(); @@ -727,7 +770,7 @@ public class QuestionnaireResponseValidatorDstu3Test { .setType(QuestionnaireItemType.STRING) .setRequired(true); - String reference = "http://example.com/Questionnaire/q1"; + String reference = QUESTIONNAIRE_URL; when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(reference))) .thenReturn(q); @@ -751,7 +794,7 @@ public class QuestionnaireResponseValidatorDstu3Test { @Test public void testOpenchoiceAnswer() { - String questionnaireRef = "http://example.com/Questionnaire/q1"; + String questionnaireRef = QUESTIONNAIRE_URL; Questionnaire q = new Questionnaire(); QuestionnaireItemComponent item = q.addItem(); @@ -874,7 +917,7 @@ public class QuestionnaireResponseValidatorDstu3Test { QuestionnaireResponse qa = new QuestionnaireResponse(); qa.setStatus(QuestionnaireResponseStatus.COMPLETED); - qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL); qa.addItem().setLinkId("link1").addAnswer().setValue(new StringType("FOO")); when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(qa.getQuestionnaire().getReference()))).thenReturn(q); @@ -892,7 +935,7 @@ public class QuestionnaireResponseValidatorDstu3Test { QuestionnaireResponse qa = new QuestionnaireResponse(); qa.setStatus(QuestionnaireResponseStatus.COMPLETED); - qa.getQuestionnaire().setReference("http://example.com/Questionnaire/q1"); + qa.getQuestionnaire().setReference(QUESTIONNAIRE_URL); qa.addItem().setLinkId("link1").addItem().setLinkId("link2"); when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(qa.getQuestionnaire().getReference()))).thenReturn(q);