Merge remote-tracking branch 'origin/test' into dev
This commit is contained in:
+10
-5
@@ -8,6 +8,7 @@ import com.baiyue.lastleg.inboundplan.dto.ManualInboundPlanRequest;
|
||||
import com.baiyue.lastleg.inboundplan.dto.UpdateFbaAppointmentRequest;
|
||||
import com.baiyue.lastleg.inboundplan.service.InboundFbaAppointmentService;
|
||||
import com.baiyue.lastleg.inboundplan.service.InboundPlanService;
|
||||
import com.baiyue.lastleg.inboundplan.service.ContainerInboundPlanConversionCoordinator;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -30,6 +31,7 @@ import static org.mockito.Mockito.when;
|
||||
class InboundPlanControllerTest {
|
||||
@Mock InboundPlanService service;
|
||||
@Mock InboundFbaAppointmentService appointmentService;
|
||||
@Mock ContainerInboundPlanConversionCoordinator containerConversion;
|
||||
private InboundPlanController controller;
|
||||
|
||||
@BeforeEach
|
||||
@@ -37,6 +39,7 @@ class InboundPlanControllerTest {
|
||||
controller = new InboundPlanController();
|
||||
ReflectionTestUtils.setField(controller, "service", service);
|
||||
ReflectionTestUtils.setField(controller, "appointmentService", appointmentService);
|
||||
ReflectionTestUtils.setField(controller, "containerConversion", containerConversion);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -79,19 +82,21 @@ class InboundPlanControllerTest {
|
||||
|
||||
@Test
|
||||
void platformContainerConversionUsesStableBookingIdentity() {
|
||||
controller.convertContainer(platform(), new ConvertContainerInboundPlanRequest(" BOOKING-1 "));
|
||||
var request = new ConvertContainerInboundPlanRequest(" BOOKING-1 ", 3, "a".repeat(64));
|
||||
controller.convertContainer(platform(), "convert-key", request);
|
||||
|
||||
verify(service).convertContainer(" BOOKING-1 ");
|
||||
verify(containerConversion).confirm(request, "convert-key");
|
||||
}
|
||||
|
||||
@Test
|
||||
void merchantCannotConvertAWholeContainer() {
|
||||
AuthenticatedUser customer = new AuthenticatedUser(8L, 100L, 100L, 9L, "customer", "MERCHANT", "WEB", Set.of());
|
||||
|
||||
assertThatThrownBy(() -> controller.convertContainer(customer,
|
||||
new ConvertContainerInboundPlanRequest("BOOKING-1")))
|
||||
assertThatThrownBy(() -> controller.convertContainer(customer, "convert-key",
|
||||
new ConvertContainerInboundPlanRequest("BOOKING-1", 3, "a".repeat(64))))
|
||||
.isInstanceOf(ResponseStatusException.class);
|
||||
verify(service, never()).convertContainer(org.mockito.ArgumentMatchers.any());
|
||||
verify(containerConversion, never()).confirm(org.mockito.ArgumentMatchers.any(),
|
||||
org.mockito.ArgumentMatchers.any());
|
||||
}
|
||||
|
||||
private static AuthenticatedUser platform() {
|
||||
|
||||
+36
@@ -8,6 +8,8 @@ import org.springframework.test.web.client.MockRestServiceServer;
|
||||
import org.springframework.web.client.RestClient;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
|
||||
@@ -159,4 +161,38 @@ class HttpWarehouseResourceGatewayTest {
|
||||
server.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
void ensurePalletBatchAcceptsJacksonStringEncodedLongs() {
|
||||
server.expect(requestTo("http://warehouse/internal/v1/warehouse-pallet-batches/ensure-reserved"))
|
||||
.andRespond(withSuccess("""
|
||||
{"code":"200","data":{"batchId":"41","batchKey":"BATCH-1","status":"RESERVED",
|
||||
"attemptNo":1,"replayed":false,"items":[{"customerId":"100","businessKey":"CIP:1:1",
|
||||
"inboundPlanNo":"IP-21","sequenceNo":1,"palletId":"51","palletCode":"PLT-51",
|
||||
"locationId":"61","resourceLockId":"71","status":"RESERVED"}]}}
|
||||
""", MediaType.APPLICATION_JSON));
|
||||
|
||||
var result = gateway.ensurePalletBatch(new WarehouseResourceGateway.PalletBatchEnsure(31L, "BATCH-1",
|
||||
List.of(new WarehouseResourceGateway.PalletBatchItemRequest(
|
||||
100L, "CIP:1:1", "IP-21", 1, "STANDARD", "ONT8"))));
|
||||
|
||||
assertThat(result.batchId()).isEqualTo(41L);
|
||||
assertThat(result.items().get(0).resourceLockId()).isEqualTo(71L);
|
||||
server.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
void palletBatchMapsBusinessConflictEnvelopeEvenWithHttp200() {
|
||||
server.expect(requestTo("http://warehouse/internal/v1/warehouse-pallet-batches/confirm"))
|
||||
.andRespond(withSuccess("""
|
||||
{"code":"A0409","message":"Batch request hash conflict","data":null}
|
||||
""", MediaType.APPLICATION_JSON));
|
||||
|
||||
assertThatThrownBy(() -> gateway.confirmPalletBatch(
|
||||
new WarehouseResourceGateway.PalletBatchCommand(31L, "BATCH-1", "CONFIRM:1")))
|
||||
.isInstanceOf(ResponseStatusException.class)
|
||||
.hasMessageContaining("409")
|
||||
.hasMessageContaining("Batch request hash conflict");
|
||||
server.verify();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
package com.baiyue.lastleg.inboundplan.service;
|
||||
|
||||
import com.baiyue.lastleg.inboundplan.dto.ConvertContainerInboundPlanRequest;
|
||||
import com.baiyue.lastleg.inboundplan.entity.ContainerPlanConversionCommand;
|
||||
import com.baiyue.lastleg.inboundplan.mapper.ContainerPlanConversionCommandMapper;
|
||||
import com.baiyue.lastleg.inboundplan.pda.gateway.WarehouseResourceGateway;
|
||||
import com.fasterxml.jackson.databind.json.JsonMapper;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class ContainerInboundPlanConversionCoordinatorTest {
|
||||
private static final String BOOKING_ID = "BOOKING-1";
|
||||
private static final String SNAPSHOT_HASH = "a".repeat(64);
|
||||
|
||||
@Mock InboundPlanService plans;
|
||||
@Mock WarehouseResourceGateway warehouse;
|
||||
@Mock ContainerPlanConversionCommandMapper commands;
|
||||
|
||||
private ContainerInboundPlanConversionCoordinator coordinator;
|
||||
private JsonMapper objectMapper;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
coordinator = new ContainerInboundPlanConversionCoordinator();
|
||||
objectMapper = JsonMapper.builder().findAndAddModules().build();
|
||||
ReflectionTestUtils.setField(coordinator, "plans", plans);
|
||||
ReflectionTestUtils.setField(coordinator, "warehouse", warehouse);
|
||||
ReflectionTestUtils.setField(coordinator, "commands", commands);
|
||||
ReflectionTestUtils.setField(coordinator, "objectMapper", objectMapper);
|
||||
}
|
||||
|
||||
@Test
|
||||
void anotherIdempotencyKeyForCompletedCabinetReplaysWithoutCreatingAnotherBatch() throws Exception {
|
||||
ConvertContainerInboundPlanRequest request = request();
|
||||
ContainerPlanConversionCommand existing = command("FIRST-KEY", "COMPLETED", request);
|
||||
existing.setResultJson("[]");
|
||||
when(commands.find(BOOKING_ID, "SECOND-KEY")).thenReturn(null);
|
||||
when(commands.findByBooking(BOOKING_ID)).thenReturn(existing);
|
||||
|
||||
assertEquals(List.of(), coordinator.confirm(request, "SECOND-KEY"));
|
||||
|
||||
verify(warehouse, never()).ensurePalletBatch(org.mockito.ArgumentMatchers.any());
|
||||
verify(commands, never()).insert(org.mockito.ArgumentMatchers.any(ContainerPlanConversionCommand.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void concurrentCabinetClaimRejectsFollowerBeforeWarehouseReservation() throws Exception {
|
||||
ConvertContainerInboundPlanRequest request = request();
|
||||
ContainerPlanConversionCommand applying = command("FIRST-KEY", "APPLYING", request);
|
||||
when(commands.find(BOOKING_ID, "SECOND-KEY")).thenReturn(null);
|
||||
when(commands.findByBooking(BOOKING_ID)).thenReturn(applying);
|
||||
|
||||
ResponseStatusException error = assertThrows(ResponseStatusException.class,
|
||||
() -> coordinator.confirm(request, "SECOND-KEY"));
|
||||
|
||||
assertEquals(409, error.getStatusCode().value());
|
||||
verify(warehouse, never()).ensurePalletBatch(org.mockito.ArgumentMatchers.any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void failedConditionalClaimStopsBeforeWarehouseReservation() throws Exception {
|
||||
ConvertContainerInboundPlanRequest request = request();
|
||||
ContainerPlanConversionCommand prepared = command("FIRST-KEY", "PREPARED", request);
|
||||
when(commands.find(BOOKING_ID, "SECOND-KEY")).thenReturn(null);
|
||||
when(commands.findByBooking(BOOKING_ID)).thenReturn(prepared);
|
||||
when(commands.claim(org.mockito.ArgumentMatchers.eq(1L), org.mockito.ArgumentMatchers.eq("PREPARED"),
|
||||
org.mockito.ArgumentMatchers.eq("APPLYING"), org.mockito.ArgumentMatchers.any())).thenReturn(0);
|
||||
|
||||
ResponseStatusException error = assertThrows(ResponseStatusException.class,
|
||||
() -> coordinator.confirm(request, "SECOND-KEY"));
|
||||
|
||||
assertEquals(409, error.getStatusCode().value());
|
||||
verify(warehouse, never()).ensurePalletBatch(org.mockito.ArgumentMatchers.any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void reservationMustMatchEveryStableItemIdentity() throws Exception {
|
||||
var expected = new ContainerInboundPlanConversionCoordinator.PreparedItem(
|
||||
"100", 11L, 21L, "IP-21", 1, 1, "CIP:1:1", "ONT8");
|
||||
var preparation = new ContainerInboundPlanConversionCoordinator.Preparation(31L, List.of(expected));
|
||||
var response = new WarehouseResourceGateway.PalletBatchResponse(41L, "BATCH-1", "RESERVED", 1,
|
||||
false, List.of(new WarehouseResourceGateway.PalletBatchItem(
|
||||
100L, "CIP:1:1", "WRONG-PLAN", 1, 51L, "PLT-51", 61L, 71L, "RESERVED")));
|
||||
|
||||
ResponseStatusException error = assertThrows(ResponseStatusException.class,
|
||||
() -> ReflectionTestUtils.invokeMethod(coordinator, "bind", preparation, response));
|
||||
|
||||
assertEquals(409, error.getStatusCode().value());
|
||||
}
|
||||
|
||||
private ConvertContainerInboundPlanRequest request() {
|
||||
return new ConvertContainerInboundPlanRequest(BOOKING_ID, 7L, SNAPSHOT_HASH);
|
||||
}
|
||||
|
||||
private ContainerPlanConversionCommand command(String key, String status,
|
||||
ConvertContainerInboundPlanRequest request) throws Exception {
|
||||
ContainerPlanConversionCommand command = new ContainerPlanConversionCommand();
|
||||
command.setId(1L);
|
||||
command.setSourceContainerBookingId(BOOKING_ID);
|
||||
command.setIdempotencyKey(key);
|
||||
command.setRequestHash(ReflectionTestUtils.invokeMethod(coordinator, "hash",
|
||||
List.of(BOOKING_ID, request.snapshotVersion(), request.snapshotHash())));
|
||||
command.setSnapshotVersion(request.snapshotVersion());
|
||||
command.setSnapshotHash(request.snapshotHash());
|
||||
command.setWarehouseId(31L);
|
||||
command.setBatchKey("BATCH-1");
|
||||
command.setStatus(status);
|
||||
command.setPreparationJson(objectMapper.writeValueAsString(
|
||||
new ContainerInboundPlanConversionCoordinator.Preparation(31L, List.of())));
|
||||
return command;
|
||||
}
|
||||
}
|
||||
+2
@@ -424,6 +424,8 @@ class InboundPlanServiceTest {
|
||||
void containerConversionPrevalidatesEveryOrderBeforeWritingIndependentPlans() throws Exception {
|
||||
assertNotNull(InboundPlanService.class.getMethod("convertContainer", String.class)
|
||||
.getAnnotation(Transactional.class));
|
||||
assertNotNull(InboundPlanService.class.getMethod("convertContainerReserved", String.class,
|
||||
long.class, String.class, List.class).getAnnotation(Transactional.class));
|
||||
when(orderMapper.lockContainerOrders("BOOKING-1")).thenReturn(List.of(
|
||||
new InboundPlanOrderMapper.ContainerOrderRef("100", 1L, "PENDING_ASSIGNMENT", "MSCU6639870", "STANDARD"),
|
||||
new InboundPlanOrderMapper.ContainerOrderRef("200", 2L, "DESTINATION_ASSIGNED", "MSCU6639870", "STANDARD")));
|
||||
|
||||
Reference in New Issue
Block a user