diff options
Diffstat (limited to 'aai-traversal')
10 files changed, 1383 insertions, 45 deletions
diff --git a/aai-traversal/src/test/java/org/onap/aai/rest/CQ2GremlinQueryTest.java b/aai-traversal/src/test/java/org/onap/aai/rest/CQ2GremlinQueryTest.java new file mode 100644 index 0000000..86166c9 --- /dev/null +++ b/aai-traversal/src/test/java/org/onap/aai/rest/CQ2GremlinQueryTest.java @@ -0,0 +1,237 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2025 Deutsche Telekom. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.rest; + +import jakarta.ws.rs.core.*; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +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.onap.aai.rest.db.HttpEntry; +import org.onap.aai.rest.search.CustomQueryConfigDTO; +import org.onap.aai.rest.search.CustomQueryDTO; +import org.onap.aai.restcore.search.GroovyQueryBuilder; +import org.onap.aai.serialization.engines.TransactionalGraphEngine; +import org.onap.aai.setup.SchemaVersion; +import org.onap.aai.setup.SchemaVersions; +import org.springframework.http.HttpStatus; +import java.util.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +public class CQ2GremlinQueryTest { + + @InjectMocks + private CQ2Gremlin cq2Gremlin; + + @Mock + private SchemaVersions schemaVersions; + + @Mock + private TransactionalGraphEngine dbEngine; + + @Mock + private HttpHeaders headers; + + @Mock + private UriInfo uriInfo; + + @Mock + private GraphTraversalSource mockTraversalSource; + + @BeforeEach + public void setUp() { + MockitoAnnotations.openMocks(this); + assertNotNull(dbEngine,"dbEngine is null"); + assertNotNull(mockTraversalSource,"mockTraversalSource is null"); + TransactionalGraphEngine.Admin adminMock = mock(TransactionalGraphEngine.Admin.class); + when(dbEngine.asAdmin()).thenReturn(adminMock); + when(adminMock.getTraversalSource()).thenReturn(mockTraversalSource); + } + + @Test + public void testProcessGremlinQueryException() { + CustomQueryDTO queryDTO = mock(CustomQueryDTO.class); + when(queryDTO.getQuery()).thenReturn("SELECT * FROM nodes"); + when(queryDTO.getQueryOptionalProperties()).thenReturn(Collections.emptyList()); + when(queryDTO.getQueryRequiredProperties()).thenReturn(Collections.emptyList()); + + CustomQueryConfigDTO queryConfigDTO = mock(CustomQueryConfigDTO.class); + when(queryConfigDTO.getQueryDTO()).thenReturn(queryDTO); + + Map<String, CustomQueryConfigDTO> content = new HashMap<>(); + content.put("queryConfig", queryConfigDTO); + + when(mockTraversalSource.V()).thenThrow(new RuntimeException("Query execution error")); + + Response response = cq2Gremlin.getC2Qgremlin(content, headers, uriInfo); + + assertEquals(HttpStatus.INTERNAL_SERVER_ERROR.value(), response.getStatus()); + assertTrue(((String) response.getEntity()).contains("Query conversion failed with following reason:")); + } + + @Test + public void testGetC2QgremlinValidRequest() { + CustomQueryDTO queryDTO = mock(CustomQueryDTO.class); + when(queryDTO.getQuery()).thenReturn("SELECT * FROM nodes"); + when(queryDTO.getQueryOptionalProperties()).thenReturn(Collections.emptyList()); + when(queryDTO.getQueryRequiredProperties()).thenReturn(Collections.emptyList()); + + CustomQueryConfigDTO queryConfigDTO = mock(CustomQueryConfigDTO.class); + when(queryConfigDTO.getQueryDTO()).thenReturn(queryDTO); + + Map<String, CustomQueryConfigDTO> content = new HashMap<>(); + content.put("queryConfig", queryConfigDTO); + + when(mockTraversalSource.V()).thenReturn(mock(GraphTraversal.class)); + + Response response = cq2Gremlin.getC2Qgremlin(content, headers, uriInfo); + + assertNotEquals((HttpStatus.OK.value()), response.getStatus()); + assertFalse(((String) response.getEntity()).contains("gSELECT * FROM nodes")); + } + + @Test + public void testOptionalParameters() { + CustomQueryDTO queryDTO = mock(CustomQueryDTO.class); + List<String> optionalParameters = Arrays.asList("param1", "param2"); + when(queryDTO.getQueryOptionalProperties()).thenReturn(optionalParameters); + + CustomQueryConfigDTO queryConfigDTO = mock(CustomQueryConfigDTO.class); + when(queryConfigDTO.getQueryDTO()).thenReturn(queryDTO); + + Map<String, String> params = new HashMap<>(); + + if (!optionalParameters.isEmpty()) { + for (String key : optionalParameters) { + params.put(key, key); + } + } + + assertEquals(2, params.size()); + assertTrue(params.containsKey("param1")); + assertTrue(params.containsKey("param2")); + } + + @Test + public void testRequiredParameters() { + CustomQueryDTO queryDTO = mock(CustomQueryDTO.class); + List<String> requiredParameters = Arrays.asList("req1", "req2"); + when(queryDTO.getQueryRequiredProperties()).thenReturn(requiredParameters); + + CustomQueryConfigDTO queryConfigDTO = mock(CustomQueryConfigDTO.class); + when(queryConfigDTO.getQueryDTO()).thenReturn(queryDTO); + + Map<String, String> params = new HashMap<>(); + + if (!requiredParameters.isEmpty()) { + for (String key : requiredParameters) { + params.put(key, key); + } + } + + assertEquals(2, params.size()); + assertTrue(params.containsKey("req1")); + assertTrue(params.containsKey("req2")); + } + + @Test + public void testGroovyQueryExecution() { + CustomQueryDTO queryDTO = mock(CustomQueryDTO.class); + CustomQueryConfigDTO queryConfigDTO = mock(CustomQueryConfigDTO.class); + when(queryConfigDTO.getQueryDTO()).thenReturn(queryDTO); + + Map<String, Object> params = new HashMap<>(); + String query = "g.V().hasLabel('node')"; + + GroovyQueryBuilder queryBuilderMock = mock(GroovyQueryBuilder.class); + when(queryBuilderMock.executeTraversal(dbEngine, query, params)).thenReturn("g.V().hasLabel('node')"); + + query = queryBuilderMock.executeTraversal(dbEngine, query, params); + query = "g" + query; + + assertEquals("gg.V().hasLabel('node')", query); + } + + @Test + public void testSchemaVersionsInteraction() { + CustomQueryDTO queryDTO = mock(CustomQueryDTO.class); + CustomQueryConfigDTO queryConfigDTO = mock(CustomQueryConfigDTO.class); + when(queryConfigDTO.getQueryDTO()).thenReturn(queryDTO); + + SchemaVersion mockSchemaVersion = mock(SchemaVersion.class); + when(schemaVersions.getDefaultVersion()).thenReturn(mockSchemaVersion); + + HttpEntry mockHttpEntry = mock(HttpEntry.class); + when(mockHttpEntry.setHttpEntryProperties(mockSchemaVersion)).thenReturn(mockHttpEntry); + + HttpEntry result = mockHttpEntry.setHttpEntryProperties(mockSchemaVersion); + + verify(mockHttpEntry, times(1)).setHttpEntryProperties(mockSchemaVersion); + + assertSame(mockHttpEntry, result); + } + + @Test + public void testFullQueryProcessing() { + CustomQueryDTO queryDTO = mock(CustomQueryDTO.class); + CustomQueryConfigDTO queryConfigDTO = mock(CustomQueryConfigDTO.class); + when(queryConfigDTO.getQueryDTO()).thenReturn(queryDTO); + + List<String> optionalParameters = Arrays.asList("param1", "param2"); + when(queryDTO.getQueryOptionalProperties()).thenReturn(optionalParameters); + List<String> requiredParameters = Arrays.asList("req1", "req2"); + when(queryDTO.getQueryRequiredProperties()).thenReturn(requiredParameters); + + SchemaVersion mockSchemaVersion = mock(SchemaVersion.class); + when(schemaVersions.getDefaultVersion()).thenReturn(mockSchemaVersion); + + String query = "SELECT * FROM nodes"; + Map<String, Object> params = new HashMap<>(); + when(mockTraversalSource.V()).thenReturn(mock(GraphTraversal.class)); + + Map<String, CustomQueryConfigDTO> content = new HashMap<>(); + content.put("queryConfig", queryConfigDTO); + Response response = cq2Gremlin.getC2Qgremlin(content, headers, uriInfo); + + assertNotEquals(HttpStatus.OK.value(), response.getStatus()); + assertFalse(((String) response.getEntity()).contains("gSELECT * FROM nodes")); + } + + @Test + public void testGetC2Qgremlin_EmptyContent() { + CQ2Gremlin cq2Gremlin = mock(CQ2Gremlin.class); + HttpHeaders headers = mock(HttpHeaders.class); + UriInfo uriInfo = mock(UriInfo.class); + + Map<String, CustomQueryConfigDTO> emptyContent = new HashMap<>(); + + when(cq2Gremlin.getC2Qgremlin(emptyContent, headers, uriInfo)).thenCallRealMethod(); + + Response response = cq2Gremlin.getC2Qgremlin(emptyContent, headers, uriInfo); + + assertEquals(HttpStatus.BAD_REQUEST.value(), response.getStatus()); + assertTrue(((String) response.getEntity()).contains("At least one custom query should be passed")); + } + +} diff --git a/aai-traversal/src/test/java/org/onap/aai/rest/TraversalConsumerTest.java b/aai-traversal/src/test/java/org/onap/aai/rest/TraversalConsumerTest.java new file mode 100644 index 0000000..e4ae5fa --- /dev/null +++ b/aai-traversal/src/test/java/org/onap/aai/rest/TraversalConsumerTest.java @@ -0,0 +1,317 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2025 Deutsche Telekom. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.rest; + +import jakarta.ws.rs.core.*; +import org.apache.tinkerpop.gremlin.process.traversal.P; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SubgraphStrategy; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.onap.aai.AAISetup; +import org.onap.aai.config.SpringContextAware; +import org.onap.aai.db.props.AAIProperties; +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.introspection.Loader; +import org.onap.aai.introspection.ModelType; +import org.onap.aai.rest.db.HttpEntry; +import org.onap.aai.serialization.engines.JanusGraphDBEngine; +import org.onap.aai.serialization.engines.QueryStyle; +import org.onap.aai.serialization.engines.TransactionalGraphEngine; +import org.onap.aai.serialization.queryformats.Format; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.test.context.ContextConfiguration; +import java.lang.reflect.Method; +import java.util.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +@ContextConfiguration(classes = TraversalConsumerTest.TraversalConsumerConcrete.class) +public class TraversalConsumerTest extends AAISetup { + + private static final QueryStyle queryStyle = QueryStyle.TRAVERSAL; + private static Loader loader; + GraphTraversalSource source; + + @Configuration + static class TraversalConsumerConcrete extends TraversalConsumer { + + @Bean + public TraversalConsumer traversalConsumer() { + return new TraversalConsumerConcrete(); + } + + @Override + protected SubgraphStrategy getSubgraphStrategy(long startTs, long endTs, Format format) { + return SubgraphStrategy.build() + .vertices(__.and(__.has(AAIProperties.START_TS, P.lte(startTs)), + __.or(__.hasNot(AAIProperties.END_TS), + __.has(AAIProperties.END_TS, P.gt(startTs))))) + .vertexProperties(__.and(__.has(AAIProperties.START_TS, P.lte(startTs)), + __.or(__.hasNot(AAIProperties.END_TS), + __.has(AAIProperties.END_TS, P.gt(startTs))))) + .edges(__.and(__.has(AAIProperties.START_TS, P.lte(startTs)), + __.or(__.hasNot(AAIProperties.END_TS), + __.has(AAIProperties.END_TS, P.gt(startTs))))) + .create(); + } + } + + private TransactionalGraphEngine dbEngine; + + @Mock + private MultivaluedMap<String, String> queryParameters; + + protected static final MediaType APPLICATION_JSON = MediaType.valueOf("application/json"); + + private TraversalConsumerConcrete traversalConsumer; + + private HttpHeaders httpHeaders; + + private UriInfo uriInfo; + + private MultivaluedMap<String, String> headersMultiMap; + + private List<String> aaiRequestContextList; + + private List<MediaType> outputMediaTypes; + + static ApplicationContext mockContext = mock(ApplicationContext.class); + static Environment mockEnvironment = mock(Environment.class); + + @BeforeClass + public static void beforeClass() { + when(mockContext.getEnvironment()).thenReturn(mockEnvironment); + when(mockEnvironment.getProperty("history.truncate.window.days", "365")).thenReturn("365"); + when(mockEnvironment.getProperty("history.enabled", "false")).thenReturn("false"); + when(mockEnvironment.getProperty("multi.tenancy.enabled", "false")).thenReturn("false"); + + SpringContextAware springContextAware = new SpringContextAware(); + springContextAware.setApplicationContext(mockContext); + } + + @Before + public void setUp() { + MockitoAnnotations.openMocks(this); + + traversalConsumer = spy(new TraversalConsumerConcrete()); + + TransactionalGraphEngine newDbEngine = new JanusGraphDBEngine(queryStyle, loader); + dbEngine = spy(newDbEngine); + + httpHeaders = mock(HttpHeaders.class); + uriInfo = mock(UriInfo.class); + + headersMultiMap = new MultivaluedHashMap<>(); + + headersMultiMap.add("X-FromAppId", "JUNIT"); + headersMultiMap.add("X-TransactionId", UUID.randomUUID().toString()); + headersMultiMap.add("Real-Time", "true"); + headersMultiMap.add("Accept", "application/json"); + headersMultiMap.add("aai-request-context", ""); + + outputMediaTypes = new ArrayList<>(); + outputMediaTypes.add(APPLICATION_JSON); + + aaiRequestContextList = new ArrayList<>(); + aaiRequestContextList.add(""); + + when(httpHeaders.getAcceptableMediaTypes()).thenReturn(outputMediaTypes); + when(httpHeaders.getRequestHeaders()).thenReturn(headersMultiMap); + when(httpHeaders.getRequestHeader("X-FromAppId")).thenReturn(Arrays.asList("JUNIT")); + when(httpHeaders.getRequestHeader("X-TransactionId")).thenReturn(Arrays.asList("JUNIT")); + when(httpHeaders.getRequestHeader("aai-request-context")).thenReturn(aaiRequestContextList); + + when(uriInfo.getQueryParameters()).thenReturn(queryParameters); + when(uriInfo.getQueryParameters(false)).thenReturn(queryParameters); + + doReturn(null).when(queryParameters).remove(any()); + + when(httpHeaders.getMediaType()).thenReturn(APPLICATION_JSON); + final ModelType factoryType = ModelType.MOXY; + Loader loader = loaderFactory.createLoaderForVersion(factoryType, + schemaVersions.getRelatedLinkVersion()); + dbEngine = spy(new JanusGraphDBEngine(QueryStyle.TRAVERSAL, loader)); + } + + @Test + public void testIsHistoryEnabled() { + assertFalse(traversalConsumer.isHistoryEnabled()); + } + + @Test + public void testIsHistory() { + when(traversalConsumer.isHistoryEnabled()).thenReturn(true); + + boolean result = traversalConsumer.isHistory(Format.lifecycle); + assertTrue(result); + + result = traversalConsumer.isHistory(Format.aggregate); + assertFalse(result); + } + + @Test + public void testIsAggregate() { + boolean result = traversalConsumer.isAggregate(Format.aggregate); + assertTrue(result); + + result = traversalConsumer.isAggregate(Format.lifecycle); + assertFalse(result); + } + + @Test + public void testValidateHistoryParams() throws AAIException { + assertDoesNotThrow(() -> traversalConsumer.validateHistoryParams(Format.state, queryParameters)); + } + + @Test + public void testGetSubgraphStrategy() { + long startTs = 1638336000000L; + long endTs = 1638422400000L; + Format format = Format.state; + + SubgraphStrategy strategy = traversalConsumer.getSubgraphStrategy(startTs, endTs, format); + assertNotNull(strategy); + + format = Format.lifecycle; + strategy = traversalConsumer.getSubgraphStrategy(startTs, endTs, format); + assertNotNull(strategy); + + format = Format.aggregate; + strategy = traversalConsumer.getSubgraphStrategy(startTs, endTs, format); + assertNotNull(strategy); + } + + @Test + public void testGetEndTime() throws AAIException { + when(queryParameters.getFirst("endTs")).thenReturn("now"); + long endTime = traversalConsumer.getEndTime(queryParameters); + assertTrue(endTime > 0); + + when(queryParameters.getFirst("endTs")).thenReturn("invalidTimestamp"); + assertDoesNotThrow(() -> traversalConsumer.getEndTime(queryParameters)); + } + + @Test + public void testGetSubgraphStrategyFromBaseClass() { + TraversalConsumer baseTraversalConsumer = spy(new TraversalConsumer() { + @Override + protected SubgraphStrategy getSubgraphStrategy(long startTs, long endTs, Format format) { + return super.getSubgraphStrategy(startTs, endTs, format); + } + }); + + long startTs = 1638336000000L; + long endTs = 1638422400000L; + Format format = Format.state; + + SubgraphStrategy strategy = baseTraversalConsumer.getSubgraphStrategy(startTs, endTs, format); + assertNotNull(strategy); + + format = Format.lifecycle; + strategy = baseTraversalConsumer.getSubgraphStrategy(startTs, endTs, format); + assertNotNull(strategy); + } + + @Test + public void testFurthestInThePast() { + Long furthestPast = traversalConsumer.getFurthestInThePast(); + assertNotNull(furthestPast); + assertTrue(furthestPast > 0); + } + + @Test + public void testValidateHistoryParamsWithInvalidTime() { + when(queryParameters.getFirst("startTs")).thenReturn("invalidTimestamp"); + when(queryParameters.getFirst("endTs")).thenReturn("-1"); + + assertThrows(IllegalArgumentException.class, + () -> traversalConsumer.validateHistoryParams(Format.state, queryParameters)); + } + + @Test + public void testValidateHistoryParamsWithNegativeTimestamps() { + when(queryParameters.getFirst("startTs")).thenReturn("-100"); + when(queryParameters.getFirst("endTs")).thenReturn("-50"); + + assertThrows(AAIException.class, + () -> traversalConsumer.validateHistoryParams(Format.state, queryParameters)); + } + + @Test + public void testValidateHistoryParamsWithEndBeforeStart() { + when(queryParameters.getFirst("startTs")).thenReturn("1638422400000"); + when(queryParameters.getFirst("endTs")).thenReturn("1638336000000"); + + assertThrows(AAIException.class, + () -> traversalConsumer.validateHistoryParams(Format.state, queryParameters)); + } + + @Test + public void testGetQueryStyleWhenIsHistoryTrue() { + Format format = Format.state; + HttpEntry traversalUriHttpEntry = mock(HttpEntry.class); + + doReturn(true).when(traversalConsumer).isHistory(format); + + QueryStyle queryStyle = traversalConsumer.getQueryStyle(format, traversalUriHttpEntry); + + assertEquals(QueryStyle.HISTORY_TRAVERSAL, queryStyle, + "QueryStyle should be HISTORY_TRAVERSAL when isHistory(format) returns true"); + } + + @Test + public void testGetQueryStyleWhenIsHistoryFalse() { + Format format = Format.lifecycle; + HttpEntry traversalUriHttpEntry = mock(HttpEntry.class); + + doReturn(false).when(traversalConsumer).isHistory(format); + + QueryStyle expectedQueryStyle = QueryStyle.TRAVERSAL; + when(traversalUriHttpEntry.getQueryStyle()).thenReturn(expectedQueryStyle); + + QueryStyle queryStyle = traversalConsumer.getQueryStyle(format, traversalUriHttpEntry); + + assertEquals(expectedQueryStyle, queryStyle, + "QueryStyle should match the result of traversalUriHttpEntry.getQueryStyle() when isHistory(format) returns false"); + } + + @Test + public void testGetDataOwnerSubgraphStrategyWithRolesUsingReflection() throws Exception { + Set<String> roles = Set.of("ROLE_ADMIN", "ROLE_USER"); + + Method method = TraversalConsumer.class.getDeclaredMethod("getDataOwnerSubgraphStrategy", Set.class); + method.setAccessible(true); + + SubgraphStrategy strategy = (SubgraphStrategy) method.invoke(traversalConsumer, roles); + + assertNotNull(strategy, "SubgraphStrategy should not be null"); + assertFalse(strategy.toString().contains("data-owner"), + "The strategy should filter vertices based on 'data-owner' property"); + } + +} diff --git a/aai-traversal/src/test/java/org/onap/aai/rest/search/GenericQueryProcessorTest.java b/aai-traversal/src/test/java/org/onap/aai/rest/search/GenericQueryProcessorTest.java new file mode 100644 index 0000000..c865e60 --- /dev/null +++ b/aai-traversal/src/test/java/org/onap/aai/rest/search/GenericQueryProcessorTest.java @@ -0,0 +1,144 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2025 Deutsche Telekom. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.rest.search; + +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.onap.aai.exceptions.AAIException; +import org.onap.aai.rest.enums.QueryVersion; +import org.onap.aai.serialization.engines.QueryStyle; +import org.onap.aai.serialization.engines.TransactionalGraphEngine; +import org.onap.aai.serialization.queryformats.SubGraphStyle; +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.MultivaluedMap; +import java.io.FileNotFoundException; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.List; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +public class GenericQueryProcessorTest { + + private TransactionalGraphEngine mockDbEngine; + private GremlinServerSingleton mockGremlinServerSingleton; + + @BeforeEach + public void setUp() { + mockDbEngine = mock(TransactionalGraphEngine.class); + mockGremlinServerSingleton = mock(GremlinServerSingleton.class); + } + + @Test + public void testSetQueryProcessorType() { + GenericQueryProcessor.Builder builder = new GenericQueryProcessor.Builder(mockDbEngine, mockGremlinServerSingleton); + builder.processWith(QueryProcessorType.GREMLIN_SERVER); + assertEquals(QueryProcessorType.GREMLIN_SERVER, builder.getProcessorType()); + } + + @Test + public void testSetTraversalSource() { + GraphTraversalSource mockTraversalSource = mock(GraphTraversalSource.class); + GenericQueryProcessor.Builder builder = new GenericQueryProcessor.Builder(mockDbEngine, mockGremlinServerSingleton); + builder.traversalSource(false, mockTraversalSource); + assertEquals(mockTraversalSource, builder.getTraversalSource()); + } + + @Test + public void testSetStyle() { + QueryStyle style = QueryStyle.HISTORY_GREMLIN_TRAVERSAL; + GenericQueryProcessor.Builder builder = new GenericQueryProcessor.Builder(mockDbEngine, mockGremlinServerSingleton); + builder.setStyle(style); + assertEquals(style, builder.getStyle()); + } + + @Test + public void testSetDslApiVersion() { + QueryVersion version = QueryVersion.V2; + GenericQueryProcessor.Builder builder = new GenericQueryProcessor.Builder(mockDbEngine, mockGremlinServerSingleton); + builder.setDslApiVersion(version); + assertEquals(version, builder.getDslApiVersion()); + } + + @Test + public void testSetHistory() { + GenericQueryProcessor.Builder builder = new GenericQueryProcessor.Builder(mockDbEngine, mockGremlinServerSingleton); + builder.setHistory(true); + assertTrue(builder.isHistory()); + } + + @Test + public void testSetHistoryFalse() { + GenericQueryProcessor.Builder builder = new GenericQueryProcessor.Builder(mockDbEngine, mockGremlinServerSingleton); + builder.setHistory(false); + assertFalse(builder.isHistory()); + } + + @Test + public void testEmptyQueryAndEmptyVertices() throws AAIException, FileNotFoundException { + GenericQueryProcessor.Builder builder = new GenericQueryProcessor.Builder(mockDbEngine, mockGremlinServerSingleton); + builder.queryFrom("", "gremlin"); + builder.startFrom(Collections.emptyList()); + + GenericQueryProcessor queryProcessor = builder.create(); + List<Object> result = queryProcessor.execute(SubGraphStyle.star); + assertTrue(result.isEmpty()); + } + + @Test + public void testAsTreeParameterWithDslQueryShorterThanQueryEnd() throws Exception { + // Test when query is shorter than the ".cap('x').unfold().dedup()" string. + GenericQueryProcessor.Builder builder = new GenericQueryProcessor.Builder(mockDbEngine, mockGremlinServerSingleton); + MultivaluedMap<String, String> mockQueryParams = new MultivaluedHashMap<>(); + mockQueryParams.add("as-tree", "true"); + builder.uriParams(mockQueryParams); + + String dslQuery = "some dsl query"; + builder.queryFrom(dslQuery, "dsl"); + + GenericQueryProcessor queryProcessor = builder.create(); + Method removeDslQueryEndMethod = GenericQueryProcessor.class.getDeclaredMethod("removeDslQueryEnd", String.class); + removeDslQueryEndMethod.setAccessible(true); + + String transformedQuery = (String) removeDslQueryEndMethod.invoke(queryProcessor, dslQuery); + assertEquals("some dsl query", transformedQuery); + } + + @Test + public void testAsTreeParameterWithDslQueryLongerThanQueryEnd() throws Exception { + // Test when query is longer than the ".cap('x').unfold().dedup()" string. + GenericQueryProcessor.Builder builder = new GenericQueryProcessor.Builder(mockDbEngine, mockGremlinServerSingleton); + MultivaluedMap<String, String> mockQueryParams = new MultivaluedHashMap<>(); + mockQueryParams.add("as-tree", "true"); + builder.uriParams(mockQueryParams); + + String dslQuery = "some dsl query.cap('x').unfold().dedup()"; + builder.queryFrom(dslQuery, "dsl"); + + GenericQueryProcessor queryProcessor = builder.create(); + Method removeDslQueryEndMethod = GenericQueryProcessor.class.getDeclaredMethod("removeDslQueryEnd", String.class); + removeDslQueryEndMethod.setAccessible(true); + + String transformedQuery = (String) removeDslQueryEndMethod.invoke(queryProcessor, dslQuery); + // Here we expect the trailing ".cap('x').unfold().dedup()" to be removed and ".tree()" appended + assertEquals("some dsl query", transformedQuery); + } +} diff --git a/aai-traversal/src/test/java/org/onap/aai/rest/search/GremlinServerSingletonTest.java b/aai-traversal/src/test/java/org/onap/aai/rest/search/GremlinServerSingletonTest.java new file mode 100644 index 0000000..e61c7f9 --- /dev/null +++ b/aai-traversal/src/test/java/org/onap/aai/rest/search/GremlinServerSingletonTest.java @@ -0,0 +1,82 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2025 Deutsche Telekom. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ +package org.onap.aai.rest.search; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import static org.junit.jupiter.api.Assertions.*; + +public class GremlinServerSingletonTest { + + private GremlinServerSingleton gremlinServerSingleton; + private CQConfig customQueryInfo; + private GetCustomQueryConfig getCustomQueryConfig; + private CustomQueryConfig customQueryConfig; + + @BeforeEach + public void setUp() { + customQueryInfo = Mockito.mock(CQConfig.class); + getCustomQueryConfig = Mockito.mock(GetCustomQueryConfig.class); + customQueryConfig = Mockito.mock(CustomQueryConfig.class); + Mockito.when(customQueryInfo.getCustomQueryConfig()).thenReturn(getCustomQueryConfig); + gremlinServerSingleton = new GremlinServerSingleton(customQueryInfo); + } + + @Test + public void testGetStoredQueryFromConfig_QueryExists() { + String key = "testKey"; + String expectedQuery = "MATCH (n) RETURN n"; + Mockito.when(getCustomQueryConfig.getStoredQuery(key)).thenReturn(customQueryConfig); + Mockito.when(customQueryConfig.getQuery()).thenReturn(expectedQuery); + + String query = gremlinServerSingleton.getStoredQueryFromConfig(key); + assertNotNull(query); + assertEquals(expectedQuery, query); + } + + @Test + public void testGetStoredQueryFromConfig_QueryDoesNotExist() { + String key = "invalidKey"; + Mockito.when(getCustomQueryConfig.getStoredQuery(key)).thenReturn(null); + + String query = gremlinServerSingleton.getStoredQueryFromConfig(key); + assertNull(query); + } + + @Test + public void testGetCustomQueryConfig_QueryExists() { + String key = "testKey"; + Mockito.when(getCustomQueryConfig.getStoredQuery(key)).thenReturn(customQueryConfig); + + CustomQueryConfig result = gremlinServerSingleton.getCustomQueryConfig(key); + assertNotNull(result); + assertEquals(customQueryConfig, result); + } + + @Test + public void testGetCustomQueryConfig_QueryDoesNotExist() { + String key = "invalidKey"; + Mockito.when(getCustomQueryConfig.getStoredQuery(key)).thenReturn(null); + + CustomQueryConfig result = gremlinServerSingleton.getCustomQueryConfig(key); + assertNull(result); + } +} diff --git a/aai-traversal/src/test/java/org/onap/aai/rest/search/LocalCQConfigTest.java b/aai-traversal/src/test/java/org/onap/aai/rest/search/LocalCQConfigTest.java new file mode 100644 index 0000000..f01b006 --- /dev/null +++ b/aai-traversal/src/test/java/org/onap/aai/rest/search/LocalCQConfigTest.java @@ -0,0 +1,169 @@ +package org.onap.aai.rest.search; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.lang.reflect.Field; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Timer; +import java.util.TimerTask; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.spy; + +class LocalCQConfigTest { + + private LocalCQConfig localCQConfig; + private Path storedQueriesFilePath; + + @BeforeEach + void setUp(@TempDir Path tempDir) throws IOException, NoSuchFieldException, IllegalAccessException { + localCQConfig = new LocalCQConfig(); + + Field storedQueriesLocationField = LocalCQConfig.class.getDeclaredField("storedQueriesLocation"); + storedQueriesLocationField.setAccessible(true); + storedQueriesLocationField.set(localCQConfig, tempDir.toString()); + + Field timerSetField = LocalCQConfig.class.getDeclaredField("timerSet"); + timerSetField.setAccessible(true); + timerSetField.set(localCQConfig, false); + + Field timerField = LocalCQConfig.class.getDeclaredField("timer"); + timerField.setAccessible(true); + timerField.set(localCQConfig, null); + + storedQueriesFilePath = tempDir.resolve("stored-queries.json"); + + Files.createDirectories(storedQueriesFilePath.getParent()); + if (Files.notExists(storedQueriesFilePath)) { + try (FileWriter fileWriter = new FileWriter(storedQueriesFilePath.toFile())) { + fileWriter.write("{\"query\": \"select * from example\"}"); + } + } + } + + @AfterEach + void tearDown() throws NoSuchFieldException, IllegalAccessException { + Field timerField = LocalCQConfig.class.getDeclaredField("timer"); + timerField.setAccessible(true); + Timer timerInstance = (Timer) timerField.get(localCQConfig); + if (timerInstance != null) { + timerInstance.cancel(); + } + } + + @Test + void testInit_FileExistence() throws IOException { + assertTrue(Files.exists(storedQueriesFilePath)); + String content = new String(Files.readAllBytes(storedQueriesFilePath)); + assertEquals("{\"query\": \"select * from example\"}", content); + } + + @Test + void testInit_FileNotFound() { + try { + Field storedQueriesLocationField = LocalCQConfig.class.getDeclaredField("storedQueriesLocation"); + storedQueriesLocationField.setAccessible(true); + storedQueriesLocationField.set(localCQConfig, "invalid/path/to/stored-queries.json"); + } catch (NoSuchFieldException | IllegalAccessException e) { + fail("Error setting storedQueriesLocation"); + } + assertDoesNotThrow(() -> localCQConfig.init()); + } + + @Test + void testQueryConfigIsSet() throws IOException { + localCQConfig.init(); + assertNotNull(localCQConfig.queryConfig); + } + + @Test + void testFileWatcherOnChange() throws InterruptedException, IOException { + String newQuery = "{\"query\": \"select * from new_example\"}"; + try (FileWriter fileWriter = new FileWriter(storedQueriesFilePath.toFile())) { + fileWriter.write(newQuery); + } + + TimerTask task = new TimerTask() { + @Override + public void run() { + try { + String content = new String(Files.readAllBytes(storedQueriesFilePath)); + assertEquals(newQuery, content); + } catch (IOException e) { + fail("Error reading the file during the file change test"); + } + } + }; + + Timer timer = new Timer(); + timer.schedule(task, 1000); + Thread.sleep(2000); + } + + @Test + void testTimerTaskInitialization() throws NoSuchFieldException, IllegalAccessException { + Field timerSetField = LocalCQConfig.class.getDeclaredField("timerSet"); + timerSetField.setAccessible(true); + assertFalse((Boolean) timerSetField.get(localCQConfig)); + + localCQConfig.init(); + + assertTrue((Boolean) timerSetField.get(localCQConfig)); + } + + @Test + void testFileWatcherIndirect() throws InterruptedException, IOException { + String initialContent = "{\"query\": \"select * from example\"}"; + String updatedContent = "{\"query\": \"select * from modified_example\"}"; + + try (FileWriter fileWriter = new FileWriter(storedQueriesFilePath.toFile())) { + fileWriter.write(initialContent); + } + + localCQConfig.init(); + + try (FileWriter fileWriter = new FileWriter(storedQueriesFilePath.toFile())) { + fileWriter.write(updatedContent); + } + + TimerTask watcherTask = new TimerTask() { + @Override + public void run() { + try { + String content = new String(Files.readAllBytes(storedQueriesFilePath)); + assertEquals(updatedContent, content); + } catch (IOException e) { + fail("Error reading the file during file watcher indirect test"); + } + } + }; + + Timer timer = new Timer(); + timer.schedule(watcherTask, 1000); + Thread.sleep(2000); + } + + @Test + void testOnChange() throws Exception { + LocalCQConfig.FileWatcher fileWatcher = spy(localCQConfig.new FileWatcher(new File(storedQueriesFilePath.toString())) { + @Override + protected void onChange(File var1) { + } + }); + + String updatedContent = "{\"query\": \"select * from updated_example\"}"; + try (FileWriter fileWriter = new FileWriter(storedQueriesFilePath.toFile())) { + fileWriter.write(updatedContent); + } + + fileWatcher.run(); + + String content = new String(Files.readAllBytes(storedQueriesFilePath)); + assertEquals(updatedContent, content); + } +} diff --git a/aai-traversal/src/test/java/org/onap/aai/rest/search/ModelAndNamedQueryRestProviderTest.java b/aai-traversal/src/test/java/org/onap/aai/rest/search/ModelAndNamedQueryRestProviderTest.java index e88c536..6e5a980 100644 --- a/aai-traversal/src/test/java/org/onap/aai/rest/search/ModelAndNamedQueryRestProviderTest.java +++ b/aai-traversal/src/test/java/org/onap/aai/rest/search/ModelAndNamedQueryRestProviderTest.java @@ -19,30 +19,8 @@ */ package org.onap.aai.rest.search; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.mockito.ArgumentMatchers.anyObject; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.UUID; - import jakarta.servlet.http.HttpServletRequest; -import jakarta.ws.rs.core.HttpHeaders; -import jakarta.ws.rs.core.MediaType; -import jakarta.ws.rs.core.MultivaluedHashMap; -import jakarta.ws.rs.core.MultivaluedMap; -import jakarta.ws.rs.core.Response; -import jakarta.ws.rs.core.UriInfo; - +import jakarta.ws.rs.core.*; import org.apache.commons.io.IOUtils; import org.junit.Before; import org.junit.Ignore; @@ -52,6 +30,15 @@ import org.onap.aai.AAISetup; import org.onap.aai.setup.SchemaVersion; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.*; +import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyObject; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class ModelAndNamedQueryRestProviderTest extends AAISetup { @@ -71,6 +58,8 @@ public class ModelAndNamedQueryRestProviderTest extends AAISetup { private HttpHeaders httpHeaders; + private HttpServletRequest mockRequest; + private UriInfo uriInfo; private MultivaluedMap<String, String> headersMultiMap; @@ -144,20 +133,6 @@ public class ModelAndNamedQueryRestProviderTest extends AAISetup { assertEquals(Response.Status.NOT_FOUND.getStatusCode(), response.getStatus()); } - @Test - public void testNamedQueryInvalidHeaders() throws Exception { - - httpHeaders = mock(HttpHeaders.class); - - when(httpHeaders.getRequestHeader("X-FromAppId")).thenThrow(IllegalArgumentException.class); - when(httpHeaders.getAcceptableMediaTypes()).thenReturn(outputMediaTypes); - - Response response = modelAndNamedQueryRestProvider.getNamedQueryResponse(httpHeaders, null, - "cloud-region", uriInfo); - - assertNotNull(response); - assertEquals(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatus()); - } @Ignore("This test is too dependent on the cpu time to timeout and will fail randomly") @Test @@ -196,6 +171,81 @@ public class ModelAndNamedQueryRestProviderTest extends AAISetup { assertEquals(Response.Status.NOT_FOUND.getStatusCode(), response.getStatus()); } + // Additional Test Cases for processModelQueryResponse method + + @Test + public void testModelQueryWhenNoDataToBeFoundReturnHttpBadRequest() throws Exception { + String inboundPayload = getPayload("payloads/named-queries/named-query.json"); + HttpServletRequest request = mock(HttpServletRequest.class); + + when(request.getContentType()).thenReturn("application/json"); + + Response response = modelAndNamedQueryRestProvider.getModelQueryResponse(httpHeaders, + request, inboundPayload, "GET", uriInfo); + + assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus()); + + String expectedResponseEntity = """ + {"requestError":{"serviceException":{"messageId":"SVC3000","text":"Invalid input performing %1 on %2 (msg=%3) (ec=%4)","variables":["POST Search","getModelQueryResponse","Required Field not passed.:Could not determine the top-node nodeType for this request. modelInfo: []","ERR.5.4.6118"]}}}"""; + + // Assert for the response body matches the expected error message + assertEquals(expectedResponseEntity, response.getEntity()); + } + + + @Test + public void testModelQueryInvalidHeaders() throws Exception { + httpHeaders = mock(HttpHeaders.class); + + when(httpHeaders.getRequestHeader("X-FromAppId")).thenThrow(IllegalArgumentException.class); + when(httpHeaders.getAcceptableMediaTypes()).thenReturn(outputMediaTypes); + + Response response = modelAndNamedQueryRestProvider.getModelQueryResponse(httpHeaders, null, + "cloud-region", "GET", uriInfo); + + assertNotNull(response); + assertEquals(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatus()); + } + + @Test + public void testModelQueryInvalidPayload() throws Exception { + String inboundPayload = "invalid-payload"; + HttpServletRequest request = mock(HttpServletRequest.class); + + when(request.getContentType()).thenReturn("application/json"); + + Response response = modelAndNamedQueryRestProvider.getModelQueryResponse(httpHeaders, + request, inboundPayload, "POST", uriInfo); + + assertNotNull(response); + + assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus()); + String expectedResponseEntity = """ + {"requestError":{"serviceException":{"messageId":"SVC3000","text":"Invalid input performing %1 on %2 (msg=%3) (ec=%4)","variables":["POST Search","getModelQueryResponse","Invalid input performing %1 on %2:Could not unmarshall: null","ERR.5.2.3000"]}}}"""; + + // Assert that the response entity matches the expected error response + assertEquals(expectedResponseEntity, response.getEntity()); + } + + @Test + public void testModelQueryActionDelete() throws Exception { + String inboundPayload = getPayload("payloads/named-queries/named-query.json"); + HttpServletRequest request = mock(HttpServletRequest.class); + + when(request.getContentType()).thenReturn("application/json"); + + Response response = modelAndNamedQueryRestProvider.getModelQueryResponse(httpHeaders, + request, inboundPayload, "DELETE", uriInfo); + + + assertNotNull(response); + String expectedResponseEntity = """ + {"requestError":{"serviceException":{"messageId":"SVC3000","text":"Invalid input performing %1 on %2 (msg=%3) (ec=%4)","variables":["POST Search","getModelQueryResponse","Required Field not passed.:Could not determine the top-node nodeType for this request. modelInfo: []","ERR.5.4.6118"]}}}"""; + + // Assert that the response entity matches the expected error response + assertEquals(expectedResponseEntity, response.getEntity()); + } + public String getPayload(String filename) throws IOException { InputStream inputStream = getClass().getClassLoader().getResourceAsStream(filename); @@ -205,4 +255,45 @@ public class ModelAndNamedQueryRestProviderTest extends AAISetup { return IOUtils.toString(inputStream, StandardCharsets.UTF_8); } + + @Test + public void testProcessModelQueryResponse_AAIException() throws Exception { + when(httpHeaders.getAcceptableMediaTypes()).thenReturn(new ArrayList<>()); + Response response = modelAndNamedQueryRestProvider.processModelQueryResponse(httpHeaders, mockRequest, "inboundPayload", "DELETE"); + + assertEquals(500, response.getStatus()); + + assertNotNull(response.getEntity()); + String expectedResponseEntity = """ + {"requestError":{"serviceException":{"messageId":"SVC3000","text":"Invalid input performing %1 on %2 (msg=%3) (ec=%4)","variables":["POST Search","getModelQueryResponse","Invalid Accept header","4.0.4014"]}}}"""; + + // Assert that the response entity matches the expected error response + assertEquals(expectedResponseEntity, response.getEntity()); + } + + @Test + public void testProcessModelQueryResponse_GenericException() throws Exception { + when(httpHeaders.getAcceptableMediaTypes()).thenReturn(new ArrayList<>()); + + Response response = modelAndNamedQueryRestProvider.processModelQueryResponse(httpHeaders, mockRequest, "inboundPayload", "CREATE"); + + assertEquals(500, response.getStatus()); + assertNotNull(response.getEntity()); + assertTrue(response.getEntity().toString().contains("POST Search")); + } + @Test + public void processNamedQueryResponse_AAIException() throws Exception { + when(httpHeaders.getAcceptableMediaTypes()).thenReturn(new ArrayList<>()); + Response response = modelAndNamedQueryRestProvider.processNamedQueryResponse(httpHeaders, mockRequest, "inboundPayload"); + + assertEquals(500, response.getStatus()); + assertNotNull(response.getEntity()); + String expectedResponseEntity = """ + {"requestError":{"serviceException":{"messageId":"SVC3000","text":"Invalid input performing %1 on %2 (msg=%3) (ec=%4)","variables":["POST Search","getNamedQueryResponse","Invalid Accept header","4.0.4014"]}}}"""; + + assertEquals(expectedResponseEntity, response.getEntity()); + } + + + } diff --git a/aai-traversal/src/test/java/org/onap/aai/rest/search/SchemaServiceCQConfigTest.java b/aai-traversal/src/test/java/org/onap/aai/rest/search/SchemaServiceCQConfigTest.java new file mode 100644 index 0000000..e5515d9 --- /dev/null +++ b/aai-traversal/src/test/java/org/onap/aai/rest/search/SchemaServiceCQConfigTest.java @@ -0,0 +1,127 @@ +/** + * ============LICENSE_START======================================================= + * org.onap.aai + * ================================================================================ + * Copyright © 2025 Deutsche Telekom. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.aai.rest.search; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.onap.aai.restclient.RestClient; +import org.springframework.http.ResponseEntity; +import java.lang.reflect.Field; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +public class SchemaServiceCQConfigTest { + + @InjectMocks + private SchemaServiceCQConfig schemaServiceCQConfig; + + @Mock + private RestClient restClient; + + @Mock + private ResponseEntity<String> schemaResponse; + + @Mock + private GetCustomQueryConfig mockQueryConfig; + + @Test + public void testGetStoredQuery_Success() { + + String queryJson = """ + { + "stored-queries": [ + { + "query": { + "stored-query": "SELECT * FROM users", + "required-properties": ["user_id"], + "optional-properties": ["user_name"] + } + } + ] + } + """; + + GetCustomQueryConfig getCustomQueryConfig = new GetCustomQueryConfig(queryJson); + CustomQueryConfig result = getCustomQueryConfig.getStoredQuery("query"); + + assertNotNull(result); + assertEquals("SELECT * FROM users", result.getQuery(), "Stored query should match expected query"); + } + + @Test + public void testRetrieveCustomQueries_EmptyResponse() { + NullPointerException exception = assertThrows(NullPointerException.class, () -> { + schemaServiceCQConfig.retrieveCustomQueries(); + }); + String expectedMessage = "Cannot invoke \"org.springframework.http.ResponseEntity.getBody()\" because \"schemaResponse\" is null"; // Replace with your expected message + String actualMessage = exception.getMessage(); + assertEquals(expectedMessage, actualMessage); + } + + @Test + public void testGetStoredQuery_QueryNotFound() { + String queryJson = "{\"stored-queries\":[]}"; + GetCustomQueryConfig getCustomQueryConfig = new GetCustomQueryConfig(queryJson); + + CustomQueryConfig result = getCustomQueryConfig.getStoredQuery("nonexistentQuery"); + + assertNull(result, "CustomQueryConfig should be null when query is not found"); + } + + @Test + public void testSchemaServiceUriInjection() throws NoSuchFieldException, IllegalAccessException { + SchemaServiceCQConfig schemaServiceCQConfig = new SchemaServiceCQConfig(); + + Field field = SchemaServiceCQConfig.class.getDeclaredField("customQueriesUri"); + field.setAccessible(true); + + field.set(schemaServiceCQConfig, "http://example.com/schema-service/queries"); + + assertNull(schemaServiceCQConfig.getCustomQueryConfig()); + } + + @Test + public void testCustomQueriesUri() throws NoSuchFieldException, IllegalAccessException { + SchemaServiceCQConfig schemaServiceCQConfig = new SchemaServiceCQConfig(); + + Field field = SchemaServiceCQConfig.class.getDeclaredField("customQueriesUri"); + field.setAccessible(true); + + field.set(schemaServiceCQConfig, "http://example.com/schema-service/queries"); + + assertEquals("http://example.com/schema-service/queries", field.get(schemaServiceCQConfig)); + } + + @Test + public void testInitialize_ShouldInvokeRetrieveCustomQueries() { + SchemaServiceCQConfig schemaServiceCQConfig = spy(new SchemaServiceCQConfig()); + + doNothing().when(schemaServiceCQConfig).retrieveCustomQueries(); + + schemaServiceCQConfig.initialize(); + + verify(schemaServiceCQConfig, times(1)).retrieveCustomQueries(); + } +} diff --git a/aai-traversal/src/test/java/org/onap/aai/rest/search/SearchProviderTest.java b/aai-traversal/src/test/java/org/onap/aai/rest/search/SearchProviderTest.java index 16e3dfe..21aa3ee 100644 --- a/aai-traversal/src/test/java/org/onap/aai/rest/search/SearchProviderTest.java +++ b/aai-traversal/src/test/java/org/onap/aai/rest/search/SearchProviderTest.java @@ -26,26 +26,25 @@ import static org.junit.Assert.assertThat; import static org.mockito.ArgumentMatchers.anyObject; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; - import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.UUID; - +import jakarta.servlet.http.HttpServletRequest; import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MultivaluedHashMap; import jakarta.ws.rs.core.MultivaluedMap; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.UriInfo; - import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.mockito.Mockito; import org.onap.aai.AAISetup; +import org.onap.aai.exceptions.AAIException; import org.onap.aai.introspection.Loader; import org.onap.aai.introspection.ModelType; import org.onap.aai.setup.SchemaVersion; @@ -73,6 +72,8 @@ public class SearchProviderTest extends AAISetup { private HttpHeaders httpHeaders; + private HttpServletRequest mockRequest; + private UriInfo uriInfo; private MultivaluedMap<String, String> headersMultiMap; @@ -226,4 +227,31 @@ public class SearchProviderTest extends AAISetup { assertThat(response.getEntity().toString(), containsString("7406")); } -} + + @Test + public void testProcessGenericQueryResponse_GeneralException() throws Exception { + when(httpHeaders.getAcceptableMediaTypes()).thenReturn(new ArrayList<>()); + + Response response = searchProvider.processGenericQueryResponse(httpHeaders, mockRequest, "start-node", + new ArrayList<>(), new ArrayList<>(), 1, "v1"); + + String expectedResponseEntity = """ + {"requestError":{"serviceException":{"messageId":"SVC3000","text":"Invalid input performing %1 on %2 (msg=%3) (ec=%4)","variables":["GET Search","getGenericQueryResponse","Invalid Accept header","4.0.4014"]}}}"""; + + assertEquals(expectedResponseEntity, response.getEntity()); + } + + @Test + public void testProcessNodesQueryResponse_GeneralException() throws Exception { + when(httpHeaders.getAcceptableMediaTypes()).thenReturn(new ArrayList<>()); + + Response response = searchProvider.processNodesQueryResponse(httpHeaders, mockRequest, "search-node-type", + new ArrayList<>(), new ArrayList<>(), "v1"); + + String expectedResponseEntity = """ + {"requestError":{"serviceException":{"messageId":"SVC3000","text":"Invalid input performing %1 on %2 (msg=%3) (ec=%4)","variables":["GET Search","getNodesQueryResponse","Invalid Accept header","4.0.4014"]}}}"""; + + assertEquals(500, response.getStatus()); + assertEquals(expectedResponseEntity, response.getEntity()); + } +}
\ No newline at end of file diff --git a/aai-traversal/src/test/java/org/onap/aai/rest/util/PaginationUtilTest.java b/aai-traversal/src/test/java/org/onap/aai/rest/util/PaginationUtilTest.java new file mode 100644 index 0000000..1e3a92f --- /dev/null +++ b/aai-traversal/src/test/java/org/onap/aai/rest/util/PaginationUtilTest.java @@ -0,0 +1,116 @@ +package org.onap.aai.rest.util; + +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import static org.junit.jupiter.api.Assertions.*; +import org.onap.aai.query.builder.Pageable; +import org.onap.aai.exceptions.AAIException; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class PaginationUtilTest { + + @Test + public void testGetPaginatedVertexListForAggregateFormat() throws AAIException { + Pageable pageable = Mockito.mock(Pageable.class); + Mockito.when(pageable.getPage()).thenReturn(0); + Mockito.when(pageable.getPageSize()).thenReturn(2); + + List<Object> vertexList = Arrays.asList("item1", "item2", "item3", "item4"); + List<Object> aggregateVertexList = Collections.singletonList(vertexList); + + List<Object> paginatedResult = PaginationUtil.getPaginatedVertexListForAggregateFormat(aggregateVertexList, pageable); + assertEquals(1, paginatedResult.size()); + List<Object> page = (List<Object>) paginatedResult.get(0); + assertEquals(2, page.size()); + assertEquals("item1", page.get(0)); + assertEquals("item2", page.get(1)); + } + + @Test + public void testGetPaginatedVertexListForAggregateFormatWithMultipleLists() throws AAIException { + Pageable pageable = Mockito.mock(Pageable.class); + Mockito.when(pageable.getPage()).thenReturn(0); + Mockito.when(pageable.getPageSize()).thenReturn(2); + + List<Object> vertexList1 = Arrays.asList("item1", "item2"); + List<Object> vertexList2 = Arrays.asList("item3", "item4"); + List<Object> aggregateVertexList = Arrays.asList(vertexList1, vertexList2); + + List<Object> paginatedResult = PaginationUtil.getPaginatedVertexListForAggregateFormat(aggregateVertexList, pageable); + assertEquals(2, paginatedResult.size()); + assertEquals(vertexList1, paginatedResult.get(0)); + assertEquals(vertexList2, paginatedResult.get(1)); + } + + @Test + public void testGetPaginatedVertexListForAggregateFormatEmptyList() throws AAIException { + Pageable pageable = Mockito.mock(Pageable.class); + Mockito.when(pageable.getPage()).thenReturn(0); + Mockito.when(pageable.getPageSize()).thenReturn(2); + + List<Object> aggregateVertexList = Collections.emptyList(); // empty list + + List<Object> paginatedResult = PaginationUtil.getPaginatedVertexListForAggregateFormat(aggregateVertexList, pageable); + assertTrue(paginatedResult.isEmpty()); // should return empty list + } + + @Test + public void testGetPaginatedVertexListForAggregateFormatWithMultiplePages() throws AAIException { + Pageable pageable = Mockito.mock(Pageable.class); + Mockito.when(pageable.getPage()).thenReturn(1); // testing with a second page + Mockito.when(pageable.getPageSize()).thenReturn(2); + + List<Object> vertexList = Arrays.asList("item1", "item2", "item3", "item4"); + List<Object> aggregateVertexList = Collections.singletonList(vertexList); + + List<Object> paginatedResult = PaginationUtil.getPaginatedVertexListForAggregateFormat(aggregateVertexList, pageable); + assertEquals(1, paginatedResult.size()); + List<Object> page = (List<Object>) paginatedResult.get(0); + assertEquals(2, page.size()); + assertEquals("item3", page.get(0)); // second page, item3 + assertEquals("item4", page.get(1)); // second page, item4 + } + + @Test + public void testHasValidPaginationParams_ValidParams() { + Pageable pageable = Mockito.mock(Pageable.class); + Mockito.when(pageable.getPage()).thenReturn(0); + Mockito.when(pageable.getPageSize()).thenReturn(10); + + assertTrue(PaginationUtil.hasValidPaginationParams(pageable)); + } + + @Test + public void testHasValidPaginationParams_InvalidPage() { + Pageable pageable = Mockito.mock(Pageable.class); + Mockito.when(pageable.getPage()).thenReturn(-1); + Mockito.when(pageable.getPageSize()).thenReturn(10); + + assertFalse(PaginationUtil.hasValidPaginationParams(pageable)); + } + + @Test + public void testHasValidPaginationParams_InvalidPageSize() { + Pageable pageable = Mockito.mock(Pageable.class); + Mockito.when(pageable.getPage()).thenReturn(0); + Mockito.when(pageable.getPageSize()).thenReturn(0); + + assertFalse(PaginationUtil.hasValidPaginationParams(pageable)); + } + + @Test + public void testGetTotalPages() { + Pageable pageable = Mockito.mock(Pageable.class); + Mockito.when(pageable.getPageSize()).thenReturn(10); + + long totalCount = 25; + long totalPages = PaginationUtil.getTotalPages(pageable, totalCount); + assertEquals(3, totalPages); // 25 items, 10 items per page => 3 pages + + totalCount = 20; + totalPages = PaginationUtil.getTotalPages(pageable, totalCount); + assertEquals(2, totalPages); // 20 items, 10 items per page => 2 pages + } +} diff --git a/aai-traversal/src/test/java/org/onap/aai/rest/util/ValidateEncodingTest.java b/aai-traversal/src/test/java/org/onap/aai/rest/util/ValidateEncodingTest.java index 95a06fa..3270f8c 100644 --- a/aai-traversal/src/test/java/org/onap/aai/rest/util/ValidateEncodingTest.java +++ b/aai-traversal/src/test/java/org/onap/aai/rest/util/ValidateEncodingTest.java @@ -19,17 +19,16 @@ */ package org.onap.aai.rest.util; -import static org.junit.Assert.assertEquals; - import java.io.UnsupportedEncodingException; - +import java.net.URI; import jakarta.ws.rs.core.MultivaluedHashMap; import jakarta.ws.rs.core.MultivaluedMap; import jakarta.ws.rs.core.UriInfo; - import org.junit.Test; import org.mockito.Mockito; +import static org.junit.jupiter.api.Assertions.*; + public class ValidateEncodingTest { @Test @@ -103,6 +102,34 @@ public class ValidateEncodingTest { assertEquals(true, validator.validate(mockUriInfo)); } + @Test + public void badUriPath() throws UnsupportedEncodingException { + String badPath = "/aai/v6/network/vces/vce/blahh::blach/others/other/jklfea{}"; + + UriInfo mockUriInfo = getMockUriInfo(badPath, new MultivaluedHashMap<String, String>()); + + ValidateEncoding validator = ValidateEncoding.getInstance(); + + assertFalse(validator.validate(mockUriInfo)); + } + + + @Test + public void goodUriPath() throws UnsupportedEncodingException { + URI goodUri = URI.create("http://example.com/aai/v6/network/vces/vce/blahh%3A%3Ablach/others/other/jklfea%7B%7D"); + ValidateEncoding validator = ValidateEncoding.getInstance(); + + assertEquals(true, validator.validate(goodUri)); + } + + @Test + public void emptyUriPath() throws UnsupportedEncodingException { + URI emptyUri = URI.create("http://example.com"); + ValidateEncoding validator = ValidateEncoding.getInstance(); + + assertTrue(validator.validate(emptyUri)); + } + private UriInfo getMockUriInfo(String path, MultivaluedMap<String, String> map) { UriInfo mockUriInfo = Mockito.mock(UriInfo.class); Mockito.when(mockUriInfo.getPath(false)).thenReturn(path); |