fix(transfer): reserve receipt resources with durable recovery intents

This commit is contained in:
2026-10-05 03:37:13 +08:00
parent 3f8ac23aba
commit c194dd2862
10 changed files with 661 additions and 9 deletions
@@ -21,6 +21,53 @@ class HttpWarehouseResourceGatewayTest {
private HttpWarehouseResourceGateway gateway;
private MockRestServiceServer server;
@Test
void reserveAcceptsOnlyCompleteMatchingActiveLockWithStringEncodedLongs() {
server.expect(requestTo("http://warehouse/internal/v1/warehouse-resource-locks"))
.andRespond(withSuccess("""
{"code":"200","data":{"lockId":"51","customerId":"1","warehouseId":"2",
"palletId":"3","locationId":"4","businessType":"WAREHOUSE_TRANSFER_RECEIPT",
"businessId":"target-hash","reservationKey":"resource-1","status":"ACTIVE"}}
""", MediaType.APPLICATION_JSON));
assertThat(gateway.reserve("1", 2, 3, 4, "WAREHOUSE_TRANSFER_RECEIPT", "target-hash", "resource-1")).isEqualTo(51);
server.verify();
}
@Test
void reserveRejectsReleasedHistoricalLockRatherThanTreatingOldKeyAsNewOccupancy() {
server.expect(requestTo("http://warehouse/internal/v1/warehouse-resource-locks"))
.andRespond(withSuccess("""
{"code":"200","data":{"lockId":51,"customerId":1,"warehouseId":2,"palletId":3,
"locationId":4,"businessType":"WAREHOUSE_TRANSFER_RECEIPT","businessId":"target-hash",
"reservationKey":"resource-1","status":"RELEASED"}}
""", MediaType.APPLICATION_JSON));
assertThatThrownBy(() -> gateway.reserve("1", 2, 3, 4, "WAREHOUSE_TRANSFER_RECEIPT", "target-hash", "resource-1"))
.isInstanceOf(ResponseStatusException.class).hasMessageContaining("requested ACTIVE lock");
server.verify();
}
@Test
void reserveRejectsWrongOwnershipEvenWhenReturnedLockIsActive() {
server.expect(requestTo("http://warehouse/internal/v1/warehouse-resource-locks"))
.andRespond(withSuccess("""
{"code":"200","data":{"lockId":51,"customerId":99,"warehouseId":2,"palletId":3,
"locationId":4,"businessType":"WAREHOUSE_TRANSFER_RECEIPT","businessId":"target-hash",
"reservationKey":"resource-1","status":"ACTIVE"}}
""", MediaType.APPLICATION_JSON));
assertThatThrownBy(() -> gateway.reserve("1", 2, 3, 4, "WAREHOUSE_TRANSFER_RECEIPT", "target-hash", "resource-1"))
.isInstanceOf(ResponseStatusException.class).hasMessageContaining("requested ACTIVE lock");
server.verify();
}
@Test
void reserveRejectsMissingActiveStateInsteadOfAcceptingBareLockId() {
server.expect(requestTo("http://warehouse/internal/v1/warehouse-resource-locks"))
.andRespond(withSuccess("{\"code\":\"200\",\"data\":{\"lockId\":51}}", MediaType.APPLICATION_JSON));
assertThatThrownBy(() -> gateway.reserve("1", 2, 3, 4, "WAREHOUSE_TRANSFER_RECEIPT", "target-hash", "resource-1"))
.isInstanceOf(ResponseStatusException.class).hasMessageContaining("requested ACTIVE lock");
server.verify();
}
@BeforeEach
void setUp() {
RestClient.Builder builder = RestClient.builder();