2  * ============LICENSE_START=======================================================
 
   3  *  Copyright (C) 2021-2022 Nordix Foundation
 
   4  *  Modifications Copyright (C) 2021 Pantheon.tech
 
   5  *  Modifications Copyright (C) 2021 Bell Canada
 
   6  *  ================================================================================
 
   7  *  Licensed under the Apache License, Version 2.0 (the "License");
 
   8  *  you may not use this file except in compliance with the License.
 
   9  *  You may obtain a copy of the License at
 
  11  *        http://www.apache.org/licenses/LICENSE-2.0
 
  13  *  Unless required by applicable law or agreed to in writing, software
 
  14  *  distributed under the License is distributed on an "AS IS" BASIS,
 
  15  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
  16  *  See the License for the specific language governing permissions and
 
  17  *  limitations under the License.
 
  19  *  SPDX-License-Identifier: Apache-2.0
 
  20  *  ============LICENSE_END=========================================================
 
  23 package org.onap.cps.ncmp.api.impl
 
  25 import static org.onap.cps.ncmp.api.impl.operations.DmiOperations.DataStoreEnum.PASSTHROUGH_OPERATIONAL
 
  26 import static org.onap.cps.ncmp.api.impl.operations.DmiOperations.DataStoreEnum.PASSTHROUGH_RUNNING
 
  27 import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.CREATE
 
  28 import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.READ
 
  29 import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.UPDATE
 
  31 import org.onap.cps.ncmp.api.impl.operations.DmiModelOperations
 
  32 import org.onap.cps.utils.JsonObjectMapper
 
  33 import com.fasterxml.jackson.core.JsonProcessingException
 
  34 import com.fasterxml.jackson.databind.ObjectMapper
 
  35 import org.onap.cps.api.CpsAdminService
 
  36 import org.onap.cps.api.CpsDataService
 
  37 import org.onap.cps.api.CpsModuleService
 
  38 import org.onap.cps.ncmp.api.impl.exception.NcmpException
 
  39 import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations
 
  40 import org.onap.cps.spi.FetchDescendantsOption
 
  41 import org.onap.cps.spi.model.DataNode
 
  42 import org.springframework.http.HttpStatus
 
  43 import org.springframework.http.ResponseEntity
 
  44 import spock.lang.Specification
 
  46 class NetworkCmProxyDataServiceImplSpec extends Specification {
 
  48     def mockCpsDataService = Mock(CpsDataService)
 
  49     def mockCpsModuleService = Mock(CpsModuleService)
 
  50     def mockCpsAdminService = Mock(CpsAdminService)
 
  51     def spiedJsonObjectMapper = Spy(new JsonObjectMapper(new ObjectMapper()))
 
  52     def mockDmiModelOperations = Mock(DmiModelOperations)
 
  53     def mockDmiDataOperations = Mock(DmiDataOperations)
 
  55     def objectUnderTest = new NetworkCmProxyDataServiceImpl(mockCpsDataService, spiedJsonObjectMapper, mockDmiDataOperations, mockDmiModelOperations,
 
  56         mockCpsModuleService, mockCpsAdminService)
 
  58     def cmHandleXPath = "/dmi-registry/cm-handles[@id='testCmHandle']"
 
  61     def 'Write resource data for pass-through running from dmi using POST #scenario cm handle properties.'() {
 
  63             def dataNode = getDataNode(includeCmHandleProperties)
 
  64         and: 'cpsDataService returns valid datanode'
 
  65             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
 
  66                 cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
 
  67         when: 'get resource data is called'
 
  68             objectUnderTest.writeResourceDataPassThroughRunningForCmHandle('testCmHandle',
 
  69                 'testResourceId', CREATE,
 
  70                 '{some-json}', 'application/json')
 
  71         then: 'dmi called with correct data'
 
  72             1 * mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi('testCmHandle', 'testResourceId',
 
  73                 CREATE, '{some-json}', 'application/json')
 
  74                 >> { new ResponseEntity<>(HttpStatus.CREATED) }
 
  76             scenario  | includeCmHandleProperties || expectedJsonForCmhandleProperties
 
  77             'with'    | true                      || '{"testName":"testValue"}'
 
  78             'without' | false                     || '{}'
 
  81     def 'Write resource data for pass-through running from dmi using POST "not found" response (from DMI).'() {
 
  83             def dataNode = getDataNode(true)
 
  84         and: 'cpsDataService returns valid dataNode'
 
  85             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
 
  86                 cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
 
  87         and: 'dmi returns a response with 404 status code'
 
  88             mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi('testCmHandle',
 
  89                 'testResourceId', CREATE,
 
  90                 '{some-json}', 'application/json')
 
  91                 >> { new ResponseEntity<>(HttpStatus.NOT_FOUND) }
 
  92         when: 'write resource data is called'
 
  93             objectUnderTest.writeResourceDataPassThroughRunningForCmHandle('testCmHandle',
 
  94                 'testResourceId', CREATE,
 
  95                 '{some-json}', 'application/json')
 
  96         then: 'exception is thrown'
 
  97             def exceptionThrown = thrown(NcmpException.class)
 
  98         and: 'details contains (not found) error code: 404'
 
  99             exceptionThrown.details.contains('404')
 
 103     def 'Get resource data for pass-through operational from dmi.'() {
 
 105             def dataNode = getDataNode(true)
 
 106         and: 'get data node is called'
 
 107             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
 
 108                 cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
 
 109         and: 'get resource data from dmi is called'
 
 110             mockDmiDataOperations.getResourceDataFromDmi(
 
 115                 PASSTHROUGH_OPERATIONAL) >> new ResponseEntity<>('result-json', HttpStatus.OK)
 
 116         when: 'get resource data operational for cm-handle is called'
 
 117             def response = objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle',
 
 121         then: 'dmi returns a json response'
 
 122             response == 'result-json'
 
 125     def 'Get resource data for pass-through operational from dmi with Json Processing Exception.'() {
 
 127             def dataNode = getDataNode(true)
 
 128         and: 'cps data service returns valid data node'
 
 129             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
 
 130                 cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
 
 131         and: 'objectMapper not able to parse object'
 
 132             spiedJsonObjectMapper.asJsonString(_) >> { throw new JsonProcessingException('testException') }
 
 133         and: 'dmi returns NOK response'
 
 134             mockDmiDataOperations.getResourceDataFromDmi(*_)
 
 135                 >> new ResponseEntity<>('NOK-json', HttpStatus.NOT_FOUND)
 
 136         when: 'get resource data is called'
 
 137             objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle',
 
 141         then: 'exception is thrown with the expected details'
 
 142             def exceptionThrown = thrown(NcmpException.class)
 
 143             exceptionThrown.details == 'DMI status code: 404, DMI response body: NOK-json'
 
 146     def 'Get resource data for pass-through operational from dmi return NOK response.'() {
 
 148             def dataNode = getDataNode(true)
 
 149         and: 'cps data service returns valid data node'
 
 150             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
 
 151                 cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
 
 152         and: 'dmi returns NOK response'
 
 153             mockDmiDataOperations.getResourceDataFromDmi('testCmHandle',
 
 157                 PASSTHROUGH_OPERATIONAL)
 
 158                 >> new ResponseEntity<>('NOK-json', HttpStatus.NOT_FOUND)
 
 159         when: 'get resource data is called'
 
 160             objectUnderTest.getResourceDataOperationalForCmHandle('testCmHandle',
 
 164         then: 'exception is thrown'
 
 165             def exceptionThrown = thrown(NcmpException.class)
 
 166         and: 'details contains the original response'
 
 167             exceptionThrown.details.contains('NOK-json')
 
 170     def 'Get resource data for pass-through running from dmi.'() {
 
 172             def dataNode = getDataNode(true)
 
 173         and: 'cpsDataService returns valid data node'
 
 174             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
 
 175                 cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
 
 176         and: 'dmi returns valid response and data'
 
 177             mockDmiDataOperations.getResourceDataFromDmi('testCmHandle',
 
 181                 PASSTHROUGH_RUNNING) >> new ResponseEntity<>('{result-json}', HttpStatus.OK)
 
 182         when: 'get resource data is called'
 
 183             def response = objectUnderTest.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
 
 187         then: 'get resource data returns expected response'
 
 188             response == '{result-json}'
 
 191     def 'Get resource data for pass-through running from dmi return NOK response.'() {
 
 193             def dataNode = getDataNode(true)
 
 194         and: 'cpsDataService returns valid dataNode'
 
 195             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
 
 196                 cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
 
 197         and: 'dmi returns NOK response'
 
 198             mockDmiDataOperations.getResourceDataFromDmi('testCmHandle',
 
 203                 >> new ResponseEntity<>('NOK-json', HttpStatus.NOT_FOUND)
 
 204         when: 'get resource data is called'
 
 205             objectUnderTest.getResourceDataPassThroughRunningForCmHandle('testCmHandle',
 
 209         then: 'exception is thrown'
 
 210             def exceptionThrown = thrown(NcmpException.class)
 
 211         and: 'details contains the original response'
 
 212             exceptionThrown.details.contains('NOK-json')
 
 215     def 'Getting Yang Resources.'() {
 
 216         when: 'yang resources is called'
 
 217             objectUnderTest.getYangResourcesModuleReferences('some cm handle')
 
 218         then: 'CPS module services is invoked for the correct dataspace and cm handle'
 
 219             1 * mockCpsModuleService.getYangResourcesModuleReferences('NFP-Operational','some cm handle')
 
 222     def 'Get cm handle identifiers for the given module names.'() {
 
 223         when: 'execute a cm handle search for the given module names'
 
 224             objectUnderTest.executeCmHandleHasAllModulesSearch(['some-module-name'])
 
 225         then: 'get anchor identifiers is invoked  with the expected parameters'
 
 226             1 * mockCpsAdminService.queryAnchorNames('NFP-Operational', ['some-module-name'])
 
 230     def 'Update resource data for pass-through running from dmi using POST #scenario cm handle properties.'() {
 
 232             def dataNode = getDataNode(includeCmHandleProperties)
 
 233         and: 'cpsDataService returns valid datanode'
 
 234             mockCpsDataService.getDataNode('NCMP-Admin', 'ncmp-dmi-registry',
 
 235                 cmHandleXPath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode
 
 236         when: 'get resource data is called'
 
 237             objectUnderTest.writeResourceDataPassThroughRunningForCmHandle('testCmHandle',
 
 238                 'testResourceId', UPDATE,
 
 239                 '{some-json}', 'application/json')
 
 240         then: 'dmi called with correct data'
 
 241             1 * mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi('testCmHandle', 'testResourceId',
 
 242                 UPDATE, '{some-json}', 'application/json')
 
 243                 >> { new ResponseEntity<>(HttpStatus.OK) }
 
 245             scenario  | includeCmHandleProperties || expectedJsonForCmhandleProperties
 
 246             'with'    | true                      || '{"testName":"testValue"}'
 
 247             'without' | false                     || '{}'
 
 250     def 'Verify error message from handleResponse is correct for #scenario operation.'() {
 
 251         given: 'writeResourceDataPassThroughRunningFromDmi fails to return OK HttpStatus'
 
 252             mockDmiDataOperations.writeResourceDataPassThroughRunningFromDmi(*_)
 
 253                 >> new ResponseEntity<>(HttpStatus.NOT_FOUND)
 
 254         when: 'get resource data is called'
 
 255             def response = objectUnderTest.writeResourceDataPassThroughRunningForCmHandle(
 
 261         then: 'an exception is thrown with the expected error message detailsd with correct operation'
 
 262             def exceptionThrown = thrown(NcmpException.class)
 
 263             exceptionThrown.getMessage().contains(expectedResponseMessage)
 
 265             scenario | givenOperation || expectedResponseMessage
 
 266             'CREATE' | CREATE         || 'Not able to create resource data.'
 
 267             'READ'   | READ           || 'Not able to read resource data.'
 
 268             'UPDATE' | UPDATE         || 'Not able to update resource data.'
 
 271     def getDataNode(boolean includeCmHandleProperties) {
 
 272         def dataNode = new DataNode()
 
 273         dataNode.leaves = ['dmi-service-name': 'testDmiService']
 
 274         if (includeCmHandleProperties) {
 
 275             def cmHandlePropertyDataNode = new DataNode()
 
 276             cmHandlePropertyDataNode.leaves = ['name': 'testName', 'value': 'testValue']
 
 277             dataNode.childDataNodes = [cmHandlePropertyDataNode]