package com.baiyue.integration.provider; import com.baiyue.integration.dto.CarrierScheduleDtos.QueryRequest; import com.baiyue.integration.provider.HedeTrackingClient.TrackingResponse; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; class HedeCarrierScheduleProviderAdapterTest { @Test void appendedLoadingAndChangedEtaPreserveGateEventProjection() throws Exception { String raw = row("FFAU4879673", "2026-08-05 04:24", "2026-08-22 00:00", "2026-07-30 21:31", "2026-08-01 15:55", "", "", "", ""); String next = row("FFAU4879673", "2026-08-05 04:24", "2026-08-23 00:00", "2026-07-30 21:31", "2026-08-01 15:55", "2026-08-04 19:45", "", "", ""); var adapter = new HedeCarrierScheduleProviderAdapter(mock(HedeTrackingClient.class)); var before = adapter.observations("FFAU4879673", raw); var after = adapter.observations("FFAU4879673", next); CarrierResponseAuditAssertions.stableSharedFacts(before, after, raw, next); assertThat(after).hasSize(before.size() + 1); } @Test void parsesConfirmedMainTableColumnsAndDoesNotTurnEtaIntoAnActualNode() throws Exception { String html = row("XYLU8251457", "2026-08-20 00:00", "2026-09-09 00:00", "", "", "", "", "", ""); var rows = HedeCarrierScheduleProviderAdapter.rows(html); HedeTrackingClient client = mock(HedeTrackingClient.class); HedeCarrierScheduleProviderAdapter adapter = new HedeCarrierScheduleProviderAdapter(client); assertThat(rows).singleElement().satisfies(value -> { assertThat(value.containerNo()).isEqualTo("XYLU8251457"); assertThat(value.vessel()).isEqualTo("POSEN"); assertThat(value.voyage()).isEqualTo("2607W"); assertThat(value.estimatedDeparture()).isEqualTo("2026-08-20 00:00"); assertThat(value.estimatedArrival()).isEqualTo("2026-09-09 00:00"); }); assertThat(adapter.observations("XYLU8251457", html)).extracting(value -> value.standardNode()) .containsExactlyInAnyOrder("DEPARTED_ORIGIN_PORT", "ARRIVED_DESTINATION_PORT"); assertThat(adapter.observations("XYLU8251457", html)).allSatisfy(value -> { assertThat(value.estimatedAt()).isNotNull(); assertThat(value.actualAt()).isNull(); }); } @Test void mapsOnlyActualHedeMilestonesForTheRequestedContainer() { String html = row("FFAU4879673", "2026-08-05 04:24", "2026-08-22 00:00", "2026-07-30 21:31", "2026-08-01 15:55", "2026-08-04 19:45", "", "", ""); HedeTrackingClient client = mock(HedeTrackingClient.class); when(client.configured()).thenReturn(true); when(client.query(new QueryRequest("HEDE", "CONTAINER_NO", "FFAU4879673"))) .thenReturn(new TrackingResponse(html)); HedeCarrierScheduleProviderAdapter adapter = new HedeCarrierScheduleProviderAdapter(client); var result = adapter.query(new QueryRequest("HEDE", "CONTAINER_NO", "FFAU4879673")); assertThat(result.outcome()).isEqualTo("SUCCESS"); assertThat(result.observations()).extracting(value -> value.standardNode()) .containsExactlyInAnyOrder("DEPARTED_ORIGIN_PORT", "ARRIVED_DESTINATION_PORT", "AT_ORIGIN_PORT", "LOADED_ON_VESSEL"); assertThat(result.observations()).allSatisfy(value -> { assertThat(value.providerEventId()).hasSize(64); assertThat(value.rawPayloadHash()).hasSize(64); assertThat(value.sourceReference()).contains("FFAU4879673").contains("POSEN/2607W"); }); assertThat(result.observations()).extracting(value -> value.observedAt()) .containsOnly(result.observations().get(0).observedAt()); } @Test void actualEmptyReturnIsMappedPrecisely() throws Exception { String html = row("FFAU4879673", "2026-08-05 04:24", "2026-08-22 00:00", "2026-07-30 21:31", "2026-08-01 15:55", "2026-08-04 19:45", "", "", "2026-08-28 08:00"); HedeTrackingClient client = mock(HedeTrackingClient.class); var result = new HedeCarrierScheduleProviderAdapter(client).observations("FFAU4879673", html); assertThat(result).filteredOn(value -> "EMPTY_RETURNED".equals(value.standardNode())) .singleElement().satisfies(value -> { assertThat(value.actualAt()).isNotNull(); assertThat(value.estimatedAt()).isNull(); }); } @Test void refusesAmbiguousBillLevelAutomation() { HedeTrackingClient client = mock(HedeTrackingClient.class); HedeCarrierScheduleProviderAdapter adapter = new HedeCarrierScheduleProviderAdapter(client); var result = adapter.query(new QueryRequest("HEDE", "BILL_OF_LADING", "HDE123")); assertThat(result.outcome()).isEqualTo("PERMANENT_FAILURE"); assertThat(result.errorCode()).isEqualTo("HEDE_CONTAINER_REQUIRED"); } private static String row(String container, String etd, String eta, String emptyOut, String fullIn, String loaded, String discharged, String fullOut, String emptyReturned) { return "" + td("***") + td(container) + td("POSEN") + td("2607W") + td(etd) + td(eta) + td(emptyOut) + td(fullIn) + td(loaded) + td(discharged) + td(fullOut) + td(emptyReturned) + td("查看") + "
"; } private static String td(String value) { return "" + value + ""; } }