优化
This commit is contained in:
+55
-12
@@ -2,12 +2,13 @@ package com.baiyue.lastleg.inboundplan.pda.service;
|
||||
|
||||
import com.baiyue.common.core.api.ApiResponse;
|
||||
import com.baiyue.common.event.EventEnvelope;
|
||||
import com.baiyue.common.event.DeliveryAck;
|
||||
import com.baiyue.common.event.EventPayloadCanonicalizer;
|
||||
import com.baiyue.common.event.EventPayloadHash;
|
||||
import com.baiyue.common.event.EventTypes;
|
||||
import com.baiyue.common.event.OverseasContainerUnloadedV1;
|
||||
import com.baiyue.common.event.RegionCode;
|
||||
import com.baiyue.lastleg.client.EventBridgeClient;
|
||||
import com.baiyue.lastleg.client.DirectEventClient;
|
||||
import com.baiyue.lastleg.entity.LastlegEventOutbox;
|
||||
import com.baiyue.lastleg.repository.UnloadCompletedOutboxRepository;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
@@ -30,7 +31,7 @@ import com.baiyue.common.core.api.ResultCode;
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class InboundUnloadCompletedOutboxRelayTest {
|
||||
@Mock private UnloadCompletedOutboxRepository repository;
|
||||
@Mock private EventBridgeClient client;
|
||||
@Mock private DirectEventClient client;
|
||||
private ObjectMapper objectMapper;
|
||||
private InboundUnloadCompletedOutboxRelay relay;
|
||||
|
||||
@@ -43,19 +44,18 @@ class InboundUnloadCompletedOutboxRelayTest {
|
||||
ReflectionTestUtils.setField(relay, "objectMapper", objectMapper);
|
||||
ReflectionTestUtils.setField(relay, "internalToken", "internal-token");
|
||||
ReflectionTestUtils.setField(relay, "leaseSeconds", 60L);
|
||||
ReflectionTestUtils.setField(relay, "retrySeconds", 30L);
|
||||
ReflectionTestUtils.setField(relay, "baseDelaySeconds", 5L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void marksOutboxPublishedAfterDomesticBridgeAcknowledgesSameEvent() throws Exception {
|
||||
void marksOutboxPublishedAfterConsumerAcknowledgesSameEvent() throws Exception {
|
||||
EventEnvelope event = event();
|
||||
LastlegEventOutbox row = row(event);
|
||||
when(repository.pending(eq(50), any(), any())).thenReturn(List.of(row));
|
||||
when(repository.claim(eq(9001L), anyString(), any(), any())).thenReturn(true);
|
||||
when(repository.claimed(eq(9001L), anyString())).thenReturn(row);
|
||||
when(client.receive(eq("internal-token"), any(EventEnvelope.class)))
|
||||
.thenReturn(ApiResponse.success(new EventBridgeClient.Acknowledgement(
|
||||
event.eventId(), "已接收", "2026-08-20T03:01:00")));
|
||||
.thenReturn(ApiResponse.success(DeliveryAck.of(event, DeliveryAck.Outcome.APPLIED)));
|
||||
|
||||
relay.relay();
|
||||
|
||||
@@ -64,23 +64,23 @@ class InboundUnloadCompletedOutboxRelayTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void releasesLeaseForRetryWhenBridgeDeliveryFails() throws Exception {
|
||||
void releasesLeaseForRetryWhenConsumerDeliveryFails() throws Exception {
|
||||
EventEnvelope event = event();
|
||||
LastlegEventOutbox row = row(event);
|
||||
when(repository.pending(eq(50), any(), any())).thenReturn(List.of(row));
|
||||
when(repository.claim(eq(9001L), anyString(), any(), any())).thenReturn(true);
|
||||
when(repository.claimed(eq(9001L), anyString())).thenReturn(row);
|
||||
when(client.receive(eq("internal-token"), any(EventEnvelope.class)))
|
||||
.thenThrow(new IllegalStateException("bridge unavailable"));
|
||||
.thenThrow(new IllegalStateException("consumer unavailable"));
|
||||
|
||||
relay.relay();
|
||||
|
||||
verify(repository).retry(eq(9001L), anyString(), any(LocalDateTime.class), eq("bridge unavailable"));
|
||||
verify(repository).retry(eq(9001L), anyString(), any(LocalDateTime.class), eq("consumer unavailable"));
|
||||
verify(repository, never()).published(anyLong(), anyString(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void preservesDomesticBridgeBusinessErrorForDeploymentDiagnosis() throws Exception {
|
||||
void preservesConsumerBusinessErrorForDeploymentDiagnosis() throws Exception {
|
||||
EventEnvelope event = event();
|
||||
LastlegEventOutbox row = row(event);
|
||||
when(repository.pending(eq(50), any(), any())).thenReturn(List.of(row));
|
||||
@@ -88,15 +88,58 @@ class InboundUnloadCompletedOutboxRelayTest {
|
||||
when(repository.claimed(eq(9001L), anyString())).thenReturn(row);
|
||||
when(client.receive(eq("internal-token"), any(EventEnvelope.class)))
|
||||
.thenReturn(ApiResponse.failure(ResultCode.BAD_REQUEST,
|
||||
"事件目标区域与当前事件桥区域不一致", "request-bridge", "trace-bridge"));
|
||||
"消费事件类型不支持", "request-bridge", "trace-bridge"));
|
||||
|
||||
relay.relay();
|
||||
|
||||
verify(repository).retry(eq(9001L), anyString(), any(LocalDateTime.class),
|
||||
contains("事件目标区域与当前事件桥区域不一致"));
|
||||
contains("消费事件类型不支持"));
|
||||
verify(repository, never()).published(anyLong(), anyString(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void mismatchedHashOrUnallowedStaleCannotMarkPublished() throws Exception {
|
||||
EventEnvelope event = event();
|
||||
LastlegEventOutbox row = row(event);
|
||||
when(repository.pending(eq(50), any(), any())).thenReturn(List.of(row));
|
||||
when(repository.claim(eq(9001L), anyString(), any(), any())).thenReturn(true);
|
||||
when(repository.claimed(eq(9001L), anyString())).thenReturn(row);
|
||||
when(client.receive(eq("internal-token"), any(EventEnvelope.class)))
|
||||
.thenReturn(ApiResponse.success(new DeliveryAck(event.eventId(), "sha256:" + "0".repeat(64), DeliveryAck.Outcome.APPLIED)))
|
||||
.thenReturn(ApiResponse.success(DeliveryAck.of(event, DeliveryAck.Outcome.STALE)));
|
||||
relay.relay();
|
||||
relay.relay();
|
||||
verify(repository, times(2)).retry(eq(9001L), anyString(), any(), anyString());
|
||||
verify(repository, never()).published(anyLong(), anyString(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void twelfthFailureGoesDeadInsteadOfSchedulingAnotherRetry() throws Exception {
|
||||
LastlegEventOutbox row = row(event());
|
||||
row.setRetryCount(11);
|
||||
when(repository.pending(eq(50), any(), any())).thenReturn(List.of(row));
|
||||
when(repository.claim(eq(9001L), anyString(), any(), any())).thenReturn(true);
|
||||
when(repository.claimed(eq(9001L), anyString())).thenReturn(row);
|
||||
when(client.receive(anyString(), any())).thenThrow(new IllegalStateException("consumer unavailable"));
|
||||
relay.relay();
|
||||
verify(repository).dead(eq(9001L), anyString(), eq(12), contains("consumer unavailable"));
|
||||
verify(repository, never()).retry(anyLong(), anyString(), any(), anyString());
|
||||
verify(repository, never()).published(anyLong(), anyString(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void recoveredExhaustedLeaseDoesNotSendAgain() throws Exception {
|
||||
LastlegEventOutbox row = row(event());
|
||||
row.setRetryCount(12);
|
||||
when(repository.pending(eq(50), any(), any())).thenReturn(List.of(row));
|
||||
when(repository.claim(eq(9001L), anyString(), any(), any())).thenReturn(true);
|
||||
when(repository.claimed(eq(9001L), anyString())).thenReturn(row);
|
||||
relay.relay();
|
||||
verifyNoInteractions(client);
|
||||
verify(repository).dead(eq(9001L), anyString(), eq(12), contains("租约超时"));
|
||||
verify(repository, never()).retry(anyLong(), anyString(), any(), anyString());
|
||||
}
|
||||
|
||||
private LastlegEventOutbox row(EventEnvelope event) throws Exception {
|
||||
LastlegEventOutbox row = new LastlegEventOutbox();
|
||||
row.setId(9001L);
|
||||
|
||||
Reference in New Issue
Block a user