From 82ebf531110deba98086f8f7cb9c745519bbc4f4 Mon Sep 17 00:00:00 2001 From: JosephKeenan Date: Wed, 8 Dec 2021 18:16:44 +0000 Subject: Changing putOperationWithJson to postOperationWithJson -Modified responseEntity to reesponseEntity -Changed behaviour of sync to use new repsonses and removed JSON parsing -Updated tests to use new responsess -Tests have been updated and added jira to docs -Added messageConverters to RestTemplate to support plain text with ResponseEntity -Added docker log output for cps & dmi containers during CSIT teardown -Moved reponse conversion from service into DMIModelOperations class -Added response handling test (edgecases) -Updated response request body for passthrough-running CSIT test to pass Issue-ID: CPS-777 Signed-off-by: JosephKeenan Change-Id: If2acf83a97b8aad5aa2c342154d807a47cace6a0 --- ...tworkCmProxyDataServiceImplModelSyncSpec.groovy | 28 +++--- .../impl/NetworkCmProxyDataServiceImplSpec.groovy | 5 - .../ncmp/api/impl/client/DmiRestClientSpec.groovy | 4 +- .../api/impl/config/NcmpConfigurationSpec.groovy | 17 ++-- .../impl/operations/DmiModelOperationsSpec.groovy | 101 +++++++++++++++++---- 5 files changed, 109 insertions(+), 46 deletions(-) (limited to 'cps-ncmp-service/src/test/groovy/org/onap') diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplModelSyncSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplModelSyncSpec.groovy index a5c1f45d54..de60a01930 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplModelSyncSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplModelSyncSpec.groovy @@ -20,15 +20,12 @@ package org.onap.cps.ncmp.api.impl - +import com.fasterxml.jackson.databind.ObjectMapper import org.onap.cps.api.CpsAdminService import org.onap.cps.api.CpsModuleService import org.onap.cps.ncmp.api.impl.operations.DmiModelOperations import org.onap.cps.ncmp.api.models.PersistenceCmHandle -import org.onap.cps.ncmp.utils.TestUtils import org.onap.cps.spi.model.ModuleReference -import org.springframework.http.HttpStatus -import org.springframework.http.ResponseEntity import spock.lang.Specification class NetworkCmProxyDataServiceImplModelSyncSpec extends Specification { @@ -38,7 +35,7 @@ class NetworkCmProxyDataServiceImplModelSyncSpec extends Specification { def mockDmiModelOperations = Mock(DmiModelOperations) def objectUnderTest = new NetworkCmProxyDataServiceImpl(null, mockDmiModelOperations, - mockCpsModuleService, null, null, mockCpsAdminService, null) + mockCpsModuleService, null, null, mockCpsAdminService, new ObjectMapper()) def expectedDataspaceName = 'NFP-Operational' @@ -50,25 +47,24 @@ class NetworkCmProxyDataServiceImplModelSyncSpec extends Specification { cmHandleForModelSync.asAdditionalProperties(additionalProperties) } and: 'dmi operations returns some module references' - def jsonData = TestUtils.getResourceFileContent('cmHandleModules.json') - def moduleReferencesFromCmHandleAsJson = new ResponseEntity(jsonData, HttpStatus.OK) - mockDmiModelOperations.getModuleReferences(cmHandleForModelSync) >> moduleReferencesFromCmHandleAsJson + def moduleReferences = [ new ModuleReference(moduleName:'module1',revision:'1'), + new ModuleReference(moduleName:'module2',revision:'2') ] + mockDmiModelOperations.getModuleReferences(cmHandleForModelSync) >> moduleReferences and: 'CPS-Core returns list of existing module resources' mockCpsModuleService.getYangResourceModuleReferences(expectedDataspaceName) >> existingModuleResourcesInCps and: 'DMI-Plugin returns resource(s) for "new" module(s)' - def moduleResources = new ResponseEntity(sdncReponseBody, HttpStatus.OK) - mockDmiModelOperations.getNewYangResourcesFromDmi(cmHandleForModelSync, [new ModuleReference('module1', '1')]) >> moduleResources + mockDmiModelOperations.getNewYangResourcesFromDmi(cmHandleForModelSync, [new ModuleReference('module1', '1')]) >> yangResourceToContentMap when: 'module sync is triggered' objectUnderTest.syncModulesAndCreateAnchor(cmHandleForModelSync) then: 'the CPS module service is called once with the correct parameters' - 1 * mockCpsModuleService.createSchemaSetFromModules(expectedDataspaceName, cmHandleForModelSync.getId(), expectedYangResourceToContentMap, expectedKnownModules) + 1 * mockCpsModuleService.createSchemaSetFromModules(expectedDataspaceName, cmHandleForModelSync.getId(), yangResourceToContentMap, expectedKnownModules) and: 'admin service create anchor method has been called with correct parameters' 1 * mockCpsAdminService.createAnchor(expectedDataspaceName, cmHandleForModelSync.getId(), cmHandleForModelSync.getId()) where: 'the following parameters are used' - scenario | additionalProperties | existingModuleResourcesInCps | sdncReponseBody || expectedYangResourceToContentMap | expectedKnownModules | expectedJsonForAdditionalProperties - 'one unknown module' | ['name1': 'value1'] | [new ModuleReference('module2', '2'), new ModuleReference('module3', '3')] | '[{"moduleName" : "module1", "revision" : "1","yangSource": "some yang source"}]' || [module1: 'some yang source'] | [new ModuleReference('module2', '2')] | '{"name1":"value1"}' - 'no add. properties' | [:] | [new ModuleReference('module2', '2'), new ModuleReference('module3', '3')] | '[{"moduleName" : "module1", "revision" : "1","yangSource": "some yang source"}]' || [module1: 'some yang source'] | [new ModuleReference('module2', '2')] | '{}' - 'additional properties is null' | null | [new ModuleReference('module2', '2'), new ModuleReference('module3', '3')] | '[{"moduleName" : "module1", "revision" : "1","yangSource": "some yang source"}]' || [module1: 'some yang source'] | [new ModuleReference('module2', '2')] | '{}' - 'no unknown module' | [:] | [new ModuleReference('module1', '1'), new ModuleReference('module2', '2')] | '[]' || [:] | [new ModuleReference('module1', '1'), new ModuleReference('module2', '2')] | '{}' + scenario | additionalProperties | existingModuleResourcesInCps | yangResourceToContentMap || expectedKnownModules | expectedJsonForAdditionalProperties + 'one unknown module' | ['name1': 'value1'] | [new ModuleReference('module2', '2'), new ModuleReference('module3', '3')] | [module1: 'some yang source'] || [new ModuleReference('module2', '2')] | '{"name1":"value1"}' + 'no add. properties' | [:] | [new ModuleReference('module2', '2'), new ModuleReference('module3', '3')] | [module1: 'some yang source'] || [new ModuleReference('module2', '2')] | '{}' + 'additional properties is null' | null | [new ModuleReference('module2', '2'), new ModuleReference('module3', '3')] | [module1: 'some yang source'] || [new ModuleReference('module2', '2')] | '{}' + 'no unknown module' | [:] | [new ModuleReference('module1', '1'), new ModuleReference('module2', '2')] | [:] || [new ModuleReference('module1', '1'), new ModuleReference('module2', '2')] | '{}' } } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy index c396a2ef26..62492710e0 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/NetworkCmProxyDataServiceImplSpec.groovy @@ -22,10 +22,6 @@ package org.onap.cps.ncmp.api.impl -import org.onap.cps.ncmp.api.impl.client.DmiRestClient -import org.onap.cps.ncmp.api.impl.operations.DmiRequestBody -import org.springframework.http.HttpHeaders - import static org.onap.cps.ncmp.api.impl.operations.DmiOperations.DataStoreEnum.PASSTHROUGH_OPERATIONAL import static org.onap.cps.ncmp.api.impl.operations.DmiOperations.DataStoreEnum.PASSTHROUGH_RUNNING import static org.onap.cps.ncmp.api.impl.operations.DmiRequestBody.OperationEnum.CREATE @@ -40,7 +36,6 @@ import org.onap.cps.api.CpsModuleService import org.onap.cps.api.CpsQueryService import org.onap.cps.ncmp.api.impl.exception.NcmpException import org.onap.cps.ncmp.api.impl.operations.DmiDataOperations -import org.onap.cps.ncmp.api.impl.operations.DmiModelOperations import org.onap.cps.spi.FetchDescendantsOption import org.onap.cps.spi.model.DataNode import org.springframework.http.HttpStatus diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy index 8c46178ddd..389086c770 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/client/DmiRestClientSpec.groovy @@ -46,7 +46,7 @@ class DmiRestClientSpec extends Specification { def 'DMI POST operation'() { given: 'the rest template returns a valid response entity' def mockResponseEntity = Mock(ResponseEntity) - mockRestTemplate.exchange(resourceUrl, HttpMethod.POST, _ as HttpEntity, String.class) >> mockResponseEntity + mockRestTemplate.exchange(resourceUrl, HttpMethod.POST, _ as HttpEntity, Object.class) >> mockResponseEntity when: 'POST operation is invoked' def result = objectUnderTest.postOperation(resourceUrl, new HttpHeaders()) then: 'the output of the method is equal to the output from the rest template' @@ -56,7 +56,7 @@ class DmiRestClientSpec extends Specification { def 'DMI POST operation with JSON.'() { given: 'the rest template returns a valid response entity' def mockResponseEntity = Mock(ResponseEntity) - mockRestTemplate.postForEntity(resourceUrl, _ as HttpEntity, String.class) >> mockResponseEntity + mockRestTemplate.postForEntity(resourceUrl, _ as HttpEntity, Object.class) >> mockResponseEntity when: 'POST operation is invoked' def result = objectUnderTest.postOperationWithJsonData(resourceUrl, 'json-data', new HttpHeaders()) then: 'the output of the method is equal to the output from the test template' diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/NcmpConfigurationSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/NcmpConfigurationSpec.groovy index 2c4ba68f29..e1aba79a50 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/NcmpConfigurationSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/NcmpConfigurationSpec.groovy @@ -22,6 +22,8 @@ package org.onap.cps.ncmp.api.impl.config import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import org.springframework.boot.web.client.RestTemplateBuilder +import org.springframework.http.MediaType +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter import org.springframework.test.context.ContextConfiguration import org.springframework.web.client.RestTemplate import spock.lang.Specification @@ -33,6 +35,8 @@ class NcmpConfigurationSpec extends Specification{ @Autowired NcmpConfiguration.DmiProperties dmiProperties + def mockRestTemplateBuilder = new RestTemplateBuilder() + def 'NcmpConfiguration Construction.'() { expect: 'the system can create an instance' new NcmpConfiguration() != null @@ -45,13 +49,14 @@ class NcmpConfigurationSpec extends Specification{ } def 'Rest Template creation.'() { - given: 'a rest template builder' - def mockRestTemplateBuilder = Mock(RestTemplateBuilder) - def expectedRestTemplate = Mock(RestTemplate) - mockRestTemplateBuilder.build() >> expectedRestTemplate when: 'a rest template is created' def result = NcmpConfiguration.restTemplate(mockRestTemplateBuilder) - then: 'the rest template from the builder is returned' - assert result == expectedRestTemplate + then: 'the rest template is returned' + assert result instanceof RestTemplate + and: 'a jackson media converter has been added' + def lastMessageConverter = result.getMessageConverters().get(result.getMessageConverters().size()-1) + lastMessageConverter instanceof MappingJackson2HttpMessageConverter + and: 'the jackson media converters supports the expected media types' + lastMessageConverter.getSupportedMediaTypes() == [MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN]; } } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy index d9d12711fb..335bc062cc 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/operations/DmiModelOperationsSpec.groovy @@ -41,8 +41,41 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec { @Autowired DmiModelOperations objectUnderTest - def 'Module references for a persistence cm handle #scenario.'() { - given: 'a persistence cm handle for #cmHandleId' + def 'Retrieving module references.'() { + given: 'a persistence cm handle' + mockPersistenceCmHandleRetrieval([]) + and: 'a positive response from dmi service when it is called with the expected parameters' + def moduleReferencesAsLisOfMaps = [[moduleName:'mod1',revision:'A'],[moduleName:'mod2',revision:'X']] + def responseFromDmi = new ResponseEntity([schemas:moduleReferencesAsLisOfMaps], HttpStatus.OK) + mockDmiRestClient.postOperationWithJsonData("${dmiServiceName}/dmi/v1/ch/${cmHandleId}/modules", + '{"cmHandleProperties":{}}', [:]) >> responseFromDmi + when: 'get module references is called' + def result = objectUnderTest.getModuleReferences(persistenceCmHandle) + then: 'the result consists of expected module references' + assert result == [new ModuleReference(moduleName:'mod1',revision:'A'), new ModuleReference(moduleName:'mod2',revision:'X')] + } + + def 'Retrieving module references edge case: #scenario.'() { + given: 'a persistence cm handle' + mockPersistenceCmHandleRetrieval([]) + and: 'any response from dmi service when it is called with the expected parameters' + // TODO (toine): production code ignores any error code from DMI, this should be improved in future + def responseFromDmi = new ResponseEntity(bodyAsMap, HttpStatus.NO_CONTENT) + mockDmiRestClient.postOperationWithJsonData(*_) >> responseFromDmi + when: 'get module references is called' + def result = objectUnderTest.getModuleReferences(persistenceCmHandle) + then: 'the result is empty' + assert result == [] + where: 'the dmi response body has the following content' + scenario | bodyAsMap + 'no modules' | [schemas:[]] + 'modules null' | [schemas:null] + 'no schema' | [something:'else'] + 'no body' | null + } + + def 'Retrieving module references, additional property handling: #scenario.'() { + given: 'a persistence cm handle' mockPersistenceCmHandleRetrieval(additionalPropertiesObject) and: 'a positive response from dmi service when it is called with tha expected parameters' def responseFromDmi = new ResponseEntity(HttpStatus.OK) @@ -51,35 +84,69 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec { when: 'a get module references is called' def result = objectUnderTest.getModuleReferences(persistenceCmHandle) then: 'the result is the response from dmi service' - assert result == responseFromDmi - where: + assert result == [] + where: 'the following additional properties are used' scenario | additionalPropertiesObject || expectedAdditionalPropertiesInRequest 'with properties' | [sampleAdditionalProperty] || '{"prop1":"val1"}' - 'with null properties' | null || "{}" - 'without properties' | [] || "{}" + 'with null properties' | null || '{}' + 'without properties' | [] || '{}' } - def 'New yang resources from dmi using persistence cm handle #scenario.'() { - given: 'a persistence cm handle for #cmHandleId' - mockPersistenceCmHandleRetrieval(additionalPropertiesObject) + def 'Retrieving yang resources.'() { + given: 'a persistence cm handle' + mockPersistenceCmHandleRetrieval([]) and: 'a positive response from dmi service when it is called with tha expected parameters' - def responseFromDmi = new ResponseEntity(HttpStatus.OK) + def responseFromDmi = new ResponseEntity([[moduleName: 'mod1', revision: 'A', yangSource: 'some yang source'], + [moduleName: 'mod2', revision: 'C', yangSource: 'other yang source']], HttpStatus.OK) + def expectedModuleReferencesInRequest = '{"name":"mod1","revision":"A"},{"name":"mod2","revision":"X"}' + mockDmiRestClient.postOperationWithJsonData("${dmiServiceName}/dmi/v1/ch/${cmHandleId}/moduleResources", + '{"data":{"modules":[' + expectedModuleReferencesInRequest + ']},"cmHandleProperties":{}}', [:]) >> responseFromDmi + when: 'get new yang resources from dmi service' + def result = objectUnderTest.getNewYangResourcesFromDmi(persistenceCmHandle, newModuleReferences) + then: 'the result has the 2 expected yang (re)sources (order is not guaranteed)' + assert result.size() == 2 + assert result.get('mod1') == 'some yang source' + assert result.get('mod2') == 'other yang source' + } + + def 'Retrieving yang resources, edge case: scenario.'() { + given: 'a persistence cm handle' + mockPersistenceCmHandleRetrieval([]) + and: 'a positive response from dmi service when it is called with tha expected parameters' + // TODO (toine): production code ignores any error code from DMI, this should be improved in future + def responseFromDmi = new ResponseEntity(responseFromDmiBody, HttpStatus.NO_CONTENT) + mockDmiRestClient.postOperationWithJsonData(*_) >> responseFromDmi + when: 'get new yang resources from dmi service' + def result = objectUnderTest.getNewYangResourcesFromDmi(persistenceCmHandle, newModuleReferences) + then: 'the result is empty' + assert result == [:] + where: 'the dmi response body has the following content' + scenario | responseFromDmiBody + 'empty array' | [] + 'null array' | null + } + + def 'Retrieving yang resources, additional property handling #scenario.'() { + given: 'a persistence cm handle' + mockPersistenceCmHandleRetrieval(additionalPropertiesObject) + and: 'a positive response from dmi service when it is called with the expected parameters' + def responseFromDmi = new ResponseEntity<>([[moduleName: 'mod1', revision: 'A', yangSource: 'some yang source']], HttpStatus.OK) mockDmiRestClient.postOperationWithJsonData("${dmiServiceName}/dmi/v1/ch/${cmHandleId}/moduleResources", '{"data":{"modules":[' + expectedModuleReferencesInRequest + ']},"cmHandleProperties":'+expectedAdditionalPropertiesInRequest+'}', [:]) >> responseFromDmi when: 'get new yang resources from dmi service' def result = objectUnderTest.getNewYangResourcesFromDmi(persistenceCmHandle, unknownModuleReferences) then: 'the result is the response from dmi service' - assert result == responseFromDmi - where: + assert result == [mod1:'some yang source'] + where: 'the following additional properties are used' scenario | additionalPropertiesObject | unknownModuleReferences || expectedAdditionalPropertiesInRequest | expectedModuleReferencesInRequest 'with module references and properties' | [sampleAdditionalProperty] | newModuleReferences || '{"prop1":"val1"}' | '{"name":"mod1","revision":"A"},{"name":"mod2","revision":"X"}' 'without module references' | [sampleAdditionalProperty] | [] || '{"prop1":"val1"}' | '' 'without properties' | [] | newModuleReferences || '{}' | '{"name":"mod1","revision":"A"},{"name":"mod2","revision":"X"}' } - def 'New yang resources from dmi with additional properties null'() { - given: 'a persistence cm handle for #cmHandleId' + def 'Retrieving yang resources from dmi with additional properties null.'() { + given: 'a persistence cm handle' mockPersistenceCmHandleRetrieval(null) when: 'a get new yang resources from dmi is called' objectUnderTest.getNewYangResourcesFromDmi(persistenceCmHandle, []) @@ -87,8 +154,8 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec { thrown(NullPointerException) } - def 'Json Processing Exception'() { - given: 'a persistence cm handle for #cmHandleId' + def 'Retrieving module references with Json processing exception.'() { + given: 'a persistence cm handle' mockPersistenceCmHandleRetrieval([]) and: 'a Json processing exception occurs' spyObjectMapper.writeValueAsString(_) >> {throw (new JsonProcessingException(''))} @@ -97,7 +164,7 @@ class DmiModelOperationsSpec extends DmiOperationsBaseSpec { then: 'an ncmp exception is thrown' def exceptionThrown = thrown(NcmpException) and: 'the message indicates a parsing error' - exceptionThrown.message.toLowerCase().contains("parsing error") + exceptionThrown.message.toLowerCase().contains('parsing error') } } -- cgit 1.2.3-korg