test: freeze server-filtered booking candidates

This commit is contained in:
2026-08-25 20:17:12 +08:00
parent ead9565a37
commit 5d2b6c22e6
3 changed files with 20 additions and 2 deletions
@@ -36,7 +36,7 @@ paths:
/api/platform/v1/ocean-bookings/link-candidates:
get:
summary: Page released booking requirement candidates for one container
description: Requires platform:firstleg:ocean-booking:list. Eligibility matches only warehouseId, channelCode and containerType, with remainingQuantity greater than zero. Capacity and max weight are derived display snapshots and never eligibility fields. Allocated quantity counts only ACTIVE containers still pointing to the same booking requirement and is calculated by one bounded set query; booking detail calls are not used.
description: Requires platform:firstleg:ocean-booking:list. Returns only linkable requirement candidates: RELEASED, remainingQuantity greater than zero, and exact warehouseId, channelCode and containerType. Capacity and max weight are derived display snapshots and never eligibility fields. Allocated quantity counts only ACTIVE containers still pointing to the same booking requirement and is calculated by one bounded set query; booking detail calls are not used. An empty result is a normal successful page. Final target-container mutability is revalidated by the link command.
x-permission: platform:firstleg:ocean-booking:list
parameters:
- {name: warehouseId, in: query, required: true, schema: {type: integer, format: int64, minimum: 1}}
@@ -71,7 +71,10 @@ class PlatformOceanBookingControllerContractTest {
.contains("warehouseId", "channelCode", "containerType")
.contains("LinkCandidateResponse", "requiredQuantity", "allocatedQuantity", "remainingQuantity")
.contains("Capacity and max weight are derived display snapshots and never eligibility fields")
.contains("booking detail calls are not used");
.contains("Returns only linkable requirement candidates")
.contains("An empty result is a normal successful page")
.contains("booking detail calls are not used")
.doesNotContain("mismatchReasons", "disabledReason");
}
@Test
@@ -210,6 +210,21 @@ class OceanBookingServiceTest {
verify(repository, never()).containers(anyLong());
}
@Test
void linkCandidatesReturnsNormalEmptyPageWithOneSetQueryAndNoDiagnosticFields() {
when(repository.linkCandidates(any())).thenReturn(new PageResult<>(List.of(), 1, 20, 0, 0));
PageResult<LinkCandidate> result = service.linkCandidates(
new LinkCandidateQuery(701L, "SEA-US", "45HC", null, null));
assertThat(result.records()).isEmpty();
assertThat(result.total()).isZero();
assertThat(result.pages()).isZero();
verify(repository, times(1)).linkCandidates(any());
assertThat(LinkCandidate.class.getRecordComponents()).extracting(java.lang.reflect.RecordComponent::getName)
.doesNotContain("mismatchReasons", "disabledReason", "warehouseId", "channelCode");
}
@Test
void releaseRejectsInvalidScheduleWithoutTransition() {
OceanBookingEntity draft = completeBooking("DRAFT", 3);