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

132 lines
7.7 KiB
Java

package com.baiyue.integration.provider;
import com.baiyue.integration.dto.CarrierScheduleDtos.QueryRequest;
import com.baiyue.integration.provider.MatsonTrackingClient.TrackingResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import java.time.Instant;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
class MatsonCarrierScheduleProviderAdapterTest {
private final ObjectMapper objectMapper = new ObjectMapper();
@Test
void appendedEventAndChangedEtaKeepOldNodeAndHoldFactsButChangeResponseAudit() throws Exception {
String raw = """
{"bookingNumber":"MATSON-REAL","containerNumber":"MATU459895","checkDigit":"6",
"originPort":"NINGBO","destPort":"LONG BEACH","originCode":"NGB","destCode":"LAX","vesselETA":"08/24/26",
"events":[{"date":1786940100000,"location":"NINGBO","status":"INGATE"},
{"date":1787160480000,"location":"","status":"CONTAINER ON HOLD - Customs Clearance"}]}
""";
String next = raw.replace("08/24/26", "08/25/26").replace("]}", ",{\"date\":1787033640000,\"location\":\"NINGBO\",\"status\":\"LOAD TO VESSEL\"}]}");
var adapter = new MatsonCarrierScheduleProviderAdapter(mock(MatsonTrackingClient.class));
var query = new QueryRequest("MATSON", "CONTAINER_NO", "MATU4598956");
var time = java.time.LocalDateTime.of(2026, 8, 21, 12, 0);
var before = adapter.observations(query, objectMapper.readTree(raw), raw, time);
var after = adapter.observations(query, objectMapper.readTree(next), next, time.plusHours(3));
CarrierResponseAuditAssertions.stableSharedFacts(before, after, raw, next);
assertThat(after).hasSize(before.size() + 1);
var hold = adapter.exceptions(query, objectMapper.readTree(raw), raw, time).get(0);
var replay = adapter.exceptions(query, objectMapper.readTree(next), next, time.plusHours(3)).get(0);
assertThat(replay).usingRecursiveComparison().ignoringFields("observedAt", "rawPayloadHash").isEqualTo(hold);
assertThat(replay.rawPayloadHash()).isNotEqualTo(hold.rawPayloadHash());
}
@Test
void realMatu4646247FixtureProducesTwoActualsAndEtaOnly() throws Exception {
String raw = """
{"bookingNumber":"MATSON-REAL","containerNumber":"MATU464624","checkDigit":"7",
"originPort":"LONG BEACH","destPort":"HONOLULU","originCode":"LAX","destCode":"HIL",
"loadPort":"LONG BEACH","dischargePort":"HONOLULU","vesselETA":"08/25/26","events":[
{"date":%d,"location":"LONG BEACH","status":"INGATE"},
{"date":%d,"location":"LONG BEACH","status":"LOAD TO VESSEL"}]}
""".formatted(Instant.parse("2026-08-18T19:15:00Z").toEpochMilli(),
Instant.parse("2026-08-18T22:12:00Z").toEpochMilli());
MatsonTrackingClient client = mock(MatsonTrackingClient.class);
when(client.configured()).thenReturn(true);
when(client.query(new QueryRequest("MATSON", "CONTAINER_NO", "MATU4646247")))
.thenReturn(new TrackingResponse(objectMapper.readTree(raw), raw));
var result = new MatsonCarrierScheduleProviderAdapter(client)
.query(new QueryRequest("MATSON", "CONTAINER_NO", "MATU4646247"));
assertThat(result.observations()).extracting(value -> value.standardNode())
.containsExactlyInAnyOrder("AT_ORIGIN_PORT", "LOADED_ON_VESSEL", "ARRIVED_DESTINATION_PORT");
assertThat(result.observations()).filteredOn(value -> value.actualAt() != null).hasSize(2);
assertThat(result.observations()).filteredOn(value -> value.estimatedAt() != null).singleElement();
assertThat(result.observations()).extracting(value -> value.observedAt())
.containsOnly(result.observations().get(0).observedAt());
assertThat(result.exceptions()).isEmpty();
}
@Test
void conservativelyMapsKnownMatsonEventsWithoutTreatingLoadAsDeparture() throws Exception {
String raw = """
{"bookingNumber":"9974749","containerNumber":"MATU459895","checkDigit":"6",
"originPort":"NINGBO","destPort":"LONG BEACH","originCode":"NGB","destCode":"LAX",
"vesselETA":"08/24/26",
"loadPort":"NINGBO","dischargePort":"LONG BEACH","events":[
{"date":1787160480000,"location":"","status":"CONTAINER ON HOLD - Customs Clearance"},
{"date":1787033640000,"location":"NINGBO","status":"LOAD TO VESSEL"},
{"date":1786940100000,"location":"NINGBO","status":"INGATE"}]}
""";
MatsonTrackingClient client = mock(MatsonTrackingClient.class);
when(client.configured()).thenReturn(true);
when(client.query(new QueryRequest("MATSON", "CONTAINER_NO", "MATU4598956")))
.thenReturn(new TrackingResponse(objectMapper.readTree(raw), raw));
MatsonCarrierScheduleProviderAdapter adapter = new MatsonCarrierScheduleProviderAdapter(client);
var result = adapter.query(new QueryRequest("MATSON", "CONTAINER_NO", "MATU4598956"));
assertThat(result.outcome()).isEqualTo("SUCCESS");
assertThat(result.observations()).extracting(value -> value.standardNode())
.containsExactlyInAnyOrder("AT_ORIGIN_PORT", "LOADED_ON_VESSEL", "ARRIVED_DESTINATION_PORT");
assertThat(result.observations()).filteredOn(value -> "ARRIVED_DESTINATION_PORT".equals(value.standardNode()))
.singleElement().satisfies(value -> {
assertThat(value.estimatedAt()).isNotNull();
assertThat(value.actualAt()).isNull();
});
assertThat(result.exceptions()).singleElement().satisfies(value -> {
assertThat(value.exceptionCode()).isEqualTo("CUSTOMS_HOLD");
assertThat(value.active()).isTrue();
assertThat(value.providerStatus()).isEqualTo("CONTAINER ON HOLD - Customs Clearance");
});
}
@Test
void mapsDestinationDischargeAndAvailabilityWithoutUsingEtaAsActualArrival() {
assertThat(MatsonCarrierScheduleProviderAdapter.standardNode("LOAD TO VESSEL"))
.isEqualTo("LOADED_ON_VESSEL");
assertThat(MatsonCarrierScheduleProviderAdapter.standardNode("DISCHARGE FROM VESSEL"))
.isEqualTo("DISCHARGED_FROM_VESSEL");
assertThat(MatsonCarrierScheduleProviderAdapter.standardNode("AVAILABLE FOR PICKUP"))
.isEqualTo("PICKUP_READY");
assertThat(MatsonCarrierScheduleProviderAdapter.standardNode("OUTGATE")).isEqualTo("PICKED_UP");
assertThat(MatsonCarrierScheduleProviderAdapter.standardNode("EMPTY RETURNED"))
.isEqualTo("EMPTY_RETURNED");
assertThat(MatsonCarrierScheduleProviderAdapter.standardNode("PICKUP APPOINTMENT"))
.isNull();
assertThat(MatsonCarrierScheduleProviderAdapter.standardNode("CONTAINER ON HOLD - Customs Clearance"))
.isNull();
assertThat(MatsonCarrierScheduleProviderAdapter.exceptionCode(
"CONTAINER ON HOLD - Customs Clearance")).isEqualTo("CUSTOMS_HOLD");
}
@Test
void emptyBusinessObjectReturnsNoFacts() throws Exception {
String raw = "{}";
MatsonTrackingClient client = mock(MatsonTrackingClient.class);
when(client.configured()).thenReturn(true);
when(client.query(new QueryRequest("MATSON", "CONTAINER_NO", "MATU5164086")))
.thenReturn(new TrackingResponse(objectMapper.readTree(raw), raw));
var result = new MatsonCarrierScheduleProviderAdapter(client)
.query(new QueryRequest("MATSON", "CONTAINER_NO", "MATU5164086"));
assertThat(result.outcome()).isEqualTo("NO_CHANGE");
assertThat(result.observations()).isEmpty();
assertThat(result.exceptions()).isEmpty();
}
}