/* * Copyright (c) 2005-2011 Grameen Foundation USA * All rights reserved. * * Licensed 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. * * See also http://www.apache.org/licenses/LICENSE-2.0.html for an * explanation of the license and how it is applied. */ package org.mifos.platform.questionnaire.validators; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mifos.framework.exceptions.SystemException; import org.mifos.platform.validations.ValidationException; import org.mifos.platform.questionnaire.exceptions.BadNumericResponseException; import org.mifos.platform.questionnaire.exceptions.MandatoryAnswerNotFoundException; import org.mifos.platform.questionnaire.persistence.EventSourceDao; import org.mifos.platform.questionnaire.persistence.QuestionDao; import org.mifos.platform.questionnaire.persistence.QuestionGroupDao; import org.mifos.platform.questionnaire.service.QuestionDetail; import org.mifos.platform.questionnaire.service.QuestionGroupDetail; import org.mifos.platform.questionnaire.service.QuestionType; import org.mifos.platform.questionnaire.service.SectionDetail; import org.mifos.platform.questionnaire.service.SectionQuestionDetail; import org.mifos.platform.questionnaire.service.dtos.EventSourceDto; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import java.util.ArrayList; import java.util.List; import java.util.Arrays; import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mifos.platform.questionnaire.QuestionnaireConstants.GENERIC_VALIDATION; import static org.mifos.platform.questionnaire.QuestionnaireConstants.INVALID_EVENT_SOURCE; import static org.mifos.platform.questionnaire.QuestionnaireConstants.INVALID_NUMERIC_BOUNDS; import static org.mifos.platform.questionnaire.QuestionnaireConstants.NO_ANSWERS_PROVIDED; import static org.mifos.platform.questionnaire.QuestionnaireConstants.NO_QUESTIONS_FOUND_IN_SECTION; import static org.mifos.platform.questionnaire.QuestionnaireConstants.QUESTION_GROUP_SECTION_NOT_PROVIDED; import static org.mifos.platform.questionnaire.QuestionnaireConstants.QUESTION_GROUP_TITLE_NOT_PROVIDED; import static org.mifos.platform.questionnaire.QuestionnaireConstants.QUESTION_TEXT_NOT_PROVIDED; import static org.mifos.platform.questionnaire.QuestionnaireConstants.ANSWER_TYPE_NOT_PROVIDED; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) public class QuestionnaireValidatorTest { private QuestionnaireValidator questionnaireValidator; @Mock private EventSourceDao eventSourceDao; @Mock private QuestionGroupDao questionGroupDao; @Mock private QuestionDao questionDao; @Before public void setUp() { questionnaireValidator = new QuestionnaireValidatorImpl(eventSourceDao, questionGroupDao, questionDao); } @Test public void shouldNotThrowExceptionWhenQuestionTitleIsProvided() { try { questionnaireValidator.validateForDefineQuestion(new QuestionDetail("Title", QuestionType.FREETEXT)); } catch (SystemException e) { fail("Should not have thrown the exception"); } } @Test public void shouldThrowExceptionWhenQuestionTitleIsProvided() { try { questionnaireValidator.validateForDefineQuestion(new QuestionDetail(null, QuestionType.FREETEXT)); fail("Should have thrown the validation exception"); } catch (SystemException e) { assertEquals(QUESTION_TEXT_NOT_PROVIDED, e.getKey()); } } @Test public void shouldThrowExceptionWhenQuestionTypeNotProvided() { try { questionnaireValidator.validateForDefineQuestion(new QuestionDetail("Title 123", QuestionType.INVALID)); fail("Should have thrown the validation exception"); } catch (SystemException e) { assertEquals(ANSWER_TYPE_NOT_PROVIDED, e.getKey()); } } @Test public void shouldNotThrowExceptionForNumericQuestionType() { try { QuestionDetail questionDetail = new QuestionDetail("Title", QuestionType.NUMERIC); questionnaireValidator.validateForDefineQuestion(questionDetail); } catch (SystemException e) { fail("Should not have thrown the exception"); } } @Test public void shouldThrowExceptionForNumericQuestionTypeWhenInvalidBoundsGiven() { try { QuestionDetail questionDetail = new QuestionDetail("Title", QuestionType.NUMERIC); questionDetail.setNumericMin(100); questionDetail.setNumericMax(10); questionnaireValidator.validateForDefineQuestion(questionDetail); fail("Should have thrown the exception"); } catch (SystemException e) { assertEquals(INVALID_NUMERIC_BOUNDS, e.getKey()); } } @Test public void shouldNotThrowExceptionForNumericQuestionTypeWhenOnlyMinBoundGiven() { try { QuestionDetail questionDetail = new QuestionDetail("Title", QuestionType.NUMERIC); questionDetail.setNumericMin(10); questionnaireValidator.validateForDefineQuestion(questionDetail); } catch (SystemException e) { fail("Should not have thrown the exception"); } } @Test public void shouldNotThrowExceptionForNumericQuestionTypeWhenOnlyMaxBoundGiven() { try { QuestionDetail questionDetail = new QuestionDetail("Title", QuestionType.NUMERIC); questionDetail.setNumericMax(-100); questionnaireValidator.validateForDefineQuestion(questionDetail); } catch (SystemException e) { fail("Should not have thrown the exception"); } } @Test public void shouldNotThrowExceptionWhenQuestionGroupTitleIsProvided() { when(eventSourceDao.retrieveCountByEventAndSource(anyString(), anyString())).thenReturn(asList((long) 1)); try { questionnaireValidator.validateForDefineQuestionGroup(getQuestionGroupDetail(0, "Title", "Create", "Client")); } catch (SystemException e) { fail("shouldNotThrowExceptionWhenQuestionGroupTitleIsProvided:Should not have thrown the exception"); } verify(eventSourceDao, times(1)).retrieveCountByEventAndSource(anyString(), anyString()); } @Test public void shouldThrowExceptionWhenQuestionGroupTitleIsProvided() { try { questionnaireValidator.validateForDefineQuestionGroup(getQuestionGroupDetail(0, null, "Create", "Client")); fail("Should have thrown the validation exception"); } catch (SystemException e) { assertEquals(QUESTION_GROUP_TITLE_NOT_PROVIDED, e.getKey()); } } @Test public void shouldNotThrowExceptionWhenQuestionGroupHasAtLeastOneSection() { when(eventSourceDao.retrieveCountByEventAndSource(anyString(), anyString())).thenReturn(asList((long) 1)); try { questionnaireValidator.validateForDefineQuestionGroup(getQuestionGroupDetail(0, "Title", "Create", "Client")); } catch (SystemException e) { fail("shouldNotThrowExceptionWhenQuestionGroupHasAtLeastOneSection:Should not have thrown the exception"); } verify(eventSourceDao, times(1)).retrieveCountByEventAndSource(anyString(), anyString()); } @Test public void shouldThrowExceptionWhenQuestionGroupHasNoSections() { try { questionnaireValidator.validateForDefineQuestionGroup(getQuestionGroupDetail(0, "Title", "Create", "Client", new ArrayList<SectionDetail>())); fail("Should have thrown the validation exception"); } catch (SystemException e) { assertEquals(QUESTION_GROUP_SECTION_NOT_PROVIDED, e.getKey()); } } @Test public void shouldThrowExceptionWhenEventIsNotProvided() { try { questionnaireValidator.validateForDefineQuestionGroup(getQuestionGroupDetail(0, "Title", null, "Client")); fail("Should have thrown the validation exception"); } catch (SystemException e) { assertEquals(INVALID_EVENT_SOURCE, e.getKey()); } verify(eventSourceDao, never()).retrieveCountByEventAndSource(anyString(), anyString()); } @Test public void shouldThrowExceptionWhenSourceIsNotProvided() { try { questionnaireValidator.validateForDefineQuestionGroup(getQuestionGroupDetail(0, "Title", "Create", null)); fail("Should have thrown the validation exception"); } catch (SystemException e) { assertEquals(INVALID_EVENT_SOURCE, e.getKey()); } verify(eventSourceDao, never()).retrieveCountByEventAndSource(anyString(), anyString()); } @Test public void shouldThrowExceptionWhenEventSourceIsNotProvided() { try { questionnaireValidator.validateForDefineQuestionGroup(new QuestionGroupDetail(0, "Title", null, asList(getSection("S1")), false)); fail("Should have thrown the validation exception"); } catch (SystemException e) { assertEquals(INVALID_EVENT_SOURCE, e.getKey()); } verify(eventSourceDao, never()).retrieveCountByEventAndSource(anyString(), anyString()); } @Test public void shouldThrowExceptionWhenAGivenSectionHasNoQuestions() { try { questionnaireValidator.validateForDefineQuestionGroup(getQuestionGroupDetail(0, "Title", "Create", "Client", asList(getSectionWithQuestions("S1")))); fail("Should have thrown the validation exception"); } catch (SystemException e) { assertEquals(NO_QUESTIONS_FOUND_IN_SECTION, e.getKey()); } verify(eventSourceDao, never()).retrieveCountByEventAndSource(anyString(), anyString()); } @Test public void shouldThrowExceptionWhenNoAnswersProvided() { try { questionnaireValidator.validateForQuestionGroupResponses(null); fail("Should have thrown the validation exception"); } catch (SystemException e) { assertEquals(NO_ANSWERS_PROVIDED, e.getKey()); } } @SuppressWarnings({"ThrowableResultOfMethodCallIgnored"}) @Test public void shouldThrowExceptionWhenAMandatoryQuestionHasNoAnswer() { QuestionGroupDetail questionGroupDetail = getQuestionGroupDetail(0, "Title", "Create", "Client"); try { questionnaireValidator.validateForQuestionGroupResponses(asList(questionGroupDetail)); fail("Should have thrown the validation exception"); } catch (ValidationException e) { assertEquals(GENERIC_VALIDATION, e.getKey()); assertEquals(true, e.hasChildExceptions()); assertEquals(1, e.getChildExceptions().size()); ValidationException childException = e.getChildExceptions().get(0); assertTrue(childException instanceof MandatoryAnswerNotFoundException); assertEquals("Q1", childException.getIdentifier()); } } @SuppressWarnings({"ThrowableResultOfMethodCallIgnored"}) @Test public void shouldThrowExceptionWhenANumericQuestionHasInvalidAnswer() { QuestionDetail questionDetail = getNumericQuestionDetail("Numeric Question", 10, 100); SectionDetail sectionWithOneQuestion = getSectionWithOneQuestion("Sec1", questionDetail, "123ab"); QuestionGroupDetail questionGroupDetail = getQuestionGroupDetail(0, "Title", "Create", "Client", asList(sectionWithOneQuestion)); try { questionnaireValidator.validateForQuestionGroupResponses(asList(questionGroupDetail)); fail("Should have thrown the validation exception"); } catch (ValidationException e) { assertEquals(GENERIC_VALIDATION, e.getKey()); assertEquals(true, e.hasChildExceptions()); assertEquals(1, e.getChildExceptions().size()); ValidationException childException = e.getChildExceptions().get(0); assertTrue(childException instanceof BadNumericResponseException); assertEquals("Numeric Question", childException.getIdentifier()); assertEquals(Integer.valueOf(10), ((BadNumericResponseException)childException).getAllowedMinValue()); assertEquals(Integer.valueOf(100), ((BadNumericResponseException)childException).getAllowedMaxValue()); } } @SuppressWarnings({"ThrowableResultOfMethodCallIgnored"}) @Test public void shouldThrowExceptionWhenANumericQuestionHasAnswerLessThanMin() { QuestionDetail questionDetail = getNumericQuestionDetail("Numeric Question", 10, null); SectionDetail sectionWithOneQuestion = getSectionWithOneQuestion("Sec1", questionDetail, "9"); QuestionGroupDetail questionGroupDetail = getQuestionGroupDetail(0, "Title", "Create", "Client", asList(sectionWithOneQuestion)); try { questionnaireValidator.validateForQuestionGroupResponses(asList(questionGroupDetail)); fail("Should have thrown the validation exception"); } catch (ValidationException e) { assertEquals(GENERIC_VALIDATION, e.getKey()); assertEquals(true, e.hasChildExceptions()); assertEquals(1, e.getChildExceptions().size()); ValidationException childException = e.getChildExceptions().get(0); assertTrue(childException instanceof BadNumericResponseException); assertEquals("Numeric Question", childException.getIdentifier()); assertEquals(Integer.valueOf(10), ((BadNumericResponseException)childException).getAllowedMinValue()); assertNull(((BadNumericResponseException)childException).getAllowedMaxValue()); } } @SuppressWarnings({"ThrowableResultOfMethodCallIgnored"}) @Test public void shouldThrowExceptionWhenANumericQuestionHasAnswerMoreThanMax() { QuestionDetail questionDetail = getNumericQuestionDetail("Numeric Question", null, 100); SectionDetail sectionWithOneQuestion = getSectionWithOneQuestion("Sec1", questionDetail, "101"); QuestionGroupDetail questionGroupDetail = getQuestionGroupDetail(0, "Title", "Create", "Client", asList(sectionWithOneQuestion)); try { questionnaireValidator.validateForQuestionGroupResponses(asList(questionGroupDetail)); fail("Should have thrown the validation exception"); } catch (ValidationException e) { assertEquals(GENERIC_VALIDATION, e.getKey()); assertEquals(true, e.hasChildExceptions()); assertEquals(1, e.getChildExceptions().size()); ValidationException childException = e.getChildExceptions().get(0); assertTrue(childException instanceof BadNumericResponseException); assertEquals("Numeric Question", childException.getIdentifier()); assertNull(((BadNumericResponseException)childException).getAllowedMinValue()); assertEquals(Integer.valueOf(100), ((BadNumericResponseException)childException).getAllowedMaxValue()); } } @SuppressWarnings({"ThrowableResultOfMethodCallIgnored"}) @Test public void shouldNotThrowExceptionWhenANumericQuestionWithNoBoundsHasAnswer() { QuestionDetail questionDetail = getNumericQuestionDetail("Numeric Question", null, null); SectionDetail sectionWithOneQuestion = getSectionWithOneQuestion("Sec1", questionDetail, "121"); QuestionGroupDetail questionGroupDetail = getQuestionGroupDetail(0, "Title", "Create", "Client", asList(sectionWithOneQuestion)); try { questionnaireValidator.validateForQuestionGroupResponses(asList(questionGroupDetail)); } catch (ValidationException e) { fail("Should not have thrown the validation exception"); } } private QuestionDetail getNumericQuestionDetail(String title, Integer numericMin, Integer numericMax) { QuestionDetail questionDetail = new QuestionDetail(title, QuestionType.NUMERIC); questionDetail.setNumericMin(numericMin); questionDetail.setNumericMax(numericMax); return questionDetail; } private SectionDetail getSectionWithOneQuestion(String name, QuestionDetail questionDetail, String response) { SectionDetail sectionDetail = new SectionDetail(); sectionDetail.setName(name); sectionDetail.setQuestionDetails(asList(new SectionQuestionDetail(questionDetail, true, response))); return sectionDetail; } private QuestionGroupDetail getQuestionGroupDetail(int id, String title, String event, String source) { return getQuestionGroupDetail(id, title, event, source, asList(getSection("S1"))); } private QuestionGroupDetail getQuestionGroupDetail(int id, String title, String event, String source, List<SectionDetail> sectionDetails) { return new QuestionGroupDetail(id, title, Arrays.asList(getEventSource(event, source)), sectionDetails, false); } private SectionDetail getSection(String name) { SectionDetail section = new SectionDetail(); section.setName(name); section.addQuestion(new SectionQuestionDetail(new QuestionDetail(12, "Q1", QuestionType.INVALID, true, true), true, null)); return section; } private SectionDetail getSectionWithQuestions(String name, int... questionIds) { SectionDetail section = new SectionDetail(); section.setName(name); if (questionIds != null) { for (int questionId : questionIds) { section.addQuestion(new SectionQuestionDetail(new QuestionDetail(questionId, null, QuestionType.INVALID, true, true), true, null)); } } return section; } private EventSourceDto getEventSource(String event, String source) { return new EventSourceDto(event, source, null); } }