|
|
@@ -0,0 +1,109 @@
|
|
|
+package com.emoon.ai.device.application;
|
|
|
+
|
|
|
+import cn.hutool.json.JSONUtil;
|
|
|
+import com.emoon.ai.device.domain.AiDeviceEventDO;
|
|
|
+import com.emoon.ai.device.mapper.DeviceEventMapper;
|
|
|
+import org.junit.jupiter.api.BeforeEach;
|
|
|
+import org.junit.jupiter.api.DisplayName;
|
|
|
+import org.junit.jupiter.api.Test;
|
|
|
+import org.junit.jupiter.api.extension.ExtendWith;
|
|
|
+import org.mockito.ArgumentCaptor;
|
|
|
+import org.mockito.Mock;
|
|
|
+import org.mockito.junit.jupiter.MockitoExtension;
|
|
|
+
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
+import static org.assertj.core.api.Assertions.assertThat;
|
|
|
+import static org.mockito.ArgumentMatchers.any;
|
|
|
+import static org.mockito.Mockito.verify;
|
|
|
+
|
|
|
+@ExtendWith(MockitoExtension.class)
|
|
|
+@DisplayName("DeviceEventService")
|
|
|
+class DeviceEventServiceTest {
|
|
|
+
|
|
|
+ @Mock
|
|
|
+ private DeviceEventMapper eventMapper;
|
|
|
+
|
|
|
+ private DeviceEventService service;
|
|
|
+
|
|
|
+ @BeforeEach
|
|
|
+ void setUp() {
|
|
|
+ service = new DeviceEventService(eventMapper);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ @DisplayName("ingest persists event with project_id for multi-tenant isolation")
|
|
|
+ void ingestPersistsWithProjectId() {
|
|
|
+ service.ingest("KIOSK-001", "P001", "evt-001", "TOUCH_INTERACTION",
|
|
|
+ Map.of("screen", "home", "action", "tap_register"),
|
|
|
+ LocalDateTime.of(2026, 6, 1, 9, 30), "trace_abc123");
|
|
|
+
|
|
|
+ ArgumentCaptor<AiDeviceEventDO> captor = ArgumentCaptor.forClass(AiDeviceEventDO.class);
|
|
|
+ verify(eventMapper).insert(captor.capture());
|
|
|
+
|
|
|
+ AiDeviceEventDO saved = captor.getValue();
|
|
|
+ assertThat(saved.getDeviceId()).isEqualTo("KIOSK-001");
|
|
|
+ assertThat(saved.getProjectId()).isEqualTo("P001");
|
|
|
+ assertThat(saved.getEventId()).isEqualTo("evt-001");
|
|
|
+ assertThat(saved.getEventType()).isEqualTo("TOUCH_INTERACTION");
|
|
|
+ assertThat(saved.getTraceId()).isEqualTo("trace_abc123");
|
|
|
+ assertThat(saved.getOccurredAt()).isEqualTo(LocalDateTime.of(2026, 6, 1, 9, 30));
|
|
|
+ assertThat(saved.getCreatedAt()).isNotNull();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ @DisplayName("ingest serialises eventPayload to JSON")
|
|
|
+ void ingestSerialisesPayloadToJson() {
|
|
|
+ Map<String, Object> payload = Map.of("screen", "home", "action", "tap_register");
|
|
|
+
|
|
|
+ service.ingest("DEV-001", "P002", "evt-002", "TOUCH_INTERACTION",
|
|
|
+ payload, null, null);
|
|
|
+
|
|
|
+ ArgumentCaptor<AiDeviceEventDO> captor = ArgumentCaptor.forClass(AiDeviceEventDO.class);
|
|
|
+ verify(eventMapper).insert(captor.capture());
|
|
|
+
|
|
|
+ String payloadJson = captor.getValue().getEventPayload();
|
|
|
+ assertThat(payloadJson).isNotNull();
|
|
|
+ Map<String, Object> parsed = JSONUtil.parseObj(payloadJson);
|
|
|
+ assertThat(parsed).containsEntry("screen", "home").containsEntry("action", "tap_register");
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ @DisplayName("ingest defaults occurredAt to now when null")
|
|
|
+ void ingestDefaultsOccurredAtToNow() {
|
|
|
+ service.ingest("DEV-001", "P003", "evt-003", "CUSTOM",
|
|
|
+ null, null, null);
|
|
|
+
|
|
|
+ ArgumentCaptor<AiDeviceEventDO> captor = ArgumentCaptor.forClass(AiDeviceEventDO.class);
|
|
|
+ verify(eventMapper).insert(captor.capture());
|
|
|
+
|
|
|
+ assertThat(captor.getValue().getOccurredAt()).isNotNull();
|
|
|
+ assertThat(captor.getValue().getOccurredAt()).isBeforeOrEqualTo(LocalDateTime.now());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ @DisplayName("ingest handles empty payload without NPE")
|
|
|
+ void ingestHandlesEmptyPayload() {
|
|
|
+ service.ingest("DEV-001", "P004", "evt-004", "QR_SCANNED",
|
|
|
+ Map.of(), LocalDateTime.now(), null);
|
|
|
+
|
|
|
+ ArgumentCaptor<AiDeviceEventDO> captor = ArgumentCaptor.forClass(AiDeviceEventDO.class);
|
|
|
+ verify(eventMapper).insert(captor.capture());
|
|
|
+
|
|
|
+ // Empty payload → no JSON stored (null or skipped)
|
|
|
+ assertThat(captor.getValue().getEventPayload()).isNull();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ @DisplayName("ingest handles null payload without NPE")
|
|
|
+ void ingestHandlesNullPayload() {
|
|
|
+ service.ingest("DEV-001", "P005", "evt-005", "DEVICE_ERROR",
|
|
|
+ null, LocalDateTime.now(), "trace_xyz");
|
|
|
+
|
|
|
+ ArgumentCaptor<AiDeviceEventDO> captor = ArgumentCaptor.forClass(AiDeviceEventDO.class);
|
|
|
+ verify(eventMapper).insert(captor.capture());
|
|
|
+
|
|
|
+ assertThat(captor.getValue().getEventPayload()).isNull();
|
|
|
+ }
|
|
|
+}
|