fix(firstleg): atomically finalize PDA completion
This commit is contained in:
+74
@@ -14,8 +14,10 @@ import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
@SpringJUnitConfig(LoadingDeliveryService.class)
|
||||
class LoadingDeliveryServiceTest {
|
||||
@@ -45,4 +47,76 @@ class LoadingDeliveryServiceTest {
|
||||
|
||||
verify(cabinetEvidence).requireComplete(99L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void pdaFinalizeUsesSourceVersionIncrementedByFullSettlement() throws Exception {
|
||||
Snapshot refreshed = new Snapshot(20L, 10L, "LOADING", 8);
|
||||
when(repository.completionTarget(10L, 20L)).thenReturn(null);
|
||||
when(repository.findInboundForUpdate(10L, 20L)).thenReturn(refreshed);
|
||||
when(repository.claimOperation(eq(10L), eq(20L), eq(9L), eq("COMPLETE_LOADING"),
|
||||
eq("pda-full"), anyString())).thenReturn(true);
|
||||
when(repository.findInbound(10L, 20L)).thenReturn(refreshed);
|
||||
when(loadingPicks.inboundProgress(10L, 20L)).thenReturn(resolvedProgress());
|
||||
when(cabinetEvidence.requireContainerBookingId(10L, 20L)).thenReturn(99L);
|
||||
when(repository.completeLoading(10L, 20L, 9L, 8)).thenReturn(1);
|
||||
when(objectMapper.writeValueAsString(org.mockito.ArgumentMatchers.any())).thenReturn("result");
|
||||
|
||||
service.completeAfterPdaSettlement(10L, 20L, 9L, "operator", "pda-full", "done");
|
||||
|
||||
verify(repository).completeLoading(10L, 20L, 9L, 8);
|
||||
}
|
||||
|
||||
@Test
|
||||
void pdaFinalizeLocksAndUsesLoadedSliceVersionAfterPartialSettlement() throws Exception {
|
||||
var target = new com.baiyue.firstleg.loading.mapper.LoadingInboundMapper.CompletionTarget(
|
||||
20L, 21L, 22L, "LOADING", 6, "LOADING", 9);
|
||||
Snapshot source = new Snapshot(20L, 10L, "LOADING", 6);
|
||||
Snapshot loadedSlice = new Snapshot(21L, 10L, "LOADING", 9);
|
||||
when(repository.completionTarget(10L, 20L)).thenReturn(target);
|
||||
when(repository.findInboundForUpdate(10L, 21L)).thenReturn(loadedSlice);
|
||||
when(repository.claimOperation(eq(10L), eq(20L), eq(9L), eq("COMPLETE_LOADING"),
|
||||
eq("pda-partial"), anyString())).thenReturn(true);
|
||||
when(repository.findInbound(10L, 20L)).thenReturn(source);
|
||||
when(loadingPicks.inboundProgress(10L, 20L)).thenReturn(resolvedProgress());
|
||||
when(cabinetEvidence.requireContainerBookingId(10L, 20L)).thenReturn(99L);
|
||||
when(repository.completeLoadedChild(10L, 20L, 9L, 9)).thenReturn(1);
|
||||
when(objectMapper.writeValueAsString(org.mockito.ArgumentMatchers.any())).thenReturn("result");
|
||||
|
||||
service.completeAfterPdaSettlement(10L, 20L, 9L, "operator", "pda-partial", "done");
|
||||
|
||||
verify(repository).findInboundForUpdate(10L, 21L);
|
||||
verify(repository).completeLoadedChild(10L, 20L, 9L, 9);
|
||||
}
|
||||
|
||||
@Test
|
||||
void pdaFinalizeKeepsOptimisticConflictForRealConcurrentChange() {
|
||||
Snapshot refreshed = new Snapshot(20L, 10L, "LOADING", 8);
|
||||
when(repository.completionTarget(10L, 20L)).thenReturn(null);
|
||||
when(repository.findInboundForUpdate(10L, 20L)).thenReturn(refreshed);
|
||||
when(repository.claimOperation(eq(10L), eq(20L), eq(9L), eq("COMPLETE_LOADING"),
|
||||
eq("pda-conflict"), anyString())).thenReturn(true);
|
||||
when(repository.findInbound(10L, 20L)).thenReturn(refreshed);
|
||||
when(loadingPicks.inboundProgress(10L, 20L)).thenReturn(resolvedProgress());
|
||||
when(cabinetEvidence.requireContainerBookingId(10L, 20L)).thenReturn(99L);
|
||||
when(repository.completeLoading(10L, 20L, 9L, 8)).thenReturn(0);
|
||||
try {
|
||||
when(objectMapper.writeValueAsString(org.mockito.ArgumentMatchers.any())).thenReturn("request");
|
||||
} catch (com.fasterxml.jackson.core.JsonProcessingException exception) {
|
||||
throw new AssertionError(exception);
|
||||
}
|
||||
|
||||
assertThatThrownBy(() -> service.completeAfterPdaSettlement(10L, 20L, 9L, "operator",
|
||||
"pda-conflict", "done")).isInstanceOf(com.baiyue.common.core.exception.BizException.class);
|
||||
|
||||
verify(repository).completeLoading(10L, 20L, 9L, 8);
|
||||
verify(histories, never()).insert(org.mockito.ArgumentMatchers
|
||||
.any(com.baiyue.firstleg.entity.InboundStatusHistoryEntity.class));
|
||||
}
|
||||
|
||||
private static InboundProgress resolvedProgress() {
|
||||
InboundProgress progress = new InboundProgress();
|
||||
progress.setExpectedCount(2); progress.setPickedCount(2); progress.setTaskCount(1);
|
||||
progress.setCompletedTaskCount(0); progress.setUnresolvedCount(0);
|
||||
return progress;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,10 +44,13 @@ import org.mockito.InOrder;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.springframework.boot.test.mock.mockito.MockBean;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
@@ -74,6 +77,8 @@ class LoadingPickServiceTest {
|
||||
@MockBean
|
||||
private LoadingCompletionCommandRepository completionCommands;
|
||||
@MockBean
|
||||
private TransactionTemplate completionTransaction;
|
||||
@MockBean
|
||||
private SmartLoadingRepository smartLoadingRepository;
|
||||
@MockBean
|
||||
private OceanBookingRepository oceanBookingRepository;
|
||||
@@ -100,6 +105,12 @@ class LoadingPickServiceTest {
|
||||
|
||||
@BeforeEach
|
||||
void activeOperationalContainer() {
|
||||
doAnswer(invocation -> {
|
||||
@SuppressWarnings("unchecked")
|
||||
Consumer<TransactionStatus> action = invocation.getArgument(0);
|
||||
action.accept(mock(TransactionStatus.class));
|
||||
return null;
|
||||
}).when(completionTransaction).executeWithoutResult(any());
|
||||
ContainerBookingEntity booking = new ContainerBookingEntity();
|
||||
booking.setId(99L);
|
||||
booking.setContainerNo("CSLU1234567");
|
||||
|
||||
Reference in New Issue
Block a user