diff --git a/modules/form-completion-status/pom.xml b/modules/form-completion-status/pom.xml index 0604c56e9f..f6b4d1a844 100644 --- a/modules/form-completion-status/pom.xml +++ b/modules/form-completion-status/pom.xml @@ -30,7 +30,7 @@ Flags form answers as incomplete or invalid - 0.34 + 0.88 diff --git a/modules/form-completion-status/src/test/java/io/uhndata/cards/formcompletionstatus/ConditionalSectionUtilsTest.java b/modules/form-completion-status/src/test/java/io/uhndata/cards/formcompletionstatus/ConditionalSectionUtilsTest.java new file mode 100644 index 0000000000..80d3bf9e42 --- /dev/null +++ b/modules/form-completion-status/src/test/java/io/uhndata/cards/formcompletionstatus/ConditionalSectionUtilsTest.java @@ -0,0 +1,638 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.uhndata.cards.formcompletionstatus; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState; +import org.apache.jackrabbit.oak.spi.state.NodeBuilder; +import org.apache.jackrabbit.oak.spi.state.NodeState; +import org.apache.sling.testing.mock.sling.ResourceResolverType; +import org.apache.sling.testing.mock.sling.junit.SlingContext; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.runners.MockitoJUnitRunner; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Unit tests for {@link ConditionalSectionUtils}. + * + * @version $Id$ + */ +@RunWith(MockitoJUnitRunner.class) +public class ConditionalSectionUtilsTest +{ + private static final String NODE_TYPE = "jcr:primaryType"; + private static final String FORM_TYPE = "cards:Form"; + private static final String SUBJECT_TYPE = "cards:Subject"; + private static final String ANSWER_SECTION_TYPE = "cards:AnswerSection"; + private static final String ANSWER_TYPE = "cards:Answer"; + private static final String CONDITIONAL_TYPE = "cards:Conditional"; + private static final String CONDITIONAL_VALUE_TYPE = "cards:ConditionalValue"; + private static final String CONDITIONAL_GROUP_TYPE = "cards:ConditionalGroup"; + private static final String QUESTIONNAIRE_PROPERTY = "questionnaire"; + private static final String QUESTION_PROPERTY = "question"; + private static final String SUBJECT_PROPERTY = "subject"; + private static final String SECTION_PROPERTY = "section"; + private static final String VALUE_PROPERTY = "value"; + private static final String DECIMAL_DATA_TYPE = "decimal"; + private static final String DATE_DATA_TYPE = "date"; + private static final String LONG_DATA_TYPE = "long"; + private static final String BOOLEAN_DATA_TYPE = "boolean"; + private static final String TEXT_DATA_TYPE = "text"; + private static final String DOUBLE_DATA_TYPE = "double"; + + private static final String TEST_QUESTION_NAME = "question_1"; + private static final String TEST_ANSWER_SECTION_NAME = "answerSection_1"; + private static final String TEST_QUESTIONNAIRE_PATH = "/Questionnaires/TestQuestionnaire"; + private static final String TEST_SECTION_PATH = "/Questionnaires/TestQuestionnaire/section_1"; + private static final String TEST_SUBJECT_PATH = "/Subjects/Test"; + private static final String TEST_QUESTION_PATH = TEST_QUESTIONNAIRE_PATH + "/" + TEST_QUESTION_NAME; + + @Rule + public SlingContext context = new SlingContext(ResourceResolverType.JCR_OAK); + + private Session session; + + @Test + public void isConditionSatisfiedForSectionWithoutConditionalReturnsTrue() + throws RepositoryException + { + boolean comparableValue = true; + createQuestion(TEST_QUESTION_PATH, "Must be true - Question", BOOLEAN_DATA_TYPE); + NodeBuilder form = getFormNodeBuilderWithAnswerAndAnswerSectionChildren(comparableValue); + boolean isConditionSatisfied = ConditionalSectionUtils.isConditionSatisfied(this.session, + form.getChildNode(TEST_ANSWER_SECTION_NAME), form); + assertTrue(isConditionSatisfied); + } + + @Test + public void isConditionSatisfiedForConditionalNodeAndBooleanTypeAnswerAndEqualComparatorReturnsFalse() + throws RepositoryException + { + boolean comparableValue = true; + boolean actualValue = false; + + createQuestion(TEST_QUESTION_PATH, "Must be true - Question", BOOLEAN_DATA_TYPE); + createConditional(TEST_SECTION_PATH + "/conditional_1", "=", BOOLEAN_DATA_TYPE, TEST_QUESTION_NAME, + comparableValue); + + NodeBuilder form = getFormNodeBuilderWithAnswerAndAnswerSectionChildren(actualValue); + boolean isConditionSatisfied = ConditionalSectionUtils.isConditionSatisfied(this.session, + form.getChildNode(TEST_ANSWER_SECTION_NAME), form); + assertFalse(isConditionSatisfied); + } + + @Test + public void isConditionSatisfiedForConditionalNodeAndBooleanTypeAnswerAndEqualComparatorReturnsTrue() + throws RepositoryException + { + boolean comparableValue = true; + + createQuestion(TEST_QUESTION_PATH, "Must be true - Question", BOOLEAN_DATA_TYPE); + createConditional(TEST_SECTION_PATH + "/conditional_1", "=", BOOLEAN_DATA_TYPE, TEST_QUESTION_NAME, + comparableValue); + + NodeBuilder form = getFormNodeBuilderWithAnswerAndAnswerSectionChildren(comparableValue); + boolean isConditionSatisfied = ConditionalSectionUtils.isConditionSatisfied(this.session, + form.getChildNode(TEST_ANSWER_SECTION_NAME), form); + assertTrue(isConditionSatisfied); + } + + @Test + public void isConditionSatisfiedForConditionalNodeAndStringBooleanTypeAnswerAndNotEqualComparatorReturnsTrue() + throws RepositoryException + { + String comparableValue = "false"; + boolean actualValue = true; + + createQuestion(TEST_QUESTION_PATH, "Must be more not false - Question", BOOLEAN_DATA_TYPE); + createConditional(TEST_SECTION_PATH + "/conditional_1", "<>", BOOLEAN_DATA_TYPE, TEST_QUESTION_NAME, + comparableValue); + + NodeBuilder form = getFormNodeBuilderWithAnswerAndAnswerSectionChildren(actualValue); + boolean isConditionSatisfied = ConditionalSectionUtils.isConditionSatisfied(this.session, + form.getChildNode(TEST_ANSWER_SECTION_NAME), form); + assertTrue(isConditionSatisfied); + } + + @Test + public void isConditionSatisfiedForConditionalNodeAndTextTypeAnswerAndIsNotEmptyComparatorReturnsTrue() + throws RepositoryException + { + String comparableValue = "someText"; + + createQuestion(TEST_QUESTION_PATH, "Must not be empty - Question", TEXT_DATA_TYPE); + createConditional(TEST_SECTION_PATH + "/conditional_1", "is not empty", TEXT_DATA_TYPE, TEST_QUESTION_NAME, + comparableValue); + + NodeBuilder form = getFormNodeBuilderWithAnswerAndAnswerSectionChildren(comparableValue); + boolean isConditionSatisfied = ConditionalSectionUtils.isConditionSatisfied(this.session, + form.getChildNode(TEST_ANSWER_SECTION_NAME), form); + assertTrue(isConditionSatisfied); + } + + @Test + public void isConditionSatisfiedForConditionalNodeAndLongTypeAnswerAndMoreComparatorReturnsTrue() + throws RepositoryException + { + int comparableValue = 100; + int actualValue = 200; + + createQuestion(TEST_QUESTION_PATH, "Must be more than 100 - Question", LONG_DATA_TYPE); + createConditional(TEST_SECTION_PATH + "/conditional_1", ">", LONG_DATA_TYPE, TEST_QUESTION_NAME, + comparableValue); + + NodeBuilder form = getFormNodeBuilderWithAnswerAndAnswerSectionChildren(actualValue); + boolean isConditionSatisfied = ConditionalSectionUtils.isConditionSatisfied(this.session, + form.getChildNode(TEST_ANSWER_SECTION_NAME), form); + assertTrue(isConditionSatisfied); + } + + @Test + public void isConditionSatisfiedForConditionalNodeAndLongTypeAnswerAndNotEqualComparatorReturnsTrue() + throws RepositoryException + { + String comparableValue = "100"; + int actualValue = 200; + + createQuestion(TEST_QUESTION_PATH, "Must be not equal to 100 - Question", LONG_DATA_TYPE); + createConditional(TEST_SECTION_PATH + "/conditional_1", "<>", LONG_DATA_TYPE, TEST_QUESTION_NAME, + comparableValue); + + NodeBuilder form = getFormNodeBuilderWithAnswerAndAnswerSectionChildren(actualValue); + boolean isConditionSatisfied = ConditionalSectionUtils.isConditionSatisfied(this.session, + form.getChildNode(TEST_ANSWER_SECTION_NAME), form); + assertTrue(isConditionSatisfied); + } + + @Test + public void isConditionSatisfiedForConditionalGroupNodeAndDoubleTypeAnswerAndEqualOrLessComparatorReturnsTrue() + throws RepositoryException + { + String comparableValue = "100"; + double actualValue = 50.0; + + createQuestion(TEST_QUESTION_PATH, "Must be equal to or less than 100 - Question", DOUBLE_DATA_TYPE); + createConditionalGroup(TEST_SECTION_PATH + "/conditionalGroup_1", true); + createConditional(TEST_SECTION_PATH + "/conditionalGroup_1/conditional_1", "<=", DOUBLE_DATA_TYPE, + TEST_QUESTION_NAME, comparableValue); + + NodeBuilder form = getFormNodeBuilderWithAnswerAndAnswerSectionChildren(actualValue); + boolean isConditionSatisfied = ConditionalSectionUtils.isConditionSatisfied(this.session, + form.getChildNode(TEST_ANSWER_SECTION_NAME), form); + assertTrue(isConditionSatisfied); + } + + @Test + public void isConditionSatisfiedForConditionalGroupNodeAndDoubleTypeAnswerAndLessComparatorReturnsTrue() + throws RepositoryException + { + double comparableValue = 100.0; + double actualValue = 50.0; + + createQuestion(TEST_QUESTION_PATH, "Must be less than 100 - Question", DOUBLE_DATA_TYPE); + createConditionalGroup(TEST_SECTION_PATH + "/conditionalGroup_1", true); + createConditional(TEST_SECTION_PATH + "/conditionalGroup_1/conditional_1", "<", DOUBLE_DATA_TYPE, + TEST_QUESTION_NAME, comparableValue); + + NodeBuilder form = getFormNodeBuilderWithAnswerAndAnswerSectionChildren(actualValue); + boolean isConditionSatisfied = ConditionalSectionUtils.isConditionSatisfied(this.session, + form.getChildNode(TEST_ANSWER_SECTION_NAME), form); + assertTrue(isConditionSatisfied); + } + + // for BigDecimal value + @Test + public void isConditionSatisfiedForConditionalNodeAndDecimalTypeAnswerAndEqualComparatorReturnsTrue() + throws RepositoryException + { + BigDecimal comparableValue = new BigDecimal(100); + + createQuestion(TEST_QUESTION_PATH, "Must be equal to 100 - Question", DECIMAL_DATA_TYPE); + createConditional(TEST_SECTION_PATH + "/conditional_1", "=", DECIMAL_DATA_TYPE, TEST_QUESTION_NAME, + comparableValue); + + NodeBuilder form = getFormNodeBuilderWithAnswerAndAnswerSectionChildren(comparableValue); + boolean isConditionSatisfied = ConditionalSectionUtils.isConditionSatisfied(this.session, + form.getChildNode(TEST_ANSWER_SECTION_NAME), form); + assertTrue(isConditionSatisfied); + } + + @Test + public void isConditionSatisfiedForConditionalNodeAndDecimalTypeAnswerAndNotEqualComparatorReturnsTrue() + throws RepositoryException + { + String comparableValue = "100"; + BigDecimal actualValue = new BigDecimal(50); + + createQuestion(TEST_QUESTION_PATH, "Must be not equal to 100 - Question", DECIMAL_DATA_TYPE); + createConditional(TEST_SECTION_PATH + "/conditional_1", "<>", DECIMAL_DATA_TYPE, TEST_QUESTION_NAME, + comparableValue); + + NodeBuilder form = getFormNodeBuilderWithAnswerAndAnswerSectionChildren(actualValue); + boolean isConditionSatisfied = ConditionalSectionUtils.isConditionSatisfied(this.session, + form.getChildNode(TEST_ANSWER_SECTION_NAME), form); + assertTrue(isConditionSatisfied); + } + + @Test + public void isConditionSatisfiedForConditionalNodeAndDecimalTypeAnswerAndLessComparatorReturnsTrue() + throws RepositoryException + { + Integer comparableValue = 100; + BigDecimal actualValue = new BigDecimal(50); + + createQuestion(TEST_QUESTION_PATH, "Must be less than 100 - Question", DECIMAL_DATA_TYPE); + createConditional(TEST_SECTION_PATH + "/conditional_1", "<", DECIMAL_DATA_TYPE, TEST_QUESTION_NAME, + comparableValue); + + NodeBuilder form = getFormNodeBuilderWithAnswerAndAnswerSectionChildren(actualValue); + boolean isConditionSatisfied = ConditionalSectionUtils.isConditionSatisfied(this.session, + form.getChildNode(TEST_ANSWER_SECTION_NAME), form); + assertTrue(isConditionSatisfied); + } + + @Test + public void isConditionSatisfiedForConditionalNodeAndDecimalTypeAnswerAndEqualOrLessComparatorReturnsTrue() + throws RepositoryException + { + Long comparableValue = 100L; + BigDecimal actualValue = new BigDecimal(100); + + createQuestion(TEST_QUESTION_PATH, "Must be equal to or less than 100 - Question", DECIMAL_DATA_TYPE); + createConditional(TEST_SECTION_PATH + "/conditional_1", "<=", DECIMAL_DATA_TYPE, TEST_QUESTION_NAME, + comparableValue); + + NodeBuilder form = getFormNodeBuilderWithAnswerAndAnswerSectionChildren(actualValue); + boolean isConditionSatisfied = ConditionalSectionUtils.isConditionSatisfied(this.session, + form.getChildNode(TEST_ANSWER_SECTION_NAME), form); + assertTrue(isConditionSatisfied); + } + + @Test + public void isConditionSatisfiedForConditionalNodeAndDecimalTypeAnswerAndEqualOrMoreComparatorReturnsTrue() + throws RepositoryException + { + Double comparableValue = 100.0; + BigDecimal actualValue = new BigDecimal(200); + + createQuestion(TEST_QUESTION_PATH, "Must be equal to or more than 100 - Question", DECIMAL_DATA_TYPE); + createConditional(TEST_SECTION_PATH + "/conditional_1", ">=", DECIMAL_DATA_TYPE, TEST_QUESTION_NAME, + comparableValue); + + NodeBuilder form = getFormNodeBuilderWithAnswerAndAnswerSectionChildren(actualValue); + boolean isConditionSatisfied = ConditionalSectionUtils.isConditionSatisfied(this.session, + form.getChildNode(TEST_ANSWER_SECTION_NAME), form); + assertTrue(isConditionSatisfied); + } + + @Test + public void isConditionSatisfiedForConditionalNodeAndDecimalTypeAnswerAndMoreComparatorReturnsTrue() + throws RepositoryException + { + Float comparableValue = 100F; + BigDecimal actualValue = new BigDecimal(200); + + createQuestion(TEST_QUESTION_PATH, "Must be more than 100 - Question", DECIMAL_DATA_TYPE); + createConditional(TEST_SECTION_PATH + "/conditional_1", ">", DECIMAL_DATA_TYPE, TEST_QUESTION_NAME, + comparableValue); + + NodeBuilder form = getFormNodeBuilderWithAnswerAndAnswerSectionChildren(actualValue); + boolean isConditionSatisfied = ConditionalSectionUtils.isConditionSatisfied(this.session, + form.getChildNode(TEST_ANSWER_SECTION_NAME), form); + assertTrue(isConditionSatisfied); + } + + + // for Date value + @Test + public void isConditionSatisfiedForConditionalGroupNodeAndCalendarTypeAnswerAndEqualComparatorReturnsTrue() + throws RepositoryException + { + Calendar comparableValue = Calendar.getInstance(); + comparableValue.set(2023, Calendar.JANUARY, 1); + + createQuestion(TEST_QUESTION_PATH, "Must be equal to 2023-01-01 - Question", DATE_DATA_TYPE); + createConditionalGroup(TEST_SECTION_PATH + "/conditionalGroup_1", false); + createConditional(TEST_SECTION_PATH + "/conditionalGroup_1/conditional_1", "=", DATE_DATA_TYPE, + TEST_QUESTION_NAME, comparableValue); + + NodeBuilder form = getFormNodeBuilderWithAnswerAndAnswerSectionChildren(comparableValue); + boolean isConditionSatisfied = ConditionalSectionUtils.isConditionSatisfied(this.session, + form.getChildNode(TEST_ANSWER_SECTION_NAME), form); + assertTrue(isConditionSatisfied); + } + + @Test + public void isConditionSatisfiedForConditionalGroupNodeAndCalendarTypeAnswerAndEqualOrLessComparatorReturnsTrue() + throws RepositoryException + { + Calendar comparableValue = Calendar.getInstance(); + comparableValue.set(2023, Calendar.JANUARY, 1); + + Calendar actualValue = Calendar.getInstance(); + actualValue.set(2022, Calendar.JANUARY, 1); + + createQuestion(TEST_QUESTION_PATH, "Must be equal to or less than 2023-01-01 - Question", DATE_DATA_TYPE); + createConditionalGroup(TEST_SECTION_PATH + "/conditionalGroup_1", false); + createConditional(TEST_SECTION_PATH + "/conditionalGroup_1/conditional_1", "<=", DATE_DATA_TYPE, + TEST_QUESTION_NAME, comparableValue); + + NodeBuilder form = getFormNodeBuilderWithAnswerAndAnswerSectionChildren(actualValue); + boolean isConditionSatisfied = ConditionalSectionUtils.isConditionSatisfied(this.session, + form.getChildNode(TEST_ANSWER_SECTION_NAME), form); + assertTrue(isConditionSatisfied); + } + + @Test + public void isConditionSatisfiedForConditionalGroupNodeAndCalendarTypeAnswerAndLessComparatorReturnsTrue() + throws RepositoryException + { + Calendar comparableValue = Calendar.getInstance(); + comparableValue.set(2023, Calendar.JANUARY, 1); + + Calendar actualValue = Calendar.getInstance(); + actualValue.set(2022, Calendar.JANUARY, 1); + + createQuestion(TEST_QUESTION_PATH, "Must be less than 2023-01-01 - Question", DATE_DATA_TYPE); + createConditionalGroup(TEST_SECTION_PATH + "/conditionalGroup_1", false); + createConditional(TEST_SECTION_PATH + "/conditionalGroup_1/conditional_1", "<", DATE_DATA_TYPE, + TEST_QUESTION_NAME, comparableValue); + + NodeBuilder form = getFormNodeBuilderWithAnswerAndAnswerSectionChildren(actualValue); + boolean isConditionSatisfied = ConditionalSectionUtils.isConditionSatisfied(this.session, + form.getChildNode(TEST_ANSWER_SECTION_NAME), form); + assertTrue(isConditionSatisfied); + } + + @Test + public void isConditionSatisfiedForConditionalGroupNodeAndCalendarTypeAnswerAndEqualOrMoreComparatorReturnsTrue() + throws RepositoryException + { + Calendar comparableValue = Calendar.getInstance(); + comparableValue.set(2023, Calendar.JANUARY, 1); + + Calendar actualValue = Calendar.getInstance(); + actualValue.set(2023, Calendar.MARCH, 1); + + createQuestion(TEST_QUESTION_PATH, "Must be equal to or more than 2023-01-01 - Question", DATE_DATA_TYPE); + createConditionalGroup(TEST_SECTION_PATH + "/conditionalGroup_1", false); + createConditional(TEST_SECTION_PATH + "/conditionalGroup_1/conditional_1", ">=", DATE_DATA_TYPE, + TEST_QUESTION_NAME, comparableValue); + + NodeBuilder form = getFormNodeBuilderWithAnswerAndAnswerSectionChildren(actualValue); + boolean isConditionSatisfied = ConditionalSectionUtils.isConditionSatisfied(this.session, + form.getChildNode(TEST_ANSWER_SECTION_NAME), form); + assertTrue(isConditionSatisfied); + } + + @Test + public void isConditionSatisfiedForConditionalGroupNodeAndStringDateTypeAnswerAndMoreComparatorReturnsTrue() + throws RepositoryException + { + String comparableValue = "2023-01-01T00:00-00:00"; + + Calendar actualValue = Calendar.getInstance(); + actualValue.set(2023, Calendar.MARCH, 1); + + createQuestion(TEST_QUESTION_PATH, "Must be more than 2023-01-01 - Question", DATE_DATA_TYPE); + createConditionalGroup(TEST_SECTION_PATH + "/conditionalGroup_1", false); + createConditional(TEST_SECTION_PATH + "/conditionalGroup_1/conditional_1", ">", DATE_DATA_TYPE, + TEST_QUESTION_NAME, comparableValue); + + NodeBuilder form = getFormNodeBuilderWithAnswerAndAnswerSectionChildren(actualValue); + boolean isConditionSatisfied = ConditionalSectionUtils.isConditionSatisfied(this.session, + form.getChildNode(TEST_ANSWER_SECTION_NAME), form); + assertTrue(isConditionSatisfied); + } + + @Test + public void isConditionSatisfiedForConditionalGroupWithSeveralConditionalsReturnsTrue() throws RepositoryException + { + double comparableValueDouble = 100.0; + double actualValueDouble = 150.0; + + double comparableValueLong = 100; + double actualValueLong = 50; + + createConditionalGroup(TEST_SECTION_PATH + "/conditionalGroup_1", true); + + createQuestion(TEST_QUESTION_PATH, "Must be equal to or more than 100 - Question", DOUBLE_DATA_TYPE); + createConditional(TEST_SECTION_PATH + "/conditionalGroup_1/conditional_1", ">=", DOUBLE_DATA_TYPE, + TEST_QUESTION_NAME, comparableValueDouble); + + createQuestion("/Questionnaires/TestQuestionnaire/question_2", "Must be less than 100 - Question", + LONG_DATA_TYPE); + createConditional(TEST_SECTION_PATH + "/conditionalGroup_1/conditional_2", "<", LONG_DATA_TYPE, + "question_2", comparableValueLong); + + NodeBuilder form = getFormNodeBuilderWithAnswerAndAnswerSectionChildren(Map.of( + TEST_QUESTION_PATH, actualValueDouble, + "/Questionnaires/TestQuestionnaire/question_2", actualValueLong + )); + boolean isConditionSatisfied = ConditionalSectionUtils.isConditionSatisfied(this.session, + form.getChildNode(TEST_ANSWER_SECTION_NAME), form); + assertTrue(isConditionSatisfied); + } + + @Test + public void isConditionSatisfiedCatchesRepositoryExceptionGettingSectionByIdReturnsTrue() + throws RepositoryException + { + NodeBuilder answerSectionNodeBuilder = createTestAnswerSection(UUID.randomUUID().toString()); + String subjectUuid = this.session.getNode(TEST_SUBJECT_PATH).getIdentifier(); + String questionnaireUuid = this.session.getNode(TEST_QUESTIONNAIRE_PATH).getIdentifier(); + NodeBuilder form = createTestForm(questionnaireUuid, subjectUuid, + Map.of(TEST_ANSWER_SECTION_NAME, answerSectionNodeBuilder.getNodeState())); + + boolean isConditionSatisfied = ConditionalSectionUtils.isConditionSatisfied(this.session, + form.getChildNode(TEST_ANSWER_SECTION_NAME), form); + assertTrue(isConditionSatisfied); + } + + @Test + public void isConditionSatisfiedForAnswerSectionWithoutSectionPropertyReturnsTrue() + throws RepositoryException + { + NodeBuilder answerSectionBuilder = EmptyNodeState.EMPTY_NODE.builder(); + answerSectionBuilder.setProperty(NODE_TYPE, ANSWER_SECTION_TYPE); + String subjectUuid = this.session.getNode(TEST_SUBJECT_PATH).getIdentifier(); + String questionnaireUuid = this.session.getNode(TEST_QUESTIONNAIRE_PATH).getIdentifier(); + NodeBuilder form = createTestForm(questionnaireUuid, subjectUuid, + Map.of(TEST_ANSWER_SECTION_NAME, answerSectionBuilder.getNodeState())); + + boolean isConditionSatisfied = ConditionalSectionUtils.isConditionSatisfied(this.session, + form.getChildNode(TEST_ANSWER_SECTION_NAME), form); + assertTrue(isConditionSatisfied); + } + + @Before + public void setupRepo() + { + this.session = this.context.resourceResolver().adaptTo(Session.class); + this.context.build() + .resource("/Questionnaires", NODE_TYPE, "cards:QuestionnairesHomepage") + .resource("/SubjectTypes", NODE_TYPE, "cards:SubjectTypesHomepage") + .resource("/Subjects", NODE_TYPE, "cards:SubjectsHomepage") + .resource("/Forms", NODE_TYPE, "cards:FormsHomepage") + .commit(); + this.context.load().json("/SubjectTypes.json", "/SubjectTypes/Root"); + this.context.build() + .resource(TEST_SUBJECT_PATH, NODE_TYPE, SUBJECT_TYPE, "type", + this.context.resourceResolver().getResource("/SubjectTypes/Root").adaptTo(Node.class)) + .commit(); + + this.context.build() + .resource(TEST_QUESTIONNAIRE_PATH, + NODE_TYPE, "cards:Questionnaire", + "title", "Test Questionnaire", + "description", "A test questionnaire") + .resource(TEST_SECTION_PATH, + NODE_TYPE, "cards:Section", + "label", "Section 1") + .commit(); + + } + + private void createQuestion(String questionPath, String text, String dataType) + { + this.context.build() + .resource(questionPath, + NODE_TYPE, "cards:Question", + "text", text, + "dataType", dataType) + .commit(); + } + + private void createConditionalGroup(String conditionalGroupPath, boolean requireAll) + { + this.context.build() + .resource(conditionalGroupPath, + NODE_TYPE, CONDITIONAL_GROUP_TYPE, + "requireAll", requireAll) + .commit(); + } + + private void createConditional(String conditionalPath, String comparator, String dataType, + String referenceAnswerName, Object value) + { + this.context.build() + .resource(conditionalPath, + NODE_TYPE, CONDITIONAL_TYPE, + "comparator", comparator, + "dataType", dataType) + .resource(conditionalPath + "/operandA", + NODE_TYPE, CONDITIONAL_VALUE_TYPE, + VALUE_PROPERTY, List.of(referenceAnswerName).toArray(), + "isReference", true) + .resource(conditionalPath + "/operandB", + NODE_TYPE, CONDITIONAL_VALUE_TYPE, + VALUE_PROPERTY, List.of(value).toArray(), + "isReference", false) + .commit(); + } + + private NodeBuilder getFormNodeBuilderWithAnswerAndAnswerSectionChildren(Map questionAnswerValues) + throws RepositoryException + { + List answers = new ArrayList<>(); + for (Map.Entry questionAnswerValue : questionAnswerValues.entrySet()) { + String questionUuid = this.session.getNode(questionAnswerValue.getKey()).getIdentifier(); + NodeBuilder answerNodeBuilder = createTestAnswer(questionUuid); + answerNodeBuilder.setProperty(VALUE_PROPERTY, questionAnswerValue.getValue()); + answers.add(answerNodeBuilder); + } + + // create answerSection with conditionals + String sectionUuid = this.session.getNode(TEST_SECTION_PATH).getIdentifier(); + NodeBuilder answerSectionNodeBuilder = createTestAnswerSection(sectionUuid); + String subjectUuid = this.session.getNode(TEST_SUBJECT_PATH).getIdentifier(); + String questionnaireUuid = this.session.getNode(TEST_QUESTIONNAIRE_PATH).getIdentifier(); + Map formChildren = new HashMap<>(); + for (NodeBuilder answer : answers) { + formChildren.put(UUID.randomUUID().toString(), answer.getNodeState()); + } + formChildren.put(TEST_ANSWER_SECTION_NAME, answerSectionNodeBuilder.getNodeState()); + + return createTestForm(questionnaireUuid, subjectUuid, formChildren); + } + + private NodeBuilder getFormNodeBuilderWithAnswerAndAnswerSectionChildren(Object answerValue) + throws RepositoryException + { + // create referencable answer + String questionUuid = this.session.getNode(TEST_QUESTION_PATH).getIdentifier(); + NodeBuilder answerNodeBuilder = createTestAnswer(questionUuid); + answerNodeBuilder.setProperty(VALUE_PROPERTY, answerValue); + + // create answerSection with conditionals + String sectionUuid = this.session.getNode(TEST_SECTION_PATH).getIdentifier(); + NodeBuilder answerSectionNodeBuilder = createTestAnswerSection(sectionUuid); + String subjectUuid = this.session.getNode(TEST_SUBJECT_PATH).getIdentifier(); + String questionnaireUuid = this.session.getNode(TEST_QUESTIONNAIRE_PATH).getIdentifier(); + NodeBuilder form = createTestForm(questionnaireUuid, subjectUuid, + Map.of("answer_1", answerNodeBuilder.getNodeState(), + TEST_ANSWER_SECTION_NAME, answerSectionNodeBuilder.getNodeState())); + + return form; + } + + private NodeBuilder createTestAnswerSection(String sectionUuid) + { + NodeBuilder answerSectionBuilder = EmptyNodeState.EMPTY_NODE.builder(); + answerSectionBuilder.setProperty(NODE_TYPE, ANSWER_SECTION_TYPE); + answerSectionBuilder.setProperty(SECTION_PROPERTY, sectionUuid); + return answerSectionBuilder; + } + + private NodeBuilder createTestForm(String questionnaireUuid, String subjectUuid, + Map children) + { + NodeBuilder formBuilder = EmptyNodeState.EMPTY_NODE.builder(); + formBuilder.setProperty(NODE_TYPE, FORM_TYPE); + formBuilder.setProperty(QUESTIONNAIRE_PROPERTY, questionnaireUuid); + formBuilder.setProperty(SUBJECT_PROPERTY, subjectUuid); + for (Map.Entry child : children.entrySet()) { + formBuilder.setChildNode(child.getKey(), child.getValue()); + } + return formBuilder; + } + + private NodeBuilder createTestAnswer(String questionUuid) + { + NodeBuilder answerBuilder = EmptyNodeState.EMPTY_NODE.builder(); + answerBuilder.setProperty(NODE_TYPE, ANSWER_TYPE); + answerBuilder.setProperty(QUESTION_PROPERTY, questionUuid); + return answerBuilder; + } + +}