package com.zy.common.service; import com.baomidou.mybatisplus.mapper.Wrapper; import com.zy.asrs.entity.BasCrnp; import com.zy.asrs.entity.LocMast; import com.zy.asrs.entity.RowLastno; import com.zy.asrs.entity.RowLastnoType; import com.zy.asrs.service.BasCrnDepthRuleService; import com.zy.asrs.service.BasCrnpService; import com.zy.asrs.service.BasDevpService; import com.zy.asrs.service.LocMastService; import com.zy.asrs.service.RowLastnoService; import com.zy.asrs.service.StaDescService; import com.zy.common.model.CrnDepthRuleProfile; import com.zy.common.model.LocTypeDto; import com.zy.common.model.StartupDto; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.test.util.ReflectionTestUtils; import java.lang.reflect.Constructor; import java.util.Arrays; import java.util.Collections; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class CommonServiceRun2AllocationTest { @Mock private BasCrnpService basCrnpService; @Mock private StaDescService staDescService; @Mock private BasDevpService basDevpService; @Mock private LocMastService locMastService; @Mock private BasCrnDepthRuleService basCrnDepthRuleService; @Mock private RowLastnoService rowLastnoService; private CommonService commonService; @BeforeEach void setUp() { commonService = new CommonService(); ReflectionTestUtils.setField(commonService, "basCrnpService", basCrnpService); ReflectionTestUtils.setField(commonService, "staDescService", staDescService); ReflectionTestUtils.setField(commonService, "basDevpService", basDevpService); ReflectionTestUtils.setField(commonService, "locMastService", locMastService); ReflectionTestUtils.setField(commonService, "basCrnDepthRuleService", basCrnDepthRuleService); ReflectionTestUtils.setField(commonService, "rowLastnoService", rowLastnoService); } @Test void findRun2EmptyLocByCrnNos_shouldStopAtFirstMatchingCraneInOrder() { RowLastno rowLastno = singleExtensionRowLastno(1, 2, 1, 4); RowLastnoType rowLastnoType = standardRowLastnoType(); StartupDto startupDto = new StartupDto(); LocTypeDto locTypeDto = fullLocType((short) 1); LocMast firstCraneLoc = openLoc("0200101", 2, 2, 1, 1); when(basCrnpService.selectById(2)).thenReturn(activeCrn(2)); when(basCrnDepthRuleService.resolveProfile(eq(rowLastno), eq(2), any())) .thenReturn(singleExtensionProfile(1, 2)); when(locMastService.selectList(any())).thenReturn(Collections.singletonList(firstCraneLoc)); LocMast result = ReflectionTestUtils.invokeMethod(commonService, "findRun2EmptyLocByCrnNos", rowLastno, rowLastnoType, Arrays.asList(2, 1), locTypeDto, 1, 100, startupDto, 1, "run2-order", false); assertEquals("0200101", result.getLocNo()); assertEquals(Integer.valueOf(2), result.getCrnNo()); verify(locMastService, times(1)).selectList(any()); } @Test void findRun2EmptyLocByCrnNos_shouldSkipInactiveCrane() { RowLastno rowLastno = singleExtensionRowLastno(1, 2, 1, 4); RowLastnoType rowLastnoType = standardRowLastnoType(); StartupDto startupDto = new StartupDto(); LocTypeDto locTypeDto = fullLocType((short) 1); LocMast secondCraneLoc = openLoc("0400101", 1, 4, 1, 1); when(basCrnpService.selectById(2)).thenReturn(inactiveCrn(2)); when(basCrnpService.selectById(1)).thenReturn(activeCrn(1)); when(basCrnDepthRuleService.resolveProfile(eq(rowLastno), eq(1), any())) .thenReturn(singleExtensionProfile(1, 4)); when(locMastService.selectList(any())).thenReturn(Collections.singletonList(secondCraneLoc)); LocMast result = ReflectionTestUtils.invokeMethod(commonService, "findRun2EmptyLocByCrnNos", rowLastno, rowLastnoType, Arrays.asList(2, 1), locTypeDto, 1, 100, startupDto, 1, "run2-skip-offline", false); assertEquals("0400101", result.getLocNo()); assertEquals(Integer.valueOf(1), result.getCrnNo()); verify(locMastService, times(1)).selectList(any()); } @Test void findOpenLocsByRow_forSingleExtension_shouldOrderByLevThenBay() { RowLastno rowLastno = singleExtensionRowLastno(1, 1, 1, 2); RowLastnoType rowLastnoType = standardRowLastnoType(); when(locMastService.selectList(any())).thenReturn(Collections.emptyList()); ReflectionTestUtils.invokeMethod(commonService, "findOpenLocsByRow", rowLastno, rowLastnoType, 1, 1, fullLocType((short) 1), true); @SuppressWarnings("rawtypes") ArgumentCaptor wrapperCaptor = ArgumentCaptor.forClass(Wrapper.class); verify(locMastService).selectList(wrapperCaptor.capture()); String sqlSegment = wrapperCaptor.getValue().getSqlSegment().toLowerCase(); assertTrue(sqlSegment.contains("order by")); assertTrue(sqlSegment.indexOf("lev1") < sqlSegment.indexOf("bay1")); } @Test void findConfiguredEmptyLocForCrn_forDoubleExtension_shouldPreferDeepOpenLoc() { RowLastno rowLastno = doubleExtensionRowLastno(); RowLastnoType rowLastnoType = standardRowLastnoType(); LocMast shallowLoc = openLoc("0100101", 1, 1, 1, 1); LocMast deepOpenLoc = openLoc("0200101", 1, 2, 1, 1); when(basCrnDepthRuleService.resolveProfile(eq(rowLastno), eq(1), any())) .thenReturn(doubleExtensionProfile()); when(locMastService.selectList(any())).thenReturn(Collections.singletonList(shallowLoc)); when(locMastService.selectOne(any())).thenReturn(deepOpenLoc); LocMast result = ReflectionTestUtils.invokeMethod(commonService, "findConfiguredEmptyLocForCrn", rowLastno, rowLastnoType, 1, 1, fullLocType((short) 1)); assertEquals("0200101", result.getLocNo()); } @Test void findConfiguredEmptyLocForCrn_forDoubleExtension_shouldFallbackToShallowWhenDeepBlocked() { RowLastno rowLastno = doubleExtensionRowLastno(); RowLastnoType rowLastnoType = standardRowLastnoType(); LocMast shallowLoc = openLoc("0100101", 1, 1, 1, 1); LocMast deepBlockedLoc = blockedLoc("0200101", 1, 2, 1, 1, "F"); when(basCrnDepthRuleService.resolveProfile(eq(rowLastno), eq(1), any())) .thenReturn(doubleExtensionProfile()); when(locMastService.selectList(any())).thenReturn(Collections.singletonList(shallowLoc)); when(locMastService.selectOne(any())).thenReturn(null, deepBlockedLoc); LocMast result = ReflectionTestUtils.invokeMethod(commonService, "findConfiguredEmptyLocForCrn", rowLastno, rowLastnoType, 1, 1, fullLocType((short) 1)); assertEquals("0100101", result.getLocNo()); } @Test void getNextRun2CurrentRow_shouldAdvanceWithinRunnableCrnsOnly() { RowLastno rowLastno = singleExtensionRowLastno(1, 4, 1, 8); Integer nextRow = ReflectionTestUtils.invokeMethod(commonService, "getNextRun2CurrentRow", rowLastno, Arrays.asList(2, 4), 2, 3); assertEquals(Integer.valueOf(7), nextRow); } @Test void resolveRun2CrnNo_shouldPreferCurrentCrnNoOverCurrentRow() { RowLastno rowLastno = singleExtensionRowLastno(1, 4, 1, 8); rowLastno.setCurrentRow(7); rowLastno.setCurrentCrnNo(2); Integer currentCrnNo = ReflectionTestUtils.invokeMethod(commonService, "resolveRun2CrnNo", rowLastno); assertEquals(Integer.valueOf(2), currentCrnNo); } @Test void resolveRun2CrnNo_shouldFallbackToStartCrnNoWhenCurrentCrnNoIsInvalid() { RowLastno rowLastno = singleExtensionRowLastno(2, 4, 3, 8); rowLastno.setCurrentCrnNo(null); assertEquals(Integer.valueOf(2), ReflectionTestUtils.invokeMethod(commonService, "resolveRun2CrnNo", rowLastno)); rowLastno.setCurrentCrnNo(0); assertEquals(Integer.valueOf(2), ReflectionTestUtils.invokeMethod(commonService, "resolveRun2CrnNo", rowLastno)); rowLastno.setCurrentCrnNo(5); assertEquals(Integer.valueOf(2), ReflectionTestUtils.invokeMethod(commonService, "resolveRun2CrnNo", rowLastno)); } @Test void advanceNormalRun2Cursor_shouldUpdateCurrentRowAndCurrentCrnNoTogether() { RowLastno rowLastno = singleExtensionRowLastno(1, 4, 1, 8); rowLastno.setCurrentRow(1); rowLastno.setCurrentCrnNo(1); ReflectionTestUtils.invokeMethod(commonService, "advanceNormalRun2Cursor", rowLastno, 1, Arrays.asList(1, 3), 1); assertEquals(Integer.valueOf(5), rowLastno.getCurrentRow()); assertEquals(Integer.valueOf(3), rowLastno.getCurrentCrnNo()); verify(rowLastnoService).updateById(rowLastno); } @Test void advanceEmptyPalletRun2Cursor_shouldUpdateCurrentRowAndCurrentCrnNoTogether() throws Exception { RowLastno rowLastno = singleExtensionRowLastno(1, 4, 1, 8); rowLastno.setCurrentRow(5); rowLastno.setCurrentCrnNo(3); LocMast locMast = openLoc("0500101", 3, 5, 1, 1); Object searchResult = run2AreaSearchResult(locMast, rowLastno, Arrays.asList(1, 3)); ReflectionTestUtils.invokeMethod(commonService, "advanceEmptyPalletRun2Cursor", searchResult, locMast); assertEquals(Integer.valueOf(1), rowLastno.getCurrentRow()); assertEquals(Integer.valueOf(1), rowLastno.getCurrentCrnNo()); verify(rowLastnoService).updateById(rowLastno); } private RowLastno singleExtensionRowLastno(int startCrnNo, int endCrnNo, int startRow, int endRow) { RowLastno rowLastno = new RowLastno(); rowLastno.setWhsType(1); rowLastno.setsCrnNo(startCrnNo); rowLastno.seteCrnNo(endCrnNo); rowLastno.setsRow(startRow); rowLastno.seteRow(endRow); rowLastno.setTypeId(2); return rowLastno; } private RowLastno doubleExtensionRowLastno() { RowLastno rowLastno = new RowLastno(); rowLastno.setWhsType(1); rowLastno.setsCrnNo(1); rowLastno.seteCrnNo(1); rowLastno.setsRow(1); rowLastno.seteRow(2); rowLastno.setTypeId(1); return rowLastno; } private RowLastnoType standardRowLastnoType() { RowLastnoType rowLastnoType = new RowLastnoType(); rowLastnoType.setType(1); return rowLastnoType; } private Object run2AreaSearchResult(LocMast locMast, RowLastno rowLastno, List runnableCrnNos) throws Exception { Class clazz = Class.forName("com.zy.common.service.CommonService$Run2AreaSearchResult"); Constructor constructor = clazz.getDeclaredConstructor(LocMast.class, RowLastno.class, List.class); constructor.setAccessible(true); return constructor.newInstance(locMast, rowLastno, runnableCrnNos); } private CrnDepthRuleProfile singleExtensionProfile(int shallowRow, int searchRow) { CrnDepthRuleProfile profile = new CrnDepthRuleProfile(); profile.setLayoutType(1); profile.setSearchRows(Collections.singletonList(searchRow)); profile.setShallowRows(Collections.singletonList(shallowRow)); return profile; } private CrnDepthRuleProfile doubleExtensionProfile() { CrnDepthRuleProfile profile = new CrnDepthRuleProfile(); profile.setLayoutType(2); profile.setSearchRows(Arrays.asList(1, 2)); profile.setShallowRows(Collections.singletonList(1)); profile.setDeepRows(Collections.singletonList(2)); profile.getShallowToDeepRow().put(1, 2); profile.getDeepToShallowRow().put(2, 1); return profile; } private BasCrnp activeCrn(int crnNo) { BasCrnp basCrnp = new BasCrnp(); basCrnp.setCrnNo(crnNo); basCrnp.setInEnable("Y"); basCrnp.setOutEnable("Y"); basCrnp.setCrnSts(3); basCrnp.setCrnErr(0L); return basCrnp; } private BasCrnp inactiveCrn(int crnNo) { BasCrnp basCrnp = activeCrn(crnNo); basCrnp.setInEnable("N"); return basCrnp; } private LocTypeDto fullLocType(short locType1) { LocTypeDto locTypeDto = new LocTypeDto(); locTypeDto.setLocType1(locType1); return locTypeDto; } private LocMast openLoc(String locNo, int crnNo, int row, int bay, int lev) { LocMast locMast = new LocMast(); locMast.setLocNo(locNo); locMast.setCrnNo(crnNo); locMast.setWhsType(1L); locMast.setRow1(row); locMast.setBay1(bay); locMast.setLev1(lev); locMast.setLocSts("O"); locMast.setLocType1((short) 1); locMast.setLocType2((short) 2); return locMast; } private LocMast blockedLoc(String locNo, int crnNo, int row, int bay, int lev, String locSts) { LocMast locMast = openLoc(locNo, crnNo, row, bay, lev); locMast.setLocSts(locSts); return locMast; } }