feat(wcs): add resource reservation ledger
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
package com.ruoyi.wcs.domain.traffic;
|
||||
|
||||
import com.ruoyi.wcs.domain.shared.DomainRuleViolation;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class ResourceLedgerTest {
|
||||
|
||||
@Test
|
||||
void grantsOnlyOneReservationForCapacityOneResource() {
|
||||
ResourceLedger ledger = ledger("NODE:A", 1);
|
||||
|
||||
assertTrue(ledger.reserve(request("R1", "V1", "NODE:A")).isGranted());
|
||||
ReservationDecision rejected = ledger.reserve(request("R2", "V2", "NODE:A"));
|
||||
|
||||
assertFalse(rejected.isGranted());
|
||||
assertEquals(Collections.singletonList("NODE:A"),
|
||||
rejected.getConflictingResourceKeys());
|
||||
assertEquals(Collections.singleton("V1"), ledger.holders("NODE:A"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void rejectsWholeMultiResourceRequestWithoutLeakingCapacity() {
|
||||
Map<String, Integer> capacities = new LinkedHashMap<String, Integer>();
|
||||
capacities.put("NODE:A", 1);
|
||||
capacities.put("NODE:B", 1);
|
||||
ResourceLedger ledger = new ResourceLedger(capacities);
|
||||
ledger.reserve(request("R1", "V1", "NODE:B"));
|
||||
|
||||
ReservationDecision rejected = ledger.reserve(new ReservationRequest(
|
||||
"R2", "O2", "S2", "V2", Arrays.asList("NODE:B", "NODE:A")));
|
||||
|
||||
assertFalse(rejected.isGranted());
|
||||
assertEquals(0, ledger.usage("NODE:A"));
|
||||
assertTrue(ledger.reserve(request("R3", "V3", "NODE:A")).isGranted());
|
||||
}
|
||||
|
||||
@Test
|
||||
void occupiedReservationCannotExpireByTime() {
|
||||
ResourceLedger ledger = ledger("NODE:A", 1);
|
||||
ReservationDecision decision = ledger.reserve(request("R1", "V1", "NODE:A"));
|
||||
ledger.occupy("R1", decision.getFencingToken());
|
||||
|
||||
ResourceReservation reservation = ledger.reservation("R1");
|
||||
|
||||
assertThrows(DomainRuleViolation.class, reservation::expire);
|
||||
}
|
||||
|
||||
@Test
|
||||
void unknownCommandKeepsOccupiedResourcesProtected() {
|
||||
ResourceLedger ledger = ledger("NODE:A", 1);
|
||||
ReservationDecision decision = ledger.reserve(request("R1", "V1", "NODE:A"));
|
||||
ledger.occupy("R1", decision.getFencingToken());
|
||||
ledger.markCommandUnknown("R1");
|
||||
|
||||
assertThrows(DomainRuleViolation.class, () -> ledger.release(
|
||||
"R1", "PLC-SNAPSHOT-1", decision.getFencingToken()));
|
||||
assertEquals(1, ledger.usage("NODE:A"));
|
||||
assertEquals(ReservationState.OCCUPIED, ledger.reservation("R1").getState());
|
||||
}
|
||||
|
||||
@Test
|
||||
void releaseRequiresMatchingFencingTokenAndPhysicalEvidence() {
|
||||
ResourceLedger ledger = ledger("NODE:A", 1);
|
||||
ReservationDecision decision = ledger.reserve(request("R1", "V1", "NODE:A"));
|
||||
ledger.occupy("R1", decision.getFencingToken());
|
||||
|
||||
assertThrows(DomainRuleViolation.class, () -> ledger.release(
|
||||
"R1", "PLC-SNAPSHOT-1", decision.getFencingToken() + 1));
|
||||
assertThrows(IllegalArgumentException.class, () -> ledger.release(
|
||||
"R1", "", decision.getFencingToken()));
|
||||
|
||||
ledger.release("R1", "PLC-SNAPSHOT-1", decision.getFencingToken());
|
||||
assertEquals(0, ledger.usage("NODE:A"));
|
||||
assertEquals(ReservationState.RELEASED, ledger.reservation("R1").getState());
|
||||
}
|
||||
|
||||
@Test
|
||||
void abortingAnUnoccupiedGrantReturnsCapacity() {
|
||||
ResourceLedger ledger = ledger("NODE:A", 1);
|
||||
ledger.reserve(request("R1", "V1", "NODE:A"));
|
||||
|
||||
ledger.abortUnoccupied("R1", "DISPATCH_CANCELLED");
|
||||
|
||||
assertEquals(0, ledger.usage("NODE:A"));
|
||||
assertTrue(ledger.reserve(request("R2", "V2", "NODE:A")).isGranted());
|
||||
}
|
||||
|
||||
private static ResourceLedger ledger(String resourceKey, int capacity) {
|
||||
return new ResourceLedger(Collections.singletonMap(resourceKey, capacity));
|
||||
}
|
||||
|
||||
private static ReservationRequest request(String reservationId,
|
||||
String vehicleId,
|
||||
String resourceKey) {
|
||||
return new ReservationRequest(reservationId, "O-" + reservationId,
|
||||
"S-" + reservationId, vehicleId,
|
||||
Collections.singletonList(resourceKey));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user