From 4bee6edd2dcf1b2ada3a808ae19b003c684025cd Mon Sep 17 00:00:00 2001 From: Renu Kumari Date: Sun, 9 Jan 2022 08:38:31 -0500 Subject: Send operation information in existing notification events Issue-ID: CPS-791 Signed-off-by: Renu Kumari Change-Id: Ic68dd476942291cf03afed99e95a245dafde5be9 --- .../cps/api/impl/CpsDataServiceImplSpec.groovy | 23 ++++---- .../CpsDataUpdateEventFactorySpec.groovy | 10 ++-- .../notification/NotificationServiceSpec.groovy | 61 +++++++++++++++++++--- 3 files changed, 68 insertions(+), 26 deletions(-) (limited to 'cps-service/src/test') diff --git a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy index ba9c156d75..6c899c7933 100644 --- a/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/api/impl/CpsDataServiceImplSpec.groovy @@ -2,7 +2,7 @@ * ============LICENSE_START======================================================= * Copyright (C) 2021 Nordix Foundation * Modifications Copyright (C) 2021 Pantheon.tech - * Modifications Copyright (C) 2021 Bell Canada. + * Modifications Copyright (C) 2021-2022 Bell Canada. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,6 +26,7 @@ import org.onap.cps.TestUtils import org.onap.cps.api.CpsAdminService import org.onap.cps.api.CpsModuleService import org.onap.cps.notification.NotificationService +import org.onap.cps.notification.Operation import org.onap.cps.spi.CpsDataPersistenceService import org.onap.cps.spi.FetchDescendantsOption import org.onap.cps.spi.exceptions.DataValidationException @@ -40,7 +41,6 @@ import java.time.OffsetDateTime class CpsDataServiceImplSpec extends Specification { def mockCpsDataPersistenceService = Mock(CpsDataPersistenceService) def mockCpsAdminService = Mock(CpsAdminService) - def mockCpsModuleService = Mock(CpsModuleService) def mockYangTextSchemaSourceSetCache = Mock(YangTextSchemaSourceSetCache) def mockNotificationService = Mock(NotificationService) @@ -49,7 +49,6 @@ class CpsDataServiceImplSpec extends Specification { def setup() { objectUnderTest.cpsDataPersistenceService = mockCpsDataPersistenceService objectUnderTest.cpsAdminService = mockCpsAdminService - objectUnderTest.cpsModuleService = mockCpsModuleService objectUnderTest.yangTextSchemaSourceSetCache = mockYangTextSchemaSourceSetCache objectUnderTest.notificationService = mockNotificationService } @@ -69,7 +68,7 @@ class CpsDataServiceImplSpec extends Specification { 1 * mockCpsDataPersistenceService.storeDataNode(dataspaceName, anchorName, { dataNode -> dataNode.xpath == '/test-tree' }) and: 'data updated event is sent to notification service' - 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp) + 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp, '/', Operation.CREATE) } def 'Saving child data fragment under existing node.'() { @@ -82,7 +81,7 @@ class CpsDataServiceImplSpec extends Specification { 1 * mockCpsDataPersistenceService.addChildDataNode(dataspaceName, anchorName, '/test-tree', { dataNode -> dataNode.xpath == '/test-tree/branch[@name=\'New\']' }) and: 'data updated event is sent to notification service' - 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp) + 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp, '/test-tree', Operation.CREATE) } def 'Saving list element data fragment under existing node.'() { @@ -102,7 +101,7 @@ class CpsDataServiceImplSpec extends Specification { } ) and: 'data updated event is sent to notification service' - 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp) + 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp, '/test-tree', Operation.UPDATE) } def 'Saving empty list element data fragment.'() { @@ -134,7 +133,7 @@ class CpsDataServiceImplSpec extends Specification { then: 'the persistence service method is invoked with correct parameters' 1 * mockCpsDataPersistenceService.updateDataLeaves(dataspaceName, anchorName, expectedNodeXpath, leaves) and: 'data updated event is sent to notification service' - 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp) + 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp, parentNodeXpath, Operation.UPDATE) where: 'following parameters were used' scenario | parentNodeXpath | jsonData || expectedNodeXpath | leaves 'top level node' | '/' | '{"test-tree": {"branch": []}}' || '/test-tree' | Collections.emptyMap() @@ -167,7 +166,7 @@ class CpsDataServiceImplSpec extends Specification { 1 * mockCpsDataPersistenceService.updateDataLeaves(dataspaceName, anchorName, "/dmi-registry/cm-handles[@id='cmHandle001']", ['id': 'cmHandle001']) and: 'the data updated event is sent to the notification service' - 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp) + 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp, '/dmi-registry', Operation.UPDATE) } def 'Replace data node: #scenario.'() { @@ -179,7 +178,7 @@ class CpsDataServiceImplSpec extends Specification { 1 * mockCpsDataPersistenceService.replaceDataNodeTree(dataspaceName, anchorName, { dataNode -> dataNode.xpath == expectedNodeXpath }) and: 'data updated event is sent to notification service' - 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp) + 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp, parentNodeXpath, Operation.UPDATE) where: 'following parameters were used' scenario | parentNodeXpath | jsonData || expectedNodeXpath 'top level node' | '/' | '{"test-tree": {"branch": []}}' || '/test-tree' @@ -203,7 +202,7 @@ class CpsDataServiceImplSpec extends Specification { } ) and: 'data updated event is sent to notification service' - 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp) + 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp, '/test-tree', Operation.UPDATE) } def 'Replace whole list content with empty list element.'() { @@ -224,7 +223,7 @@ class CpsDataServiceImplSpec extends Specification { then: 'the persistence service method is invoked with correct parameters' 1 * mockCpsDataPersistenceService.deleteListDataNode(dataspaceName, anchorName, '/test-tree/branch') and: 'data updated event is sent to notification service' - 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp) + 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp, '/test-tree/branch', Operation.DELETE) } def 'Delete data node under anchor and dataspace.'() { @@ -235,7 +234,7 @@ class CpsDataServiceImplSpec extends Specification { then: 'the persistence service method is invoked with the correct parameters' 1 * mockCpsDataPersistenceService.deleteDataNode(dataspaceName, anchorName, '/data-node') and: 'data updated event is sent to notification service' - 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp) + 1 * mockNotificationService.processDataUpdatedEvent(dataspaceName, anchorName, observedTimestamp, '/data-node', Operation.DELETE) } def setupSchemaSetMocks(String... yangResources) { diff --git a/cps-service/src/test/groovy/org/onap/cps/notification/CpsDataUpdateEventFactorySpec.groovy b/cps-service/src/test/groovy/org/onap/cps/notification/CpsDataUpdateEventFactorySpec.groovy index aa0c7c0b39..67ed3d90fa 100644 --- a/cps-service/src/test/groovy/org/onap/cps/notification/CpsDataUpdateEventFactorySpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/notification/CpsDataUpdateEventFactorySpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (c) 2021 Bell Canada. + * Copyright (c) 2021-2022 Bell Canada. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ import java.time.format.DateTimeFormatter import org.onap.cps.utils.DateTimeUtility import org.onap.cps.api.CpsAdminService import org.onap.cps.api.CpsDataService +import org.onap.cps.event.model.Content import org.onap.cps.event.model.Data import org.onap.cps.spi.FetchDescendantsOption import org.onap.cps.spi.model.Anchor @@ -45,7 +46,6 @@ class CpsDataUpdateEventFactorySpec extends Specification { def dateTimeFormat = 'yyyy-MM-dd\'T\'HH:mm:ss.SSSZ' def 'Create a CPS data updated event successfully: #scenario'() { - given: 'cps admin service is able to return anchor details' mockCpsAdminService.getAnchor(myDataspaceName, myAnchorName) >> new Anchor(myAnchorName, myDataspaceName, mySchemasetName) @@ -54,13 +54,10 @@ class CpsDataUpdateEventFactorySpec extends Specification { def dataNode = new DataNodeBuilder().withXpath(xpath).withLeaves(['leafName': 'leafValue']).build() mockCpsDataService.getDataNode( myDataspaceName, myAnchorName, xpath, FetchDescendantsOption.INCLUDE_ALL_DESCENDANTS) >> dataNode - when: 'CPS data updated event is created' def cpsDataUpdatedEvent = objectUnderTest.createCpsDataUpdatedEvent(myDataspaceName, - myAnchorName, DateTimeUtility.toOffsetDateTime(inputObservedTimestamp)) - + myAnchorName, DateTimeUtility.toOffsetDateTime(inputObservedTimestamp), Operation.CREATE) then: 'CPS data updated event is created with correct envelope' - with(cpsDataUpdatedEvent) { type == 'org.onap.cps.data-updated-event' source == new URI('urn:cps:org.onap.cps') @@ -79,6 +76,7 @@ class CpsDataUpdateEventFactorySpec extends Specification { assert anchorName == myAnchorName assert dataspaceName == myDataspaceName assert schemaSetName == mySchemasetName + assert operation == Content.Operation.CREATE assert data == new Data().withAdditionalProperty('leafName', 'leafValue') } where: diff --git a/cps-service/src/test/groovy/org/onap/cps/notification/NotificationServiceSpec.groovy b/cps-service/src/test/groovy/org/onap/cps/notification/NotificationServiceSpec.groovy index ca704edb4c..306e187a00 100644 --- a/cps-service/src/test/groovy/org/onap/cps/notification/NotificationServiceSpec.groovy +++ b/cps-service/src/test/groovy/org/onap/cps/notification/NotificationServiceSpec.groovy @@ -1,6 +1,6 @@ /* * ============LICENSE_START======================================================= - * Copyright (c) 2021 Bell Canada. + * Copyright (c) 2021-2022 Bell Canada. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -61,7 +61,7 @@ class NotificationServiceSpec extends Specification { given: 'notification is disabled' spyNotificationProperties.isEnabled() >> false when: 'dataUpdatedEvent is received' - objectUnderTest.processDataUpdatedEvent(myDataspacePublishedName, myAnchorName, myObservedTimestamp) + objectUnderTest.processDataUpdatedEvent(myDataspacePublishedName, myAnchorName, myObservedTimestamp, '/', Operation.CREATE) then: 'the notification is not sent' 0 * mockNotificationPublisher.sendNotification(_) } @@ -71,10 +71,12 @@ class NotificationServiceSpec extends Specification { spyNotificationProperties.isEnabled() >> true and: 'event factory can create event successfully' def cpsDataUpdatedEvent = new CpsDataUpdatedEvent() - mockCpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(dataspaceName, myAnchorName, myObservedTimestamp) >> - cpsDataUpdatedEvent + mockCpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(dataspaceName, myAnchorName, myObservedTimestamp, + Operation.CREATE) >> + cpsDataUpdatedEvent when: 'dataUpdatedEvent is received' - def future = objectUnderTest.processDataUpdatedEvent(dataspaceName, myAnchorName, myObservedTimestamp) + def future = objectUnderTest.processDataUpdatedEvent(dataspaceName, myAnchorName, myObservedTimestamp, + '/', Operation.CREATE) and: 'wait for async processing to complete' future.get(10, TimeUnit.SECONDS) then: 'async process completed successfully' @@ -87,14 +89,57 @@ class NotificationServiceSpec extends Specification { 'dataspace name matches filter' | myDataspacePublishedName || 1 } + def 'Send UPDATE operation when non-root data nodes are changed.'() { + given: 'notification is enabled' + spyNotificationProperties.isEnabled() >> true + and: 'event factory creates event if operation is UPDATE' + def cpsDataUpdatedEvent = new CpsDataUpdatedEvent() + mockCpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(myDataspacePublishedName, myAnchorName, myObservedTimestamp, + Operation.UPDATE) >> cpsDataUpdatedEvent + when: 'dataUpdatedEvent is received for non-root xpath' + def future = objectUnderTest.processDataUpdatedEvent(myDataspacePublishedName, myAnchorName, myObservedTimestamp, '/non-root-node', + operation) + and: 'wait for async processing to complete' + future.get(10, TimeUnit.SECONDS) + then: 'async process completed successfully' + future.isDone() + and: 'notification is sent' + 1 * mockNotificationPublisher.sendNotification(cpsDataUpdatedEvent) + where: + operation << [Operation.CREATE, Operation.UPDATE, Operation.DELETE] + } + + def 'Send same operation when root nodes are changed.'() { + given: 'notification is enabled' + spyNotificationProperties.isEnabled() >> true + and: 'event factory creates event if operation is #operation' + def cpsDataUpdatedEvent = new CpsDataUpdatedEvent() + mockCpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(myDataspacePublishedName, myAnchorName, myObservedTimestamp, + operation) >> cpsDataUpdatedEvent + when: 'dataUpdatedEvent is received for root xpath' + def future = objectUnderTest.processDataUpdatedEvent(myDataspacePublishedName, myAnchorName, myObservedTimestamp, '/', + operation) + and: 'wait for async processing to complete' + future.get(10, TimeUnit.SECONDS) + then: 'async process completed successfully' + future.isDone() + and: 'notification is sent' + 1 * mockNotificationPublisher.sendNotification(cpsDataUpdatedEvent) + where: + operation << [Operation.CREATE, Operation.UPDATE, Operation.DELETE] + } + + def 'Error handling in notification service.'() { given: 'notification is enabled' spyNotificationProperties.isEnabled() >> true and: 'event factory can not create event successfully' - mockCpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(myDataspacePublishedName, myAnchorName, myObservedTimestamp) >> - { throw new Exception("Could not create event") } + mockCpsDataUpdatedEventFactory.createCpsDataUpdatedEvent(myDataspacePublishedName, myAnchorName, + myObservedTimestamp, Operation.CREATE) >> + { throw new Exception("Could not create event") } when: 'event is sent for processing' - def future = objectUnderTest.processDataUpdatedEvent(myDataspacePublishedName, myAnchorName, myObservedTimestamp) + def future = objectUnderTest.processDataUpdatedEvent(myDataspacePublishedName, myAnchorName, + myObservedTimestamp, '/', Operation.CREATE) and: 'wait for async processing to complete' future.get(10, TimeUnit.SECONDS) then: 'async process completed successfully' -- cgit 1.2.3-korg