Files
baiyue-firstleg/event-bridge-service/src/test/java/com/baiyue/eventbridge/service/EventInboxServiceTest.java
T

126 lines
5.1 KiB
Java
Raw Normal View History

2026-07-30 18:01:36 +08:00
package com.baiyue.eventbridge.service;
import com.baiyue.common.event.EventEnvelope;
import com.baiyue.common.event.EventPayloadCanonicalizer;
import com.baiyue.common.event.EventPayloadHash;
import com.baiyue.common.event.RegionCode;
import com.baiyue.eventbridge.domain.BridgeInbox;
import com.fasterxml.jackson.databind.ObjectMapper;
2026-08-05 10:35:07 +08:00
import jakarta.annotation.Resource;
2026-07-31 16:53:07 +08:00
import org.junit.jupiter.api.DisplayName;
2026-08-05 10:35:07 +08:00
import org.junit.jupiter.api.AfterEach;
2026-07-30 18:01:36 +08:00
import org.junit.jupiter.api.Test;
2026-08-05 10:35:07 +08:00
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
2026-07-30 18:01:36 +08:00
import org.springframework.dao.DuplicateKeyException;
2026-08-05 10:35:07 +08:00
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import org.springframework.test.util.ReflectionTestUtils;
2026-07-30 18:01:36 +08:00
import java.time.Instant;
import java.time.LocalDateTime;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
2026-07-31 16:53:07 +08:00
@DisplayName("跨区域事件收件箱")
2026-08-05 10:35:07 +08:00
@SpringJUnitConfig(classes = {EventInboxService.class, EventInboxServiceTest.TestConfig.class})
@TestPropertySource(properties = "baiyue.region=OVERSEAS")
2026-07-30 18:01:36 +08:00
class EventInboxServiceTest {
2026-08-05 10:35:07 +08:00
@Resource
private ObjectMapper objectMapper;
@MockBean
private InboxStore store;
@Resource
private EventInboxService service;
@Configuration(proxyBeanMethods = false)
static class TestConfig {
@Bean
ObjectMapper objectMapper() {
return new ObjectMapper();
}
}
@AfterEach
void resetRegion() {
ReflectionTestUtils.setField(service, "localRegion", "OVERSEAS");
}
2026-07-30 18:01:36 +08:00
@Test
2026-07-31 16:53:07 +08:00
void firstEventPersistsAndReturnsReceived() {
2026-07-30 18:01:36 +08:00
EventEnvelope envelope = envelope("sha256:placeholder");
envelope = withHash(envelope);
assertThat(service.receive(envelope).result()).isEqualTo("已接收");
verify(store).insert(any(BridgeInbox.class));
}
@Test
2026-07-31 16:53:07 +08:00
void identicalEventAndDigestDoesNotPersistAgain() {
2026-07-30 18:01:36 +08:00
EventEnvelope envelope = withHash(envelope("sha256:placeholder"));
BridgeInbox existing = existing(envelope.payloadHash());
when(store.findByEventId(envelope.eventId())).thenReturn(existing);
assertThat(service.receive(envelope).result()).isEqualTo("重复");
}
@Test
2026-07-31 16:53:07 +08:00
void sameEventWithDifferentDigestRejectsOverwrite() {
2026-07-30 18:01:36 +08:00
EventEnvelope envelope = withHash(envelope("sha256:placeholder"));
when(store.findByEventId(envelope.eventId())).thenReturn(existing("sha256:" + "0".repeat(64)));
assertThatThrownBy(() -> service.receive(envelope))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("拒绝覆盖");
}
@Test
2026-07-31 16:53:07 +08:00
void duplicateInsertAfterRaceIsTreatedAsDuplicate() {
2026-07-30 18:01:36 +08:00
EventEnvelope envelope = withHash(envelope("sha256:placeholder"));
when(store.findByEventId(envelope.eventId())).thenReturn(null, existing(envelope.payloadHash()));
doThrow(new DuplicateKeyException("并发重复")).when(store).insert(any());
assertThat(service.receive(envelope).result()).isEqualTo("重复");
}
@Test
2026-07-31 16:53:07 +08:00
void targetRegionDifferentFromCurrentDeploymentIsRejected() {
2026-08-05 10:35:07 +08:00
ReflectionTestUtils.setField(service, "localRegion", "CN");
2026-07-30 18:01:36 +08:00
EventEnvelope envelope = withHash(envelope("sha256:placeholder"));
2026-08-05 10:35:07 +08:00
assertThatThrownBy(() -> service.receive(envelope))
2026-07-30 18:01:36 +08:00
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("目标区域");
}
private EventEnvelope envelope(String hash) {
return new EventEnvelope("550e8400-e29b-41d4-a716-446655440000", "OrderApproved", 1,
Instant.parse("2026-07-29T08:30:00Z"), RegionCode.CN, RegionCode.OVERSEAS,
"20001", "ORDER", "90001", 1, null, "trace-1", "flow-1", null,
objectMapper.createObjectNode().put("orderId", "90001"), hash);
}
private EventEnvelope withHash(EventEnvelope source) {
String hash = EventPayloadHash.sha256(EventPayloadCanonicalizer.canonicalize(source.payload()));
return new EventEnvelope(source.eventId(), source.eventType(), source.eventVersion(), source.occurredAt(),
source.sourceRegion(), source.targetRegion(), source.tenantId(), source.aggregateType(),
source.aggregateId(), source.aggregateVersion(), source.handoverVersion(), source.traceId(),
source.correlationId(), source.causationId(), source.payload(), hash);
}
private BridgeInbox existing(String hash) {
BridgeInbox inbox = new BridgeInbox();
inbox.setEventId("550e8400-e29b-41d4-a716-446655440000");
inbox.setPayloadHash(hash);
inbox.setReceivedAt(LocalDateTime.of(2026, 7, 29, 18, 0));
return inbox;
}
}