aboutsummaryrefslogtreecommitdiffstats
path: root/src/test/groovy/org/onap
diff options
context:
space:
mode:
authorRenu Kumari <renu.kumari@bell.ca>2021-08-10 16:43:04 -0400
committerRenu Kumari <renu.kumari@bell.ca>2021-08-19 07:13:57 -0400
commitea04c07ad990b5543766e95e234cae746bd1fbc1 (patch)
treec50008212dbf1471e859c0f9f681baa146a57c7c /src/test/groovy/org/onap
parent25ed5a8a63e88edd0b9c8285b640839a6e93866a (diff)
Validate controller input and provide expected response
- validate endpoint input - set default values if it is missing in the optional fields - used hateoas for previous and next record link generation - use service layer in controller layer Issue-ID: CPS-449 Signed-off-by: Renu Kumari <renu.kumari@bell.ca> Change-Id: I7936cd9e8e7dead3b5650b421bb12f10d14ffa9b
Diffstat (limited to 'src/test/groovy/org/onap')
-rw-r--r--src/test/groovy/org/onap/cps/temporal/controller/event/model/CpsDataUpdatedEventMapperSpec.groovy2
-rw-r--r--src/test/groovy/org/onap/cps/temporal/controller/rest/QueryControllerDataBuilder.groovy159
-rw-r--r--src/test/groovy/org/onap/cps/temporal/controller/rest/QueryControllerSpec.groovy234
-rw-r--r--src/test/groovy/org/onap/cps/temporal/domain/SearchCriteriaSpec.groovy33
-rw-r--r--src/test/groovy/org/onap/cps/temporal/repository/NetworkDataRepositoryImplSpec.groovy3
-rw-r--r--src/test/groovy/org/onap/cps/temporal/service/NetworkDataServiceImplSpec.groovy33
6 files changed, 427 insertions, 37 deletions
diff --git a/src/test/groovy/org/onap/cps/temporal/controller/event/model/CpsDataUpdatedEventMapperSpec.groovy b/src/test/groovy/org/onap/cps/temporal/controller/event/model/CpsDataUpdatedEventMapperSpec.groovy
index 132ff6d..a51c4fe 100644
--- a/src/test/groovy/org/onap/cps/temporal/controller/event/model/CpsDataUpdatedEventMapperSpec.groovy
+++ b/src/test/groovy/org/onap/cps/temporal/controller/event/model/CpsDataUpdatedEventMapperSpec.groovy
@@ -110,7 +110,7 @@ class CpsDataUpdatedEventMapperSpec extends Specification {
result != null
and: 'all result entity properties are the ones from the event'
result.getObservedTimestamp() ==
- OffsetDateTime.parse(event.getContent().getObservedTimestamp(), isoTimestampFormatter)
+ OffsetDateTime.parse(event.getContent().getObservedTimestamp(), isoTimestampFormatter)
result.getDataspace() == event.getContent().getDataspaceName()
result.getSchemaSet() == event.getContent().getSchemaSetName()
result.getAnchor() == event.getContent().getAnchorName()
diff --git a/src/test/groovy/org/onap/cps/temporal/controller/rest/QueryControllerDataBuilder.groovy b/src/test/groovy/org/onap/cps/temporal/controller/rest/QueryControllerDataBuilder.groovy
new file mode 100644
index 0000000..dee1e06
--- /dev/null
+++ b/src/test/groovy/org/onap/cps/temporal/controller/rest/QueryControllerDataBuilder.groovy
@@ -0,0 +1,159 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (c) 2021 Bell Canada.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.temporal.controller.rest
+
+import org.onap.cps.temporal.controller.utils.DateTimeUtility
+import org.onap.cps.temporal.domain.SearchCriteria
+import org.springframework.data.domain.Sort
+import org.springframework.http.MediaType
+import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
+import org.springframework.util.CollectionUtils
+import org.springframework.util.MultiValueMap
+import org.springframework.web.util.DefaultUriBuilderFactory
+import org.springframework.web.util.UriComponentsBuilder
+import org.springframework.web.util.UriUtils
+
+import java.nio.charset.Charset
+import java.time.OffsetDateTime
+
+/*
+To create objects required for the test based on same input
+ */
+
+class QueryControllerDataBuilder {
+
+ private static String POINT_IN_TIME_QUERY_PARAM = 'pointInTime'
+ private static String OBSERVED_TIMESTAMP_AFTER_QUERY_PARAM = 'observedTimestampAfter'
+ private static String PAGE_NUMBER_QUERY_PARAM = 'pageNumber'
+ private static String PAGE_LIMIT_QUERY_PARAM = 'pageLimit'
+ private static String SORT_QUERY_PARAM = 'sort'
+ private static String SIMPLE_PAYLOAD_FILTER_QUERY_PARAM = 'simplePayloadFilter'
+
+ private static int DEFAULT_PAGE_NUMBER = 0
+ private static int DEFAULT_PAGE_SIZE = 1000
+ private static String DEFAULT_SORT = 'observed_timestamp:desc'
+
+ private static Map SORT_MAP = ['anchor:asc' : Sort.by(Sort.Direction.ASC, 'anchor'),
+ 'observed_timestamp:desc': Sort.by(Sort.Direction.DESC, 'observed_timestamp')]
+ private static Map URI_MAP =
+ ['anchor by name' : '/cps-temporal/api/v1/dataspaces/{dataspace}/anchors/{anchor}/history',
+ 'anchors by schemaset': '/cps-temporal/api/v1/dataspaces/{dataspace}/anchors/history?schema-set-name={schemaSet}']
+
+ Map parameters
+ String endpoint
+
+ QueryControllerDataBuilder(final String endPointName, final Map parameters) {
+ this.parameters = parameters
+ def replacements = ['{dataspace}': parameters.dataspace,
+ '{schemaSet}': parameters.schemaSet,
+ '{anchor}' : parameters.anchor]
+ endpoint = URI_MAP.get(endPointName).replace(replacements)
+ }
+
+ MockHttpServletRequestBuilder createMockHttpRequestBuilder() {
+ def requestBuilder = MockMvcRequestBuilders.get(endpoint)
+ if (parameters.pointInTime != null)
+ requestBuilder.queryParam(POINT_IN_TIME_QUERY_PARAM, parameters.pointInTime)
+ if (parameters.observedTimestampAfter != null)
+ requestBuilder.queryParam(OBSERVED_TIMESTAMP_AFTER_QUERY_PARAM, parameters.observedTimestampAfter)
+ if (parameters.pageNumber != null)
+ requestBuilder.queryParam(PAGE_NUMBER_QUERY_PARAM, parameters.pageNumber.toString())
+ if (parameters.pageLimit != null)
+ requestBuilder.queryParam(PAGE_LIMIT_QUERY_PARAM, parameters.pageLimit.toString())
+ if (parameters.sortAsString != null)
+ requestBuilder.queryParam(SORT_QUERY_PARAM, parameters.sortAsString)
+ if (parameters.payloadFilter != null)
+ requestBuilder.queryParam(SIMPLE_PAYLOAD_FILTER_QUERY_PARAM, parameters.payloadFilter)
+ return requestBuilder.contentType(MediaType.APPLICATION_JSON)
+ }
+
+ SearchCriteria.Builder createSearchCriteriaBuilder() {
+ def searchCriteriaBuilder = SearchCriteria.builder()
+ searchCriteriaBuilder.dataspaceName(parameters.dataspace)
+ .anchorName(parameters.anchor)
+ .schemaSetName(parameters.schemaSet)
+ if (parameters.pointInTime != null)
+ searchCriteriaBuilder.createdBefore(DateTimeUtility.toOffsetDateTime(parameters.pointInTime))
+ if (parameters.observedTimestampAfter != null)
+ searchCriteriaBuilder.observedAfter(DateTimeUtility.toOffsetDateTime(parameters.observedTimestampAfter))
+ if (parameters.pageNumber != null)
+ searchCriteriaBuilder.pagination(parameters.pageNumber, parameters.pageLimit)
+ if (parameters.payloadFilter != null)
+ searchCriteriaBuilder.simplePayloadFilter(parameters.payloadFilter)
+ if (parameters.sortAsString != null)
+ searchCriteriaBuilder.sort(SORT_MAP.get(((String) parameters.sortAsString).toLowerCase()))
+ return searchCriteriaBuilder
+ }
+
+ private int getPageNumber() {
+ return parameters.pageNumber == null ?
+ DEFAULT_PAGE_NUMBER :
+ parameters.pageNumber
+ }
+
+ void isExpectedNextRecordsLink(String actualNextLink) {
+ isExpectedLink(getPageNumber() + 1, actualNextLink)
+ }
+
+ void isExpectedPreviousRecordsLink(String actualNextLink) {
+ isExpectedLink(getPageNumber() - 1, actualNextLink)
+ }
+
+ void isExpectedLink(int pageNumber, String actualLink) {
+ def actualUriComponents = UriComponentsBuilder.fromUriString(actualLink).build()
+ def actualQueryParams = actualUriComponents.getQueryParams()
+
+ if (parameters.observedTimestampAfter != null) {
+ validateQueryParam(OBSERVED_TIMESTAMP_AFTER_QUERY_PARAM, parameters.observedTimestampAfter, actualQueryParams)
+ }
+ if (parameters.payloadFilter != null) {
+ validateQueryParam(SIMPLE_PAYLOAD_FILTER_QUERY_PARAM, parameters.payloadFilter, actualQueryParams)
+ }
+ validatePointInTime(actualQueryParams)
+ validateQueryParam(PAGE_NUMBER_QUERY_PARAM, Integer.toString(pageNumber), actualQueryParams)
+ validateQueryParam(PAGE_LIMIT_QUERY_PARAM,
+ Integer.toString(parameters.pageLimit == null ? DEFAULT_PAGE_SIZE : parameters.pageLimit), actualQueryParams)
+ validateQueryParam(SORT_QUERY_PARAM,
+ parameters.sortAsString == null ? DEFAULT_SORT : parameters.sortAsString, actualQueryParams)
+
+ }
+
+ private void validateQueryParam(String paramName, Object expectedValue, MultiValueMap<String, String> queryParams) {
+ def values = queryParams.get(paramName)
+ assert (!CollectionUtils.isEmpty(values))
+ assert (expectedValue == URLDecoder.decode(values.get(0), Charset.defaultCharset()))
+ }
+
+ boolean validatePointInTime(MultiValueMap<String, String> queryParams) {
+
+ def values = queryParams.get(POINT_IN_TIME_QUERY_PARAM)
+ assert (!CollectionUtils.isEmpty(values))
+ def actualValue = URLDecoder.decode(values.get(0), Charset.defaultCharset())
+
+ if (parameters.pointInTime == null) {
+ assert DateTimeUtility.toOffsetDateTime(actualValue).isAfter(OffsetDateTime.now().minusMinutes(2))
+ } else {
+ assert parameters.pointInTime == actualValue
+ }
+ }
+
+}
diff --git a/src/test/groovy/org/onap/cps/temporal/controller/rest/QueryControllerSpec.groovy b/src/test/groovy/org/onap/cps/temporal/controller/rest/QueryControllerSpec.groovy
index 771a3fc..a18a134 100644
--- a/src/test/groovy/org/onap/cps/temporal/controller/rest/QueryControllerSpec.groovy
+++ b/src/test/groovy/org/onap/cps/temporal/controller/rest/QueryControllerSpec.groovy
@@ -20,59 +20,237 @@
package org.onap.cps.temporal.controller.rest
+import org.onap.cps.temporal.controller.utils.DateTimeUtility
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
+
+import java.time.OffsetDateTime
+import org.onap.cps.temporal.controller.rest.model.AnchorDetails
+import org.onap.cps.temporal.controller.rest.model.AnchorDetailsMapperImpl
+import org.onap.cps.temporal.controller.rest.model.AnchorHistory
+import org.onap.cps.temporal.controller.rest.model.ErrorMessage
+import org.onap.cps.temporal.controller.rest.model.SortMapper
+import org.onap.cps.temporal.domain.NetworkData
+import org.onap.cps.temporal.domain.SearchCriteria
+import org.onap.cps.temporal.service.NetworkDataService
+import org.spockframework.spring.SpringBean
import org.springframework.beans.factory.annotation.Autowired
-import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
+import org.springframework.context.annotation.Import
+import org.springframework.data.domain.PageRequest
+import org.springframework.data.domain.SliceImpl
+import org.springframework.data.domain.Sort
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.test.web.servlet.MockMvc
-
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
-
+import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper
+import spock.lang.Shared
import spock.lang.Specification
-/**
- * Specification for Query Controller.
- */
@WebMvcTest(QueryController)
+@Import([SortMapper, QueryResponseFactory, AnchorDetailsMapperImpl])
class QueryControllerSpec extends Specification {
+ @SpringBean
+ NetworkDataService mockNetworkDataService = Mock()
+
@Autowired
MockMvc mvc
- @Value('${rest.api.base-path}')
- def basePath
-
def myDataspace = 'my-dataspace'
+ @Shared
def myAnchor = 'my-anchor'
+ @Shared
def mySchemaset = 'my-schemaset'
+ @Shared
+ def objectMapper = new ObjectMapper()
- def 'Get anchors by name is not implemented.'(){
- given: 'an endpoint'
- def getAnchorsByNameEndpoint = "${basePath}/v1/dataspaces/{dataspace-name}/anchors/{anchor-name}/history"
+ @Shared
+ def observedDescSortOrder = new Sort.Order(Sort.Direction.DESC, 'observed_timestamp')
+ @Shared
+ def anchorAscSortOrder = new Sort.Order(Sort.Direction.ASC, 'anchor')
- when: 'get anchors by name endpoint is called'
- def response = mvc.perform( get(getAnchorsByNameEndpoint, myDataspace, myAnchor)
- .contentType(MediaType.APPLICATION_JSON))
- .andReturn().response
+ def 'Get #endpointName: default values if missing'() {
- then: 'received unsupported operation response'
- response.getStatus() == HttpStatus.NOT_IMPLEMENTED.value()
+ def controllerDataBuilder = new QueryControllerDataBuilder(endpointName,
+ [dataspace: myDataspace] << urlSpecifParams)
+ given: 'network data to be returned'
+ def networkData = createNetworkData()
+ when: 'endpoint is called without pageNumber, pageLimit, sort and pointInTime'
+ def requestBuilder = controllerDataBuilder.
+ createMockHttpRequestBuilder();
+ def response = mvc.perform(requestBuilder).andReturn().response
+ then: 'pageNumber, pageSize and sort has default values'
+ interaction {
+ def expectedPageable = PageRequest.of(0, 1000,
+ Sort.by(Sort.Order.desc('observed_timestamp')))
+ 1 * mockNetworkDataService.searchNetworkData(_ as SearchCriteria) >> {
+ SearchCriteria searchCriteria ->
+ assert searchCriteria.getPageable() == expectedPageable
+ assert searchCriteria.getObservedAfter() == null
+ assert searchCriteria.getCreatedBefore().isAfter(OffsetDateTime.now().minusMinutes(2))
+ return new SliceImpl([networkData], searchCriteria.getPageable(), false)
+ }
+ }
+ and: 'response is ok'
+ response.getStatus() == HttpStatus.OK.value()
+ def anchorHistory = objectMapper.readValue(response.getContentAsString(), AnchorHistory)
+ and: 'content has expected values'
+ anchorHistory.getPreviousRecordsLink() == null
+ anchorHistory.getNextRecordsLink() == null
+ anchorHistory.getRecords() == List.of(toAnchorDetails(networkData))
+ where:
+ endpointName | urlSpecifParams
+ 'anchor by name' | [anchor: myAnchor]
+ 'anchors by schemaset' | [schemaSet: mySchemaset]
+ }
+ def 'Get #endpointName: query data #scenario'() {
+ def inputParameters = [
+ dataspace : myDataspace,
+ pointInTime : '2021-07-24T01:00:01.000-0400',
+ pageNumber : 2, pageLimit: 10,
+ sortAsString: 'observed_timestamp:desc']
+ inputParameters << urlSpecifParams
+ inputParameters << parameters
+ def controllerDataBuilder = new QueryControllerDataBuilder(endpointName, inputParameters)
+ given:
+ def searchCriteria = controllerDataBuilder.createSearchCriteriaBuilder().build()
+ def networkData = createNetworkData()
+ mockNetworkDataService.searchNetworkData(searchCriteria) >> new SliceImpl<NetworkData>(
+ List.of(networkData), searchCriteria.getPageable(), true)
+ when: 'endpoint is called with all parameters'
+ def requestBuilder = controllerDataBuilder.createMockHttpRequestBuilder()
+ def response = mvc.perform(requestBuilder
+ .contentType(MediaType.APPLICATION_JSON)).andReturn().response
+ def responseBody = objectMapper.readValue(response.getContentAsString(), AnchorHistory)
+ then: 'status is ok'
+ response.getStatus() == HttpStatus.OK.value()
+ and: 'next and previous record links have expected value'
+ controllerDataBuilder.isExpectedNextRecordsLink(responseBody.getNextRecordsLink())
+ controllerDataBuilder.isExpectedPreviousRecordsLink(responseBody.getPreviousRecordsLink())
+ and: 'has expected network data records'
+ responseBody.getRecords().size() == 1
+ responseBody.getRecords() == [toAnchorDetails(networkData)]
+ where:
+ scenario | endpointName | urlSpecifParams | parameters
+ 'without observedTimestampAfter and with payloadFilter' | 'anchor by name' | [anchor: myAnchor] | [observedTimestampAfter: null, payloadFilter: null]
+ 'with observedTimestampAfter and without payloadFilter' | 'anchor by name' | [anchor: myAnchor] | [observedTimestampAfter: '2021-07-24T03:00:01.000-0400', payloadFilter: null]
+ 'without observedTimestampAfter and with payloadFilter' | 'anchor by name' | [anchor: myAnchor] | [observedTimestampAfter: null, payloadFilter: '{"message" : "hello+world"}']
+ 'with observedTimestampAfter and with payloadFilter' | 'anchor by name' | [anchor: myAnchor] | [observedTimestampAfter: '2021-07-24T03:00:01.000+0400', payloadFilter: '{"message" : "hello world"}']
+ 'without observedTimestampAfter and without payloadFilter' | 'anchors by schemaset' | [schemaSet: mySchemaset] | [observedTimestampAfter: null, payloadFilter: null]
+ 'with observedTimestampAfter and without payloadFilter' | 'anchors by schemaset' | [schemaSet: mySchemaset] | [observedTimestampAfter: '2021-07-24T03:00:01.000-0400', payloadFilter: null]
+ 'without observedTimestampAfter and with payloadFilter' | 'anchors by schemaset' | [schemaSet: mySchemaset] | [observedTimestampAfter: null, payloadFilter: '{"message" : "hello world"}']
+ 'with observedTimestampAfter and with payloadFilter' | 'anchors by schemaset' | [schemaSet: mySchemaset] | [observedTimestampAfter: '2021-07-24T03:00:01.000+0400', payloadFilter: '{"message" : "hello world"}']
}
- def 'Get anchors by dataspace name is not implemented.'(){
- given: 'an endpoint'
- def getAnchorsByDataspaceEndpoint = "${basePath}/v1/dataspaces/{dataspace-name}/anchors/history"
+ def 'Get #endpointName: Sort by #sortAsString'() {
+ given: 'sort parameters'
+ def parameters = [dataspace: myDataspace, sortAsString: sortAsString] << uriSpecificParams
+ when: 'endpoint is called'
+ def controllerDataBuilder = new QueryControllerDataBuilder(endpointName, parameters)
+ def response = mvc.perform(controllerDataBuilder.createMockHttpRequestBuilder())
+ .andReturn().response
+ then: 'network data service is called with expected sort'
+ 1 * mockNetworkDataService.searchNetworkData(_ as SearchCriteria) >> {
+ SearchCriteria searchCriteria ->
+ assert searchCriteria.getPageable().getSort() == expectedSort
+ return new SliceImpl([], searchCriteria.getPageable(), true)
+ }
+ and: 'response is ok'
+ response.getStatus() == HttpStatus.OK.value()
+ def anchorHistory = objectMapper.readValue(response.getContentAsString(), AnchorHistory)
+ and: 'content has expected values'
+ controllerDataBuilder.isExpectedNextRecordsLink(anchorHistory.getNextRecordsLink())
+ anchorHistory.getPreviousRecordsLink() == null
+ where:
+ endpointName | uriSpecificParams | sortAsString || expectedSort
+ 'anchor by name' | [anchor: myAnchor] | 'observed_timestamp:desc' || Sort.by(observedDescSortOrder)
+ 'anchor by name' | [anchor: myAnchor] | 'anchor:asc,observed_timestamp:desc' || Sort.by(anchorAscSortOrder, observedDescSortOrder)
+ 'anchors by schemaset' | [schemaSet: mySchemaset] | 'observed_timestamp:desc' || Sort.by(observedDescSortOrder)
+ 'anchors by schemaset' | [schemaSet: mySchemaset] | 'anchor:asc,observed_timestamp:desc' || Sort.by(anchorAscSortOrder, observedDescSortOrder)
+ }
- when: 'get anchors by dataspace name endpoint is called'
- def response = mvc.perform( get(getAnchorsByDataspaceEndpoint, myDataspace).queryParam('schema-set-name', mySchemaset)
- .contentType(MediaType.APPLICATION_JSON))
- .andReturn().response
+ def 'Get #endpointName Error handling: invalid date format in #queryParamName '() {
+ given: 'sort parameters'
+ def parameters = [dataspace: myDataspace] << uriSpecificParams
+ parameters[queryParamName] = 'invalid-date-string'
+ when: 'endpoint is called'
+ QueryControllerDataBuilder dataBuilder = new QueryControllerDataBuilder(endpointName, parameters)
+ def response = mvc.perform(dataBuilder.createMockHttpRequestBuilder())
+ .andReturn().response
+ then: 'received bad request status'
+ response.getStatus() == HttpStatus.BAD_REQUEST.value()
+ and: 'error details'
+ def errorMessage = objectMapper.readValue(response.getContentAsString(), ErrorMessage)
+ errorMessage.getStatus() == HttpStatus.BAD_REQUEST.value().toString()
+ errorMessage.getMessage().contains(queryParamName)
+ errorMessage.getMessage().contains("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
+ where:
+ endpointName | uriSpecificParams | queryParamName
+ 'anchor by name' | [anchor: myAnchor] | 'pointInTime'
+ 'anchor by name' | [anchor: myAnchor] | 'observedTimestampAfter'
+ 'anchors by schemaset' | [schemaSet: mySchemaset] | 'pointInTime'
+ 'anchors by schemaset' | [schemaSet: mySchemaset] | 'observedTimestampAfter'
+ }
+
+ def 'Get #endpointName Error handling: invalid sort format #scenario'() {
+ given: 'sort parameters'
+ def parameters = [dataspace: myDataspace, sortAsString: sortAsString] << uriSpecificParams
+ when: 'endpoint is called'
+ def controllerDataBuilder = new QueryControllerDataBuilder(endpointName, parameters)
+ def response = mvc.perform(controllerDataBuilder.createMockHttpRequestBuilder())
+ .andReturn().response
+ then: 'received bad request status'
+ response.getStatus() == HttpStatus.BAD_REQUEST.value()
+ and: 'error details'
+ def errorMessage = objectMapper.readValue(response.getContentAsString(), ErrorMessage)
+ errorMessage.getStatus() == HttpStatus.BAD_REQUEST.value().toString()
+ errorMessage.getMessage().contains("sort")
+ errorMessage.getMessage().contains("'$sortAsString'")
+ errorMessage.getMessage().contains('<fieldname>:<direction>,...,<fieldname>:<direction>')
+ where:
+ scenario | sortAsString | endpointName | uriSpecificParams
+ 'missing direction' | 'observed_timestamp' | 'anchor by name' | [anchor: myAnchor]
+ 'missing separator' | 'observed_timestampdesc' | 'anchor by name' | [anchor: myAnchor]
+ 'missing direction' | 'observed_timestamp' | 'anchors by schemaset' | [schemaSet: mySchemaset]
+ 'missing separator' | 'observed_timestampdesc' | 'anchors by schemaset' | [schemaSet: mySchemaset]
+ }
- then: 'received unsupported operation response'
- response.getStatus() == HttpStatus.NOT_IMPLEMENTED.value()
+ def 'Get #endpointName Error handling: invalid simple payload filter '() {
+ given: 'payload filter parameters'
+ def parameters = [dataspace: myDataspace, payloadFilter: 'invalid-json'] << uriSpecificParams
+ when: 'endpoint is called'
+ def controllerDataBuilder = new QueryControllerDataBuilder(endpointName, parameters)
+ def response = mvc.perform(controllerDataBuilder.createMockHttpRequestBuilder())
+ .andReturn().response
+ then: 'received bad request status'
+ response.getStatus() == HttpStatus.BAD_REQUEST.value()
+ and: 'error details'
+ def errorMessage = objectMapper.readValue(response.getContentAsString(), ErrorMessage)
+ errorMessage.getStatus() == HttpStatus.BAD_REQUEST.value().toString()
+ errorMessage.getMessage().contains('simplePayloadFilter')
+ where: 'endpoints are provided'
+ endpointName | uriSpecificParams
+ 'anchor by name' | [anchor: myAnchor]
+ 'anchors by schemaset' | [schemaSet: mySchemaset]
+ }
+ NetworkData createNetworkData() {
+ return NetworkData.builder().dataspace(myDataspace)
+ .schemaSet(mySchemaset).anchor(myAnchor).payload('{"message" : "Hello World"}')
+ .observedTimestamp(OffsetDateTime.now())
+ .createdTimestamp(OffsetDateTime.now()).build()
}
+ AnchorDetails toAnchorDetails(NetworkData networkData) {
+ AnchorDetails anchorDetails = new AnchorDetails()
+ anchorDetails.setDataspace(networkData.getDataspace())
+ anchorDetails.setAnchor(networkData.getAnchor())
+ anchorDetails.setSchemaSet(networkData.getSchemaSet())
+ anchorDetails.setObservedTimestamp(DateTimeUtility.toString(networkData.getObservedTimestamp()))
+ anchorDetails.setData(networkData.getPayload())
+ return anchorDetails
+ }
+
+
}
diff --git a/src/test/groovy/org/onap/cps/temporal/domain/SearchCriteriaSpec.groovy b/src/test/groovy/org/onap/cps/temporal/domain/SearchCriteriaSpec.groovy
index d7b6d1f..3d6a354 100644
--- a/src/test/groovy/org/onap/cps/temporal/domain/SearchCriteriaSpec.groovy
+++ b/src/test/groovy/org/onap/cps/temporal/domain/SearchCriteriaSpec.groovy
@@ -21,7 +21,6 @@ package org.onap.cps.temporal.domain
import org.springframework.data.domain.Sort
import spock.lang.Specification
-
import java.time.OffsetDateTime
class SearchCriteriaSpec extends Specification {
@@ -57,11 +56,13 @@ class SearchCriteriaSpec extends Specification {
def 'Search Criteria with the provided values.'() {
given: 'sort by parameter'
- def sortBy = Sort.by(Sort.Direction.ASC, 'observed_timestamp')
+ def sortBy = Sort.by(Sort.Direction.DESC, 'observed_timestamp')
and: 'data created one day ago'
def lastDayAsCreatedBefore = OffsetDateTime.now().minusDays(1)
and: 'observed timestamp'
def nowAsObservedAfter = OffsetDateTime.now()
+ and: 'simple payload filter'
+ def simplePayloadFilter = '{"message":"hello world"}'
when: 'search criteria is created'
def searchCriteria = SearchCriteria.builder()
@@ -69,6 +70,7 @@ class SearchCriteriaSpec extends Specification {
.schemaSetName(myschemaSetName)
.anchorName(myAnchorName)
.pagination(0, 10)
+ .simplePayloadFilter(simplePayloadFilter)
.sort(sortBy)
.observedAfter(nowAsObservedAfter)
.createdBefore(lastDayAsCreatedBefore)
@@ -81,6 +83,7 @@ class SearchCriteriaSpec extends Specification {
anchorName == myAnchorName
observedAfter == nowAsObservedAfter
createdBefore == lastDayAsCreatedBefore
+ it.simplePayloadFilter == simplePayloadFilter
pageable.getPageNumber() == 0
pageable.getPageSize() == 10
pageable.getSort() == sortBy
@@ -117,13 +120,35 @@ class SearchCriteriaSpec extends Specification {
thrown(IllegalStateException)
}
- def 'Error Handling: sort must be not null.'() {
+ def 'Error Handling: sort based on #scenario .'() {
when: 'search criteria is created without sorting information'
SearchCriteria.builder()
.dataspaceName(myDataspace)
.anchorName(myAnchorName)
.pagination(0, 1)
- .sort(null)
+ .sort(sort)
+ .build()
+ then: 'exception is thrown'
+ def illegalArgumentException = thrown(IllegalArgumentException)
+ def message = illegalArgumentException.getMessage();
+ assert message.contains("sort")
+ assert message.contains(expectedExceptionMessage)
+ where:
+ scenario | sort | expectedExceptionMessage
+ 'null' | null | "null"
+ 'unsupported properties' | Sort.by(Sort.Direction.ASC, 'unsupported') | "Invalid sorting"
+ 'missing required sort' | Sort.by(Sort.Direction.ASC, 'anchor') | 'Missing mandatory sort'
+ }
+
+ def 'Error Handling: Invalid simple payload filter.'() {
+ given: 'invalid simple payload filter'
+ def inavlidSimplePayloadFilter = 'invalid-json'
+ when: 'search criteria is created without invalid simple payload filter'
+ SearchCriteria.builder()
+ .dataspaceName(myDataspace)
+ .anchorName(myAnchorName)
+ .pagination(0, 1)
+ .simplePayloadFilter(inavlidSimplePayloadFilter)
.build()
then: 'exception is thrown'
thrown(IllegalArgumentException)
diff --git a/src/test/groovy/org/onap/cps/temporal/repository/NetworkDataRepositoryImplSpec.groovy b/src/test/groovy/org/onap/cps/temporal/repository/NetworkDataRepositoryImplSpec.groovy
index a5cc721..d33df75 100644
--- a/src/test/groovy/org/onap/cps/temporal/repository/NetworkDataRepositoryImplSpec.groovy
+++ b/src/test/groovy/org/onap/cps/temporal/repository/NetworkDataRepositoryImplSpec.groovy
@@ -165,8 +165,7 @@ class NetworkDataRepositoryImplSpec extends Specification {
}
where:
scenario | sortOrder || expectedObservedTimestamp | expectedAnchorName
- 'observed timestamp asc' | Sort.by(observedAscSortOrder) || '2021-07-22 00:00:01.000' | 'ANCHOR-01'
- 'observed timestamp asc' | Sort.by(observedDescSortOrder) || '2021-07-24 00:00:01.000' | 'ANCHOR-02'
+ 'observed timestamp desc' | Sort.by(observedDescSortOrder) || '2021-07-24 00:00:01.000' | 'ANCHOR-02'
'anchor asc, ' +
'observed timestamp desc' | Sort.by(anchorAscSortOrder, observedDescSortOrder) || '2021-07-23 00:00:01.000' | 'ANCHOR-01'
diff --git a/src/test/groovy/org/onap/cps/temporal/service/NetworkDataServiceImplSpec.groovy b/src/test/groovy/org/onap/cps/temporal/service/NetworkDataServiceImplSpec.groovy
index c55c3c7..2e04ca8 100644
--- a/src/test/groovy/org/onap/cps/temporal/service/NetworkDataServiceImplSpec.groovy
+++ b/src/test/groovy/org/onap/cps/temporal/service/NetworkDataServiceImplSpec.groovy
@@ -22,7 +22,14 @@ package org.onap.cps.temporal.service
import org.onap.cps.temporal.domain.NetworkDataId
import org.onap.cps.temporal.domain.SearchCriteria
+import org.spockframework.spring.SpringBean
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.beans.factory.annotation.Value
+import org.springframework.boot.test.context.SpringBootTest
import org.springframework.data.domain.PageImpl
+import org.springframework.test.context.ContextConfiguration
+
+import javax.validation.ValidationException
import java.time.OffsetDateTime
import org.onap.cps.temporal.domain.NetworkData
import org.onap.cps.temporal.repository.NetworkDataRepository
@@ -31,11 +38,18 @@ import spock.lang.Specification
/**
* Test specification for network data service.
*/
+@SpringBootTest
+@ContextConfiguration(classes = NetworkDataServiceImpl)
class NetworkDataServiceImplSpec extends Specification {
- def mockNetworkDataRepository = Mock(NetworkDataRepository)
+ @SpringBean
+ NetworkDataRepository mockNetworkDataRepository = Mock()
+
+ @Autowired
+ NetworkDataService objectUnderTest
- def objectUnderTest = new NetworkDataServiceImpl(mockNetworkDataRepository)
+ @Value('${app.query.response.max-page-size}')
+ int maxPageSize
def networkData = new NetworkData()
@@ -88,4 +102,19 @@ class NetworkDataServiceImplSpec extends Specification {
}
+ def 'Query network data with more than max page-size'() {
+ given: 'search criteria with more than max page size'
+ def searchCriteria = SearchCriteria.builder()
+ .dataspaceName('my-dataspaceName')
+ .schemaSetName('my-schemaset')
+ .pagination(0, maxPageSize + 1)
+ .build()
+ when: 'search is executed'
+ objectUnderTest.searchNetworkData(searchCriteria)
+
+ then: 'throws error'
+ thrown(ValidationException)
+
+ }
+
}