From d5b8ee8070ced23a44315bcf0cb73298f4eb08a8 Mon Sep 17 00:00:00 2001 From: ToineSiebelink Date: Thu, 27 Jun 2024 09:06:31 +0100 Subject: Simplify package structure cps-ncmp-rest Issue-ID: CPS-2294 Change-Id: Idc3605c1b877be13a8d344875612da6bef791fc7 Signed-off-by: ToineSiebelink --- .../rest/controller/NcmpRestInputMapperSpec.groovy | 134 --------------- .../controller/NetworkCmProxyControllerSpec.groovy | 5 +- .../NetworkCmProxyInventoryControllerSpec.groovy | 1 + .../NetworkCmProxyRestExceptionHandlerSpec.groovy | 182 +++++++++++++++++++++ .../NetworkCmProxyRestExceptionHandlerSpec.groovy | 182 --------------------- .../rest/mapper/CmHandleStateMapperSpec.groovy | 84 ---------- .../ncmp/rest/util/CmHandleStateMapperSpec.groovy | 84 ++++++++++ .../ncmp/rest/util/NcmpRestInputMapperSpec.groovy | 134 +++++++++++++++ 8 files changed, 404 insertions(+), 402 deletions(-) delete mode 100644 cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NcmpRestInputMapperSpec.groovy create mode 100644 cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyRestExceptionHandlerSpec.groovy delete mode 100644 cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy delete mode 100644 cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapperSpec.groovy create mode 100644 cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/util/CmHandleStateMapperSpec.groovy create mode 100644 cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/util/NcmpRestInputMapperSpec.groovy (limited to 'cps-ncmp-rest/src/test/groovy/org') diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NcmpRestInputMapperSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NcmpRestInputMapperSpec.groovy deleted file mode 100644 index 41521f6398..0000000000 --- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NcmpRestInputMapperSpec.groovy +++ /dev/null @@ -1,134 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2022-2024 Nordix Foundation - * ================================================================================ - * 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.ncmp.rest.controller - -import org.mapstruct.factory.Mappers -import org.onap.cps.ncmp.api.inventory.models.CmHandleQueryServiceParameters -import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle -import org.onap.cps.ncmp.api.inventory.models.TrustLevel -import org.onap.cps.ncmp.rest.model.CmHandleQueryParameters -import org.onap.cps.ncmp.rest.model.ConditionProperties -import org.onap.cps.ncmp.rest.model.RestDmiPluginRegistration -import org.onap.cps.ncmp.rest.model.RestInputCmHandle -import org.onap.cps.ncmp.rest.model.RestModuleDefinition -import org.onap.cps.ncmp.rest.model.RestModuleReference -import org.onap.cps.spi.model.ModuleDefinition -import org.onap.cps.spi.model.ModuleReference -import spock.lang.Specification - -class NcmpRestInputMapperSpec extends Specification { - - def objectUnderTest = Mappers.getMapper(NcmpRestInputMapper.class) - - def 'Convert a created REST CM Handle Input to an NCMP Service CM Handle with #scenario'() { - given: 'a rest cm handle input' - def inputRestCmHandle = new RestInputCmHandle(cmHandle : 'example-id', cmHandleProperties: registrationDmiProperties, - publicCmHandleProperties: registrationPublicProperties, trustLevel: registrationTrustLevel, alternateId: 'my-alternate-id', moduleSetTag: 'my-module-set-tag', dataProducerIdentifier: 'my-data-producer-identifier') - def restDmiPluginRegistration = new RestDmiPluginRegistration( - createdCmHandles: [inputRestCmHandle]) - when: 'to plugin dmi registration is called' - def result = objectUnderTest.toDmiPluginRegistration(restDmiPluginRegistration) - then: 'the result returns the correct number of cm handles' - result.createdCmHandles.size() == 1 - and: 'the converted cm handle has the same id' - result.createdCmHandles[0].cmHandleId == 'example-id' - and: '(empty) properties are converted correctly' - result.createdCmHandles[0].dmiProperties == mappedDmiProperties - result.createdCmHandles[0].publicProperties == mappedPublicProperties - and: 'other fields are mapped correctly' - result.createdCmHandles[0].alternateId == 'my-alternate-id' - result.createdCmHandles[0].moduleSetTag == 'my-module-set-tag' - result.createdCmHandles[0].registrationTrustLevel == mappedTrustLevel - result.createdCmHandles[0].dataProducerIdentifier == 'my-data-producer-identifier' - where: 'the following parameters are used' - scenario | registrationDmiProperties | registrationPublicProperties | registrationTrustLevel || mappedDmiProperties | mappedPublicProperties | mappedTrustLevel - 'dmi and public properties' | ['Property-Example': 'example property'] | ['Public-Property-Example': 'public example property'] | 'COMPLETE' || ['Property-Example': 'example property'] | ['Public-Property-Example': 'public example property'] | TrustLevel.COMPLETE - 'no properties' | null | null | null || [:] | [:] | null - } - - def 'Handling empty dmi registration'() { - given: 'a rest cm handle input without any cm handles' - def restDmiPluginRegistration = new RestDmiPluginRegistration() - when: 'to plugin dmi registration is called' - def result = objectUnderTest.toDmiPluginRegistration(restDmiPluginRegistration) - then: 'unspecified lists remain as empty lists' - assert result.createdCmHandles == [] - assert result.updatedCmHandles == [] - assert result.removedCmHandles == [] - } - - def 'Handling non-empty dmi registration'() { - given: 'a rest cm handle input with cm handles' - def restDmiPluginRegistration = new RestDmiPluginRegistration( - createdCmHandles: [new RestInputCmHandle()], - updatedCmHandles: [new RestInputCmHandle()], - removedCmHandles: ["some-cmHandle"] - ) - when: 'to dmi plugin registration is called' - def result = objectUnderTest.toDmiPluginRegistration(restDmiPluginRegistration) - then: 'Lists contain values' - assert result.createdCmHandles[0].class == NcmpServiceCmHandle.class - assert result.updatedCmHandles[0].class == NcmpServiceCmHandle.class - assert result.removedCmHandles == ["some-cmHandle"] - } - - def 'Convert a ModuleReference to a RestModuleReference'() { - given: 'a ModuleReference' - def moduleReference = new ModuleReference() - when: 'toRestModuleReference is called' - def result = objectUnderTest.toRestModuleReference(moduleReference) - then: 'the result is of the correct class RestModuleReference' - result.class == RestModuleReference.class - } - - def 'Convert a ModuleDefinition to a RestModuleDefinition'() { - given: 'a ModuleDefinition' - def moduleDefinition = new ModuleDefinition('moduleName','revision', 'content') - when: 'toRestModuleDefinition is called' - def result = objectUnderTest.toRestModuleDefinition(moduleDefinition) - then: 'the result is of the correct class RestModuleDefinition' - result.class == RestModuleDefinition.class - and: 'all contents are mapped correctly' - result.toString()=='class RestModuleDefinition {\n' + - ' moduleName: moduleName\n' + - ' revision: revision\n' + - ' content: content\n' + - '}' - } - - def 'Convert a CmHandle REST query to CmHandle query service parameters.'() { - given: 'a CmHandle REST query with two conditions' - def conditionParameter1 = new ConditionProperties(conditionName: 'some condition', conditionParameters: [[p1:1]] ) - def conditionParameter2 = new ConditionProperties(conditionName: 'other condition', conditionParameters: [[p2:2]] ) - def cmHandleQuery = new CmHandleQueryParameters() - cmHandleQuery.cmHandleQueryParameters = [conditionParameter1, conditionParameter2] - when: 'it is converted into CmHandle query service parameters' - def result = objectUnderTest.toCmHandleQueryServiceParameters(cmHandleQuery) - then: 'the result is of the correct class' - assert result instanceof CmHandleQueryServiceParameters - and: 'the result has the same conditions' - assert result.cmHandleQueryParameters.size() == 2 - assert result.cmHandleQueryParameters[0].conditionName == 'some condition' - assert result.cmHandleQueryParameters[0].conditionParameters == [[p1:1]] - assert result.cmHandleQueryParameters[1].conditionName == 'other condition' - assert result.cmHandleQueryParameters[1].conditionParameters == [[p2:2]] - } -} 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 65d6d8ca76..ddf041631c 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 @@ -41,11 +41,12 @@ import org.onap.cps.ncmp.api.models.CmResourceAddress import org.onap.cps.ncmp.impl.inventory.DataStoreSyncState import org.onap.cps.ncmp.impl.inventory.models.CmHandleState import org.onap.cps.ncmp.impl.inventory.models.LockReasonCategory -import org.onap.cps.ncmp.rest.mapper.CmHandleStateMapper -import org.onap.cps.ncmp.rest.mapper.DataOperationRequestMapper import org.onap.cps.ncmp.rest.model.DataOperationDefinition import org.onap.cps.ncmp.rest.model.DataOperationRequest +import org.onap.cps.ncmp.rest.util.CmHandleStateMapper +import org.onap.cps.ncmp.rest.util.DataOperationRequestMapper import org.onap.cps.ncmp.rest.util.DeprecationHelper +import org.onap.cps.ncmp.rest.util.NcmpRestInputMapper import org.onap.cps.spi.model.ModuleDefinition import org.onap.cps.spi.model.ModuleReference import org.onap.cps.utils.JsonObjectMapper diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryControllerSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryControllerSpec.groovy index 244ecfaea7..97c68f08f3 100644 --- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryControllerSpec.groovy +++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyInventoryControllerSpec.groovy @@ -32,6 +32,7 @@ import org.onap.cps.ncmp.rest.model.CmHandleQueryParameters import org.onap.cps.ncmp.rest.model.CmHandlerRegistrationErrorResponse import org.onap.cps.ncmp.rest.model.DmiPluginRegistrationErrorResponse import org.onap.cps.ncmp.rest.model.RestDmiPluginRegistration +import org.onap.cps.ncmp.rest.util.NcmpRestInputMapper import org.onap.cps.utils.JsonObjectMapper import org.spockframework.spring.SpringBean import org.springframework.beans.factory.annotation.Autowired diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyRestExceptionHandlerSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyRestExceptionHandlerSpec.groovy new file mode 100644 index 0000000000..b1ccf8092a --- /dev/null +++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/controller/NetworkCmProxyRestExceptionHandlerSpec.groovy @@ -0,0 +1,182 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 highstreet technologies GmbH + * Modifications Copyright (C) 2021-2024 Nordix Foundation + * ================================================================================ + * 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.ncmp.rest.controller + +import groovy.json.JsonSlurper +import org.mapstruct.factory.Mappers +import org.onap.cps.TestUtils +import org.onap.cps.ncmp.api.impl.NcmpCachedResourceRequestHandler +import org.onap.cps.ncmp.api.impl.NcmpPassthroughResourceRequestHandler +import org.onap.cps.ncmp.api.impl.NetworkCmProxyFacade +import org.onap.cps.ncmp.api.impl.exception.DmiClientRequestException +import org.onap.cps.ncmp.api.impl.exception.DmiRequestException +import org.onap.cps.ncmp.api.impl.exception.ServerNcmpException +import org.onap.cps.ncmp.api.inventory.NetworkCmProxyInventoryFacade +import org.onap.cps.ncmp.exceptions.PayloadTooLargeException +import org.onap.cps.ncmp.rest.util.CmHandleStateMapper +import org.onap.cps.ncmp.rest.util.DataOperationRequestMapper +import org.onap.cps.ncmp.rest.util.DeprecationHelper +import org.onap.cps.ncmp.rest.util.NcmpRestInputMapper +import org.onap.cps.spi.exceptions.AlreadyDefinedException +import org.onap.cps.spi.exceptions.CpsException +import org.onap.cps.spi.exceptions.DataNodeNotFoundException +import org.onap.cps.spi.exceptions.DataValidationException +import org.onap.cps.utils.JsonObjectMapper +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.http.MediaType +import org.springframework.test.web.servlet.MockMvc +import spock.lang.Shared +import spock.lang.Specification + +import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNABLE_TO_READ_RESOURCE_DATA +import static org.onap.cps.ncmp.rest.controller.NetworkCmProxyRestExceptionHandlerSpec.ApiType.NCMP +import static org.onap.cps.ncmp.rest.controller.NetworkCmProxyRestExceptionHandlerSpec.ApiType.NCMPINVENTORY +import static org.springframework.http.HttpStatus.BAD_GATEWAY +import static org.springframework.http.HttpStatus.BAD_REQUEST +import static org.springframework.http.HttpStatus.CONFLICT +import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR +import static org.springframework.http.HttpStatus.NOT_FOUND +import static org.springframework.http.HttpStatus.PAYLOAD_TOO_LARGE +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post + +@WebMvcTest +class NetworkCmProxyRestExceptionHandlerSpec extends Specification { + + @Autowired + MockMvc mvc + + @SpringBean + NetworkCmProxyFacade mockNetworkCmProxyFacade = Mock() + + @SpringBean + NetworkCmProxyInventoryFacade mockNetworkCmProxyInventoryFacade = Mock() + + @SpringBean + JsonObjectMapper stubbedJsonObjectMapper = Stub() + + @SpringBean + NcmpRestInputMapper ncmpRestInputMapper = Mappers.getMapper(NcmpRestInputMapper) + + @SpringBean + CmHandleStateMapper cmHandleStateMapper = Mappers.getMapper(CmHandleStateMapper) + + @SpringBean + DataOperationRequestMapper dataOperationRequestMapper = Mappers.getMapper(DataOperationRequestMapper) + + @SpringBean + DeprecationHelper stubbedDeprecationHelper = Stub() + + @SpringBean + NcmpCachedResourceRequestHandler stubbedNcmpCachedResourceRequestHandler = Stub() + + @SpringBean + NcmpPassthroughResourceRequestHandler StubbedNcmpPassthroughResourceRequestHandler = Stub() + + @Value('${rest.api.ncmp-base-path}') + def basePathNcmp + + @Value('${rest.api.ncmp-inventory-base-path}') + def basePathNcmpInventory + + def dataNodeBaseEndpointNcmp + def dataNodeBaseEndpointNcmpInventory + + @Shared + def sampleErrorMessage = 'some error message' + @Shared + def sampleErrorDetails = 'some error details' + + def setup() { + dataNodeBaseEndpointNcmp = "$basePathNcmp/v1" + dataNodeBaseEndpointNcmpInventory = "$basePathNcmpInventory/v1" + } + + def 'Get request with #scenario exception returns correct HTTP Status with #scenario'() { + when: 'an exception is thrown by the service' + setupTestException(exception, NCMP) + def response = performTestRequest(NCMP) + then: 'an HTTP response is returned with correct message and details' + assertTestResponse(response, expectedErrorCode, expectedErrorMessage, expectedErrorDetails) + where: + scenario | exception || expectedErrorCode | expectedErrorMessage | expectedErrorDetails + 'CPS' | new CpsException(sampleErrorMessage, sampleErrorDetails) || INTERNAL_SERVER_ERROR | sampleErrorMessage | sampleErrorDetails + 'NCMP-server' | new ServerNcmpException(sampleErrorMessage, sampleErrorDetails) || INTERNAL_SERVER_ERROR | sampleErrorMessage | null + 'NCMP-client' | new DmiRequestException(sampleErrorMessage, sampleErrorDetails) || BAD_REQUEST | sampleErrorMessage | null + 'DataNode Validation' | new DataNodeNotFoundException('myDataspaceName', 'myAnchorName') || NOT_FOUND | 'DataNode not found' | null + 'other' | new IllegalStateException(sampleErrorMessage) || INTERNAL_SERVER_ERROR | sampleErrorMessage | null + 'Data Node Not Found' | new DataNodeNotFoundException('myDataspaceName', 'myAnchorName') || NOT_FOUND | 'DataNode not found' | 'DataNode not found' + 'Existing entry' | new AlreadyDefinedException('name',null) || CONFLICT | 'Already defined exception' | 'name already exists' + 'Existing entries' | AlreadyDefinedException.forDataNodes(['A', 'B'], 'myAnchorName') || CONFLICT | 'Already defined exception' | '2 data node(s) already exist' + 'Operation too large' | new PayloadTooLargeException(sampleErrorMessage) || PAYLOAD_TOO_LARGE | sampleErrorMessage | 'Check logs' + } + + def 'Post request with exception returns correct HTTP Status.'() { + given: 'the service throws data validation exception' + def exception = new DataValidationException(sampleErrorMessage, sampleErrorDetails) + setupTestException(exception, NCMPINVENTORY) + when: 'the HTTP request is made' + def response = performTestRequest(NCMPINVENTORY) + then: 'an HTTP response is returned with correct message and details' + assertTestResponse(response, BAD_REQUEST, sampleErrorMessage, sampleErrorDetails) + } + + def 'Failing DMI Request - passthrough scenario'() { + given: 'failing DMI request' + setupTestException(new DmiClientRequestException(400, 'Error Message Details NCMP', 'Bad Request from DMI', UNABLE_TO_READ_RESOURCE_DATA), NCMP) + when: 'the DMI request is executed' + def response = performTestRequest(NCMP) + then: 'NCMP service responds with 502 Bad Gateway status' + response.status == BAD_GATEWAY.value() + and: 'the NCMP response also contains the original DMI response details' + response.contentAsString.contains('400') + response.contentAsString.contains('Bad Request from DMI') + } + + def setupTestException(exception, apiType) { + if (NCMP == apiType) { + mockNetworkCmProxyInventoryFacade.getYangResourcesModuleReferences(*_) >> { throw exception } + } + mockNetworkCmProxyInventoryFacade.updateDmiRegistrationAndSyncModule(*_) >> { throw exception } + } + + def performTestRequest(apiType) { + if (NCMP == apiType) { + return mvc.perform(get("$dataNodeBaseEndpointNcmp/ch/testCmHandle/modules")).andReturn().response + } + def jsonData = TestUtils.getResourceFileContent('dmi_registration_all_singing_and_dancing.json') + return mvc.perform(post("$dataNodeBaseEndpointNcmpInventory/ch").contentType(MediaType.APPLICATION_JSON).content(jsonData)).andReturn().response + } + + static void assertTestResponse(response, expectedStatus, expectedErrorMessage, expectedErrorDetails) { + assert response.status == expectedStatus.value() + def content = new JsonSlurper().parseText(response.contentAsString) + assert content['status'].toString().contains(expectedStatus.toString()) + assert expectedErrorMessage == null || content['message'].toString().contains(expectedErrorMessage) + assert expectedErrorDetails == null || content['details'].toString().contains(expectedErrorDetails) + } + + enum ApiType { NCMP, NCMPINVENTORY } +} diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy deleted file mode 100644 index 10452b8469..0000000000 --- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/exceptions/NetworkCmProxyRestExceptionHandlerSpec.groovy +++ /dev/null @@ -1,182 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2021 highstreet technologies GmbH - * Modifications Copyright (C) 2021-2024 Nordix Foundation - * ================================================================================ - * 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.ncmp.rest.exceptions - -import groovy.json.JsonSlurper -import org.mapstruct.factory.Mappers -import org.onap.cps.TestUtils -import org.onap.cps.ncmp.api.impl.NcmpCachedResourceRequestHandler -import org.onap.cps.ncmp.api.impl.NcmpPassthroughResourceRequestHandler -import org.onap.cps.ncmp.api.impl.NetworkCmProxyFacade -import org.onap.cps.ncmp.api.impl.exception.DmiClientRequestException -import org.onap.cps.ncmp.api.impl.exception.DmiRequestException -import org.onap.cps.ncmp.api.impl.exception.ServerNcmpException -import org.onap.cps.ncmp.api.inventory.NetworkCmProxyInventoryFacade -import org.onap.cps.ncmp.exceptions.PayloadTooLargeException -import org.onap.cps.ncmp.rest.controller.NcmpRestInputMapper -import org.onap.cps.ncmp.rest.mapper.CmHandleStateMapper -import org.onap.cps.ncmp.rest.mapper.DataOperationRequestMapper -import org.onap.cps.ncmp.rest.util.DeprecationHelper -import org.onap.cps.spi.exceptions.AlreadyDefinedException -import org.onap.cps.spi.exceptions.CpsException -import org.onap.cps.spi.exceptions.DataNodeNotFoundException -import org.onap.cps.spi.exceptions.DataValidationException -import org.onap.cps.utils.JsonObjectMapper -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.http.MediaType -import org.springframework.test.web.servlet.MockMvc -import spock.lang.Shared -import spock.lang.Specification - -import static org.onap.cps.ncmp.api.NcmpResponseStatus.UNABLE_TO_READ_RESOURCE_DATA -import static org.onap.cps.ncmp.rest.exceptions.NetworkCmProxyRestExceptionHandlerSpec.ApiType.NCMP -import static org.onap.cps.ncmp.rest.exceptions.NetworkCmProxyRestExceptionHandlerSpec.ApiType.NCMPINVENTORY -import static org.springframework.http.HttpStatus.BAD_GATEWAY -import static org.springframework.http.HttpStatus.BAD_REQUEST -import static org.springframework.http.HttpStatus.CONFLICT -import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR -import static org.springframework.http.HttpStatus.NOT_FOUND -import static org.springframework.http.HttpStatus.PAYLOAD_TOO_LARGE -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post - -@WebMvcTest -class NetworkCmProxyRestExceptionHandlerSpec extends Specification { - - @Autowired - MockMvc mvc - - @SpringBean - NetworkCmProxyFacade mockNetworkCmProxyFacade = Mock() - - @SpringBean - NetworkCmProxyInventoryFacade mockNetworkCmProxyInventoryFacade = Mock() - - @SpringBean - JsonObjectMapper stubbedJsonObjectMapper = Stub() - - @SpringBean - NcmpRestInputMapper ncmpRestInputMapper = Mappers.getMapper(NcmpRestInputMapper) - - @SpringBean - CmHandleStateMapper cmHandleStateMapper = Mappers.getMapper(CmHandleStateMapper) - - @SpringBean - DataOperationRequestMapper dataOperationRequestMapper = Mappers.getMapper(DataOperationRequestMapper) - - @SpringBean - DeprecationHelper stubbedDeprecationHelper = Stub() - - @SpringBean - NcmpCachedResourceRequestHandler stubbedNcmpCachedResourceRequestHandler = Stub() - - @SpringBean - NcmpPassthroughResourceRequestHandler StubbedNcmpPassthroughResourceRequestHandler = Stub() - - @Value('${rest.api.ncmp-base-path}') - def basePathNcmp - - @Value('${rest.api.ncmp-inventory-base-path}') - def basePathNcmpInventory - - def dataNodeBaseEndpointNcmp - def dataNodeBaseEndpointNcmpInventory - - @Shared - def sampleErrorMessage = 'some error message' - @Shared - def sampleErrorDetails = 'some error details' - - def setup() { - dataNodeBaseEndpointNcmp = "$basePathNcmp/v1" - dataNodeBaseEndpointNcmpInventory = "$basePathNcmpInventory/v1" - } - - def 'Get request with #scenario exception returns correct HTTP Status with #scenario'() { - when: 'an exception is thrown by the service' - setupTestException(exception, NCMP) - def response = performTestRequest(NCMP) - then: 'an HTTP response is returned with correct message and details' - assertTestResponse(response, expectedErrorCode, expectedErrorMessage, expectedErrorDetails) - where: - scenario | exception || expectedErrorCode | expectedErrorMessage | expectedErrorDetails - 'CPS' | new CpsException(sampleErrorMessage, sampleErrorDetails) || INTERNAL_SERVER_ERROR | sampleErrorMessage | sampleErrorDetails - 'NCMP-server' | new ServerNcmpException(sampleErrorMessage, sampleErrorDetails) || INTERNAL_SERVER_ERROR | sampleErrorMessage | null - 'NCMP-client' | new DmiRequestException(sampleErrorMessage, sampleErrorDetails) || BAD_REQUEST | sampleErrorMessage | null - 'DataNode Validation' | new DataNodeNotFoundException('myDataspaceName', 'myAnchorName') || NOT_FOUND | 'DataNode not found' | null - 'other' | new IllegalStateException(sampleErrorMessage) || INTERNAL_SERVER_ERROR | sampleErrorMessage | null - 'Data Node Not Found' | new DataNodeNotFoundException('myDataspaceName', 'myAnchorName') || NOT_FOUND | 'DataNode not found' | 'DataNode not found' - 'Existing entry' | new AlreadyDefinedException('name',null) || CONFLICT | 'Already defined exception' | 'name already exists' - 'Existing entries' | AlreadyDefinedException.forDataNodes(['A', 'B'], 'myAnchorName') || CONFLICT | 'Already defined exception' | '2 data node(s) already exist' - 'Operation too large' | new PayloadTooLargeException(sampleErrorMessage) || PAYLOAD_TOO_LARGE | sampleErrorMessage | 'Check logs' - } - - def 'Post request with exception returns correct HTTP Status.'() { - given: 'the service throws data validation exception' - def exception = new DataValidationException(sampleErrorMessage, sampleErrorDetails) - setupTestException(exception, NCMPINVENTORY) - when: 'the HTTP request is made' - def response = performTestRequest(NCMPINVENTORY) - then: 'an HTTP response is returned with correct message and details' - assertTestResponse(response, BAD_REQUEST, sampleErrorMessage, sampleErrorDetails) - } - - def 'Failing DMI Request - passthrough scenario'() { - given: 'failing DMI request' - setupTestException(new DmiClientRequestException(400, 'Error Message Details NCMP', 'Bad Request from DMI', UNABLE_TO_READ_RESOURCE_DATA), NCMP) - when: 'the DMI request is executed' - def response = performTestRequest(NCMP) - then: 'NCMP service responds with 502 Bad Gateway status' - response.status == BAD_GATEWAY.value() - and: 'the NCMP response also contains the original DMI response details' - response.contentAsString.contains('400') - response.contentAsString.contains('Bad Request from DMI') - } - - def setupTestException(exception, apiType) { - if (NCMP == apiType) { - mockNetworkCmProxyInventoryFacade.getYangResourcesModuleReferences(*_) >> { throw exception } - } - mockNetworkCmProxyInventoryFacade.updateDmiRegistrationAndSyncModule(*_) >> { throw exception } - } - - def performTestRequest(apiType) { - if (NCMP == apiType) { - return mvc.perform(get("$dataNodeBaseEndpointNcmp/ch/testCmHandle/modules")).andReturn().response - } - def jsonData = TestUtils.getResourceFileContent('dmi_registration_all_singing_and_dancing.json') - return mvc.perform(post("$dataNodeBaseEndpointNcmpInventory/ch").contentType(MediaType.APPLICATION_JSON).content(jsonData)).andReturn().response - } - - static void assertTestResponse(response, expectedStatus, expectedErrorMessage, expectedErrorDetails) { - assert response.status == expectedStatus.value() - def content = new JsonSlurper().parseText(response.contentAsString) - assert content['status'].toString().contains(expectedStatus.toString()) - assert expectedErrorMessage == null || content['message'].toString().contains(expectedErrorMessage) - assert expectedErrorDetails == null || content['details'].toString().contains(expectedErrorDetails) - } - - enum ApiType { NCMP, NCMPINVENTORY } -} diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapperSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapperSpec.groovy deleted file mode 100644 index 8d5e91147f..0000000000 --- a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/mapper/CmHandleStateMapperSpec.groovy +++ /dev/null @@ -1,84 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2022-2023 Nordix Foundation - * ================================================================================ - * 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.ncmp.rest.mapper - -import org.mapstruct.factory.Mappers -import org.onap.cps.ncmp.api.inventory.models.CompositeStateBuilder -import org.onap.cps.ncmp.impl.inventory.DataStoreSyncState -import org.onap.cps.ncmp.impl.inventory.models.CmHandleState -import org.onap.cps.ncmp.rest.model.CmHandleCompositeState -import spock.lang.Specification - -import java.time.OffsetDateTime -import java.time.ZoneOffset -import java.time.format.DateTimeFormatter - -import static org.onap.cps.ncmp.impl.inventory.models.LockReasonCategory.LOCKED_MISBEHAVING -import static org.onap.cps.ncmp.impl.inventory.models.LockReasonCategory.MODULE_SYNC_FAILED - -class CmHandleStateMapperSpec extends Specification { - - 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 objectUnderTest = Mappers.getMapper(CmHandleStateMapper) - - def 'Composite State to CmHandleCompositeState'() { - given: 'a composite state model' - def compositeState = new CompositeStateBuilder() - .withCmHandleState(CmHandleState.ADVISED) - .withLastUpdatedTime(formattedDateAndTime.toString()) - .withLockReason(MODULE_SYNC_FAILED, 'locked details') - .withOperationalDataStores(DataStoreSyncState.SYNCHRONIZED, formattedDateAndTime).build() - compositeState.setDataSyncEnabled(false) - when: 'mapper is called' - def result = objectUnderTest.toCmHandleCompositeStateExternalLockReason(compositeState) - then: 'result is of the correct type' - assert result.class == CmHandleCompositeState.class - and: 'mapped result should have correct values' - assert !result.dataSyncEnabled - assert result.lastUpdateTime == formattedDateAndTime - assert result.lockReason.reason == MODULE_SYNC_FAILED.name() - assert result.lockReason.details == 'locked details' - assert result.cmHandleState == 'ADVISED' - assert result.dataSyncState.operational.getSyncState() != null - } - - def 'Handling null state.'() { - expect: 'converting null returns null' - CmHandleStateMapper.toDataStores(null) == null - } - - def 'Internal to External Lock Reason Mapping of #scenario'() { - given: 'a LOCKED composite state with locked reason of #scenario' - def compositeState = new CompositeStateBuilder() - .withCmHandleState(CmHandleState.LOCKED) - .withLockReason(lockReason, '').build() - when: 'the composite state is mapped to a CMHandle composite state' - def result = objectUnderTest.toCmHandleCompositeStateExternalLockReason(compositeState) - then: 'the composite state contains the expected lock Reason and details' - result.getLockReason().getReason() == (expectedExternalLockReason as String) - where: - scenario | lockReason || expectedExternalLockReason - 'MODULE_SYNC_FAILED' | MODULE_SYNC_FAILED || MODULE_SYNC_FAILED - 'null value' | null || LOCKED_MISBEHAVING - } - -} diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/util/CmHandleStateMapperSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/util/CmHandleStateMapperSpec.groovy new file mode 100644 index 0000000000..24f45ad8a1 --- /dev/null +++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/util/CmHandleStateMapperSpec.groovy @@ -0,0 +1,84 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2022-2023 Nordix Foundation + * ================================================================================ + * 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.ncmp.rest.util + +import org.mapstruct.factory.Mappers +import org.onap.cps.ncmp.api.inventory.models.CompositeStateBuilder +import org.onap.cps.ncmp.impl.inventory.DataStoreSyncState +import org.onap.cps.ncmp.impl.inventory.models.CmHandleState +import org.onap.cps.ncmp.rest.model.CmHandleCompositeState +import spock.lang.Specification + +import java.time.OffsetDateTime +import java.time.ZoneOffset +import java.time.format.DateTimeFormatter + +import static org.onap.cps.ncmp.impl.inventory.models.LockReasonCategory.LOCKED_MISBEHAVING +import static org.onap.cps.ncmp.impl.inventory.models.LockReasonCategory.MODULE_SYNC_FAILED + +class CmHandleStateMapperSpec extends Specification { + + 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 objectUnderTest = Mappers.getMapper(CmHandleStateMapper) + + def 'Composite State to CmHandleCompositeState'() { + given: 'a composite state model' + def compositeState = new CompositeStateBuilder() + .withCmHandleState(CmHandleState.ADVISED) + .withLastUpdatedTime(formattedDateAndTime.toString()) + .withLockReason(MODULE_SYNC_FAILED, 'locked details') + .withOperationalDataStores(DataStoreSyncState.SYNCHRONIZED, formattedDateAndTime).build() + compositeState.setDataSyncEnabled(false) + when: 'mapper is called' + def result = objectUnderTest.toCmHandleCompositeStateExternalLockReason(compositeState) + then: 'result is of the correct type' + assert result.class == CmHandleCompositeState.class + and: 'mapped result should have correct values' + assert !result.dataSyncEnabled + assert result.lastUpdateTime == formattedDateAndTime + assert result.lockReason.reason == MODULE_SYNC_FAILED.name() + assert result.lockReason.details == 'locked details' + assert result.cmHandleState == 'ADVISED' + assert result.dataSyncState.operational.getSyncState() != null + } + + def 'Handling null state.'() { + expect: 'converting null returns null' + CmHandleStateMapper.toDataStores(null) == null + } + + def 'Internal to External Lock Reason Mapping of #scenario'() { + given: 'a LOCKED composite state with locked reason of #scenario' + def compositeState = new CompositeStateBuilder() + .withCmHandleState(CmHandleState.LOCKED) + .withLockReason(lockReason, '').build() + when: 'the composite state is mapped to a CMHandle composite state' + def result = objectUnderTest.toCmHandleCompositeStateExternalLockReason(compositeState) + then: 'the composite state contains the expected lock Reason and details' + result.getLockReason().getReason() == (expectedExternalLockReason as String) + where: + scenario | lockReason || expectedExternalLockReason + 'MODULE_SYNC_FAILED' | MODULE_SYNC_FAILED || MODULE_SYNC_FAILED + 'null value' | null || LOCKED_MISBEHAVING + } + +} diff --git a/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/util/NcmpRestInputMapperSpec.groovy b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/util/NcmpRestInputMapperSpec.groovy new file mode 100644 index 0000000000..3fd7e40345 --- /dev/null +++ b/cps-ncmp-rest/src/test/groovy/org/onap/cps/ncmp/rest/util/NcmpRestInputMapperSpec.groovy @@ -0,0 +1,134 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2022-2024 Nordix Foundation + * ================================================================================ + * 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.ncmp.rest.util + +import org.mapstruct.factory.Mappers +import org.onap.cps.ncmp.api.inventory.models.CmHandleQueryServiceParameters +import org.onap.cps.ncmp.api.inventory.models.NcmpServiceCmHandle +import org.onap.cps.ncmp.api.inventory.models.TrustLevel +import org.onap.cps.ncmp.rest.model.CmHandleQueryParameters +import org.onap.cps.ncmp.rest.model.ConditionProperties +import org.onap.cps.ncmp.rest.model.RestDmiPluginRegistration +import org.onap.cps.ncmp.rest.model.RestInputCmHandle +import org.onap.cps.ncmp.rest.model.RestModuleDefinition +import org.onap.cps.ncmp.rest.model.RestModuleReference +import org.onap.cps.spi.model.ModuleDefinition +import org.onap.cps.spi.model.ModuleReference +import spock.lang.Specification + +class NcmpRestInputMapperSpec extends Specification { + + def objectUnderTest = Mappers.getMapper(NcmpRestInputMapper.class) + + def 'Convert a created REST CM Handle Input to an NCMP Service CM Handle with #scenario'() { + given: 'a rest cm handle input' + def inputRestCmHandle = new RestInputCmHandle(cmHandle : 'example-id', cmHandleProperties: registrationDmiProperties, + publicCmHandleProperties: registrationPublicProperties, trustLevel: registrationTrustLevel, alternateId: 'my-alternate-id', moduleSetTag: 'my-module-set-tag', dataProducerIdentifier: 'my-data-producer-identifier') + def restDmiPluginRegistration = new RestDmiPluginRegistration( + createdCmHandles: [inputRestCmHandle]) + when: 'to plugin dmi registration is called' + def result = objectUnderTest.toDmiPluginRegistration(restDmiPluginRegistration) + then: 'the result returns the correct number of cm handles' + result.createdCmHandles.size() == 1 + and: 'the converted cm handle has the same id' + result.createdCmHandles[0].cmHandleId == 'example-id' + and: '(empty) properties are converted correctly' + result.createdCmHandles[0].dmiProperties == mappedDmiProperties + result.createdCmHandles[0].publicProperties == mappedPublicProperties + and: 'other fields are mapped correctly' + result.createdCmHandles[0].alternateId == 'my-alternate-id' + result.createdCmHandles[0].moduleSetTag == 'my-module-set-tag' + result.createdCmHandles[0].registrationTrustLevel == mappedTrustLevel + result.createdCmHandles[0].dataProducerIdentifier == 'my-data-producer-identifier' + where: 'the following parameters are used' + scenario | registrationDmiProperties | registrationPublicProperties | registrationTrustLevel || mappedDmiProperties | mappedPublicProperties | mappedTrustLevel + 'dmi and public properties' | ['Property-Example': 'example property'] | ['Public-Property-Example': 'public example property'] | 'COMPLETE' || ['Property-Example': 'example property'] | ['Public-Property-Example': 'public example property'] | TrustLevel.COMPLETE + 'no properties' | null | null | null || [:] | [:] | null + } + + def 'Handling empty dmi registration'() { + given: 'a rest cm handle input without any cm handles' + def restDmiPluginRegistration = new RestDmiPluginRegistration() + when: 'to plugin dmi registration is called' + def result = objectUnderTest.toDmiPluginRegistration(restDmiPluginRegistration) + then: 'unspecified lists remain as empty lists' + assert result.createdCmHandles == [] + assert result.updatedCmHandles == [] + assert result.removedCmHandles == [] + } + + def 'Handling non-empty dmi registration'() { + given: 'a rest cm handle input with cm handles' + def restDmiPluginRegistration = new RestDmiPluginRegistration( + createdCmHandles: [new RestInputCmHandle()], + updatedCmHandles: [new RestInputCmHandle()], + removedCmHandles: ["some-cmHandle"] + ) + when: 'to dmi plugin registration is called' + def result = objectUnderTest.toDmiPluginRegistration(restDmiPluginRegistration) + then: 'Lists contain values' + assert result.createdCmHandles[0].class == NcmpServiceCmHandle.class + assert result.updatedCmHandles[0].class == NcmpServiceCmHandle.class + assert result.removedCmHandles == ["some-cmHandle"] + } + + def 'Convert a ModuleReference to a RestModuleReference'() { + given: 'a ModuleReference' + def moduleReference = new ModuleReference() + when: 'toRestModuleReference is called' + def result = objectUnderTest.toRestModuleReference(moduleReference) + then: 'the result is of the correct class RestModuleReference' + result.class == RestModuleReference.class + } + + def 'Convert a ModuleDefinition to a RestModuleDefinition'() { + given: 'a ModuleDefinition' + def moduleDefinition = new ModuleDefinition('moduleName','revision', 'content') + when: 'toRestModuleDefinition is called' + def result = objectUnderTest.toRestModuleDefinition(moduleDefinition) + then: 'the result is of the correct class RestModuleDefinition' + result.class == RestModuleDefinition.class + and: 'all contents are mapped correctly' + result.toString()=='class RestModuleDefinition {\n' + + ' moduleName: moduleName\n' + + ' revision: revision\n' + + ' content: content\n' + + '}' + } + + def 'Convert a CmHandle REST query to CmHandle query service parameters.'() { + given: 'a CmHandle REST query with two conditions' + def conditionParameter1 = new ConditionProperties(conditionName: 'some condition', conditionParameters: [[p1:1]] ) + def conditionParameter2 = new ConditionProperties(conditionName: 'other condition', conditionParameters: [[p2:2]] ) + def cmHandleQuery = new CmHandleQueryParameters() + cmHandleQuery.cmHandleQueryParameters = [conditionParameter1, conditionParameter2] + when: 'it is converted into CmHandle query service parameters' + def result = objectUnderTest.toCmHandleQueryServiceParameters(cmHandleQuery) + then: 'the result is of the correct class' + assert result instanceof CmHandleQueryServiceParameters + and: 'the result has the same conditions' + assert result.cmHandleQueryParameters.size() == 2 + assert result.cmHandleQueryParameters[0].conditionName == 'some condition' + assert result.cmHandleQueryParameters[0].conditionParameters == [[p1:1]] + assert result.cmHandleQueryParameters[1].conditionName == 'other condition' + assert result.cmHandleQueryParameters[1].conditionParameters == [[p2:2]] + } +} -- cgit 1.2.3-korg