merge: sync latest test environment changes
This commit is contained in:
+6
@@ -95,6 +95,12 @@ class ReceivingContainerContractTest {
|
||||
assertTrue(schema.contains("柜号全局唯一锁键"));
|
||||
assertTrue(schema.contains("create table if not exists ll_receiving_container_operation"));
|
||||
assertTrue(schema.contains("unique key uk_ll_receiving_operation(operator_key,idempotency_key)"));
|
||||
assertFalse(tableBlock(schema, "ll_receiving_container_session").contains("customer_id"));
|
||||
assertFalse(tableBlock(schema, "ll_receiving_container_operation").contains("customer_id"));
|
||||
assertFalse(tableBlock(schema, "ll_container_shipping_gate").contains("customer_id"));
|
||||
assertTrue(unified.contains("alter table ll_receiving_container_session drop column customer_id"));
|
||||
assertTrue(unified.contains("alter table ll_receiving_container_operation drop column customer_id"));
|
||||
assertTrue(unified.contains("alter table ll_container_shipping_gate drop column customer_id"));
|
||||
assertTrue(schema.contains("create table if not exists ll_receiving_sku_receipt"));
|
||||
assertTrue(schema.contains("unique key uk_ll_receiving_sku_pallet (customer_id,line_id,pallet_id,sku_code)"));
|
||||
String receivingSession = tableBlock(schema, "ll_receiving_container_session");
|
||||
|
||||
+9
-6
@@ -46,13 +46,15 @@ class InboundPdaContainerCompletionTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void twoPlansOfOneContainerStockInTogetherAndPublishOneContainerEvent() {
|
||||
void twoCustomerPlansOfOneContainerStockInTogetherAndPublishOneContainerEvent() {
|
||||
InboundPlan first = plan(1L, "PLAN-1");
|
||||
InboundPlan second = plan(2L, "PLAN-2");
|
||||
second.setCustomerId("200");
|
||||
InboundPdaTask firstTask = task(11L, 1L);
|
||||
InboundPdaTask secondTask = task(22L, 2L);
|
||||
secondTask.setCustomerId("200");
|
||||
when(repository.lockTask("100", 1L)).thenReturn(firstTask);
|
||||
when(repository.lockTask("100", 2L)).thenReturn(secondTask);
|
||||
when(repository.lockTask("200", 2L)).thenReturn(secondTask);
|
||||
when(repository.lines(1L)).thenReturn(List.of(line(101L)));
|
||||
when(repository.lines(2L)).thenReturn(List.of(line(202L)));
|
||||
when(repository.allocations(1L)).thenReturn(List.of(allocation(101L)));
|
||||
@@ -63,11 +65,12 @@ class InboundPdaContainerCompletionTest {
|
||||
when(orders.selectById(101L)).thenReturn(source);
|
||||
when(orders.selectById(102L)).thenReturn(source);
|
||||
|
||||
service.completeContainer("100", List.of(second, first));
|
||||
service.completeContainer("100", List.of(first, second));
|
||||
service.completeContainer(List.of(second, first));
|
||||
service.completeContainer(List.of(first, second));
|
||||
|
||||
verify(stockReceipts, times(2)).stockIn(eq("100"), any(), any(), any(), any(), any(), any());
|
||||
verify(outbox, times(1)).publishContainerCompleted(eq("100"), eq("BOOKING-1"), eq("CONT-1"),
|
||||
verify(stockReceipts).stockIn(eq("100"), any(), any(), any(), any(), any(), any());
|
||||
verify(stockReceipts).stockIn(eq("200"), any(), any(), any(), any(), any(), any());
|
||||
verify(outbox, times(1)).publishContainerCompleted(eq("BOOKING-1"), eq("CONT-1"),
|
||||
any(Instant.class), any(Instant.class), anyLong());
|
||||
verify(repository, times(2)).update(any(InboundPlan.class));
|
||||
verify(repository, times(2)).update(any(InboundPdaTask.class));
|
||||
|
||||
+1
-1
@@ -592,7 +592,7 @@ class InboundPdaWorkflowServiceTest {
|
||||
verify(repository).update(plan);
|
||||
verify(stockReceipts).stockIn(eq("100"), eq(plan), eq(task), eq(List.of(targetLine)),
|
||||
eq(List.of(target, moved)), any(), any());
|
||||
verify(unloadCompletedOutbox).publishContainerCompleted(eq("100"), eq("BOOKING-1"), eq("CONT-1"),
|
||||
verify(unloadCompletedOutbox).publishContainerCompleted(eq("BOOKING-1"), eq("CONT-1"),
|
||||
any(), any(), anyLong());
|
||||
}
|
||||
|
||||
|
||||
+7
-5
@@ -45,7 +45,7 @@ class InboundUnloadCompletedOutboxServiceTest {
|
||||
Instant photoUploadedAt = Instant.parse("2026-08-24T08:00:00Z");
|
||||
Instant containerCompletedAt = Instant.parse("2026-08-24T09:30:00Z");
|
||||
|
||||
service.publishContainerCompleted("100", "booking-1", "MSCU6639870",
|
||||
service.publishContainerCompleted("booking-1", "MSCU6639870",
|
||||
containerCompletedAt, photoUploadedAt, 7);
|
||||
|
||||
ArgumentCaptor<LastlegEventOutbox> captured = ArgumentCaptor.forClass(LastlegEventOutbox.class);
|
||||
@@ -57,6 +57,8 @@ class InboundUnloadCompletedOutboxServiceTest {
|
||||
assertThat(payload.unloadingCompletedAt()).isEqualTo(containerCompletedAt);
|
||||
assertThat(payload.unloadingPhotoUploadedAt()).isEqualTo(photoUploadedAt);
|
||||
assertThat(row.getEventKey()).isEqualTo(EventTypes.OVERSEAS_CONTAINER_UNLOADED + ":booking-1");
|
||||
assertThat(row.getCustomerId()).isEqualTo("0");
|
||||
assertThat(envelope.tenantId()).isEqualTo("0");
|
||||
assertThat(envelope.aggregateId()).isEqualTo("booking-1");
|
||||
}
|
||||
|
||||
@@ -87,11 +89,11 @@ class InboundUnloadCompletedOutboxServiceTest {
|
||||
|
||||
@Test
|
||||
void repeatedContainerCompletionDoesNotPublishAnotherEvent() {
|
||||
LastlegEventOutbox existing = existing("100", "booking-1", "MSCU6639870");
|
||||
LastlegEventOutbox existing = existing("0", "booking-1", "MSCU6639870");
|
||||
when(mapper.findByEventKey(EventTypes.OVERSEAS_CONTAINER_UNLOADED + ":booking-1"))
|
||||
.thenReturn(existing);
|
||||
|
||||
service.publishContainerCompleted("100", "booking-1", "MSCU6639870",
|
||||
service.publishContainerCompleted("booking-1", "MSCU6639870",
|
||||
Instant.parse("2026-08-24T10:00:00Z"), Instant.parse("2026-08-24T08:00:00Z"), 8);
|
||||
|
||||
verify(mapper, never()).insert(any(LastlegEventOutbox.class));
|
||||
@@ -99,11 +101,11 @@ class InboundUnloadCompletedOutboxServiceTest {
|
||||
|
||||
@Test
|
||||
void sameIdempotencyKeyCannotRepresentAnotherPhysicalContainer() {
|
||||
LastlegEventOutbox existing = existing("100", "booking-1", "MSCU6639870");
|
||||
LastlegEventOutbox existing = existing("0", "booking-1", "MSCU6639870");
|
||||
when(mapper.findByEventKey(EventTypes.OVERSEAS_CONTAINER_UNLOADED + ":booking-1"))
|
||||
.thenReturn(existing);
|
||||
|
||||
assertThatThrownBy(() -> service.publishContainerCompleted("100", "booking-1", "ZCSU7881970",
|
||||
assertThatThrownBy(() -> service.publishContainerCompleted("booking-1", "ZCSU7881970",
|
||||
Instant.parse("2026-08-24T10:00:00Z"), Instant.parse("2026-08-24T08:00:00Z"), 8))
|
||||
.isInstanceOf(IllegalStateException.class)
|
||||
.hasMessageContaining("conflicts with another container");
|
||||
|
||||
+51
-15
@@ -93,7 +93,7 @@ class ReceivingContainerServiceBehaviorTest {
|
||||
when(lines.selectList(any())).thenReturn(List.of(line("FBA")));
|
||||
LastlegOrder order = containerOrder(1L, "100", "CONT-A", "BOOKING-A", "IN-1");
|
||||
when(orders.selectList(any())).thenReturn(List.of(order));
|
||||
when(shippingGates.receivingAvailable("100", "CONT-A")).thenReturn(0);
|
||||
when(shippingGates.receivingAvailable("BOOKING-A", "CONT-A")).thenReturn(0);
|
||||
|
||||
assertThat(service.candidates("7", 100L)).isEmpty();
|
||||
assertThatThrownBy(() -> service.prepare("7", "receiver", 100L, "CONT-A",
|
||||
@@ -118,7 +118,6 @@ class ReceivingContainerServiceBehaviorTest {
|
||||
assertThat(response.overseasWarehouseName()).isEqualTo("Shared LAX");
|
||||
ArgumentCaptor<ReceivingContainerSession> selected = ArgumentCaptor.forClass(ReceivingContainerSession.class);
|
||||
verify(sessions).insert(selected.capture());
|
||||
assertThat(selected.getValue().getCustomerId()).isEqualTo("100");
|
||||
assertThat(selected.getValue().getActiveOperatorKey()).isEqualTo("OPERATOR:7");
|
||||
assertThat(selected.getValue().getActiveContainerKey()).isEqualTo("CONTAINER:CONT-A");
|
||||
ArgumentCaptor<ReceivingContainerOperation> saved = ArgumentCaptor.forClass(ReceivingContainerOperation.class);
|
||||
@@ -131,7 +130,7 @@ class ReceivingContainerServiceBehaviorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void pickerCandidatesSpanCustomersButEachContainerMustResolveOneCustomer() {
|
||||
void pickerCandidatesAllowMultipleCustomersInTheSameContainer() {
|
||||
InboundPlan first = plan("CONT-A");
|
||||
InboundPlan second = plan("CONT-B"); second.setId(3L); second.setCustomerId("200");
|
||||
when(plans.selectList(any())).thenReturn(List.of(first, second), List.of(first), List.of(second));
|
||||
@@ -149,7 +148,7 @@ class ReceivingContainerServiceBehaviorTest {
|
||||
when(orders.selectList(any())).thenReturn(List.of(
|
||||
containerOrder(1L, "100", "CONT-A", "BOOKING-A", "IN-1"),
|
||||
containerOrder(2L, "200", "CONT-A", "BOOKING-A", "IN-2")));
|
||||
assertThat(service.candidates("7", 100L)).isEmpty();
|
||||
assertThat(service.candidates("7", 100L)).extracting("containerNo").containsExactly("CONT-A");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -311,6 +310,9 @@ class ReceivingContainerServiceBehaviorTest {
|
||||
@Test
|
||||
void detailRejectsContainerReservedByAnotherOperator() {
|
||||
when(plans.selectList(any())).thenReturn(List.of(plan("CONT-B")));
|
||||
when(lines.selectList(any())).thenReturn(List.of(line("FBA")));
|
||||
when(orders.selectList(any())).thenReturn(List.of(
|
||||
containerOrder(1L, "100", "CONT-B", "BOOKING-B", "IN-1")));
|
||||
ReceivingContainerSession other = session("CONT-B", "SIGNED");
|
||||
other.setOperatorKey("8"); other.setActiveOperatorKey("OPERATOR:8");
|
||||
other.setActiveContainerKey("CONTAINER:CONT-B");
|
||||
@@ -324,6 +326,9 @@ class ReceivingContainerServiceBehaviorTest {
|
||||
@Test
|
||||
void waitSignSelectionAlreadyLocksAccountFromViewingAnotherContainer() {
|
||||
when(plans.selectList(any())).thenReturn(List.of(plan("CONT-B")));
|
||||
when(lines.selectList(any())).thenReturn(List.of(line("FBA")));
|
||||
when(orders.selectList(any())).thenReturn(List.of(
|
||||
containerOrder(1L, "100", "CONT-B", "BOOKING-B", "IN-1")));
|
||||
when(sessions.findActiveByOperator("OPERATOR:7")).thenReturn(session("CONT-A", "WAIT_SIGN"));
|
||||
assertThatThrownBy(() -> service.detail("7", 100L, "CONT-B"))
|
||||
.isInstanceOf(ResponseStatusException.class).hasMessageContaining("409");
|
||||
@@ -413,8 +418,6 @@ class ReceivingContainerServiceBehaviorTest {
|
||||
|
||||
org.mockito.Mockito.reset(plans, lines, orders, sessions, operations, cartons, warehouse, pallets, workflow);
|
||||
stubCurrent("OVERSEAS", "CONT-A");
|
||||
when(cartons.selectOne(any())).thenReturn(carton(1L, "BOX-1"));
|
||||
when(pallets.selectList(any())).thenReturn(List.of(planPallet()));
|
||||
assertThatThrownBy(() -> service.receiveCarton("7", "receiver",
|
||||
new ReceivingContainerRequests.CartonReceive("BOX-1", "PAL-1", "duplicate-key")))
|
||||
.isInstanceOf(ResponseStatusException.class);
|
||||
@@ -445,7 +448,7 @@ class ReceivingContainerServiceBehaviorTest {
|
||||
when(allocations.selectList(any())).thenReturn(List.of(partial));
|
||||
assertThatThrownBy(() -> service.complete("7", new ReceivingContainerRequests.Action("complete-key")))
|
||||
.isInstanceOf(ResponseStatusException.class).hasMessageContaining("409");
|
||||
verify(workflow, never()).completeContainer(any(), any());
|
||||
verify(workflow, never()).completeContainer(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -463,7 +466,7 @@ class ReceivingContainerServiceBehaviorTest {
|
||||
|
||||
assertThat(completed.status()).isEqualTo("COMPLETED");
|
||||
verifyNoInteractions(skuReceipts);
|
||||
verify(workflow).completeContainer(any(), any());
|
||||
verify(workflow).completeContainer(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -526,6 +529,39 @@ class ReceivingContainerServiceBehaviorTest {
|
||||
org.mockito.ArgumentMatchers.same(occupied));
|
||||
}
|
||||
|
||||
@Test
|
||||
void bulkReceivingAcceptsFrozenSourcePalletCodeForItsPreReservedResource() {
|
||||
stubCurrent("FBA", "CONT-A");
|
||||
stubSku();
|
||||
InboundPlanPallet reserved = planPallet();
|
||||
reserved.setPalletNo("PAL-RESERVED");
|
||||
reserved.setSourcePalletNo("CC001");
|
||||
reserved.setWarehousePalletId(30L);
|
||||
reserved.setWarehouseLocationId(40L);
|
||||
reserved.setWarehouseResourceLockId(50L);
|
||||
reserved.setAssignmentStatus("RESERVED");
|
||||
when(pallets.selectList(any())).thenReturn(List.of(reserved));
|
||||
WarehouseResourceGateway.WarehousePalletReference occupied =
|
||||
new WarehouseResourceGateway.WarehousePalletReference(30L, "PAL-RESERVED", 100L, 40L,
|
||||
"OCCUPIED", "FBA-1", 0);
|
||||
when(warehouse.resolveReservedPallet("PAL-RESERVED", "100", 100L, 30L, 40L, 50L))
|
||||
.thenReturn(occupied);
|
||||
when(workflow.receiveBulkResolved(any(), any(), any(), any(), anyInt(), any(), any()))
|
||||
.thenReturn(1);
|
||||
when(sessions.updateById(any(ReceivingContainerSession.class))).thenReturn(1);
|
||||
when(operations.insert(any(ReceivingContainerOperation.class))).thenReturn(1);
|
||||
|
||||
var result = service.receiveBulk("7", "receiver",
|
||||
new ReceivingContainerRequests.BulkReceive("IN-1", "CC001", 1, "source-code-key"));
|
||||
|
||||
assertThat(result.palletCode()).isEqualTo("PAL-RESERVED");
|
||||
verify(warehouse).resolveReservedPallet("PAL-RESERVED", "100", 100L, 30L, 40L, 50L);
|
||||
verify(warehouse, never()).resolveAvailablePallet("CC001");
|
||||
verify(workflow).assignResolvedPallet(org.mockito.ArgumentMatchers.eq("100"),
|
||||
any(InboundPlan.class), org.mockito.ArgumentMatchers.same(reserved),
|
||||
org.mockito.ArgumentMatchers.same(occupied));
|
||||
}
|
||||
|
||||
@Test
|
||||
void signRequiresEachTypedPhotoExactlyOnceAndRestoresRoles() {
|
||||
when(sessions.lockActiveByOperator("OPERATOR:7")).thenReturn(session("CONT-A", "WAIT_SIGN"));
|
||||
@@ -550,9 +586,9 @@ class ReceivingContainerServiceBehaviorTest {
|
||||
"/api/pda/v1/last-leg/receiving-containers/current/unload-photos/files/2092476334483931138/download",
|
||||
"/api/pda/v1/last-leg/receiving-containers/current/unload-photos/files/2/download",
|
||||
"/api/pda/v1/last-leg/receiving-containers/current/unload-photos/files/3/download");
|
||||
verify(unloadPhotos).validate("100", 8L, 2092476334483931138L);
|
||||
verify(unloadPhotos).validate("100", 8L, 2L);
|
||||
verify(unloadPhotos).validate("100", 8L, 3L);
|
||||
verify(unloadPhotos).validate("0", 8L, 2092476334483931138L);
|
||||
verify(unloadPhotos).validate("0", 8L, 2L);
|
||||
verify(unloadPhotos).validate("0", 8L, 3L);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -592,7 +628,7 @@ class ReceivingContainerServiceBehaviorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void currentSignedPhotoCanBeDownloadedOnlyThroughItsOperatorCustomerAndSessionScope() {
|
||||
void currentSignedPhotoCanBeDownloadedOnlyThroughItsPlatformSessionScope() {
|
||||
ReceivingContainerSession current = session("CONT-A", "SIGNED");
|
||||
current.setSignPhotos("[{\"role\":\"LICENSE_PLATE\",\"fileId\":\"9\"},"
|
||||
+ "{\"role\":\"CONTAINER_SEAL\",\"fileId\":\"10\"},"
|
||||
@@ -600,10 +636,10 @@ class ReceivingContainerServiceBehaviorTest {
|
||||
when(sessions.findActiveByOperator("OPERATOR:7")).thenReturn(current);
|
||||
UnloadPhotoFileGateway.DownloadedFile expected =
|
||||
new UnloadPhotoFileGateway.DownloadedFile("image".getBytes(), "plate.jpg", "image/jpeg");
|
||||
when(unloadPhotos.download("100", 8L, 9L)).thenReturn(expected);
|
||||
when(unloadPhotos.download("0", 8L, 9L)).thenReturn(expected);
|
||||
|
||||
assertThat(service.downloadCurrentPhoto("7", "9")).isSameAs(expected);
|
||||
verify(unloadPhotos).download("100", 8L, 9L);
|
||||
verify(unloadPhotos).download("0", 8L, 9L);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -866,7 +902,7 @@ class ReceivingContainerServiceBehaviorTest {
|
||||
|
||||
private static ReceivingContainerSession session(String containerNo, String status) {
|
||||
ReceivingContainerSession value = new ReceivingContainerSession();
|
||||
value.setId(8L); value.setCustomerId("100"); value.setContainerNo(containerNo); value.setOperatorKey("7");
|
||||
value.setId(8L); value.setContainerNo(containerNo); value.setOperatorKey("7");
|
||||
value.setOverseasWarehouseId(100L); value.setOverseasWarehouseCode("SHARED-LAX");
|
||||
value.setOverseasWarehouseName("Shared LAX");
|
||||
value.setStatus(status); value.setVersion(0);
|
||||
|
||||
Reference in New Issue
Block a user