2  * ============LICENSE_START=======================================================
 
   3  *  Copyright (C) 2021 Nordix Foundation
 
   4  *  ================================================================================
 
   5  *  Licensed under the Apache License, Version 2.0 (the "License");
 
   6  *  you may not use this file except in compliance with the License.
 
   7  *  You may obtain a copy of the License at
 
   9  *        http://www.apache.org/licenses/LICENSE-2.0
 
  11  *  Unless required by applicable law or agreed to in writing, software
 
  12  *  distributed under the License is distributed on an "AS IS" BASIS,
 
  13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
  14  *  See the License for the specific language governing permissions and
 
  15  *  limitations under the License.
 
  17  *  SPDX-License-Identifier: Apache-2.0
 
  18  *  ============LICENSE_END=========================================================
 
  21 package org.onap.cps.ncmp.api.impl
 
  23 import com.fasterxml.jackson.core.JsonProcessingException
 
  24 import com.fasterxml.jackson.databind.ObjectMapper
 
  25 import org.onap.cps.api.CpsDataService
 
  26 import org.onap.cps.api.CpsModuleService
 
  27 import org.onap.cps.ncmp.api.impl.exception.NcmpException
 
  28 import org.onap.cps.ncmp.api.models.CmHandle
 
  29 import org.onap.cps.ncmp.api.models.DmiPluginRegistration
 
  30 import org.onap.cps.spi.exceptions.DataNodeNotFoundException
 
  31 import org.onap.cps.spi.exceptions.DataValidationException
 
  32 import spock.lang.Shared
 
  33 import spock.lang.Specification
 
  35 import static org.onap.cps.spi.CascadeDeleteAllowed.CASCADE_DELETE_ALLOWED
 
  37 class NetworkCmProxyDataServiceImplRegistrationSpec extends Specification {
 
  40     def persistenceCmHandle = new CmHandle()
 
  43     def cmHandlesArray = ['cmHandle001']
 
  45     def mockCpsDataService = Mock(CpsDataService)
 
  46     def mockCpsModuleService = Mock(CpsModuleService)
 
  47     def spyObjectMapper = Spy(ObjectMapper)
 
  49     def noTimestamp = null
 
  51     def 'Register or re-register a DMI Plugin for the given cm-handle(s) with #scenario process.'() {
 
  52         given: 'a registration'
 
  53             def objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
 
  54             def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin:'my-server')
 
  55             persistenceCmHandle.cmHandleID = '123'
 
  56             persistenceCmHandle.cmHandleProperties = [name1: 'value1', name2: 'value2']
 
  57             dmiPluginRegistration.createdCmHandles = createdCmHandles
 
  58             dmiPluginRegistration.updatedCmHandles = updatedCmHandles
 
  59             dmiPluginRegistration.removedCmHandles = removedCmHandles
 
  60             def expectedJsonData = '{"cm-handles":[{"id":"123","dmi-service-name":"my-server","dmi-data-service-name":null,"dmi-model-service-name":null,"additional-properties":[{"name":"name1","value":"value1"},{"name":"name2","value":"value2"}]}]}'
 
  61         when: 'registration is updated and modules are synced'
 
  62             objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
 
  63         then: 'save list elements is invoked with the expected parameters'
 
  64             expectedCallsToSaveNode * mockCpsDataService.saveListElements('NCMP-Admin', 'ncmp-dmi-registry',
 
  65                     '/dmi-registry', expectedJsonData, noTimestamp)
 
  66         and: 'update node and child data nodes is invoked with correct parameters'
 
  67             expectedCallsToUpdateNode * mockCpsDataService.updateNodeLeavesAndExistingDescendantLeaves('NCMP-Admin',
 
  68                     'ncmp-dmi-registry', '/dmi-registry', expectedJsonData, noTimestamp)
 
  69         and: 'delete schema set is invoked with the correct parameters'
 
  70             expectedCallsToDeleteSchemaSetAndListElement * mockCpsModuleService.deleteSchemaSet('NFP-Operational', 'cmHandle001', CASCADE_DELETE_ALLOWED)
 
  71         and: 'delete list or list element is invoked with the correct parameters'
 
  72             expectedCallsToDeleteSchemaSetAndListElement * mockCpsDataService.deleteListOrListElement('NCMP-Admin',
 
  73                     'ncmp-dmi-registry', "/dmi-registry/cm-handles[@id='cmHandle001']", noTimestamp)
 
  75             scenario                    | createdCmHandles      | updatedCmHandles      | removedCmHandles || expectedCallsToSaveNode | expectedCallsToUpdateNode | expectedCallsToDeleteSchemaSetAndListElement
 
  76             'create'                    | [persistenceCmHandle] | []                    | []               || 1                       | 0                         | 0
 
  77             'update'                    | []                    | [persistenceCmHandle] | []               || 0                       | 1                         | 0
 
  78             'delete'                    | []                    | []                    | cmHandlesArray   || 0                       | 0                         | 1
 
  79             'create, update and delete' | [persistenceCmHandle] | [persistenceCmHandle] | cmHandlesArray   || 1                       | 1                         | 1
 
  80             'no valid data'             | null                  | null                  | null             || 0                       | 0                         | 0
 
  83     def 'Register a DMI Plugin for the given cm-handle(s) without additional properties.'() {
 
  84         given: 'a registration without cm-handle properties'
 
  85             NetworkCmProxyDataServiceImpl objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
 
  86             def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin:'my-server')
 
  87             persistenceCmHandle.cmHandleID = '123'
 
  88             persistenceCmHandle.cmHandleProperties = null
 
  89             dmiPluginRegistration.createdCmHandles = [persistenceCmHandle]
 
  90             def expectedJsonData = '{"cm-handles":[{"id":"123","dmi-service-name":"my-server","dmi-data-service-name":null,"dmi-model-service-name":null,"additional-properties":[]}]}'
 
  91         when: 'registration is updated'
 
  92             objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
 
  93         then: 'save list elements is invoked with the expected parameters'
 
  94             1 * mockCpsDataService.saveListElements('NCMP-Admin', 'ncmp-dmi-registry',
 
  95                     '/dmi-registry', expectedJsonData, noTimestamp)
 
  98     def 'Register a DMI Plugin for a given cm-handle(s) with JSON processing errors during #scenario process.'() {
 
  99         given: 'a registration without cm-handle properties '
 
 100             NetworkCmProxyDataServiceImpl objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
 
 101             def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin:'some-plugin')
 
 102             dmiPluginRegistration.createdCmHandles = createdCmHandles
 
 103             dmiPluginRegistration.updatedCmHandles = updatedCmHandles
 
 104         and: 'an json processing exception occurs'
 
 105             spyObjectMapper.writeValueAsString(_) >> { throw (new JsonProcessingException('')) }
 
 106         when: 'registration is updated and modules are synced'
 
 107             objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
 
 108         then: 'a data validation exception is thrown'
 
 109             thrown(DataValidationException)
 
 111             scenario | createdCmHandles      | updatedCmHandles
 
 112             'create' | [persistenceCmHandle] | []
 
 113             'update' | []                    | [persistenceCmHandle]
 
 116     def 'Register a DMI Plugin for the given cm-handle(s) with no data found during delete process.'() {
 
 117         given: 'a registration without cm-handle properties '
 
 118             NetworkCmProxyDataServiceImpl objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
 
 119             def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin:'some-plugin')
 
 120             dmiPluginRegistration.removedCmHandles = ['some cm handle']
 
 121         and: 'an json processing exception occurs during delete process'
 
 122             mockCpsDataService.deleteListOrListElement(*_) >>  { throw (new DataNodeNotFoundException('','')) }
 
 123         when: 'registration is updated and modules are synced'
 
 124             objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
 
 125         then: 'no exception is thrown'
 
 129     def 'Register a DMI Plugin for the given cm-handle(s) with no schema set found during delete process.'() {
 
 130         given: 'a registration'
 
 131             def objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
 
 132             def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin:'my-server')
 
 133             dmiPluginRegistration.removedCmHandles = cmHandlesArray
 
 134         and: 'an exception occurs during delete schema set process'
 
 135             mockCpsModuleService.deleteSchemaSet(_,_,_) >>  { throw (new Exception('')) }
 
 136         when: 'registration is updated and modules are synced'
 
 137             objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
 
 138         then: 'delete list or list element is still called'
 
 139             1 * mockCpsDataService.deleteListOrListElement(_,_,_,_)
 
 142     def 'Dmi plugin registration with #scenario'() {
 
 143         given: 'a registration '
 
 144             def objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
 
 145             def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin:dmiPlugin, dmiModelPlugin:dmiModelPlugin,
 
 146                     dmiDataPlugin:dmiDataPlugin)
 
 147             dmiPluginRegistration.createdCmHandles = [persistenceCmHandle]
 
 148         when: 'update registration and sync module is called with correct DMI plugin information'
 
 149             objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
 
 150         then: 'create cm handles registration and sync modules is called with the correct plugin information'
 
 151             1 * objectUnderTest.parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(dmiPluginRegistration)
 
 153             scenario                          | dmiPlugin  | dmiModelPlugin | dmiDataPlugin
 
 154             'combined DMI plugin'             | 'service1' | ''             | ''
 
 155             'data & model DMI plugins'        | ''         | 'service1'     | 'service2'
 
 156             'data & model using same service' | ''         | 'service1'     | 'service1'
 
 159     def 'Invalid dmi plugin registration with #scenario'() {
 
 160         given: 'a registration '
 
 161             def objectUnderTest = getObjectUnderTestWithModelSyncDisabled()
 
 162             def dmiPluginRegistration = new DmiPluginRegistration(dmiPlugin:dmiPlugin, dmiModelPlugin:dmiModelPlugin,
 
 163                     dmiDataPlugin:dmiDataPlugin)
 
 164             dmiPluginRegistration.createdCmHandles = [persistenceCmHandle]
 
 165         when: 'registration is called with incorrect DMI plugin information'
 
 166             objectUnderTest.updateDmiRegistrationAndSyncModule(dmiPluginRegistration)
 
 167         then: 'an NcmpException is thrown with correct message details'
 
 168             def exceptionThrown = thrown(NcmpException)
 
 169             assert exceptionThrown.getMessage().contains(expectedMessageDetails)
 
 170         and: 'registration is not called'
 
 171             0 * objectUnderTest.parseAndCreateCmHandlesInDmiRegistrationAndSyncModules(dmiPluginRegistration)
 
 173             scenario                        | dmiPlugin  | dmiModelPlugin | dmiDataPlugin || expectedMessageDetails
 
 174             'empty DMI plugins'             | ''         | ''             | ''            || 'No DMI plugin service names'
 
 175             'blank DMI plugins'             | ' '        | ' '            | ' '           || 'No DMI plugin service names'
 
 176             'null DMI plugins'              | null       | null           | null          || 'No DMI plugin service names'
 
 177             'all DMI plugins'               | 'service1' | 'service2'     | 'service3'    || 'Cannot register combined plugin service name and other service names'
 
 178             '(combined)DMI and Data Plugin' | 'service1' | ''             | 'service2'    || 'Cannot register combined plugin service name and other service names'
 
 179             '(combined)DMI and model Plugin'| 'service1' | 'service2'     | ''            || 'Cannot register combined plugin service name and other service names'
 
 180             'only model DMI plugin'         | ''         | 'service1'     | ''            || 'Cannot register just a Data or Model plugin service name'
 
 181             'only data DMI plugin'          | ''         | ''             | 'service1'    || 'Cannot register just a Data or Model plugin service name'
 
 184     def getObjectUnderTestWithModelSyncDisabled() {
 
 185         def objectUnderTest = Spy(new NetworkCmProxyDataServiceImpl(null, null, mockCpsModuleService,
 
 186                 mockCpsDataService, null, spyObjectMapper))
 
 187         objectUnderTest.syncModulesAndCreateAnchor(*_) >> null
 
 188         return objectUnderTest