Files
ai-security-xdr/haobang-security-dm/syslog-serve/src/test/java/com/haobang/dispatch/SyslogWalDispatcherTest.java
T

146 lines
6.4 KiB
Java
Raw Normal View History

package com.haobang.dispatch;
import com.common.entity.DeviceDevice;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.haobang.config.AppProperties;
import com.haobang.wal.SyslogIngressEvent;
import com.haobang.wal.SyslogWalService;
import com.kafka.kafkaProducer;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.nio.file.Path;
import java.nio.file.Files;
import java.time.LocalDateTime;
import java.util.concurrent.CompletableFuture;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
class SyslogWalDispatcherTest {
@TempDir Path tempDirectory;
private SyslogWalService wal;
private SyslogWalDispatcher dispatcher;
@AfterEach
void tearDown() {
if (dispatcher != null) dispatcher.shutdown();
if (wal != null) wal.shutdown();
}
@Test
void outOfOrderKafkaAckAdvancesOnlyContiguousCheckpoint() throws Exception {
AppProperties properties = properties(tempDirectory);
properties.setSyslogPointTimeWriteEnabled(true);
wal = new SyslogWalService(properties, new ObjectMapper());
wal.initialize();
SyslogIngressEvent firstEvent = wal.prepareForWal(event("20260811-event-1", "first"));
SyslogIngressEvent secondEvent = wal.prepareForWal(event("20260811-event-2", "second"));
wal.tryAppend(firstEvent);
wal.tryAppend(secondEvent);
waitUntil(() -> wal.getAppendedCount() == 2L);
DeviceDevice device = new DeviceDevice();
device.setId(126);
device.setName("device");
device.setVendor("vendor");
DeviceContextResolver resolver = mock(DeviceContextResolver.class);
when(resolver.resolve(anyString(), anyString()))
.thenReturn(DeviceContextResolver.Resolution.registered(device));
CompletableFuture<kafkaProducer.DeliveryResult> first = new CompletableFuture<>();
CompletableFuture<kafkaProducer.DeliveryResult> second = new CompletableFuture<>();
kafkaProducer producer = mock(kafkaProducer.class);
when(producer.messagePush(any(DeviceDevice.class), anyString(), anyString(), anyString(), any()))
.thenAnswer(invocation -> "20260811-event-1".equals(invocation.getArgument(3)) ? first : second);
dispatcher = new SyslogWalDispatcher(wal, resolver, producer, properties);
dispatcher.start();
waitUntil(() -> dispatcher.getInflight() == 2L);
second.complete(null);
Thread.sleep(100L);
assertEquals(0L, wal.getCheckpointSequence());
first.complete(null);
waitUntil(() -> wal.getCheckpointSequence() == 2L);
assertEquals(2L, dispatcher.getKafkaAcked());
assertEquals(0L, dispatcher.getRetryCount());
verify(producer).messagePush(any(DeviceDevice.class), eq("first"),
eq("20260811200000000"), eq("20260811-event-1"), eq(firstEvent.getPointTimeNs()));
verify(producer).messagePush(any(DeviceDevice.class), eq("second"),
eq("20260811200000000"), eq("20260811-event-2"), eq(secondEvent.getPointTimeNs()));
}
@Test
void terminalDeviceStateIsPersistedBeforeCheckpointAdvances() throws Exception {
AppProperties properties = properties(tempDirectory);
wal = new SyslogWalService(properties, new ObjectMapper());
wal.initialize();
wal.tryAppend(event("20260811-event-1", "first"));
waitUntil(() -> wal.getAppendedCount() == 1L);
DeviceContextResolver resolver = mock(DeviceContextResolver.class);
when(resolver.resolve(anyString(), anyString())).thenReturn(DeviceContextResolver.Resolution.terminal(
DeviceContextResolver.Type.UNKNOWN_DEVICE, null, "not registered"));
kafkaProducer producer = mock(kafkaProducer.class);
dispatcher = new SyslogWalDispatcher(wal, resolver, producer, properties);
dispatcher.start();
waitUntil(() -> wal.getCheckpointSequence() == 1L);
assertEquals(1L, dispatcher.getTerminalFiltered());
assertTrue(Files.list(tempDirectory.resolve("terminal")).findAny().isPresent());
verify(producer, never()).messagePush(any(), anyString(), anyString(), anyString(), any());
}
private SyslogIngressEvent event(String id, String raw) {
return new SyslogIngressEvent(id, LocalDateTime.of(2026, 8, 11, 20, 0),
"192.168.4.19", 514, "TCP", raw);
}
private AppProperties properties(Path directory) {
AppProperties value = new AppProperties();
value.setSyslogWalEnabled(true);
value.setSyslogWalDirectory(directory.toString());
value.setSyslogWalQueueCapacity(16);
value.setSyslogWalHighWatermarkPercent(70);
value.setSyslogWalLowWatermarkPercent(40);
value.setSyslogWalRetryIntervalMs(5);
value.setSyslogWalDiskRetryIntervalMs(50);
value.setSyslogWalFlushRecords(1);
value.setSyslogWalFlushIntervalMs(5);
value.setSyslogWalSegmentMaxBytes(1024 * 1024);
value.setSyslogWalRetentionDays(0);
value.setSyslogWalShutdownTimeoutMs(5000);
value.setSyslogWalCheckpointFlushRecords(1);
value.setSyslogWalCheckpointFlushIntervalMs(5);
value.setSyslogWalDiskHighWatermarkPercent(98);
value.setSyslogWalDiskCriticalWatermarkPercent(99);
value.setSyslogWalMinFreeBytes(0);
value.setSyslogCharset("UTF-8");
value.setSyslogDispatchWorkerThreads(2);
value.setSyslogDispatchMaxInflight(8);
value.setSyslogDispatchRetryBackoffMs(10);
value.setSyslogDispatchShutdownTimeoutMs(2000);
value.setSyslogDeviceContextCacheTtlMs(60000);
value.setSyslogStatusLogIntervalMs(60000);
return value;
}
private void waitUntil(Check condition) throws Exception {
long deadline = System.currentTimeMillis() + 5000L;
while (!condition.done() && System.currentTimeMillis() < deadline) Thread.sleep(10L);
assertTrue(condition.done(), "condition not reached before timeout");
}
private interface Check { boolean done() throws Exception; }
}