package com.trafficaudit.holiday.service; import com.trafficaudit.holiday.HolidayConstants; import com.trafficaudit.holiday.HolidayNumberUtils; import com.trafficaudit.holiday.entity.HolidayCalendar; import com.trafficaudit.holiday.entity.HolidayPassengerFlow; import com.trafficaudit.holiday.entity.HolidayVehicleFlow; import com.trafficaudit.holiday.mapper.HolidayPassengerFlowMapper; import com.trafficaudit.holiday.mapper.HolidayVehicleFlowMapper; import org.junit.jupiter.api.Test; import java.lang.reflect.Field; import java.math.BigDecimal; import java.time.LocalDate; import java.util.ArrayList; import java.util.List; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; /** * The Excel comparison base column (C) is an operator backup value only: it must never change the * national/provincial ring ratio. This locks in that behaviour. */ class HolidayComparisonBaseTest { @Test void ringRatioUsesPreviousDayEvenWhenComparisonBaseIsStored() throws Exception { HolidayConfigService configService = mock(HolidayConfigService.class); HolidayPassengerFlowMapper passengerMapper = mock(HolidayPassengerFlowMapper.class); HolidayVehicleFlowMapper vehicleMapper = mock(HolidayVehicleFlowMapper.class); when(configService.coefficientOf(2026, HolidayConstants.CATEGORY_HOLIDAY)).thenReturn(new BigDecimal("2.4")); when(passengerMapper.selectList(any())).thenReturn(new ArrayList()); when(vehicleMapper.selectList(any())).thenReturn(vehicleRows()); HolidayStatService service = new HolidayStatService(); inject(service, "configService", configService); inject(service, "passengerFlowMapper", passengerMapper); inject(service, "vehicleFlowMapper", vehicleMapper); List> rows = service.buildOverviewRows(2026, HolidayConstants.TYPE_NATIONAL_DAY, calendar()); BigDecimal ringRatio = (BigDecimal) rows.get(3).get("nationalProvincialRingRatio"); assertEquals(ratio(new BigDecimal("322.7695"), new BigDecimal("323.7837")), ringRatio); assertNotEquals(ratio(new BigDecimal("322.7695"), new BigDecimal("320.2418")), ringRatio); assertEquals(new BigDecimal("320.2418"), rows.get(2).get("comparisonBase")); } @Test void exportFormulaComparesPreviousRowInsteadOfComparisonBaseColumn() { assertEquals("=(D6-D5)/D5", HolidayWorkbookService.ringRatioFormula(6, 5)); } private static BigDecimal ratio(BigDecimal current, BigDecimal base) { return HolidayNumberUtils.normalize(HolidayNumberUtils.divide(current, base).subtract(BigDecimal.ONE)); } private static List vehicleRows() { List rows = new ArrayList<>(); rows.add(vehicle("2026-10-03", "323.7837", "320.2418")); rows.add(vehicle("2026-10-04", "322.7695", null)); return rows; } private static HolidayVehicleFlow vehicle(String date, String value, String base) { HolidayVehicleFlow row = new HolidayVehicleFlow(); row.setYear(2026); row.setFlowDate(LocalDate.parse(date)); row.setRoadCategory(HolidayConstants.ROAD_NATIONAL_PROVINCIAL); row.setVehicleType(HolidayConstants.VEHICLE_MEDIUM_SMALL_PASSENGER); row.setVehicleCount(new BigDecimal(value)); row.setComparisonBase(base == null ? null : new BigDecimal(base)); return row; } private static List calendar() { List days = new ArrayList<>(); for (int i = 1; i <= 4; i++) { HolidayCalendar day = new HolidayCalendar(); day.setYear(2026); day.setHolidayType(HolidayConstants.TYPE_NATIONAL_DAY); day.setDayIndex(i); day.setHolidayDate(LocalDate.of(2026, 10, i)); days.add(day); } return days; } private static void inject(Object target, String field, Object value) throws Exception { Field declared = target.getClass().getDeclaredField(field); declared.setAccessible(true); declared.set(target, value); } }