Files
baiyue-firstleg/integration-service/src/test/java/com/baiyue/integration/provider/ZimCarrierScheduleProviderAdapterTest.java
T

107 lines
7.9 KiB
Java
Raw Normal View History

2026-08-21 16:04:16 +08:00
package com.baiyue.integration.provider;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import java.time.LocalDateTime;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
class ZimCarrierScheduleProviderAdapterTest {
private final ObjectMapper objectMapper = new ObjectMapper();
@Test
void v2FixtureSelectsOnlyTheExactContainerAndSeparatesActualFromPlans() throws Exception {
String fixture = """
{"bookingReference":" BK-STRING-001 ","blReference":" BL-STRING-001 ",
"finalETA":{"etaValue":"2023-09-03T08:00:00","etaValueDate":"2023-09-03","podInd":"ETA"},
"routeDetails":[
{"sailingDateTz":"2023-08-10T12:00:00","arrivalDateTz":"2023-08-17T06:00:00","portCode":"CNNGB","port":"Ningbo","country":"CN","vesselName":"V1","voyage":"001E","leg":"1"},
{"sailingDateDT":"2023-08-18T10:00:00","arrivalDateDT":"2023-09-02T04:00:00","portCode":"USNYC","port":"New York","country":"US","vesselName":"V2","voyage":"002E","leg":"2"},
{"portCode":"USNYC","port":"New York","country":"US"}],
"bkBlDetails":{"consContainerList":[
{"containerNumber":"SIBLING000001 ","containerEventsList":[
{"activityDateTz":"2023-08-01T01:00:00","eventName":"Export Gate-In to Port of Loading","portCode":"CNNGB"}]},
{"containerNumber":" zcsu7881970 ","containerEventsList":[
{"activityDateTz":"2023-08-01T01:00:00","eventName":"Empty container dispatched from Port of Loading to Customer","portCode":"CNNGB"},
{"activityDateTz":"2023-08-10T02:00:00","eventName":"Export gate-in from Customer to Port of Loading","portCode":"CNNGB","port":"Ningbo","country":"CN"},
{"activityDateTz":"2023-08-18T11:00:00","eventName":"Vessel departure from first Transshipment Port to next Transshipment Port","portCode":"SGSIN","port":"Singapore","country":"SG","vesselName":"V2","voyage":"002E","leg":"2"},
{"activityDateTz":"2023-08-19T12:00:00","eventName":"Container was loaded at Transshipment Port to Transshipment Port","portCode":"SGSIN","port":"Singapore","country":"SG","vesselName":"V2","voyage":"002E","leg":"2"},
{"activityDate":"2023-08-20T12:00:00","eventName":"Container was loaded at Transshipment Port to Transshipment Port","portCode":"SGSIN","port":"Singapore","country":"SG","vesselName":"V2","voyage":"002E","leg":"2"},
{"activityDateTz":"2023-08-21T12:00:00","eventName":"Unknown Future Event","portCode":"SGSIN"}]}]}}
""";
var payload = objectMapper.readValue(fixture, ZimTrackingClient.TracingPayload.class);
var response = new ZimTrackingClient.TrackingResponse(payload, fixture);
LocalDateTime queriedAt = LocalDateTime.of(2026, 8, 21, 12, 0);
var values = new ZimCarrierScheduleProviderAdapter(mock(ZimTrackingClient.class))
.observations("ZCSU7881970", response, queriedAt);
assertThat(values).hasSize(7);
assertThat(values).extracting(value -> value.observedAt()).containsOnly(queriedAt);
assertThat(values).filteredOn(value -> value.actualAt() != null)
.extracting(value -> value.standardNode())
.containsExactlyInAnyOrder("AT_ORIGIN_PORT", "IN_TRANSIT", "LOADED_ON_VESSEL", "LOADED_ON_VESSEL");
assertThat(values).filteredOn(value -> value.estimatedAt() != null)
.extracting(value -> value.standardNode())
.containsExactlyInAnyOrder("DEPARTED_ORIGIN_PORT", "ARRIVED_DESTINATION_PORT",
"ARRIVED_DESTINATION_PORT");
assertThat(values).noneMatch(value -> LocalDateTime.of(2023, 8, 1, 1, 0).equals(value.actualAt()));
assertThat(values).extracting(value -> value.providerNodeCode())
.doesNotContain("EMPTY CONTAINER DISPATCHED FROM PORT OF LOADING TO CUSTOMER", "UNKNOWN FUTURE EVENT");
assertThat(values).allSatisfy(value -> assertThat(value.sourceReference())
.isEqualTo("ZIM:CONTAINER:ZCSU7881970|BOOKING:BK-STRING-001|BL:BL-STRING-001"));
}
@Test
void eventNameMappingIsAnExactExpandableWhitelist() {
assertThat(ZimCarrierScheduleProviderAdapter.standardNode(
"Export Gate-In to Port of Loading")).isEqualTo("AT_ORIGIN_PORT");
assertThat(ZimCarrierScheduleProviderAdapter.standardNode(
"Container was loaded at Transshipment Port to Transshipment Port")).isEqualTo("LOADED_ON_VESSEL");
assertThat(ZimCarrierScheduleProviderAdapter.standardNode(
"Vessel departure from first Transshipment Port to next Transshipment Port")).isEqualTo("IN_TRANSIT");
assertThat(ZimCarrierScheduleProviderAdapter.standardNode(
"Container loaded at Port of Loading")).isEqualTo("LOADED_ON_VESSEL");
assertThat(ZimCarrierScheduleProviderAdapter.standardNode(
"Vessel departure from Port of Loading")).isEqualTo("DEPARTED_ORIGIN_PORT");
assertThat(ZimCarrierScheduleProviderAdapter.standardNode(
"Container was loaded at Port of Loading to Port of Discharge")).isEqualTo("LOADED_ON_VESSEL");
assertThat(ZimCarrierScheduleProviderAdapter.standardNode(
"Vessel departure from Port of Loading to Port of Discharge")).isEqualTo("DEPARTED_ORIGIN_PORT");
assertThat(ZimCarrierScheduleProviderAdapter.standardNode(
"Empty container dispatched from Port of Loading to Customer")).isNull();
assertThat(ZimCarrierScheduleProviderAdapter.standardNode("Unknown Future Event")).isNull();
}
@Test
void liveBkBlOnlyFixtureToleratesMissingPlansAndDoesNotAdvanceCarrierRelease() throws Exception {
String fixture = """
{"bkBlDetails":{"consContainerList":[
{"containerNumber":" ZCSU7205614 ","containerEventsList":[
{"activityDateTz":"2026-08-01T01:00:00Z","eventName":"Empty container dispatched from inland point to Customer","portCode":"CNNGB"},
{"activityDateTz":"2026-08-03T02:00:00Z","eventName":"Export gate-in from Customer to Port of Loading","portCode":"CNNGB","port":"Ningbo","country":"CN"},
{"activityDateTz":"2026-08-05T03:00:00Z","eventName":"Container was loaded at Port of Loading to Port of Discharge","portCode":"CNNGB","port":"Ningbo","country":"CN","vesselName":"LIVE VESSEL","voyage":"001E","leg":"1"},
{"activityDateTz":"2026-08-06T04:00:00Z","eventName":"Vessel departure from Port of Loading to Port of Discharge","portCode":"CNNGB","port":"Ningbo","country":"CN","vesselName":"LIVE VESSEL","voyage":"001E","leg":"1"},
{"activityDateTz":"2026-08-20T05:00:00Z","eventName":"Carrier Release","portCode":"USLAX","port":"Los Angeles","country":"US"}]},
{"containerNumber":"ZCSU7960370","containerEventsList":[
{"activityDateTz":"2026-08-07T00:00:00Z","eventName":"Export gate-in from Customer to Port of Loading"}]}]}}
""";
var payload = objectMapper.readValue(fixture, ZimTrackingClient.TracingPayload.class);
var response = new ZimTrackingClient.TrackingResponse(payload, fixture);
var values = new ZimCarrierScheduleProviderAdapter(mock(ZimTrackingClient.class))
.observations("zcsu7205614", response, LocalDateTime.of(2026, 8, 21, 12, 0));
assertThat(payload.routeDetails()).isNull();
assertThat(payload.finalETA()).isNull();
assertThat(values).hasSize(3).allSatisfy(value -> assertThat(value.actualAt()).isNotNull());
assertThat(values).extracting(value -> value.standardNode())
.containsExactlyInAnyOrder("AT_ORIGIN_PORT", "LOADED_ON_VESSEL", "DEPARTED_ORIGIN_PORT");
assertThat(values).extracting(value -> value.standardNode()).doesNotContain("PICKUP_READY");
assertThat(values).extracting(value -> value.providerNodeCode())
.doesNotContain("CARRIER RELEASE", "EMPTY CONTAINER DISPATCHED FROM INLAND POINT TO CUSTOMER");
}
}