diff --git a/pom.xml b/pom.xml
index 48bd091..1318a2b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
org.eclipse.ecsp
- 1.0.0
+ 1.0.1
ro
ro
RO library for ECSP project
@@ -124,25 +124,14 @@
**/org/eclipse/ecsp/platform/services/ro/constant/*.java,
**/org/eclipse/ecsp/platform/services/ro/service/*.java,
**/org/eclipse/ecsp/platform/services/ro/configure/*.java,
- **/org/eclipse/ecsp/ro/utils/NotificationUtil.java,
- **/org/eclipse/ecsp/ro/queue/AbstractQueueHandler.java,
- **/org/eclipse/ecsp/ro/queue/RequestQueueHandler.java,
**/org/eclipse/ecsp/ro/RoScheduleV2DAOMongoImpl.java,
**/org/eclipse/ecsp/ro/utils/CacheUtil.java,
- **/org/eclipse/ecsp/ro/utils/CachedKeyUtil.java,
- **/org/eclipse/ecsp/ro/utils/OutboundUtil.java,
- **/org/eclipse/ecsp/ro/queue/DeviceMessageFailureQueueHandler.java,
- **/org/eclipse/ecsp/ro/utils/Utils.java,
- **/org/eclipse/ecsp/ro/utils/TimeZoneUtils.java,
- **/org/eclipse/ecsp/ro/RoDAOMongoImpl.java,
**/org/eclipse/ecsp/platform/services/ro/rest/RCPDController.java,
**/org/eclipse/ecsp/platform/services/ro/rest/ROHoodTrunkLiftgateController.java,
- **/org/eclipse/ecsp/ro/notification/StockingRuleNotificationResolver.java,
**/org/eclipse/ecsp/platform/services/ro/rest/ROHornLightsAlarmController.java,
**/org/eclipse/ecsp/platform/services/ro/rest/ROStatusHistoryController.java,
**/org/eclipse/ecsp/ro/queue/ResponseQueueHandler.java,
**/org/eclipse/ecsp/platform/services/ro/handler/ApiRequestHandler.java,
- **/org/eclipse/ecsp/ro/notification/identifier/NotificationArchAndECUTypeResolver.java
@@ -192,6 +181,7 @@
${project.basedir}/checkstyle.xml
${project.build.directory}/checkstyle-result.xml
+ 0.7.0
@@ -632,16 +622,6 @@
-
-
- ossrh
- https://oss.sonatype.org/content/repositories/snapshots
-
-
- ossrh
- https://oss.sonatype.org/service/local/staging/deploy/maven2/
-
-
@@ -671,27 +651,19 @@
true
-
-
- ossrh
- https://oss.sonatype.org/content/repositories/snapshots
-
-
- ossrh
- https://oss.sonatype.org/service/local/staging/deploy/maven2/
-
-
- org.sonatype.plugins
- nexus-staging-maven-plugin
- 1.6.13
+ org.sonatype.central
+ central-publishing-maven-plugin
+ ${central-publishing-maven-plugin.version}
true
- ossrh
- https://oss.sonatype.org/
- true
+ central
+ true
+ published
+ https://central.sonatype.com/repository/maven-snapshots/
+ false
@@ -741,16 +713,6 @@
true
-
-
- ossrh
- https://oss.sonatype.org/content/repositories/snapshots
-
-
- ossrh
- https://oss.sonatype.org/service/local/staging/deploy/maven2/
-
-
diff --git a/ro-api/pom.xml b/ro-api/pom.xml
index 9c31d8b..c77cc11 100644
--- a/ro-api/pom.xml
+++ b/ro-api/pom.xml
@@ -6,10 +6,12 @@
org.eclipse.ecsp
ro
- 1.0.0
+ 1.0.1
ro-api
ro-api
+ ro library for ECSP project
+ https://github.com/eclipse-ecsp/ro
UTF-8
org.eclipse.ecsp.platform.services.ro.Application
diff --git a/ro-entities/pom.xml b/ro-entities/pom.xml
index 7153cdb..e445a6b 100644
--- a/ro-entities/pom.xml
+++ b/ro-entities/pom.xml
@@ -5,10 +5,12 @@
org.eclipse.ecsp
ro
- 1.0.0
+ 1.0.1
ro-entities
ro-entities
+ ro library for ECSP project
+ https://github.com/eclipse-ecsp/ro
UTF-8
diff --git a/ro-sp/pom.xml b/ro-sp/pom.xml
index 3619cad..f192690 100644
--- a/ro-sp/pom.xml
+++ b/ro-sp/pom.xml
@@ -5,10 +5,12 @@
org.eclipse.ecsp
ro
- 1.0.0
+ 1.0.1
ro-sp
ro-sp
+ ro library for ECSP project
+ https://github.com/eclipse-ecsp/ro
jar
UTF-8
diff --git a/ro-sp/src/test/java/org/eclipse/ecsp/ro/RoDAOMongoImplTest.java b/ro-sp/src/test/java/org/eclipse/ecsp/ro/RoDAOMongoImplTest.java
index e57fa20..c141a75 100644
--- a/ro-sp/src/test/java/org/eclipse/ecsp/ro/RoDAOMongoImplTest.java
+++ b/ro-sp/src/test/java/org/eclipse/ecsp/ro/RoDAOMongoImplTest.java
@@ -38,6 +38,8 @@
package org.eclipse.ecsp.ro;
import io.prometheus.client.CollectorRegistry;
+import org.eclipse.ecsp.domain.ro.Ro;
+import org.eclipse.ecsp.nosqldao.IgniteQuery;
import org.eclipse.ecsp.testutils.CommonTestBase;
import org.junit.Test;
import org.junit.jupiter.api.AfterEach;
@@ -51,6 +53,7 @@
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
+import java.util.Optional;
/**
* Tests for {@link RoDAOMongoImpl}.
@@ -81,4 +84,45 @@ public void getLatesRIEntityForNotification() {
Assertions.assertNull(roDAOMongo.getLatesRIEntityForNotification("sessionId", "vehicleId"));
}
+ @Test
+ public void testGetRIEntityByFieldName_ReturnsEmpty() {
+ Optional result = roDAOMongo.getRIEntityByFieldName("someRequestID", "someVehicleId");
+ Assertions.assertTrue(result.isEmpty());
+ }
+
+ @Test
+ public void testPrepareIgniteQueryForRoRequest() {
+ IgniteQuery query = roDAOMongo.prepareIgniteQueryForRoRequest("roReqId", "vehicleId");
+ Assertions.assertNotNull(query);
+ }
+
+ @Test
+ public void testPrepareIgniteQueryBySessionIdANDMsgId() {
+ IgniteQuery query = roDAOMongo.prepareIgniteQueryBySessionIdANDMsgId("vehicleId", "sessionId", "msgId");
+ Assertions.assertNotNull(query);
+ }
+
+ @Test
+ public void testPrepareIgniteQueryForRIRequestWithSessionId() {
+ IgniteQuery query = roDAOMongo.prepareIgniteQueryForRIRequestWithSessionId("sessionId", "vehicleid");
+ Assertions.assertNotNull(query);
+ }
+
+ @Test
+ public void testGetROEntityByFieldNameByRoReqIdExceptACV_ReturnsEmpty() {
+ Optional result = roDAOMongo.getROEntityByFieldNameByRoReqIdExceptACV("vehicleId", "roRequestId");
+ Assertions.assertTrue(result.isEmpty());
+ }
+
+ @Test
+ public void testGetROEntityByFieldNameByRoReqId_ReturnsEmpty() {
+ Optional result = roDAOMongo.getROEntityByFieldNameByRoReqId("vehicleId", "roRequestId");
+ Assertions.assertTrue(result.isEmpty());
+ }
+
+ @Test
+ public void testGetROEntityByFieldNameByBizIdExceptACV_ReturnsEmpty() {
+ Optional result = roDAOMongo.getROEntityByFieldNameByBizIdExceptACV("vehicleId", "sessionId");
+ Assertions.assertTrue(result.isEmpty());
+ }
}
\ No newline at end of file
diff --git a/ro-sp/src/test/java/org/eclipse/ecsp/ro/dma/DefaultDMAShoulderTapResolverTest.java b/ro-sp/src/test/java/org/eclipse/ecsp/ro/dma/DefaultDMAShoulderTapResolverTest.java
new file mode 100644
index 0000000..966d296
--- /dev/null
+++ b/ro-sp/src/test/java/org/eclipse/ecsp/ro/dma/DefaultDMAShoulderTapResolverTest.java
@@ -0,0 +1,47 @@
+package org.eclipse.ecsp.ro.dma;
+
+import org.eclipse.ecsp.entities.IgniteEvent;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.lang.reflect.Field;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+
+class DefaultDMAShoulderTapResolverTest {
+
+ private DefaultDMAShoulderTapResolver resolver;
+
+ @BeforeEach
+ void setUp() {
+ resolver = new DefaultDMAShoulderTapResolver();
+ }
+
+ private void injectShoulderTapValue(boolean value) throws Exception {
+ Field field = DefaultDMAShoulderTapResolver.class.getDeclaredField("shoulderTap");
+ field.setAccessible(true);
+ field.set(resolver, value);
+ }
+
+ @Test
+ void testIsShoulderTap_WhenTrue() throws Exception {
+ injectShoulderTapValue(true);
+
+ IgniteEvent mockEvent = mock(IgniteEvent.class);
+ boolean result = resolver.isShoulderTap(mockEvent);
+
+ assertTrue(result, "Expected shoulderTap to be true");
+ }
+
+ @Test
+ void testIsShoulderTap_WhenFalse() throws Exception {
+ injectShoulderTapValue(false);
+
+ IgniteEvent mockEvent = mock(IgniteEvent.class);
+ boolean result = resolver.isShoulderTap(mockEvent);
+
+ assertFalse(result, "Expected shoulderTap to be false");
+ }
+}
\ No newline at end of file
diff --git a/ro-sp/src/test/java/org/eclipse/ecsp/ro/processor/strategy/impl/rcpd/RCPDRequestProcessorTest.java b/ro-sp/src/test/java/org/eclipse/ecsp/ro/processor/strategy/impl/rcpd/RCPDRequestProcessorTest.java
new file mode 100644
index 0000000..c8ad84b
--- /dev/null
+++ b/ro-sp/src/test/java/org/eclipse/ecsp/ro/processor/strategy/impl/rcpd/RCPDRequestProcessorTest.java
@@ -0,0 +1,95 @@
+package org.eclipse.ecsp.ro.processor.strategy.impl.rcpd;
+
+import org.apache.kafka.streams.processor.api.Record;
+import org.eclipse.ecsp.analytics.stream.base.StreamProcessingContext;
+import org.eclipse.ecsp.entities.IgniteEvent;
+import org.eclipse.ecsp.entities.IgniteEventImpl;
+import org.eclipse.ecsp.key.IgniteKey;
+import org.eclipse.ecsp.ro.processor.RCPDHandler;
+import org.eclipse.ecsp.services.utils.ServiceUtil;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.test.util.ReflectionTestUtils;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * RCPDRequestProcessorTest - This class contains unit tests for the RCPDRequestProcessor.
+ */
+@ExtendWith(MockitoExtension.class)
+class RCPDRequestProcessorTest {
+
+ // Mock dependencies using @Mock
+ @Mock
+ private RCPDHandler rcpdEventHandler;
+
+ @Mock
+ private ServiceUtil serviceUtil;
+
+ @InjectMocks
+ private RCPDRequestProcessor rcpdRequestProcessor;
+
+ // Define a value for the rcpdMqttTopic, which would normally be injected by Spring
+ private static final String MOCK_MQTT_TOPIC = "mock/rcpd/topic";
+
+ /**
+ * Set up method executed before each test.
+ * Used to inject the mocked @Value property using ReflectionTestUtils.
+ */
+ @BeforeEach
+ void setUp() {
+ // Use ReflectionTestUtils to set the private field 'rcpdMqttTopic'
+ // This simulates how @Value would inject the property in a real Spring context.
+ ReflectionTestUtils.setField(rcpdRequestProcessor, "rcpdMqttTopic", MOCK_MQTT_TOPIC);
+ }
+
+ /**
+ * Test case for the process method.
+ * Verifies that:
+ * 1. rcpdEventHandler.processRCPDRequest is called with correct arguments.
+ * 2. The returned IgniteEventImpl has the correct devMsgTopicSuffix set.
+ * 3. ctxt.forward is called with the correctly constructed Kafka Record.
+ */
+ @Test
+ void testProcess() {
+ IgniteKey mockKey = mock(IgniteKey.class);
+ IgniteEvent mockValue = mock(IgniteEvent.class);
+ Record, IgniteEvent> kafkaRecordIn = new Record<>(mockKey, mockValue, System.currentTimeMillis());
+
+ StreamProcessingContext mockCtxt = mock(StreamProcessingContext.class);
+
+ IgniteEventImpl mockRcpdReqImpl = mock(IgniteEventImpl.class);
+
+ when(rcpdEventHandler.processRCPDRequest(
+ mockKey,
+ mockValue,
+ serviceUtil
+ )).thenReturn(mockRcpdReqImpl);
+
+ // Act
+ rcpdRequestProcessor.process(kafkaRecordIn, mockCtxt);
+
+ // Assert
+ verify(rcpdEventHandler).processRCPDRequest(mockKey, mockValue, serviceUtil);
+
+ verify(mockRcpdReqImpl).setDevMsgTopicSuffix(MOCK_MQTT_TOPIC);
+
+ ArgumentCaptor, IgniteEvent>> recordCaptor = ArgumentCaptor.forClass(Record.class);
+ verify(mockCtxt).forward(recordCaptor.capture());
+
+ Record, IgniteEvent> forwardedRecord = recordCaptor.getValue();
+
+ // Assertions on the forwarded record
+ // Verify the key of the forwarded record is the same as the input key
+ assertEquals(mockKey, forwardedRecord.key());
+ // Verify the value of the forwarded record is the mockRcpdReqImpl (after topic suffix is set)
+ assertEquals(mockRcpdReqImpl, forwardedRecord.value());
+ }
+}
\ No newline at end of file
diff --git a/ro-sp/src/test/java/org/eclipse/ecsp/ro/queue/AbstractQueueHandlerTest.java b/ro-sp/src/test/java/org/eclipse/ecsp/ro/queue/AbstractQueueHandlerTest.java
new file mode 100644
index 0000000..8c1fcff
--- /dev/null
+++ b/ro-sp/src/test/java/org/eclipse/ecsp/ro/queue/AbstractQueueHandlerTest.java
@@ -0,0 +1,157 @@
+package org.eclipse.ecsp.ro.queue;
+
+import org.apache.kafka.streams.processor.api.Record;
+import org.bson.types.ObjectId;
+import org.eclipse.ecsp.analytics.stream.base.StreamProcessingContext;
+import org.eclipse.ecsp.domain.ro.ROStatus;
+import org.eclipse.ecsp.domain.ro.Ro;
+import org.eclipse.ecsp.entities.AbstractIgniteEvent;
+import org.eclipse.ecsp.entities.IgniteEvent;
+import org.eclipse.ecsp.key.IgniteKey;
+import org.eclipse.ecsp.nosqldao.Updates;
+import org.eclipse.ecsp.ro.RoDAOMongoImpl;
+import org.eclipse.ecsp.services.utils.ServiceUtil;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.redisson.api.RQueue;
+import org.redisson.api.RedissonClient;
+import java.time.Instant;
+import java.util.List;
+import java.util.Optional;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.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;
+
+/**
+ * Test for {@link AbstractQueueHandler} class.
+ */
+class AbstractQueueHandlerTest {
+
+ static class TestQueueHandler extends AbstractQueueHandler {
+ @Override
+ public void process(IgniteKey key, IgniteEvent event, StreamProcessingContext context) {
+ // No-op for test
+ }
+ }
+
+ @InjectMocks
+ private TestQueueHandler queueHandler;
+
+ @Mock
+ private RoDAOMongoImpl roDAOMongoImpl;
+
+ @Mock
+ private RedissonClient redissonClient;
+
+ @Mock
+ private ServiceUtil serviceUtil;
+
+ @Mock
+ private StreamProcessingContext ctxt;
+
+ @Mock
+ private RQueue queue;
+
+ private static final int DEFAULT_RO_FOREACH_TTL = 180000;
+
+ private static final int TTL_SUBTRACTION_MILLIS = 200000;
+
+ @BeforeEach
+ void setUp() {
+ MockitoAnnotations.openMocks(this);
+ queueHandler = new TestQueueHandler();
+ queueHandler.roDAOMongoImpl = roDAOMongoImpl;
+ queueHandler.redissonClient = redissonClient;
+ queueHandler.serviceUtil = serviceUtil;
+ queueHandler.roForeachTTL = DEFAULT_RO_FOREACH_TTL;
+ }
+
+ @Test
+ void testUpdateEntityByROStatus_updatesWhenRoPresent() {
+ IgniteEvent event = mock(IgniteEvent.class);
+ when(event.getVehicleId()).thenReturn("VIN123");
+ when(event.getRequestId()).thenReturn("REQ123");
+
+ Ro ro = new Ro();
+ ObjectId id = new ObjectId();
+ ro.setId(id);
+
+ when(roDAOMongoImpl.getROEntityByFieldNameByRoReqIdExceptACV("VIN123", "REQ123"))
+ .thenReturn(Optional.of(ro));
+
+ queueHandler.updateEntityByROStatus(event, ROStatus.TTL_EXPIRED);
+
+ verify(roDAOMongoImpl).update(eq(id), any(Updates.class));
+ }
+
+ @Test
+ void testUpdateEntityByROStatusWithVinAndRoRequestID_updatesWhenRoPresent() {
+ Ro ro = new Ro();
+ ObjectId id = new ObjectId();
+ ro.setId(id);
+
+ when(roDAOMongoImpl.getROEntityByFieldNameByRoReqIdExceptACV("VIN999", "REQ999"))
+ .thenReturn(Optional.of(ro));
+
+ queueHandler.updateEntityByROStatusWithVinAndRoRequestID("REQ999", "VIN999", ROStatus.TTL_EXPIRED);
+
+ verify(roDAOMongoImpl).update(eq(id), any(Updates.class));
+ }
+
+ @Test
+ void testSendToDevice_forwardsToKafkaStream() {
+ AbstractIgniteEvent event = mock(AbstractIgniteEvent.class);
+ when(event.getRequestId()).thenReturn("REQ001");
+ when(event.getVehicleId()).thenReturn("VIN001");
+
+ queueHandler.sendToDevice(event, ctxt);
+
+ verify(event).setResponseExpected(true);
+ verify(event).setDeviceRoutable(true);
+ verify(event).setShoulderTapEnabled(true);
+ verify(ctxt).forward(any(Record.class));
+ }
+
+ @Test
+ void testCheckTTLExpireANDForwad_sendsEventIfNotExpired() {
+ AbstractIgniteEvent event = mock(AbstractIgniteEvent.class);
+ when(event.getTimestamp()).thenReturn(Instant.now().toEpochMilli());
+ when(event.getVehicleId()).thenReturn("VIN001");
+
+ when(queue.iterator()).thenReturn(List.of(event).iterator());
+
+ queueHandler.checkTTLExpireANDForwad(queue, ctxt);
+
+ verify(ctxt).forward(any(Record.class));
+ verify(queue, never()).poll();
+ }
+
+ @Test
+ void testCheckTTLExpireANDForward_expiresEventIfOld() {
+ AbstractIgniteEvent event = mock(AbstractIgniteEvent.class);
+ long oldTimestamp = Instant.now().minusMillis(TTL_SUBTRACTION_MILLIS).toEpochMilli();
+ when(event.getTimestamp()).thenReturn(oldTimestamp);
+ when(event.getVehicleId()).thenReturn("VIN123");
+ when(event.getRequestId()).thenReturn("REQ123");
+
+ Ro ro = new Ro();
+ ObjectId id = new ObjectId();
+ ro.setId(id);
+
+ when(roDAOMongoImpl.getROEntityByFieldNameByRoReqIdExceptACV("VIN123", "REQ123"))
+ .thenReturn(Optional.of(ro));
+
+ when(queue.iterator()).thenReturn(List.of(event).iterator());
+
+ queueHandler.checkTTLExpireANDForwad(queue, ctxt);
+
+ verify(roDAOMongoImpl).update(eq(id), any(Updates.class));
+ verify(queue).poll();
+ }
+}
\ No newline at end of file
diff --git a/ro-sp/src/test/java/org/eclipse/ecsp/ro/queue/RequestQueueHandlerTest.java b/ro-sp/src/test/java/org/eclipse/ecsp/ro/queue/RequestQueueHandlerTest.java
new file mode 100644
index 0000000..dcd9fb7
--- /dev/null
+++ b/ro-sp/src/test/java/org/eclipse/ecsp/ro/queue/RequestQueueHandlerTest.java
@@ -0,0 +1,108 @@
+package org.eclipse.ecsp.ro.queue;
+
+import org.eclipse.ecsp.analytics.stream.base.StreamProcessingContext;
+import org.eclipse.ecsp.entities.AbstractIgniteEvent;
+import org.eclipse.ecsp.entities.IgniteEvent;
+import org.eclipse.ecsp.key.IgniteKey;
+import org.eclipse.ecsp.ro.RoDAOMongoImpl;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.redisson.api.RQueue;
+import org.redisson.api.RedissonClient;
+import java.time.Instant;
+import java.util.Collections;
+import java.util.Iterator;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Test for {@link RequestQueueHandler} class.
+ */
+class RequestQueueHandlerTest {
+
+ @InjectMocks
+ private RequestQueueHandler requestQueueHandler;
+
+ @Mock
+ private RoDAOMongoImpl roDAOMongoImpl;
+
+ @Mock
+ private RedissonClient redissonClient;
+
+ @Mock
+ private RQueue queue;
+
+ @Mock
+ private StreamProcessingContext context;
+
+ @Mock
+ private IgniteKey igniteKey;
+
+ private IgniteEvent igniteEvent;
+
+ private static final int DEFAULT_RO_FOREACH_TTL = 180000;
+
+ private static final int TTL_SUBTRACTION_MILLIS = 200000;
+
+ @BeforeEach
+ void setUp() {
+ MockitoAnnotations.openMocks(this);
+
+ igniteEvent = mock(AbstractIgniteEvent.class);
+ when(igniteEvent.getVehicleId()).thenReturn("VIN1234");
+ when(igniteEvent.getRequestId()).thenReturn("REQ-1");
+ queue = mock(RQueue.class);
+ when(redissonClient.getQueue(anyString())).thenReturn((RQueue) queue);
+ }
+
+ @Test
+ void testProcess_withNonExpiredEvent_shouldSendToDevice() {
+ AbstractIgniteEvent event = mock(AbstractIgniteEvent.class);
+ long currentTime = Instant.now().toEpochMilli();
+ when(event.getTimestamp()).thenReturn(currentTime);
+ when(event.getVehicleId()).thenReturn("VIN1234");
+ when(event.getRequestId()).thenReturn("REQ-1");
+
+ when(redissonClient.getQueue(anyString())).thenReturn((RQueue) queue);
+ when(queue.iterator()).thenReturn(Collections.singletonList(event).iterator());
+ when(queue.size()).thenReturn(1);
+
+ requestQueueHandler.roForeachTTL = DEFAULT_RO_FOREACH_TTL;
+
+ requestQueueHandler.process(igniteKey, igniteEvent, context);
+
+ // Should send to device, TTL not expired
+ verify(queue).offer((AbstractIgniteEvent) igniteEvent);
+ }
+
+ @Test
+ void testProcess_withExpiredEvent_shouldUpdateStatusAndRemove() {
+ AbstractIgniteEvent expiredEvent = mock(AbstractIgniteEvent.class);
+ long oldTimestamp = Instant.now().minusMillis(TTL_SUBTRACTION_MILLIS).toEpochMilli();
+
+ when(expiredEvent.getTimestamp()).thenReturn(oldTimestamp);
+ when(expiredEvent.getVehicleId()).thenReturn("VIN1234");
+ when(expiredEvent.getRequestId()).thenReturn("REQ-2");
+
+ Iterator iterator = mock(Iterator.class);
+ when(iterator.hasNext()).thenReturn(true, false);
+ when(iterator.next()).thenReturn(expiredEvent);
+
+ when(redissonClient.getQueue(anyString())).thenReturn((RQueue) queue);
+ when(queue.iterator()).thenReturn(iterator);
+ when(queue.size()).thenReturn(1);
+
+ requestQueueHandler.roForeachTTL = DEFAULT_RO_FOREACH_TTL;
+
+ requestQueueHandler.process(igniteKey, igniteEvent, context);
+
+ // Should call updateEntityByROStatus
+ verify(roDAOMongoImpl, atLeastOnce()).getROEntityByFieldNameByRoReqIdExceptACV("VIN1234", "REQ-2");
+ }
+}
\ No newline at end of file
diff --git a/ro-sp/src/test/java/org/eclipse/ecsp/ro/utils/CachedKeyUtilTest.java b/ro-sp/src/test/java/org/eclipse/ecsp/ro/utils/CachedKeyUtilTest.java
new file mode 100644
index 0000000..eacc68d
--- /dev/null
+++ b/ro-sp/src/test/java/org/eclipse/ecsp/ro/utils/CachedKeyUtilTest.java
@@ -0,0 +1,47 @@
+package org.eclipse.ecsp.ro.utils;
+
+import org.eclipse.ecsp.entities.IgniteEvent;
+import org.eclipse.ecsp.ro.constants.Constants;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * Test for {@link CachedKeyUtil} class.
+ */
+class CachedKeyUtilTest {
+
+ @Test
+ void getEngineStatusKey_shouldReturnCorrectKey() {
+ IgniteEvent event = mock(IgniteEvent.class);
+ when(event.getVehicleId()).thenReturn("VH001");
+
+ String expected = Constants.RO_ENGINE_STATUS_PREFIX + "VH001";
+ String result = CachedKeyUtil.getEngineStatusKey(event);
+
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void getROQueueKey_shouldReturnCorrectKey() {
+ IgniteEvent event = mock(IgniteEvent.class);
+ when(event.getVehicleId()).thenReturn("VH002");
+
+ String expected = Constants.RO_QUEUE_PREFIX + "VH002";
+ String result = CachedKeyUtil.getROQueueKey(event);
+
+ assertEquals(expected, result);
+ }
+
+ @Test
+ void getRONotificationMappingKey_shouldReturnCorrectKey() {
+ IgniteEvent event = mock(IgniteEvent.class);
+ when(event.getVehicleId()).thenReturn("VH003");
+
+ String expected = Constants.NOTIFICATION_MAPPING + Constants.UNDER_SCORE + "VH003";
+ String result = CachedKeyUtil.getRONotificationMappingKey(event);
+
+ assertEquals(expected, result);
+ }
+}
\ No newline at end of file
diff --git a/ro-sp/src/test/java/org/eclipse/ecsp/ro/utils/NotificationUtilTest.java b/ro-sp/src/test/java/org/eclipse/ecsp/ro/utils/NotificationUtilTest.java
new file mode 100644
index 0000000..fb87cb9
--- /dev/null
+++ b/ro-sp/src/test/java/org/eclipse/ecsp/ro/utils/NotificationUtilTest.java
@@ -0,0 +1,127 @@
+package org.eclipse.ecsp.ro.utils;
+
+import org.eclipse.ecsp.analytics.stream.base.StreamProcessingContext;
+import org.eclipse.ecsp.domain.GenericCustomExtension;
+import org.eclipse.ecsp.domain.ro.RemoteOperationResponseV1_1;
+import org.eclipse.ecsp.domain.ro.RemoteOperationResponseV1_1.Response;
+import org.eclipse.ecsp.entities.IgniteEvent;
+import org.eclipse.ecsp.key.IgniteKey;
+import org.eclipse.ecsp.ro.notification.NotificationResolver;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import java.lang.reflect.Field;
+import java.util.Map;
+import java.util.Optional;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.when;
+
+/**
+ * Test for {@link NotificationUtil} class.
+ */
+class NotificationUtilTest {
+
+ @InjectMocks
+ private NotificationUtil notificationUtil;
+
+ @Mock
+ private NotificationResolver notificationResolver;
+
+ @Mock
+ private StreamProcessingContext context;
+
+ @Mock
+ private IgniteKey igniteKey;
+
+ @Mock
+ private IgniteEvent igniteEvent;
+
+ @Mock
+ private RemoteOperationResponseV1_1 response;
+
+ @Mock
+ private GenericCustomExtension customExtension;
+
+ /**
+ * Initializes mocks and sets up the test environment before each test case.
+ */
+ @BeforeEach
+ void setUp() throws Exception {
+ MockitoAnnotations.openMocks(this);
+
+ notificationUtil = new NotificationUtil();
+
+ // Inject private fields using reflection directly
+ setField(notificationUtil, "notificationStatusMapping", Map.of("notif-1", "SUCCESS"));
+ setField(notificationUtil, "notificationIdMapping", Map.of("RESPONSE_OK", "notif-1"));
+ setField(notificationUtil, "whitelistedDffOrigins", new String[]{"THIRDPARTY2"});
+ setField(notificationUtil, "sinkTopics", new String[]{"sink-topic"});
+ setField(notificationUtil, "sourceTopics", new String[]{"source-topic"});
+ setField(notificationUtil, "notificationResolver", notificationResolver);
+ }
+
+ // Inline reflection helper inside the test class
+ private void setField(Object target, String fieldName, Object value) throws Exception {
+ Field field = target.getClass().getDeclaredField(fieldName);
+ field.setAccessible(true);
+ field.set(target, value);
+ }
+
+ @Test
+ void testSendRONotification_withValidInput_shouldForwardEvent() {
+ when(igniteEvent.getVehicleId()).thenReturn("veh123");
+ when(igniteEvent.getBizTransactionId()).thenReturn("biz123");
+
+ when(response.getRoRequestId()).thenReturn("ro-req-1");
+ when(response.getResponse()).thenReturn(Response.SUCCESS);
+
+ when(response.getCustomExtension()).thenReturn(Optional.of(customExtension));
+ when(customExtension.getCustomData()).thenReturn(Map.of("response", "SUCCESS"));
+
+ notificationUtil.sendRONotification(
+ igniteKey,
+ igniteEvent,
+ context,
+ "THIRDPARTY2",
+ "notif-1",
+ response
+ );
+
+ verify(context).forwardDirectly(any(IgniteKey.class), any(IgniteEvent.class), eq("sink-topic"));
+ }
+
+ @Test
+ void testSendRONotification_withNonWhitelistedOrigin_shouldNotSend() {
+ notificationUtil.sendRONotification(
+ igniteKey,
+ igniteEvent,
+ context,
+ "UNKNOWN_ORIGIN",
+ "notif-1",
+ response
+ );
+
+ verifyNoInteractions(context);
+ }
+
+ @Test
+ void testSendRONotification_withEmptyNotificationId_shouldSkip() {
+ when(response.getRoRequestId()).thenReturn("ro-req-1");
+
+ notificationUtil.sendRONotification(
+ igniteKey,
+ igniteEvent,
+ context,
+ "THIRDPARTY2",
+ "",
+ response
+ );
+
+ verifyNoInteractions(context);
+ }
+}
\ No newline at end of file
diff --git a/ro-sp/src/test/java/org/eclipse/ecsp/ro/utils/OutboundUtilTest.java b/ro-sp/src/test/java/org/eclipse/ecsp/ro/utils/OutboundUtilTest.java
new file mode 100644
index 0000000..c262fcc
--- /dev/null
+++ b/ro-sp/src/test/java/org/eclipse/ecsp/ro/utils/OutboundUtilTest.java
@@ -0,0 +1,104 @@
+package org.eclipse.ecsp.ro.utils;
+
+import org.eclipse.ecsp.analytics.stream.base.StreamProcessingContext;
+import org.eclipse.ecsp.domain.ro.RemoteOperationResponseV1_1;
+import org.eclipse.ecsp.entities.IgniteEvent;
+import org.eclipse.ecsp.key.IgniteKey;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+/**
+ * Test for {@link OutboundUtil} class.
+ */
+class OutboundUtilTest {
+
+ private OutboundUtil outboundUtil;
+ private StreamProcessingContext mockContext;
+ private IgniteKey> mockKey;
+
+ @BeforeEach
+ void setUp() {
+ outboundUtil = new OutboundUtil();
+ mockContext = mock(StreamProcessingContext.class);
+ mockKey = mock(IgniteKey.class);
+ }
+
+ @Test
+ void testSendROResponseOutbound_shouldForwardEvent() {
+ // Given
+ String eventId = "RO_EVENT";
+ String vehicleId = "VH001";
+ String requestId = "REQ123";
+ String bizTransactionId = "BTID456";
+ String origin = "THIRDPARTY";
+ String userId = "user-abc";
+ RemoteOperationResponseV1_1 response = new RemoteOperationResponseV1_1();
+ response.setRoRequestId(requestId);
+ response.setUserId(userId);
+
+ // When
+ outboundUtil.sendROResponseOutbound(
+ mockKey,
+ mockContext,
+ eventId,
+ vehicleId,
+ requestId,
+ bizTransactionId,
+ origin,
+ userId,
+ response
+ );
+
+ // Then
+ ArgumentCaptor.forClass(IgniteEvent.class);
+ verify(mockContext).forward(any());
+ }
+
+ @Test
+ void testCreateRemoteOperationResponseV1_1_shouldPopulateAllFields() {
+ // Given
+ String requestId = "REQ123";
+ String userId = "user-xyz";
+ String partnerId = "partner-321";
+ RemoteOperationResponseV1_1.Response responseEnum = RemoteOperationResponseV1_1.Response.SUCCESS;
+
+ // When
+ RemoteOperationResponseV1_1 result = outboundUtil.createRemoteOperationResponseV1_1(
+ requestId, userId, partnerId, responseEnum
+ );
+
+ // Then
+ assertNotNull(result);
+ assertEquals(requestId, result.getRoRequestId());
+ assertEquals(userId, result.getUserId());
+ assertEquals(partnerId, result.getPartnerId());
+ assertEquals(responseEnum, result.getResponse());
+ }
+
+ @Test
+ void testCreateRemoteOperationResponseV1_1_shouldSkipEmptyPartnerId() {
+ // Given
+ String requestId = "REQ789";
+ String userId = "user-000";
+ String partnerId = null;
+ RemoteOperationResponseV1_1.Response responseEnum = RemoteOperationResponseV1_1.Response.FAIL;
+
+ // When
+ RemoteOperationResponseV1_1 result = outboundUtil.createRemoteOperationResponseV1_1(
+ requestId, userId, partnerId, responseEnum
+ );
+
+ // Then
+ assertEquals(requestId, result.getRoRequestId());
+ assertEquals(userId, result.getUserId());
+ assertNull(result.getPartnerId());
+ assertEquals(responseEnum, result.getResponse());
+ }
+}
\ No newline at end of file
diff --git a/ro-sp/src/test/java/org/eclipse/ecsp/ro/utils/StockingRuleConfigUtilTest.java b/ro-sp/src/test/java/org/eclipse/ecsp/ro/utils/StockingRuleConfigUtilTest.java
index ec0b5c1..976db4e 100644
--- a/ro-sp/src/test/java/org/eclipse/ecsp/ro/utils/StockingRuleConfigUtilTest.java
+++ b/ro-sp/src/test/java/org/eclipse/ecsp/ro/utils/StockingRuleConfigUtilTest.java
@@ -57,4 +57,39 @@ public void getStockingRuleConfig() {
Map resultMap = StockingRuleConfigUtil.getStockingRuleConfig(input);
Assertions.assertEquals("vehicle_archType1_precondition", resultMap.get("mappingName"));
}
+
+ @Test
+ void getStockingRuleConfig_withMissingKey() {
+ // Missing STOCKING_RULE_CONFIGURATIONOBJECT key
+ Map input = new HashMap<>();
+ Map resultMap = StockingRuleConfigUtil.getStockingRuleConfig(input);
+ Assertions.assertTrue(resultMap == null || resultMap.isEmpty(), "Expected empty map when key is missing");
+ }
+
+ @Test
+ void getStockingRuleConfig_withNullInput() {
+ // Null input
+ Map resultMap = StockingRuleConfigUtil.getStockingRuleConfig(null);
+ Assertions.assertTrue(resultMap == null || resultMap.isEmpty(), "Expected empty map when input is null");
+ }
+
+ @Test
+ void getStockingRuleConfig_withInvalidValue() {
+ // Value that can't be converted to string
+ Map input = new HashMap<>();
+ input.put(Constants.STOCKING_RULE_CONFIGURATIONOBJECT, new Object());
+ Map resultMap = StockingRuleConfigUtil.getStockingRuleConfig(input);
+ Assertions.assertTrue(resultMap == null || resultMap.isEmpty(), "Expected empty map on parse error");
+ }
+
+ @Test
+ void getStockingRuleConfig_withMalformedJsonString() {
+ // Manually passing malformed JSON string
+ Map input = new HashMap<>();
+ input.put(Constants.STOCKING_RULE_CONFIGURATIONOBJECT, "{invalidJson: }");
+ Map resultMap = StockingRuleConfigUtil.getStockingRuleConfig(input);
+ Assertions.assertTrue(resultMap == null || resultMap.isEmpty(), "Expected empty map for invalid JSON string");
+ }
+
+
}
\ No newline at end of file
diff --git a/ro-sp/src/test/java/org/eclipse/ecsp/ro/utils/TimeZoneUtilsTest.java b/ro-sp/src/test/java/org/eclipse/ecsp/ro/utils/TimeZoneUtilsTest.java
new file mode 100644
index 0000000..34fbd23
--- /dev/null
+++ b/ro-sp/src/test/java/org/eclipse/ecsp/ro/utils/TimeZoneUtilsTest.java
@@ -0,0 +1,64 @@
+package org.eclipse.ecsp.ro.utils;
+
+import org.junit.jupiter.api.Test;
+import java.time.ZoneId;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Test for {@link TimeZoneUtils} class.
+ */
+class TimeZoneUtilsTest {
+
+ private static final double NY_LAT = 40.7128;
+
+ private static final double NY_LONG = -74.0060;
+
+ @Test
+ void testGetZoneIdByLocation_validCoordinates() {
+ // Sample coordinates for New York City
+ double latitude = NY_LAT;
+ double longitude = NY_LONG;
+
+ ZoneId zoneId = TimeZoneUtils.getZoneIdByLocation(latitude, longitude);
+
+ assertNotNull(zoneId, "ZoneId should not be null for valid coordinates");
+ System.out.println("Zone ID for NYC: " + zoneId);
+ }
+
+ private static final double INV_LAT = -90.0;
+
+ private static final double INV_LONG = -180.0;
+
+ @Test
+ void testGetZoneIdByLocation_invalidCoordinates() {
+ // Invalid coordinates in the middle of the ocean
+ double invlatitude = INV_LAT;
+ double invlongitude = INV_LONG;
+
+ ZoneId zoneId = TimeZoneUtils.getZoneIdByLocation(invlatitude, invlongitude);
+
+ assertNull(zoneId, "ZoneId should be null for invalid coordinates");
+ }
+
+ @Test
+ void testGetUTCTimestamp_validInput() {
+ ZoneId zoneId = ZoneId.of("Asia/Kolkata");
+ String timeStr = "2025/06/08 10:30:00";
+ String pattern = "yyyy/MM/dd HH:mm:ss";
+
+ long utcTimestamp = TimeZoneUtils.getUTCTimestamp(zoneId, timeStr, pattern);
+
+ assertTrue(utcTimestamp > 0, "UTC timestamp should be greater than zero");
+ System.out.println("UTC Timestamp: " + utcTimestamp);
+ }
+
+ @Test
+ void testGetCurrentUTCTimestamp() {
+ long currentUtc = TimeZoneUtils.getCurrentUTCTimestamp();
+
+ assertTrue(currentUtc > 0, "Current UTC timestamp should be greater than zero");
+ System.out.println("Current UTC Timestamp: " + currentUtc);
+ }
+}
\ No newline at end of file
diff --git a/ro-sp/src/test/java/org/eclipse/ecsp/ro/utils/UtilsTest.java b/ro-sp/src/test/java/org/eclipse/ecsp/ro/utils/UtilsTest.java
index 95c8fe8..35a3245 100644
--- a/ro-sp/src/test/java/org/eclipse/ecsp/ro/utils/UtilsTest.java
+++ b/ro-sp/src/test/java/org/eclipse/ecsp/ro/utils/UtilsTest.java
@@ -50,6 +50,7 @@
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
+import org.junit.jupiter.api.Assertions;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
@@ -154,4 +155,19 @@ public void testUTCConvert3() {
LOGGER.debug("firstScheduleTs: {}", firstScheduleTs);
Assert.assertNotNull(zoneId);
}
+
+ @Test
+ public void testLogForging_withInput() {
+ String input = "Hello\nWorld\rTab\tEnd";
+ String expected = "Hello_World_Tab_End";
+
+ String result = Utils.logForging(input);
+ Assertions.assertEquals(expected, result);
+ }
+
+ @Test
+ public void testLogForging_withNull() {
+ String result = Utils.logForging(null);
+ Assertions.assertNull(result);
+ }
}
\ No newline at end of file