diff options
author | lukegleeson <luke.gleeson@est.tech> | 2023-04-06 15:28:56 +0100 |
---|---|---|
committer | lukegleeson <luke.gleeson@est.tech> | 2023-04-28 12:22:37 +0100 |
commit | 3c0ea89e2bd77d2f4f86f0729643455e1b29c5ac (patch) | |
tree | 689099c10faf6bde751ed8b716a204d7d476bbde /cps-ncmp-service/src/test/groovy | |
parent | a4cfede5bac589107ab81968f1d3a88c11a464d2 (diff) |
Timeout for Subscription Create Partial Response
- Implemented default 30s timeout for DMI Responses
- Placeholders have been TODO'd for Outcome Response generation and Persisted Subscription Updating
- Refactored common HazelcastCacheConfig methods
- Some tests required a blocking variable due to seperate thread usage
Issue-ID: CPS-1599
Signed-off-by: lukegleeson <luke.gleeson@est.tech>
Change-Id: I2b1a35e93939daa0524d379ac4736d714ef61a6f
Diffstat (limited to 'cps-ncmp-service/src/test/groovy')
5 files changed, 206 insertions, 9 deletions
diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/SynchronizationCacheConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/SynchronizationCacheConfigSpec.groovy index 6829d834d2..567debdded 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/SynchronizationCacheConfigSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/SynchronizationCacheConfigSpec.groovy @@ -51,8 +51,8 @@ class SynchronizationCacheConfigSpec extends Specification { assert null != moduleSyncStartedOnCmHandles and: 'system is able to create an instance of a map to hold data sync semaphores' assert null != dataSyncSemaphores - and: 'there 3 instances' - assert Hazelcast.allHazelcastInstances.size() == 3 + and: 'there are at least 3 instances' + assert Hazelcast.allHazelcastInstances.size() > 2 and: 'they have the correct names (in any order)' assert Hazelcast.allHazelcastInstances.name.containsAll('moduleSyncWorkQueue', 'moduleSyncStartedOnCmHandles', 'dataSyncSemaphores' ) } diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/event/avc/ForwardedSubscriptionEventCacheConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/event/avc/ForwardedSubscriptionEventCacheConfigSpec.groovy new file mode 100644 index 0000000000..7448daf598 --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/event/avc/ForwardedSubscriptionEventCacheConfigSpec.groovy @@ -0,0 +1,75 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 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.api.impl.event.avc + +import com.hazelcast.config.Config +import com.hazelcast.core.Hazelcast +import com.hazelcast.map.IMap +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import spock.lang.Specification + +@SpringBootTest(classes = [ForwardedSubscriptionEventCacheConfig]) +class ForwardedSubscriptionEventCacheConfigSpec extends Specification { + + @Autowired + private IMap<String, Set<String>> forwardedSubscriptionEventCache + + def 'Embedded (hazelcast) cache for Forwarded Subscription Event Cache.'() { + expect: 'system is able to create an instance of the Forwarded Subscription Event Cache' + assert null != forwardedSubscriptionEventCache + and: 'there is at least 1 instance' + assert Hazelcast.allHazelcastInstances.size() > 0 + and: 'Forwarded Subscription Event Cache is present' + assert Hazelcast.allHazelcastInstances.name.contains('hazelCastInstanceSubscriptionEvents') + } + + def 'Verify configs for Distributed Caches'(){ + given: 'the Forwarded Subscription Event Cache config' + def forwardedSubscriptionEventCacheConfig = Hazelcast.getHazelcastInstanceByName('hazelCastInstanceSubscriptionEvents').config.mapConfigs.get('forwardedSubscriptionEventCacheMapConfig') + expect: 'system created instance with correct config' + assert forwardedSubscriptionEventCacheConfig.backupCount == 3 + assert forwardedSubscriptionEventCacheConfig.asyncBackupCount == 3 + } + + def 'Verify deployment network configs for Distributed Caches'() { + given: 'the Forwarded Subscription Event Cache config' + def forwardedSubscriptionEventCacheNetworkConfig = Hazelcast.getHazelcastInstanceByName('hazelCastInstanceSubscriptionEvents').config.networkConfig + expect: 'system created instance with correct config' + assert forwardedSubscriptionEventCacheNetworkConfig.join.autoDetectionConfig.enabled + assert !forwardedSubscriptionEventCacheNetworkConfig.join.kubernetesConfig.enabled + } + + def 'Verify network config'() { + given: 'Synchronization config object and test configuration' + def objectUnderTest = new ForwardedSubscriptionEventCacheConfig() + def testConfig = new Config() + when: 'kubernetes properties are enabled' + objectUnderTest.cacheKubernetesEnabled = true + objectUnderTest.cacheKubernetesServiceName = 'test-service-name' + and: 'method called to update the discovery mode' + objectUnderTest.updateDiscoveryMode(testConfig) + then: 'applied properties are reflected' + assert testConfig.networkConfig.join.kubernetesConfig.enabled + assert testConfig.networkConfig.join.kubernetesConfig.properties.get('service-name') == 'test-service-name' + + } +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/event/avc/SubscriptionEventResponseConsumerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/event/avc/SubscriptionEventResponseConsumerSpec.groovy new file mode 100644 index 0000000000..a673462008 --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/event/avc/SubscriptionEventResponseConsumerSpec.groovy @@ -0,0 +1,79 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (c) 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.api.impl.event.avc + +import com.fasterxml.jackson.databind.ObjectMapper +import com.hazelcast.map.IMap +import org.onap.cps.ncmp.api.kafka.MessagingBaseSpec +import org.onap.cps.ncmp.api.models.SubscriptionEventResponse +import org.onap.cps.utils.JsonObjectMapper +import org.springframework.boot.test.context.SpringBootTest + +@SpringBootTest(classes = [ObjectMapper, JsonObjectMapper]) +class SubscriptionEventResponseConsumerSpec extends MessagingBaseSpec { + + IMap<String, Set<String>> mockForwardedSubscriptionEventCache = Mock(IMap<String, Set<String>>) + + def objectUnderTest = new SubscriptionEventResponseConsumer(mockForwardedSubscriptionEventCache) + + + def 'Consume Subscription Event Response where all DMIs have responded'() { + given: 'a subscription event response with a clientId, subscriptionName and dmiName' + def testEventReceived = new SubscriptionEventResponse() + testEventReceived.clientId = 'some-client-id' + testEventReceived.subscriptionName = 'some-subscription-name' + testEventReceived.dmiName = 'some-dmi-name' + and: 'notifications are enabled' + objectUnderTest.notificationFeatureEnabled = true + and: 'subscription model loader is enabled' + objectUnderTest.subscriptionModelLoaderEnabled = true + when: 'the valid event is consumed' + objectUnderTest.consumeSubscriptionEventResponse(testEventReceived) + then: 'the forwarded subscription event cache returns only the received dmiName existing for the subscription create event' + 1 * mockForwardedSubscriptionEventCache.containsKey('some-client-idsome-subscription-name') >> true + 1 * mockForwardedSubscriptionEventCache.get('some-client-idsome-subscription-name') >> (['some-dmi-name'] as Set) + and: 'the forwarded subscription event cache returns an empty Map when the dmiName has been removed' + 1 * mockForwardedSubscriptionEventCache.get('some-client-idsome-subscription-name') >> ([] as Set) + and: 'the subscription event is removed from the map' + 1 * mockForwardedSubscriptionEventCache.remove('some-client-idsome-subscription-name') + } + + def 'Consume Subscription Event Response where another DMI has not yet responded'() { + given: 'a subscription event response with a clientId, subscriptionName and dmiName' + def testEventReceived = new SubscriptionEventResponse() + testEventReceived.clientId = 'some-client-id' + testEventReceived.subscriptionName = 'some-subscription-name' + testEventReceived.dmiName = 'some-dmi-name' + and: 'notifications are enabled' + objectUnderTest.notificationFeatureEnabled = true + and: 'subscription model loader is enabled' + objectUnderTest.subscriptionModelLoaderEnabled = true + when: 'the valid event is consumed' + objectUnderTest.consumeSubscriptionEventResponse(testEventReceived) + then: 'the forwarded subscription event cache returns only the received dmiName existing for the subscription create event' + 1 * mockForwardedSubscriptionEventCache.containsKey('some-client-idsome-subscription-name') >> true + 1 * mockForwardedSubscriptionEventCache.get('some-client-idsome-subscription-name') >> (['some-dmi-name', 'non-responded-dmi'] as Set) + and: 'the forwarded subscription event cache returns an empty Map when the dmiName has been removed' + 1 * mockForwardedSubscriptionEventCache.get('some-client-idsome-subscription-name') >> (['non-responded-dmi'] as Set) + and: 'the subscription event is not removed from the map' + 0 * mockForwardedSubscriptionEventCache.remove(_) + } +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/avc/SubscriptionEventMapperSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/avc/SubscriptionEventMapperSpec.groovy index 3a7aa481c3..f2ff1f7b23 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/avc/SubscriptionEventMapperSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/avc/SubscriptionEventMapperSpec.groovy @@ -48,7 +48,7 @@ class SubscriptionEventMapperSpec extends Specification { def result = objectUnderTest.toYangModelSubscriptionEvent(testEventToMap) then: 'the resulting yang model subscription event contains the correct clientId' assert result.clientId == "SCO-9989752" - and: 'client name' + and: 'subscription name' assert result.subscriptionName == "cm-subscription-001" and: 'is tagged value is false' assert !result.isTagged @@ -60,4 +60,21 @@ class SubscriptionEventMapperSpec extends Specification { assert result.topic == null } + def 'Map null subscription event to yang model subscription event where #scenario'() { + given: 'a new Subscription Event with no data' + def testEventToMap = new SubscriptionEvent() + when: 'the event is mapped to a yang model subscription' + def result = objectUnderTest.toYangModelSubscriptionEvent(testEventToMap) + then: 'the resulting yang model subscription event contains null clientId' + assert result.clientId == null + and: 'subscription name is null' + assert result.subscriptionName == null + and: 'is tagged value is false' + assert result.isTagged == false + and: 'predicates is null' + assert result.predicates == null + and: 'the topic is null' + assert result.topic == null + } + }
\ No newline at end of file diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/avcsubscription/SubscriptionEventForwarderSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/avcsubscription/SubscriptionEventForwarderSpec.groovy index 2b0adf3420..457eb6fa99 100644 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/avcsubscription/SubscriptionEventForwarderSpec.groovy +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/avcsubscription/SubscriptionEventForwarderSpec.groovy @@ -21,6 +21,7 @@ package org.onap.cps.ncmp.api.impl.events.avcsubscription import com.fasterxml.jackson.databind.ObjectMapper +import com.hazelcast.map.IMap import org.onap.cps.ncmp.api.impl.events.EventsPublisher import org.onap.cps.ncmp.api.impl.yangmodels.YangModelCmHandle import org.onap.cps.ncmp.api.inventory.InventoryPersistence @@ -29,20 +30,28 @@ import org.onap.cps.ncmp.event.model.SubscriptionEvent import org.onap.cps.ncmp.utils.TestUtils import org.onap.cps.spi.exceptions.OperationNotYetSupportedException import org.onap.cps.utils.JsonObjectMapper +import org.spockframework.spring.SpringBean import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest +import spock.util.concurrent.BlockingVariable -@SpringBootTest(classes = [ObjectMapper, JsonObjectMapper]) +@SpringBootTest(classes = [ObjectMapper, JsonObjectMapper, SubscriptionEventForwarder]) class SubscriptionEventForwarderSpec extends MessagingBaseSpec { - def mockInventoryPersistence = Mock(InventoryPersistence) - def mockSubscriptionEventPublisher = Mock(EventsPublisher<SubscriptionEvent>) - def objectUnderTest = new SubscriptionEventForwarder(mockInventoryPersistence, mockSubscriptionEventPublisher) + @Autowired + SubscriptionEventForwarder objectUnderTest + + @SpringBean + InventoryPersistence mockInventoryPersistence = Mock(InventoryPersistence) + @SpringBean + EventsPublisher<SubscriptionEvent> mockSubscriptionEventPublisher = Mock(EventsPublisher<SubscriptionEvent>) + @SpringBean + IMap<String, Set<String>> mockForwardedSubscriptionEventCache = Mock(IMap<String, Set<String>>) @Autowired JsonObjectMapper jsonObjectMapper - def 'Forward valid CM create subscription'() { + def 'Forward valid CM create subscription and simulate timeout where #scenario'() { given: 'an event' def jsonData = TestUtils.getResourceFileContent('avcSubscriptionCreationEvent.json') def testEventSent = jsonObjectMapper.convertJsonString(jsonData, SubscriptionEvent.class) @@ -52,9 +61,17 @@ class SubscriptionEventForwarderSpec extends MessagingBaseSpec { createYangModelCmHandleWithDmiProperty(2, 1,"shape","square"), createYangModelCmHandleWithDmiProperty(3, 2,"shape","triangle") ] + and: 'the thread creation delay is reduced to 2 seconds for testing' + objectUnderTest.dmiResponseTimeoutInMs = 2000 + and: 'a Blocking Variable is used for the Asynchronous call with a timeout of 5 seconds' + def block = new BlockingVariable<Object>(5) when: 'the valid event is forwarded' objectUnderTest.forwardCreateSubscriptionEvent(testEventSent) - then: 'the event is forwarded twice with the CMHandle private properties and provides a valid listenable future' + then: 'An asynchronous call is made to the blocking variable' + block.get() + then: 'the event is added to the forwarded subscription event cache' + 1 * mockForwardedSubscriptionEventCache.put("SCO-9989752cm-subscription-001", ["DMIName1", "DMIName2"] as Set) + and: 'the event is forwarded twice with the CMHandle private properties and provides a valid listenable future' 1 * mockSubscriptionEventPublisher.publishEvent("ncmp-dmi-cm-avc-subscription-DMIName1", "SCO-9989752-cm-subscription-001-DMIName1", subscriptionEvent -> { Map targets = subscriptionEvent.getEvent().getPredicates().getTargets().get(0) @@ -68,6 +85,15 @@ class SubscriptionEventForwarderSpec extends MessagingBaseSpec { targets["CMHandle3"] == ["shape":"triangle"] } ) + and: 'a separate thread has been created where the map is polled' + 1 * mockForwardedSubscriptionEventCache.containsKey("SCO-9989752cm-subscription-001") >> true + 1 * mockForwardedSubscriptionEventCache.get(_) >> (DMINamesInMap) + and: 'the subscription id is removed from the event cache map returning the asynchronous blocking variable' + 1 * mockForwardedSubscriptionEventCache.remove("SCO-9989752cm-subscription-001") >> {block.set(_)} + where: + scenario | DMINamesInMap + 'there are dmis which have not responded' | ["DMIName1", "DMIName2"] as Set + 'all dmis have responded ' | [] as Set } def 'Forward CM create subscription where target CM Handles are #scenario'() { |