feat: gate overseas receiving and harden phase4 workflows
This commit is contained in:
+78
@@ -0,0 +1,78 @@
|
||||
package com.baiyue.lastleg.pda.warehouse;
|
||||
|
||||
import com.baiyue.common.security.AuthenticatedUser;
|
||||
import com.baiyue.lastleg.pda.security.PdaWarehouseIdentity;
|
||||
import com.baiyue.lastleg.pda.warehouse.controller.PdaWarehouseController;
|
||||
import com.baiyue.lastleg.pda.warehouse.dto.PdaWarehouseRequests;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.lang.reflect.RecordComponent;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
class PdaWarehouseContractTest {
|
||||
@Test
|
||||
void requestsNeverAcceptServerOwnedIdentityOrResourceIds() {
|
||||
Set<String> forbidden = Set.of("customerId", "warehouseId", "planNo", "allocationId", "operatorId");
|
||||
for (Class<?> type : PdaWarehouseRequests.class.getDeclaredClasses()) {
|
||||
if (!type.isRecord()) continue;
|
||||
Set<String> fields = Arrays.stream(type.getRecordComponents()).map(RecordComponent::getName)
|
||||
.collect(Collectors.toSet());
|
||||
assertThat(fields).doesNotContainAnyElementsOf(forbidden);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void controllerRequiresPickerRoleAndPdaAuthority() {
|
||||
PreAuthorize permission = PdaWarehouseController.class.getAnnotation(PreAuthorize.class);
|
||||
assertThat(permission.value()).isEqualTo(
|
||||
"hasAuthority('lastleg:pda:operate') and hasAuthority('ROLE_WAREHOUSE_PICKER')");
|
||||
}
|
||||
|
||||
@Test
|
||||
void identityRejectsUnboundOrNonPickerPlatformAccounts() {
|
||||
AuthenticatedUser unbound = user(Set.of(PdaWarehouseIdentity.PICKER_ROLE,
|
||||
PdaWarehouseIdentity.OPERATE_PERMISSION), null);
|
||||
assertThatThrownBy(() -> PdaWarehouseIdentity.require(unbound))
|
||||
.isInstanceOf(ResponseStatusException.class).hasMessageContaining("403");
|
||||
AuthenticatedUser admin = user(Set.of(PdaWarehouseIdentity.OPERATE_PERMISSION), 900L);
|
||||
assertThatThrownBy(() -> PdaWarehouseIdentity.require(admin))
|
||||
.isInstanceOf(ResponseStatusException.class).hasMessageContaining("403");
|
||||
assertThat(PdaWarehouseIdentity.require(user(Set.of(PdaWarehouseIdentity.PICKER_ROLE,
|
||||
PdaWarehouseIdentity.OPERATE_PERMISSION), 900L)).overseasWarehouseId()).isEqualTo(900L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void baselineContainsShippingGateAndRealOperationHistory() throws Exception {
|
||||
String sql = Files.readString(Path.of("src/main/resources/db/lastleg.sql")).toLowerCase();
|
||||
assertThat(sql).contains("create table if not exists ll_container_shipping_gate")
|
||||
.contains("awaiting_pickup_ready")
|
||||
.contains("receiving_available")
|
||||
.contains("create table if not exists ll_pda_operation_history")
|
||||
.contains("unique key uk_ll_pda_history_idempotency(operator_key,idempotency_key)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void openApiAndGatewayPublishOnlyStablePdaPaths() throws Exception {
|
||||
String openapi = Files.readString(Path.of("src/main/resources/openapi/overseas-business-v1.yaml"));
|
||||
for (String path : Set.of("/api/pda/v1/last-leg/inventory/resolve:",
|
||||
"/api/pda/v1/last-leg/locations/resolve:", "/api/pda/v1/last-leg/pallets/resolve:",
|
||||
"/api/pda/v1/last-leg/operations:")) assertThat(openapi).contains(path);
|
||||
assertThat(openapi).doesNotContain("/api/pda/v1/last-leg/inbound-plans/{planNo}");
|
||||
String gateway = Files.readString(Path.of("../gateway-overseas/src/main/resources/application-overseas.yml"));
|
||||
assertThat(gateway).contains("/api/pda/v1/last-leg/**").contains("lb://lastleg-service");
|
||||
}
|
||||
|
||||
private static AuthenticatedUser user(Set<String> authorities, Long warehouseId) {
|
||||
return new AuthenticatedUser(42L, 0L, 0L, 8L, "picker", "PLATFORM", "PDA",
|
||||
authorities, false, null, null, false, warehouseId);
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package com.baiyue.lastleg.pda.warehouse.service;
|
||||
|
||||
import com.baiyue.lastleg.inboundplan.pda.gateway.WarehouseResourceGateway;
|
||||
import com.baiyue.lastleg.pda.warehouse.dto.PdaWarehouseRequests;
|
||||
import com.baiyue.lastleg.pda.warehouse.mapper.PdaWarehouseMapper;
|
||||
import com.baiyue.lastleg.pda.warehouse.model.PdaInventoryProjection;
|
||||
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.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class PdaWarehouseServiceTest {
|
||||
@Mock PdaWarehouseMapper mapper;
|
||||
@Mock WarehouseResourceGateway warehouse;
|
||||
private PdaWarehouseService service;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
service = new PdaWarehouseService();
|
||||
ReflectionTestUtils.setField(service, "mapper", mapper);
|
||||
ReflectionTestUtils.setField(service, "warehouse", warehouse);
|
||||
}
|
||||
|
||||
@Test
|
||||
void inventoryScanIsBoundToTokenWarehouse() {
|
||||
when(mapper.scanInventory("900", "SKU-1")).thenReturn(List.of(inventory()));
|
||||
var result = service.inventory(900L, new PdaWarehouseRequests.Scan("SKU-1"));
|
||||
assertThat(result.items()).hasSize(1);
|
||||
assertThat(result.items().get(0).customerId()).isEqualTo("100");
|
||||
}
|
||||
|
||||
@Test
|
||||
void palletFromAnotherWarehouseIsRejected() {
|
||||
when(warehouse.resolvePallet("PAL-1")).thenReturn(new WarehouseResourceGateway.WarehousePalletReference(
|
||||
1L, "PAL-1", 901L, 2L, "OCCUPIED", null, 1));
|
||||
assertThatThrownBy(() -> service.pallet(900L, new PdaWarehouseRequests.Scan("PAL-1")))
|
||||
.isInstanceOf(ResponseStatusException.class).hasMessageContaining("409");
|
||||
}
|
||||
|
||||
@Test
|
||||
void locationCombinesPhysicalCapacityAndInventoryOccupation() {
|
||||
when(warehouse.resolveLocation(900L, "A-01")).thenReturn(
|
||||
new WarehouseResourceGateway.WarehouseLocationReference(2L, "A-01", "STORAGE",
|
||||
new BigDecimal("1000"), "OCCUPIED"));
|
||||
when(mapper.inventoryByLocation("900", 2L, "A-01", "__NO_CARTON__"))
|
||||
.thenReturn(List.of(inventory()));
|
||||
var result = service.location(900L, new PdaWarehouseRequests.Scan("A-01"));
|
||||
assertThat(result.occupiedPackages()).isEqualTo(5);
|
||||
assertThat(result.inventoryPositionCount()).isEqualTo(1);
|
||||
}
|
||||
|
||||
private static PdaInventoryProjection inventory() {
|
||||
PdaInventoryProjection value = new PdaInventoryProjection(); value.setCustomerId("100");
|
||||
value.setBalanceId(10L); value.setPositionId(11L); value.setSkuCode("SKU-1");
|
||||
value.setInboundCode("IN-1"); value.setPalletId(12L); value.setPalletCode("PAL-1");
|
||||
value.setLocationId(2L); value.setLocationCode("A-01"); value.setAvailablePackages(5L);
|
||||
value.setHoldPackages(0L); value.setReceivingPackages(0L); value.setVersion(1L); value.setStatus("OCCUPIED");
|
||||
return value;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user