暂存提交

This commit is contained in:
2026-08-19 16:24:02 +08:00
parent f5c4bf130a
commit 6acb6178da
354 changed files with 25000 additions and 198 deletions
@@ -0,0 +1,59 @@
package com.baiyue.warehouse.service;
import com.baiyue.warehouse.dto.WarehouseRequests;
import com.baiyue.warehouse.entity.WarehouseEntity;
import com.baiyue.warehouse.entity.WarehouseLocationEntity;
import com.baiyue.warehouse.repository.WarehouseRepository;
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.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
@ExtendWith(MockitoExtension.class)
class WarehouseApplicationServiceTest {
@Mock WarehouseRepository repository;
private WarehouseApplicationService service;
@BeforeEach
void setUp() {
service = new WarehouseApplicationService();
ReflectionTestUtils.setField(service, "repository", repository);
}
@Test
void warehouseCreationAlsoCreatesActiveDefaultVirtualLocation() {
doAnswer(call -> {
((WarehouseEntity) call.getArgument(0)).setId(20001L);
return null;
}).when(repository).insertWarehouse(any());
service.create(request());
ArgumentCaptor<WarehouseLocationEntity> location = ArgumentCaptor.forClass(WarehouseLocationEntity.class);
verify(repository).insertLocation(location.capture());
assertThat(location.getValue().getCustomerId()).isEqualTo(10001L);
assertThat(location.getValue().getWarehouseId()).isEqualTo(20001L);
assertThat(location.getValue().getLocationCode()).isEqualTo("LOC-US-LAX-01-DEFAULT");
assertThat(location.getValue().getLocationType()).isEqualTo("VIRTUAL");
assertThat(location.getValue().getStatus()).isEqualTo("ACTIVE");
}
private static WarehouseRequests.SaveRequest request() {
WarehouseRequests.AddressRequest address = new WarehouseRequests.AddressRequest(
"WAREHOUSE", "US", "CA", "Los Angeles", null, "90001",
"1 Logistics Ave", null, null, null);
return new WarehouseRequests.SaveRequest(10001L, "US-LAX-01", "LAX Warehouse",
"OVERSEAS", "US", "America/Los_Angeles", null, null, null,
"ACTIVE", null, address, List.of());
}
}
@@ -0,0 +1,263 @@
package com.baiyue.warehouse.service;
import com.baiyue.common.core.exception.BizException;
import com.baiyue.warehouse.dto.WarehouseResourceLockRequests;
import com.baiyue.warehouse.entity.WarehouseLocationEntity;
import com.baiyue.warehouse.entity.WarehousePalletEntity;
import com.baiyue.warehouse.entity.WarehouseResourceLockEntity;
import com.baiyue.warehouse.repository.WarehouseResourceLockRepository;
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 static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class WarehouseResourceLockServiceTest {
private static final long TENANT = 101L;
private static final long WAREHOUSE = 201L;
private static final long PALLET = 301L;
private static final long LOCATION = 401L;
@Mock WarehouseResourceLockRepository repository;
private WarehouseResourceLockService service;
@BeforeEach
void setUp() {
service = new WarehouseResourceLockService();
ReflectionTestUtils.setField(service, "repository", repository);
}
@Test
void atomicallyOccupiesIdlePalletAndLocation() {
var request = request("reserve-1");
when(repository.findPallet(TENANT, WAREHOUSE, PALLET)).thenReturn(pallet("IDLE"));
when(repository.findLocation(TENANT, WAREHOUSE, LOCATION)).thenReturn(location("IDLE"));
when(repository.occupyPallet(TENANT, WAREHOUSE, PALLET, LOCATION)).thenReturn(1);
when(repository.occupyLocation(TENANT, WAREHOUSE, LOCATION)).thenReturn(1);
doAnswer(call -> {
((WarehouseResourceLockEntity) call.getArgument(0)).setId(501L);
return null;
}).when(repository).insert(any());
var result = service.reserve(request);
assertThat(result.lockId()).isEqualTo(501L);
assertThat(result.status()).isEqualTo("ACTIVE");
ArgumentCaptor<WarehouseResourceLockEntity> lock = ArgumentCaptor.forClass(WarehouseResourceLockEntity.class);
verify(repository).insert(lock.capture());
assertThat(lock.getValue().getReservationKey()).isEqualTo("reserve-1");
assertThat(lock.getValue().getPalletId()).isEqualTo(PALLET);
assertThat(lock.getValue().getLocationId()).isEqualTo(LOCATION);
}
@Test
void duplicateReservationReturnsExistingLockWithoutMutatingResources() {
WarehouseResourceLockEntity existing = activeLock();
when(repository.findByReservationKey(TENANT, "reserve-1")).thenReturn(existing);
var result = service.reserve(request("reserve-1"));
assertThat(result.lockId()).isEqualTo(existing.getId());
verify(repository, never()).occupyPallet(TENANT, WAREHOUSE, PALLET, LOCATION);
verify(repository, never()).insert(any());
}
@Test
void concurrentDuplicateReturnsExistingAfterResourcesBecomeOccupied() {
WarehouseResourceLockEntity existing = activeLock();
when(repository.findByReservationKey(TENANT, "reserve-1")).thenReturn(null, existing);
when(repository.findPallet(TENANT, WAREHOUSE, PALLET)).thenReturn(pallet("OCCUPIED"));
when(repository.findLocation(TENANT, WAREHOUSE, LOCATION)).thenReturn(location("OCCUPIED"));
var result = service.reserve(request("reserve-1"));
assertThat(result.lockId()).isEqualTo(existing.getId());
verify(repository, never()).occupyPallet(TENANT, WAREHOUSE, PALLET, LOCATION);
}
@Test
void reservationKeyCannotBeReusedForDifferentResources() {
WarehouseResourceLockEntity existing = activeLock();
existing.setPalletId(999L);
when(repository.findByReservationKey(TENANT, "reserve-1")).thenReturn(existing);
assertThatThrownBy(() -> service.reserve(request("reserve-1")))
.isInstanceOf(BizException.class)
.hasMessageContaining("different resources");
}
@Test
void rejectsResourceOutsideTenantWarehouse() {
when(repository.findPallet(TENANT, WAREHOUSE, PALLET)).thenReturn(null);
when(repository.findLocation(TENANT, WAREHOUSE, LOCATION)).thenReturn(location("IDLE"));
assertThatThrownBy(() -> service.reserve(request("reserve-1")))
.isInstanceOf(BizException.class)
.hasMessageContaining("tenant warehouse");
verify(repository, never()).occupyPallet(TENANT, WAREHOUSE, PALLET, LOCATION);
}
@Test
void rejectsNonIdleResources() {
when(repository.findPallet(TENANT, WAREHOUSE, PALLET)).thenReturn(pallet("OCCUPIED"));
when(repository.findLocation(TENANT, WAREHOUSE, LOCATION)).thenReturn(location("IDLE"));
assertThatThrownBy(() -> service.reserve(request("reserve-1")))
.isInstanceOf(BizException.class)
.hasMessageContaining("Only IDLE");
}
@Test
void releaseRestoresBothResourcesAndClosesLock() {
WarehouseResourceLockEntity lock = activeLock();
when(repository.find(TENANT, lock.getId())).thenReturn(lock);
when(repository.findLocation(TENANT, WAREHOUSE, LOCATION)).thenReturn(location("OCCUPIED"));
when(repository.releasePallet(lock)).thenReturn(1);
when(repository.releaseLocation(lock)).thenReturn(1);
when(repository.close(lock, "RELEASED")).thenReturn(1);
var result = service.release(TENANT, lock.getId());
assertThat(result.status()).isEqualTo("RELEASED");
verify(repository).releasePallet(lock);
verify(repository).releaseLocation(lock);
}
@Test
void scrapDiscardsPalletAndReleasesLocation() {
WarehouseResourceLockEntity lock = activeLock();
when(repository.find(TENANT, lock.getId())).thenReturn(lock);
when(repository.findLocation(TENANT, WAREHOUSE, LOCATION)).thenReturn(location("OCCUPIED"));
when(repository.scrapPallet(lock)).thenReturn(1);
when(repository.releaseLocation(lock)).thenReturn(1);
when(repository.close(lock, "SCRAPPED")).thenReturn(1);
var result = service.scrap(TENANT, lock.getId());
assertThat(result.status()).isEqualTo("SCRAPPED");
verify(repository).scrapPallet(lock);
verify(repository).releaseLocation(lock);
}
@Test
void concurrentLocationClaimFailsWholeOperation() {
when(repository.findPallet(TENANT, WAREHOUSE, PALLET)).thenReturn(pallet("IDLE"));
when(repository.findLocation(TENANT, WAREHOUSE, LOCATION)).thenReturn(location("IDLE"));
when(repository.occupyPallet(TENANT, WAREHOUSE, PALLET, LOCATION)).thenReturn(1);
when(repository.occupyLocation(TENANT, WAREHOUSE, LOCATION)).thenReturn(0);
assertThatThrownBy(() -> service.reserve(request("reserve-1")))
.isInstanceOf(BizException.class)
.hasMessageContaining("no longer IDLE");
verify(repository, never()).insert(any());
}
@Test
void virtualLocationIsNeverOccupiedOrReleased() {
WarehouseLocationEntity virtual = location("ACTIVE");
virtual.setLocationType("VIRTUAL");
when(repository.findPallet(TENANT, WAREHOUSE, PALLET)).thenReturn(pallet("IDLE"));
when(repository.findLocation(TENANT, WAREHOUSE, LOCATION)).thenReturn(virtual);
when(repository.occupyPallet(TENANT, WAREHOUSE, PALLET, LOCATION)).thenReturn(1);
doAnswer(call -> {
((WarehouseResourceLockEntity) call.getArgument(0)).setId(502L);
return null;
}).when(repository).insert(any());
service.reserve(request("virtual-reserve"));
verify(repository, never()).occupyLocation(TENANT, WAREHOUSE, LOCATION);
}
@Test
void scrappingAtVirtualLocationKeepsLocationUntouched() {
WarehouseResourceLockEntity lock = activeLock();
WarehouseLocationEntity virtual = location("ACTIVE");
virtual.setLocationType("VIRTUAL");
when(repository.find(TENANT, lock.getId())).thenReturn(lock);
when(repository.findLocation(TENANT, WAREHOUSE, LOCATION)).thenReturn(virtual);
when(repository.scrapPalletAtVirtualLocation(lock)).thenReturn(1);
when(repository.close(lock, "SCRAPPED")).thenReturn(1);
var result = service.scrap(TENANT, lock.getId());
assertThat(result.status()).isEqualTo("SCRAPPED");
verify(repository).scrapPalletAtVirtualLocation(lock);
verify(repository, never()).releaseLocation(lock);
}
@Test
void scrapByPalletClosesItsActiveLockAndKeepsLocationReference() {
WarehousePalletEntity pallet = pallet("OCCUPIED");
pallet.setWarehouseId(WAREHOUSE);
pallet.setLocationId(LOCATION);
WarehouseResourceLockEntity lock = activeLock();
when(repository.findPallet(TENANT, PALLET)).thenReturn(pallet);
when(repository.findActiveByPallet(TENANT, PALLET)).thenReturn(lock);
when(repository.find(TENANT, lock.getId())).thenReturn(lock);
WarehouseLocationEntity virtual = location("ACTIVE");
virtual.setLocationType("VIRTUAL");
when(repository.findLocation(TENANT, WAREHOUSE, LOCATION)).thenReturn(virtual);
when(repository.scrapPalletAtVirtualLocation(lock)).thenReturn(1);
when(repository.close(lock, "SCRAPPED")).thenReturn(1);
var result = service.scrapByPallet(TENANT, PALLET);
assertThat(result.status()).isEqualTo("SCRAPPED");
assertThat(result.locationId()).isEqualTo(LOCATION);
verify(repository).scrapPalletAtVirtualLocation(lock);
}
@Test
void scrapByPalletIsIdempotentWhenAlreadyScrapped() {
WarehousePalletEntity pallet = pallet("SCRAPPED");
pallet.setWarehouseId(WAREHOUSE);
pallet.setLocationId(LOCATION);
when(repository.findPallet(TENANT, PALLET)).thenReturn(pallet);
assertThat(service.scrapByPallet(TENANT, PALLET).status()).isEqualTo("SCRAPPED");
verify(repository, never()).findActiveByPallet(TENANT, PALLET);
}
private static WarehouseResourceLockRequests.ReserveRequest request(String key) {
return new WarehouseResourceLockRequests.ReserveRequest(TENANT, WAREHOUSE, PALLET, LOCATION,
"INBOUND_PLAN_PALLET", "plan-pallet-1", key);
}
private static WarehousePalletEntity pallet(String status) {
WarehousePalletEntity entity = new WarehousePalletEntity();
entity.setStatus(status);
return entity;
}
private static WarehouseLocationEntity location(String status) {
WarehouseLocationEntity entity = new WarehouseLocationEntity();
entity.setStatus(status);
return entity;
}
private static WarehouseResourceLockEntity activeLock() {
WarehouseResourceLockEntity lock = new WarehouseResourceLockEntity();
lock.setId(501L);
lock.setCustomerId(TENANT);
lock.setWarehouseId(WAREHOUSE);
lock.setPalletId(PALLET);
lock.setLocationId(LOCATION);
lock.setBusinessType("INBOUND_PLAN_PALLET");
lock.setBusinessId("plan-pallet-1");
lock.setReservationKey("reserve-1");
lock.setStatus("ACTIVE");
return lock;
}
}
@@ -0,0 +1,260 @@
package com.baiyue.warehouse.service;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baiyue.common.core.exception.BizException;
import com.baiyue.warehouse.dto.LocationRequests;
import com.baiyue.warehouse.dto.PalletRequests;
import com.baiyue.warehouse.entity.WarehouseLocationEntity;
import com.baiyue.warehouse.entity.WarehousePalletEntity;
import com.baiyue.warehouse.entity.WarehouseEntity;
import com.baiyue.warehouse.repository.WarehouseResourceRepository;
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.math.BigDecimal;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class WarehouseResourceServiceTest {
private static final long TENANT = 10001L;
private static final long WAREHOUSE = 20001L;
@Mock
private WarehouseResourceRepository repository;
private WarehouseResourceService service;
@BeforeEach
void setUp() {
service = new WarehouseResourceService();
ReflectionTestUtils.setField(service, "repository", repository);
}
@Test
void resourceIdsUseMybatisSnowflakeAssignment() throws NoSuchFieldException {
assertThat(WarehouseLocationEntity.class.getDeclaredField("id").getAnnotation(TableId.class).type())
.isEqualTo(IdType.ASSIGN_ID);
assertThat(WarehousePalletEntity.class.getDeclaredField("id").getAnnotation(TableId.class).type())
.isEqualTo(IdType.ASSIGN_ID);
}
@Test
void createsLocationWithSnowflakeAssignedByMapper() {
when(repository.warehouseOwned(TENANT, WAREHOUSE)).thenReturn(true);
doAnswer(invocation -> {
((WarehouseLocationEntity) invocation.getArgument(0)).setId(2089000000000000001L);
return null;
}).when(repository).insertLocation(org.mockito.ArgumentMatchers.any());
Long id = service.createLocation(TENANT, WAREHOUSE, locationRequest("A-01"));
assertThat(id).isEqualTo(2089000000000000001L);
ArgumentCaptor<WarehouseLocationEntity> captor = ArgumentCaptor.forClass(WarehouseLocationEntity.class);
verify(repository).insertLocation(captor.capture());
assertThat(captor.getValue().getCustomerId()).isEqualTo(TENANT);
assertThat(captor.getValue().getWarehouseId()).isEqualTo(WAREHOUSE);
}
@Test
void rejectsResourceAccessWhenWarehouseDoesNotBelongToTenant() {
when(repository.warehouseOwned(TENANT, WAREHOUSE)).thenReturn(false);
assertThatThrownBy(() -> service.createLocation(TENANT, WAREHOUSE, locationRequest("A-01")))
.isInstanceOf(BizException.class)
.hasMessageContaining("仓库不存在");
verify(repository, never()).insertLocation(org.mockito.ArgumentMatchers.any());
}
@Test
void rejectsDuplicateLocationCodeWithinWarehouse() {
when(repository.warehouseOwned(TENANT, WAREHOUSE)).thenReturn(true);
when(repository.locationCodeExists(TENANT, WAREHOUSE, "A-01", null)).thenReturn(true);
assertThatThrownBy(() -> service.createLocation(TENANT, WAREHOUSE, locationRequest("A-01")))
.isInstanceOf(BizException.class)
.hasMessageContaining("库位编码已存在");
}
@Test
void palletDefaultsToTwoCubicMetresAndBindsOwnedLocation() {
when(repository.warehouseOwned(TENANT, WAREHOUSE)).thenReturn(true);
when(repository.findWarehouse(TENANT, WAREHOUSE)).thenReturn(warehouse());
WarehouseLocationEntity location = new WarehouseLocationEntity();
location.setId(30001L);
when(repository.findLocation(TENANT, WAREHOUSE, 30001L)).thenReturn(location);
doAnswer(invocation -> {
((WarehousePalletEntity) invocation.getArgument(0)).setId(2089000000000000002L);
return null;
}).when(repository).insertPallet(org.mockito.ArgumentMatchers.any());
Long id = service.createPallet(TENANT, WAREHOUSE,
new PalletRequests.SaveRequest("PLT-01", "STANDARD", null,
new BigDecimal("12.50"), "IDLE", 30001L, null));
assertThat(id).isEqualTo(2089000000000000002L);
ArgumentCaptor<WarehousePalletEntity> captor = ArgumentCaptor.forClass(WarehousePalletEntity.class);
verify(repository).insertPallet(captor.capture());
assertThat(captor.getValue().getCapacityCbm()).isEqualByComparingTo("2.0000");
assertThat(captor.getValue().getLocationId()).isEqualTo(30001L);
}
@Test
void rejectsLocationFromAnotherWarehouseWhenBindingPallet() {
when(repository.warehouseOwned(TENANT, WAREHOUSE)).thenReturn(true);
when(repository.findWarehouse(TENANT, WAREHOUSE)).thenReturn(warehouse());
when(repository.findLocation(TENANT, WAREHOUSE, 30002L)).thenReturn(null);
assertThatThrownBy(() -> service.createPallet(TENANT, WAREHOUSE,
new PalletRequests.SaveRequest("PLT-01", "STANDARD", null,
BigDecimal.ONE, "IDLE", 30002L, null)))
.isInstanceOf(BizException.class)
.hasMessageContaining("库位不存在");
verify(repository, never()).insertPallet(org.mockito.ArgumentMatchers.any());
}
@Test
void preventsDeletingLocationThatStillHasPallets() {
when(repository.warehouseOwned(TENANT, WAREHOUSE)).thenReturn(true);
when(repository.findLocation(TENANT, WAREHOUSE, 30001L)).thenReturn(new WarehouseLocationEntity());
when(repository.locationHasPallets(TENANT, WAREHOUSE, 30001L)).thenReturn(true);
assertThatThrownBy(() -> service.deleteLocation(TENANT, WAREHOUSE, 30001L))
.isInstanceOf(BizException.class)
.hasMessageContaining("仍绑定托盘");
verify(repository, never()).deleteLocation(30001L);
}
@Test
void preventsDeletingOccupiedPallet() {
when(repository.warehouseOwned(TENANT, WAREHOUSE)).thenReturn(true);
WarehousePalletEntity pallet = new WarehousePalletEntity();
pallet.setStatus("OCCUPIED");
when(repository.findPallet(TENANT, WAREHOUSE, 40001L)).thenReturn(pallet);
assertThatThrownBy(() -> service.deletePallet(TENANT, WAREHOUSE, 40001L))
.isInstanceOf(BizException.class)
.hasMessageContaining("占用中的托盘");
verify(repository, never()).deletePallet(40001L);
}
@Test
void preventsDeletingScrappedPalletSoItsIdentityCannotBeReused() {
when(repository.warehouseOwned(TENANT, WAREHOUSE)).thenReturn(true);
WarehousePalletEntity pallet = new WarehousePalletEntity();
pallet.setStatus("SCRAPPED");
when(repository.findPallet(TENANT, WAREHOUSE, 40003L)).thenReturn(pallet);
assertThatThrownBy(() -> service.deletePallet(TENANT, WAREHOUSE, 40003L))
.isInstanceOf(BizException.class)
.hasMessageContaining("cannot be reused");
verify(repository, never()).deletePallet(40003L);
}
@Test
void rejectsUnknownPalletStatus() {
when(repository.findWarehouse(TENANT, WAREHOUSE)).thenReturn(warehouse());
assertThatThrownBy(() -> service.createPallet(TENANT, WAREHOUSE,
new PalletRequests.SaveRequest("PLT-01", "STANDARD", null,
BigDecimal.ONE, "BROKEN", null, null)))
.isInstanceOf(BizException.class)
.hasMessageContaining("IDLE、OCCUPIED、SCRAPPED");
}
private static LocationRequests.SaveRequest locationRequest(String code) {
return new LocationRequests.SaveRequest("A", code, "STORAGE",
new BigDecimal("1000.000"), "ACTIVE", null);
}
@Test
void generatesRestartSafePalletCodeAndUsesDefaultVirtualLocation() {
when(repository.findWarehouse(TENANT, WAREHOUSE)).thenReturn(warehouse());
WarehouseLocationEntity location = new WarehouseLocationEntity();
location.setId(30003L);
location.setLocationType("VIRTUAL");
location.setStatus("ACTIVE");
when(repository.findLocationByCode(TENANT, WAREHOUSE, "LOC-US-LAX-01-DEFAULT"))
.thenReturn(location);
when(repository.warehouseOwned(TENANT, WAREHOUSE)).thenReturn(true);
when(repository.findLocation(TENANT, WAREHOUSE, 30003L)).thenReturn(location);
service.createPallet(TENANT, WAREHOUSE,
new PalletRequests.SaveRequest(null, "STANDARD", null, BigDecimal.ONE, "IDLE", null, null));
ArgumentCaptor<WarehousePalletEntity> captor = ArgumentCaptor.forClass(WarehousePalletEntity.class);
verify(repository).insertPallet(captor.capture());
assertThat(captor.getValue().getPalletCode()).matches("PLT-US-LAX-01-[0-9]{8}-[0-9]+");
assertThat(captor.getValue().getLocationId()).isEqualTo(30003L);
assertThat(captor.getValue().getId()).isNotNull();
}
@Test
void defaultVirtualLocationIsInsertedIdempotentlyForAnExistingWarehouse() {
when(repository.findWarehouse(TENANT, WAREHOUSE)).thenReturn(warehouse());
WarehouseLocationEntity resolved = new WarehouseLocationEntity();
resolved.setId(30004L);
resolved.setLocationType("VIRTUAL");
resolved.setStatus("ACTIVE");
when(repository.findLocationByCode(TENANT, WAREHOUSE, "LOC-US-LAX-01-DEFAULT"))
.thenReturn(null, resolved);
WarehouseLocationEntity result = service.ensureDefaultVirtualLocation(TENANT, WAREHOUSE);
assertThat(result.getId()).isEqualTo(30004L);
ArgumentCaptor<WarehouseLocationEntity> location = ArgumentCaptor.forClass(WarehouseLocationEntity.class);
verify(repository).insertDefaultVirtualLocationIgnore(location.capture());
assertThat(location.getValue().getLocationCode()).isEqualTo("LOC-US-LAX-01-DEFAULT");
assertThat(location.getValue().getLocationType()).isEqualTo("VIRTUAL");
assertThat(location.getValue().getStatus()).isEqualTo("ACTIVE");
}
@Test
void rejectsAConflictingDefaultLocationCode() {
when(repository.findWarehouse(TENANT, WAREHOUSE)).thenReturn(warehouse());
WarehouseLocationEntity conflict = new WarehouseLocationEntity();
conflict.setLocationType("STORAGE");
conflict.setStatus("ACTIVE");
when(repository.findLocationByCode(TENANT, WAREHOUSE, "LOC-US-LAX-01-DEFAULT"))
.thenReturn(conflict);
assertThatThrownBy(() -> service.ensureDefaultVirtualLocation(TENANT, WAREHOUSE))
.isInstanceOf(BizException.class)
.hasMessageContaining("Default virtual location code");
verify(repository, never()).insertDefaultVirtualLocationIgnore(org.mockito.ArgumentMatchers.any());
}
@Test
void scrappedPalletCannotBeReactivated() {
when(repository.warehouseOwned(TENANT, WAREHOUSE)).thenReturn(true);
WarehousePalletEntity existing = new WarehousePalletEntity();
existing.setId(40002L);
existing.setPalletCode("PLT-OLD");
existing.setStatus("SCRAPPED");
when(repository.findPallet(TENANT, WAREHOUSE, 40002L)).thenReturn(existing);
assertThatThrownBy(() -> service.updatePallet(TENANT, WAREHOUSE, 40002L,
new PalletRequests.SaveRequest(null, "STANDARD", null, BigDecimal.ONE, "IDLE", null, null)))
.isInstanceOf(BizException.class)
.hasMessageContaining("cannot be reused");
}
private static WarehouseEntity warehouse() {
WarehouseEntity warehouse = new WarehouseEntity();
warehouse.setId(WAREHOUSE);
warehouse.setCustomerId(TENANT);
warehouse.setWarehouseCode("US-LAX-01");
return warehouse;
}
}