107 lines
7.9 KiB
Java
107 lines
7.9 KiB
Java
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");
|
||
|
|
}
|
||
|
|
}
|