X-Git-Url: https://gerrit.onap.org/r/gitweb?a=blobdiff_plain;f=cps-ncmp-rest%2Fsrc%2Ftest%2Fgroovy%2Forg%2Fonap%2Fcps%2Fncmp%2Frest%2Fcontroller%2FNetworkCmProxyControllerSpec.groovy;h=331530425801fcf835e043c53c8122534603bf61;hb=f31c7f8bd4985c84f9126d071439c1a4de57f704;hp=88633450b3d414d7ff741d019753707e0c037cb9;hpb=d69742c1f02585ae5d82f49542581698367e9cde;p=cps.git diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy index 88633450b..331530425 100644 --- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy +++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyControllerSpec.groovy @@ -1,15 +1,16 @@ /* * ============LICENSE_START======================================================= * Copyright (C) 2021 Pantheon.tech - * Modification Copyright (C) 2021 highstreet technologies GmbH - * Modification Copyright (C) 2021-2022 Nordix Foundation - * Modification Copyright (C) 2021-2022 Bell Canada. + * Modifications Copyright (C) 2021 highstreet technologies GmbH + * Modifications Copyright (C) 2021-2022 Nordix Foundation + * Modifications Copyright (C) 2021-2022 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. @@ -23,10 +24,22 @@ package org.onap.cps.ncmp.rest.controller import org.mapstruct.factory.Mappers +import org.onap.cps.ncmp.api.inventory.CmHandleState +import org.onap.cps.ncmp.api.inventory.CompositeState import org.onap.cps.ncmp.api.models.NcmpServiceCmHandle +import org.onap.cps.ncmp.rest.mapper.RestOutputCmHandleStateMapper +import org.onap.cps.ncmp.rest.executor.CpsNcmpTaskExecutor import spock.lang.Shared +import java.time.OffsetDateTime +import java.time.ZoneOffset +import java.time.format.DateTimeFormatter + import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.PATCH +import static org.onap.cps.ncmp.api.inventory.CompositeState.DataStores +import static org.onap.cps.ncmp.api.inventory.CompositeState.LockReason +import static org.onap.cps.ncmp.api.inventory.CompositeState.Operational +import static org.onap.cps.ncmp.api.inventory.CompositeState.Running import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch @@ -60,11 +73,20 @@ class NetworkCmProxyControllerSpec extends Specification { NetworkCmProxyDataService mockNetworkCmProxyDataService = Mock() @SpringBean - JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) + ObjectMapper objectMapper = new ObjectMapper() + + @SpringBean + JsonObjectMapper jsonObjectMapper = new JsonObjectMapper(objectMapper) @SpringBean NcmpRestInputMapper ncmpRestInputMapper = Mappers.getMapper(NcmpRestInputMapper) + @SpringBean + RestOutputCmHandleStateMapper restOutputCmHandleStateMapper = Mappers.getMapper(RestOutputCmHandleStateMapper) + + @SpringBean + CpsNcmpTaskExecutor spiedCpsTaskExecutor = Spy() + @Value('${rest.api.ncmp-base-path}/v1') def ncmpBasePathV1 @@ -72,6 +94,10 @@ class NetworkCmProxyControllerSpec extends Specification { @Shared def NO_TOPIC = null + def NO_REQUEST_ID = null + + def formattedDateAndTime = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ") + .format(OffsetDateTime.of(2022, 12, 31, 20, 30, 40, 1, ZoneOffset.UTC)) def 'Get Resource Data from pass-through operational.'() { given: 'resource data url' @@ -86,34 +112,52 @@ class NetworkCmProxyControllerSpec extends Specification { 1 * mockNetworkCmProxyDataService.getResourceDataOperationalForCmHandle('testCmHandle', 'parent/child', '(a=1,b=2)', - NO_TOPIC) + NO_TOPIC, + NO_REQUEST_ID) and: 'response status is Ok' response.status == HttpStatus.OK.value() } - def 'Get Resource Data from pass-through operational with #scenario.'() { + def 'Get Resource Data from #datastoreInUrl with #scenario.'() { given: 'resource data url' - def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-operational" + + def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:${datastoreInUrl}" + "?resourceIdentifier=parent/child&options=(a=1,b=2)${topicQueryParam}" when: 'get data resource request is performed' def response = mvc.perform( - get(getUrl) - .contentType(MediaType.APPLICATION_JSON) - ).andReturn().response - then: 'the NCMP data service is called with operational data for cm handle' - 1 * mockNetworkCmProxyDataService.getResourceDataOperationalForCmHandle('testCmHandle', - 'parent/child', - '(a=1,b=2)', - expectedTopicName) - and: 'response status is Ok' + get(getUrl).contentType(MediaType.APPLICATION_JSON)).andReturn().response + then: 'task executor is called appropriate number of times' + expectedNumberOfExecutorExecutions * spiedCpsTaskExecutor.executeTask(_, 2000) + and: 'response status is expected' response.status == HttpStatus.OK.value() where: 'the following parameters are used' - scenario | topicQueryParam || expectedTopicName - 'Url with valid topic' | "&topic=my-topic-name" || "my-topic-name" - 'No topic in url' | '' || NO_TOPIC - 'Null topic in url' | "&topic=null" || "null" - 'Empty topic in url' | "&topic=\"\"" || "\"\"" - 'Missing topic in url' | "&topic=" || "" + scenario | datastoreInUrl | topicQueryParam || expectedTopicName | expectedNumberOfExecutorExecutions + 'url with valid topic' | 'passthrough-operational' | '&topic=my-topic-name' || 'my-topic-name' | 1 + 'no topic in url' | 'passthrough-operational' | '' || NO_TOPIC | 0 + 'null topic in url' | 'passthrough-operational' | '&topic=null' || 'null' | 1 + 'url with valid topic' | 'passthrough-running' | '&topic=my-topic-name' || 'my-topic-name' | 1 + 'no topic in url' | 'passthrough-running' | '' || NO_TOPIC | 0 + 'null topic in url' | 'passthrough-running' | '&topic=null' || 'null' | 1 + } + + def 'Fail to get Resource Data from #datastoreInUrl when #scenario.'() { + given: 'resource data url' + def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:${datastoreInUrl}" + + "?resourceIdentifier=parent/child&options=(a=1,b=2)${topicQueryParam}" + when: 'get data resource request is performed' + def response = mvc.perform( + get(getUrl).contentType(MediaType.APPLICATION_JSON)).andReturn().response + then: 'abad request is returned' + response.status == HttpStatus.BAD_REQUEST.value() + where: 'the following parameters are used' + scenario | datastoreInUrl | topicQueryParam + 'empty topic in url' | 'passthrough-operational' | '&topic=\"\"' + 'missing topic in url' | 'passthrough-operational' | '&topic=' + 'blank topic value in url' | 'passthrough-operational' | '&topic=\" \"' + 'invalid non-empty topic value in url' | 'passthrough-operational' | '&topic=1_5_*_#' + 'empty topic in url' | 'passthrough-running' | '&topic=\"\"' + 'missing topic in url' | 'passthrough-running' | '&topic=' + 'blank topic value in url' | 'passthrough-running' | '&topic=\" \"' + 'invalid non-empty topic value in url' | 'passthrough-running' | '&topic=1_5_*_#' } def 'Get Resource Data from pass-through running with #scenario value in resource identifier param.'() { @@ -124,7 +168,8 @@ class NetworkCmProxyControllerSpec extends Specification { mockNetworkCmProxyDataService.getResourceDataPassThroughRunningForCmHandle('testCmHandle', resourceIdentifier, '(a=1,b=2)', - NO_TOPIC) >> '{valid-json}' + NO_TOPIC, + NO_REQUEST_ID) >> '{valid-json}' when: 'get data resource request is performed' def response = mvc.perform( get(getUrl) @@ -207,28 +252,51 @@ class NetworkCmProxyControllerSpec extends Specification { response.contentAsString == '{"cmHandles":[{"cmHandleId":"some-cmhandle-id1"},{"cmHandleId":"some-cmhandle-id2"}]}' } - def 'Get Cm Handle details by Cm Handle id.' () { + def 'Get Cm Handle details by Cm Handle id.'() { given: 'an endpoint and a cm handle' - def cmHandleDetailsEndpoint = "$ncmpBasePathV1/ch/Some-Cm-Handle" + def cmHandleDetailsEndpoint = "$ncmpBasePathV1/ch/some-cm-handle" and: 'an existing ncmp service cm handle' - def cmHandleId = 'Some-Cm-Handle' + def cmHandleId = 'some-cm-handle' def dmiProperties = [ prop:'some DMI property' ] def publicProperties = [ "public prop":'some public property' ] - def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleID: cmHandleId, dmiProperties: dmiProperties, publicProperties: publicProperties) + def compositeState = new CompositeState(cmhandleState: CmHandleState.ADVISED, + lockReason: LockReason.builder().reason('LOCKED_OTHER').details("lock-misbehaving-details").build(), + lastUpdateTime: formattedDateAndTime.toString(), + dataSyncEnabled: false, + dataStores: dataStores()) + def ncmpServiceCmHandle = new NcmpServiceCmHandle(cmHandleId: cmHandleId, dmiProperties: dmiProperties, publicProperties: publicProperties, compositeState: compositeState) and: 'the service method is invoked with the cm handle id' - 1 * mockNetworkCmProxyDataService.getNcmpServiceCmHandle('Some-Cm-Handle') >> ncmpServiceCmHandle + 1 * mockNetworkCmProxyDataService.getNcmpServiceCmHandle('some-cm-handle') >> ncmpServiceCmHandle when: 'the cm handle details api is invoked' def response = mvc.perform(get(cmHandleDetailsEndpoint)).andReturn().response then: 'the correct response is returned' response.status == HttpStatus.OK.value() - and: 'the response returns public properties and the correct properties' + and: 'the response returns public properties and the correct cm handle states' response.contentAsString.contains('publicCmHandleProperties') - response.contentAsString.contains('public prop') - response.contentAsString.contains('some public property') + response.contentAsString.contains('LOCKED_OTHER') + response.contentAsString.contains('lock-misbehaving-details') + response.contentAsString.contains('ADVISED') + response.contentAsString.contains('NONE_REQUESTED') + response.contentAsString.contains('2022-12-31T20:30:40.000+0000') and: 'the content does not contain dmi properties' !response.contentAsString.contains("some DMI property") } + def 'Get Cm Handle public properties by Cm Handle id.' () { + given: 'a cm handle properties endpoint' + def cmHandlePropertiesEndpoint = "$ncmpBasePathV1/ch/some-cm-handle/properties" + and: 'some cm handle public properties' + def publicProperties = [ 'public prop':'some public property' ] + and: 'the service method is invoked with the cm handle id returning the cm handle public properties' + 1 * mockNetworkCmProxyDataService.getCmHandlePublicProperties('some-cm-handle') >> publicProperties + when: 'the cm handle properties api is invoked' + def response = mvc.perform(get(cmHandlePropertiesEndpoint)).andReturn().response + then: 'the correct response is returned' + response.status == HttpStatus.OK.value() + and: 'the response returns public properties and the correct properties' + response.contentAsString.equals('{"publicCmHandleProperties":[{"public prop":"some public property"}]}') + } + def 'Call execute cm handle searches with unrecognized condition name.'() { given: 'an endpoint and json data' def searchesEndpoint = "$ncmpBasePathV1/ch/searches" @@ -241,6 +309,31 @@ class NetworkCmProxyControllerSpec extends Specification { response.contentAsString == '{"cmHandles":[]}' } + def 'Query for cm handles matching query parameters'() { + given: 'an endpoint and json data' + def searchesEndpoint = "$ncmpBasePathV1/data/ch/searches" + String jsonString = '{"publicCmHandleProperties": {"name": "Contact", "value": "newemailforstore@bookstore.com"}}' + and: 'the service method is invoked with module names and returns cm handle ids' + 1 * mockNetworkCmProxyDataService.queryCmHandles(_) >> ['some-cmhandle-id1', 'some-cmhandle-id2'] + when: 'the searches api is invoked' + def response = mvc.perform(post(searchesEndpoint) + .contentType(MediaType.APPLICATION_JSON) + .content(jsonString)).andReturn().response + then: 'cm handle ids are returned' + response.contentAsString == '["some-cmhandle-id1","some-cmhandle-id2"]' + } + + def 'Query for cm handles with invalid request payload'() { + when: 'the searches api is invoked' + def searchesEndpoint = "$ncmpBasePathV1/data/ch/searches" + def invalidInputData = '{invalidJson}' + def response = mvc.perform(post(searchesEndpoint) + .contentType(MediaType.APPLICATION_JSON) + .content(invalidInputData)).andReturn().response + then: 'BAD_REQUEST is returned' + response.getStatus() == 400 + } + def 'Patch resource data in pass-through running datastore.' () { given: 'patch resource data url' def url = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:passthrough-running" + @@ -271,5 +364,35 @@ class NetworkCmProxyControllerSpec extends Specification { and: 'the response is No Content' response.status == HttpStatus.NO_CONTENT.value() } + + def 'Get resource data from DMI with valid topic i.e. async request for #scenario'() { + given: 'resource data url' + def getUrl = "$ncmpBasePathV1/ch/testCmHandle/data/ds/ncmp-datastore:${datastoreInUrl}" + + "?resourceIdentifier=parent/child&options=(a=1,b=2)&topic=my-topic-name" + when: 'get data resource request is performed' + def response = mvc.perform( + get(getUrl) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON_VALUE) + ).andReturn().response + then: 'async request id is generated' + assert response.contentAsString.contains("requestId") + where: 'the following parameters are used' + scenario | datastoreInUrl + ':passthrough-operational' | 'passthrough-operational' + ':passthrough-running' | 'passthrough-running' + } + + def dataStores() { + DataStores.builder() + .operationalDataStore(Operational.builder() + .syncState('NONE_REQUESTED') + .lastSyncTime(formattedDateAndTime.toString()).build()) + .runningDataStore(Running.builder() + .syncState('NONE_REQUESTED') + .lastSyncTime(formattedDateAndTime.toString()).build()) + .build() + } + }