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