diff --git a/.github/workflows/on.pr.yml b/.github/workflows/on.pr.yml index c0c7ee9..446f477 100644 --- a/.github/workflows/on.pr.yml +++ b/.github/workflows/on.pr.yml @@ -33,6 +33,11 @@ jobs: ${{ runner.os }}-maven-5Jun- - name: Run unit tests run: mvn -f pom.xml clean package + - name: Set up JDK 21 for Sonar + uses: actions/setup-java@v4 + with: + distribution: 'corretto' + java-version: 21 - name: Cache SonarCloud packages uses: actions/cache@v4 with: diff --git a/api/src/main/java/ca/bc/gov/educ/api/ruleengine/rule/FailedAssessmentsRule.java b/api/src/main/java/ca/bc/gov/educ/api/ruleengine/rule/FailedAssessmentsRule.java index 3e6a3c9..7408e26 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/ruleengine/rule/FailedAssessmentsRule.java +++ b/api/src/main/java/ca/bc/gov/educ/api/ruleengine/rule/FailedAssessmentsRule.java @@ -1,5 +1,6 @@ package ca.bc.gov.educ.api.ruleengine.rule; +import ca.bc.gov.educ.api.ruleengine.dto.OptionalProgramRuleProcessor; import ca.bc.gov.educ.api.ruleengine.dto.RuleData; import ca.bc.gov.educ.api.ruleengine.dto.RuleProcessorData; import ca.bc.gov.educ.api.ruleengine.dto.StudentAssessment; @@ -13,6 +14,8 @@ import org.springframework.stereotype.Component; import java.util.List; +import java.util.Map; +import java.util.Objects; @Data @Component @@ -28,37 +31,48 @@ public class FailedAssessmentsRule implements Rule { @Override public RuleData fire() { - List studentAssessmentList = RuleProcessorRuleUtils.getUniqueStudentAssessments(ruleProcessorData.getStudentAssessments(),ruleProcessorData.isProjected()); + List studentAssessmentList = RuleProcessorRuleUtils.getUniqueStudentAssessments(ruleProcessorData.getStudentAssessments(), ruleProcessorData.isProjected()); - for (StudentAssessment studentAssessment : studentAssessmentList) { + markFailedAssessments(studentAssessmentList); + markOptionalProgramAssessmentsFailed(); + ruleProcessorData.setExcludedAssessments(RuleProcessorRuleUtils.maintainExcludedAssessments(studentAssessmentList,ruleProcessorData.getExcludedAssessments(),ruleProcessorData.isProjected())); + ruleProcessorData.setStudentAssessments(studentAssessmentList); + + logger.debug("Failed Assessments: {}", + (int) studentAssessmentList.stream().filter(StudentAssessment::isFailed).count()); + + return ruleProcessorData; + } + + private void markOptionalProgramAssessmentsFailed() { + Map optionalPrograms = ruleProcessorData.getMapOptional(); + if (optionalPrograms == null) { + return; + } + + optionalPrograms.values().stream() + .map(OptionalProgramRuleProcessor::getStudentAssessmentsOptionalProgram) + .filter(Objects::nonNull) + .forEach(this::markFailedAssessments); + } + + private void markFailedAssessments(List studentAssessments) { + for (StudentAssessment studentAssessment : studentAssessments) { boolean failed = ruleProcessorData.getSpecialCaseList().stream() - .anyMatch(lg -> lg.getSpCase() + .anyMatch(specialCase -> specialCase.getSpCase() .compareTo(studentAssessment.getSpecialCase() != null ? studentAssessment.getSpecialCase().trim() : "") == 0 - && lg.getPassFlag().compareTo("N") == 0); + && specialCase.getPassFlag().compareTo("N") == 0); - if (failed) - studentAssessment.setFailed(true); - String exceededWriteFlag; - if(studentAssessment.getExceededWriteFlag() == null) { - exceededWriteFlag = ""; - }else { - exceededWriteFlag = studentAssessment.getExceededWriteFlag(); - } - if ("Y".compareTo(exceededWriteFlag.trim()) == 0) { + String exceededWriteFlag = studentAssessment.getExceededWriteFlag() == null + ? "" + : studentAssessment.getExceededWriteFlag(); + if (failed || "Y".equals(exceededWriteFlag.trim())) { studentAssessment.setFailed(true); } } - - ruleProcessorData.setExcludedAssessments(RuleProcessorRuleUtils.maintainExcludedAssessments(studentAssessmentList,ruleProcessorData.getExcludedAssessments(),ruleProcessorData.isProjected())); - ruleProcessorData.setStudentAssessments(studentAssessmentList); - - logger.debug("Failed Assessments: {}", - (int) studentAssessmentList.stream().filter(StudentAssessment::isFailed).count()); - - return ruleProcessorData; } public void setInputData(RuleData inputData) { diff --git a/api/src/test/java/ca/bc/gov/educ/api/ruleengine/rule/FailedAssessmentsRuleTest.java b/api/src/test/java/ca/bc/gov/educ/api/ruleengine/rule/FailedAssessmentsRuleTest.java new file mode 100644 index 0000000..ffa4948 --- /dev/null +++ b/api/src/test/java/ca/bc/gov/educ/api/ruleengine/rule/FailedAssessmentsRuleTest.java @@ -0,0 +1,92 @@ +package ca.bc.gov.educ.api.ruleengine.rule; + +import ca.bc.gov.educ.api.ruleengine.dto.OptionalProgramRuleProcessor; +import ca.bc.gov.educ.api.ruleengine.dto.RuleProcessorData; +import ca.bc.gov.educ.api.ruleengine.dto.SpecialCase; +import ca.bc.gov.educ.api.ruleengine.dto.StudentAssessment; +import ca.bc.gov.educ.api.ruleengine.util.RuleProcessorRuleUtils; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class FailedAssessmentsRuleTest { + + @Test + public void shouldMarkDualDogwoodAssessmentsFailedForNonPassingSpecialCases() { + assertAssessmentFailsForMainAndOptionalProgram("DD", "LTE10", "Q"); + assertAssessmentFailsForMainAndOptionalProgram("DD", "LTE12", "X"); + } + + @Test + public void shouldMarkFrenchImmersionAssessmentFailedForNonPassingSpecialCases() { + assertAssessmentFailsForMainAndOptionalProgram("FI", "LTF12", "Q"); + assertAssessmentFailsForMainAndOptionalProgram("FI", "LTF12", "X"); + } + + @Test + public void shouldNotMarkPassingSpecialCaseFailed() { + RuleProcessorData result = processAssessment("FI", "LTF12", "M"); + + assertFalse(result.getStudentAssessments().get(0).isFailed()); + assertFalse(result.getMapOptional().get("FI").getStudentAssessmentsOptionalProgram().get(0).isFailed()); + } + + private void assertAssessmentFailsForMainAndOptionalProgram(String optionalProgramCode, String assessmentCode, String specialCase) { + RuleProcessorData result = processAssessment(optionalProgramCode, assessmentCode, specialCase); + + assertTrue(result.getStudentAssessments().get(0).isFailed()); + assertTrue(result.getMapOptional().get(optionalProgramCode).getStudentAssessmentsOptionalProgram().get(0).isFailed()); + assertTrue(RuleProcessorRuleUtils.getUniqueStudentAssessments( + result.getMapOptional().get(optionalProgramCode).getStudentAssessmentsOptionalProgram(), false).isEmpty()); + } + + private RuleProcessorData processAssessment(String optionalProgramCode, String assessmentCode, String specialCase) { + StudentAssessment mainAssessment = createAssessment(assessmentCode, specialCase); + StudentAssessment optionalAssessment = createAssessment(assessmentCode, specialCase); + + OptionalProgramRuleProcessor optionalProgram = new OptionalProgramRuleProcessor(); + optionalProgram.setStudentAssessmentsOptionalProgram(new ArrayList<>(List.of(optionalAssessment))); + + Map optionalPrograms = new HashMap<>(); + optionalPrograms.put(optionalProgramCode, optionalProgram); + + RuleProcessorData ruleProcessorData = new RuleProcessorData(); + ruleProcessorData.setStudentAssessments(new ArrayList<>(List.of(mainAssessment))); + ruleProcessorData.setExcludedAssessments(new ArrayList<>()); + ruleProcessorData.setSpecialCaseList(createSpecialCases()); + ruleProcessorData.setMapOptional(optionalPrograms); + ruleProcessorData.setProjected(false); + + return (RuleProcessorData) new FailedAssessmentsRule(ruleProcessorData).fire(); + } + + private StudentAssessment createAssessment(String assessmentCode, String specialCase) { + StudentAssessment assessment = new StudentAssessment(); + assessment.setPen("123456789"); + assessment.setAssessmentCode(assessmentCode); + assessment.setSessionDate("2026/06"); + assessment.setSpecialCase(specialCase); + return assessment; + } + + private List createSpecialCases() { + return List.of( + createSpecialCase("Q", "N"), + createSpecialCase("X", "N"), + createSpecialCase("M", "Y") + ); + } + + private SpecialCase createSpecialCase(String code, String passFlag) { + SpecialCase specialCase = new SpecialCase(); + specialCase.setSpCase(code); + specialCase.setPassFlag(passFlag); + return specialCase; + } +}