From bac230bdbe224023d424eda2cd2ef7422f5c3ed6 Mon Sep 17 00:00:00 2001 From: mpriyank Date: Mon, 1 Jul 2024 12:32:27 +0100 Subject: refactor cmsubscription code - Moved cmsubscription to the impl package instead of api. - Below packages are renamed or newly created for code and tests - moved ..api.cmsubsription to ..impl.cmsubscription - renamed ..api.cmsubscription.mappers to ..impl.cmsubscription.ncmp or dmi as per the functionality - renamed ..api.cmsubscription.model to ..impl.cmsubscription.models - removed ..api.cmsubscription.producer/consumer and classes moved to ..impl.cmsubscription.ncmp/dmi - new package ..impl.cmsubscription.cache to hold cache config and cache operations - new package ..impl.cmsubscription.avc to hold the CmAvcEventConsumer for the notifications emitted as a result of the subscriptions - Removed the prefix CmNotificationSubscription for majority of the classes , not removed for Config and model classes - Renamed the schemas and package structure as per the code refactoring Note: *EventsFacade and *MappersFacade to be removed in the next patchset Issue-ID: CPS-2298 Change-Id: I1d788ab745d65965570e28beaefa511cbe4a8547 Signed-off-by: mpriyank --- ...etryCmNotificationSubscriptionConfigSpec.groovy | 81 ++++++++ .../api/impl/config/OpenTelemetryConfigSpec.groovy | 81 -------- ...mNotificationSubscriptionCacheConfigSpec.groovy | 63 ------ .../impl/events/avc/AvcEventConsumerSpec.groovy | 95 ---------- .../CmNotificationSubscriptionDeltaSpec.groovy | 60 ------ ...cationSubscriptionDmiInEventProducerSpec.groovy | 65 ------- ...ationSubscriptionDmiOutEventConsumerSpec.groovy | 119 ------------ ...otificationSubscriptionEventsHandlerSpec.groovy | 58 ------ ...tificationSubscriptionMappersHandlerSpec.groovy | 64 ------- ...ationSubscriptionNcmpInEventConsumerSpec.groovy | 113 ----------- ...tionSubscriptionNcmpOutEventProducerSpec.groovy | 88 --------- ...NotificationSubscriptionCacheHandlerSpec.groovy | 211 --------------------- ...ficationSubscriptionDmiInEventMapperSpec.groovy | 57 ------ ...cationSubscriptionNcmpOutEventMapperSpec.groovy | 64 ------- ...cationSubscriptionHandlerServiceImplSpec.groovy | 147 -------------- ...onSubscriptionPersistenceServiceImplSpec.groovy | 192 ------------------- .../EventsFacadeSpec.groovy | 58 ++++++ .../MappersFacadeSpec.groovy | 65 +++++++ .../cache/CmSubscriptionConfigSpec.groovy | 63 ++++++ .../cache/DmiCacheHandlerSpec.groovy | 211 +++++++++++++++++++++ .../cmavc/CmAvcEventConsumerSpec.groovy | 95 ++++++++++ .../dmi/DmiInEventMapperSpec.groovy | 57 ++++++ .../dmi/DmiInEventProducerSpec.groovy | 64 +++++++ .../dmi/DmiOutEventConsumerSpec.groovy | 123 ++++++++++++ .../ncmp/CmSubscriptionComparatorSpec.groovy | 62 ++++++ .../ncmp/CmSubscriptionHandlerImplSpec.groovy | 145 ++++++++++++++ .../ncmp/NcmpInEventConsumerSpec.groovy | 111 +++++++++++ .../ncmp/NcmpOutEventMapperSpec.groovy | 69 +++++++ .../ncmp/NcmpOutEventProducerSpec.groovy | 89 +++++++++ .../CmSubscriptionPersistenceServiceSpec.groovy | 191 +++++++++++++++++++ 30 files changed, 1484 insertions(+), 1477 deletions(-) create mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/OpenTelemetryCmNotificationSubscriptionConfigSpec.groovy delete mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/OpenTelemetryConfigSpec.groovy delete mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/CmNotificationSubscriptionCacheConfigSpec.groovy delete mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/avc/AvcEventConsumerSpec.groovy delete mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDeltaSpec.groovy delete mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDmiInEventProducerSpec.groovy delete mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDmiOutEventConsumerSpec.groovy delete mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionEventsHandlerSpec.groovy delete mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionMappersHandlerSpec.groovy delete mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpInEventConsumerSpec.groovy delete mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpOutEventProducerSpec.groovy delete mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/DmiCmNotificationSubscriptionCacheHandlerSpec.groovy delete mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/mapper/CmNotificationSubscriptionDmiInEventMapperSpec.groovy delete mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/mapper/CmNotificationSubscriptionNcmpOutEventMapperSpec.groovy delete mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerServiceImplSpec.groovy delete mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImplSpec.groovy create mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/EventsFacadeSpec.groovy create mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/MappersFacadeSpec.groovy create mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/CmSubscriptionConfigSpec.groovy create mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/DmiCacheHandlerSpec.groovy create mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/cmavc/CmAvcEventConsumerSpec.groovy create mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiInEventMapperSpec.groovy create mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiInEventProducerSpec.groovy create mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiOutEventConsumerSpec.groovy create mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/CmSubscriptionComparatorSpec.groovy create mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/CmSubscriptionHandlerImplSpec.groovy create mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpInEventConsumerSpec.groovy create mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventMapperSpec.groovy create mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventProducerSpec.groovy create mode 100644 cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/utils/CmSubscriptionPersistenceServiceSpec.groovy (limited to 'cps-ncmp-service/src/test') diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/OpenTelemetryCmNotificationSubscriptionConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/OpenTelemetryCmNotificationSubscriptionConfigSpec.groovy new file mode 100644 index 0000000000..07395cf5bc --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/OpenTelemetryCmNotificationSubscriptionConfigSpec.groovy @@ -0,0 +1,81 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 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.config + +import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter +import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter +import io.opentelemetry.sdk.extension.trace.jaeger.sampler.JaegerRemoteSampler +import org.spockframework.spring.SpringBean +import org.springframework.boot.actuate.autoconfigure.observation.ObservationRegistryCustomizer +import spock.lang.Shared +import spock.lang.Specification + +class OpenTelemetryConfigSpec extends Specification{ + + @Shared + @SpringBean + OpenTelemetryConfig openTelemetryConfig = new OpenTelemetryConfig() + + def setupSpec() { + openTelemetryConfig.tracingExporterEndpointUrl="http://tracingExporterEndpointUrl" + openTelemetryConfig.jaegerRemoteSamplerUrl="http://jaegerremotesamplerurl" + openTelemetryConfig.serviceId ="cps-application" + } + + def 'OpenTelemetryConfig Construction.'() { + expect: 'the system can create an instance' + new OpenTelemetryConfig() != null + } + + def 'OTLP Exporter creation with Grpc protocol'(){ + when: 'an OTLP exporter is created' + def result = openTelemetryConfig.createOtlpExporterGrpc() + then: 'an OTLP Exporter is created' + assert result instanceof OtlpGrpcSpanExporter + } + + def 'OTLP Exporter creation with HTTP protocol'(){ + when: 'an OTLP exporter is created' + def result = openTelemetryConfig.createOtlpExporterHttp() + then: 'an OTLP Exporter is created' + assert result instanceof OtlpHttpSpanExporter + and: + assert result.builder.endpoint=="http://tracingExporterEndpointUrl" + } + + def 'Jaeger Remote Sampler Creation'(){ + when: 'an OTLP exporter is created' + def result = openTelemetryConfig.createJaegerRemoteSampler() + then: 'an OTLP Exporter is created' + assert result instanceof JaegerRemoteSampler + and: + assert result.delegate.type=="remoteSampling" + and: + assert result.delegate.url.toString().startsWith("http://jaegerremotesamplerurl") + } + + def 'Skipping Acutator endpoints'(){ + when: 'an OTLP exporter is created' + def result = openTelemetryConfig.skipActuatorEndpointsFromObservation() + then: 'an OTLP Exporter is created' + assert result instanceof ObservationRegistryCustomizer + } +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/OpenTelemetryConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/OpenTelemetryConfigSpec.groovy deleted file mode 100644 index 07395cf5bc..0000000000 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/OpenTelemetryConfigSpec.groovy +++ /dev/null @@ -1,81 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2024 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.config - -import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter -import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter -import io.opentelemetry.sdk.extension.trace.jaeger.sampler.JaegerRemoteSampler -import org.spockframework.spring.SpringBean -import org.springframework.boot.actuate.autoconfigure.observation.ObservationRegistryCustomizer -import spock.lang.Shared -import spock.lang.Specification - -class OpenTelemetryConfigSpec extends Specification{ - - @Shared - @SpringBean - OpenTelemetryConfig openTelemetryConfig = new OpenTelemetryConfig() - - def setupSpec() { - openTelemetryConfig.tracingExporterEndpointUrl="http://tracingExporterEndpointUrl" - openTelemetryConfig.jaegerRemoteSamplerUrl="http://jaegerremotesamplerurl" - openTelemetryConfig.serviceId ="cps-application" - } - - def 'OpenTelemetryConfig Construction.'() { - expect: 'the system can create an instance' - new OpenTelemetryConfig() != null - } - - def 'OTLP Exporter creation with Grpc protocol'(){ - when: 'an OTLP exporter is created' - def result = openTelemetryConfig.createOtlpExporterGrpc() - then: 'an OTLP Exporter is created' - assert result instanceof OtlpGrpcSpanExporter - } - - def 'OTLP Exporter creation with HTTP protocol'(){ - when: 'an OTLP exporter is created' - def result = openTelemetryConfig.createOtlpExporterHttp() - then: 'an OTLP Exporter is created' - assert result instanceof OtlpHttpSpanExporter - and: - assert result.builder.endpoint=="http://tracingExporterEndpointUrl" - } - - def 'Jaeger Remote Sampler Creation'(){ - when: 'an OTLP exporter is created' - def result = openTelemetryConfig.createJaegerRemoteSampler() - then: 'an OTLP Exporter is created' - assert result instanceof JaegerRemoteSampler - and: - assert result.delegate.type=="remoteSampling" - and: - assert result.delegate.url.toString().startsWith("http://jaegerremotesamplerurl") - } - - def 'Skipping Acutator endpoints'(){ - when: 'an OTLP exporter is created' - def result = openTelemetryConfig.skipActuatorEndpointsFromObservation() - then: 'an OTLP Exporter is created' - assert result instanceof ObservationRegistryCustomizer - } -} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/CmNotificationSubscriptionCacheConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/CmNotificationSubscriptionCacheConfigSpec.groovy deleted file mode 100644 index adb1dfda24..0000000000 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/config/embeddedcache/CmNotificationSubscriptionCacheConfigSpec.groovy +++ /dev/null @@ -1,63 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2024 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.config.embeddedcache - -import com.hazelcast.core.Hazelcast -import com.hazelcast.map.IMap -import org.onap.cps.ncmp.api.data.models.DatastoreType -import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.CmNotificationSubscriptionStatus -import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.DmiCmNotificationSubscriptionDetails -import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.DmiCmNotificationSubscriptionPredicate -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.context.SpringBootTest -import spock.lang.Specification - -@SpringBootTest(classes = [CmNotificationSubscriptionCacheConfig]) -class CmNotificationSubscriptionCacheConfigSpec extends Specification { - - @Autowired - IMap> cmNotificationSubscriptionCache; - - def 'Embedded (hazelcast) cache for Cm Notification Subscription Cache.'() { - expect: 'system is able to create an instance of the Cm Notification Subscription Cache' - assert null != cmNotificationSubscriptionCache - and: 'there is at least 1 instance' - assert Hazelcast.allHazelcastInstances.size() > 0 - and: 'Cm Notification Subscription Cache is present' - assert Hazelcast.allHazelcastInstances.name.contains('hazelCastInstanceCmNotificationSubscription') - } - - def 'Provided CM Subscription data'() { - given: 'a cm subscription properties' - def subscriptionId = 'sub123' - def dmiPluginName = 'dummydmi' - def cmSubscriptionPredicate = new DmiCmNotificationSubscriptionPredicate(['cmhandle1', 'cmhandle2'].toSet(), DatastoreType.PASSTHROUGH_RUNNING, ['/a/b/c'].toSet()) - def cmSubscriptionCacheObject = new DmiCmNotificationSubscriptionDetails([cmSubscriptionPredicate], CmNotificationSubscriptionStatus.PENDING) - when: 'the cache is populated' - cmNotificationSubscriptionCache.put(subscriptionId, [(dmiPluginName): cmSubscriptionCacheObject]) - then: 'the values are present in memory' - assert cmNotificationSubscriptionCache.get(subscriptionId) != null - and: 'properties match' - assert dmiPluginName == cmNotificationSubscriptionCache.get(subscriptionId).keySet()[0] - assert cmSubscriptionCacheObject.cmNotificationSubscriptionStatus == cmNotificationSubscriptionCache.get(subscriptionId).values().cmNotificationSubscriptionStatus[0] - assert cmSubscriptionCacheObject.dmiCmNotificationSubscriptionPredicates[0].targetCmHandleIds == cmNotificationSubscriptionCache.get(subscriptionId).values().dmiCmNotificationSubscriptionPredicates[0].targetCmHandleIds[0] - } -} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/avc/AvcEventConsumerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/avc/AvcEventConsumerSpec.groovy deleted file mode 100644 index 0f5d4fe5af..0000000000 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/avc/AvcEventConsumerSpec.groovy +++ /dev/null @@ -1,95 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (c) 2023-2024 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.events.avc - -import com.fasterxml.jackson.databind.ObjectMapper -import io.cloudevents.CloudEvent -import io.cloudevents.core.builder.CloudEventBuilder -import io.cloudevents.kafka.CloudEventDeserializer -import io.cloudevents.kafka.impl.KafkaHeaders -import org.apache.kafka.clients.consumer.ConsumerRecord -import org.apache.kafka.clients.consumer.KafkaConsumer -import org.onap.cps.events.EventsPublisher -import org.onap.cps.ncmp.api.kafka.MessagingBaseSpec -import org.onap.cps.ncmp.events.avc1_0_0.AvcEvent -import org.onap.cps.ncmp.utils.TestUtils -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 org.springframework.test.annotation.DirtiesContext -import org.testcontainers.spock.Testcontainers - -import java.time.Duration - -import static org.onap.cps.ncmp.utils.events.CloudEventMapper.toTargetEvent - -@SpringBootTest(classes = [EventsPublisher, AvcEventConsumer, ObjectMapper, JsonObjectMapper]) -@Testcontainers -@DirtiesContext -class AvcEventConsumerSpec extends MessagingBaseSpec { - - @SpringBean - EventsPublisher eventsPublisher = new EventsPublisher(legacyEventKafkaTemplate, cloudEventKafkaTemplate) - - @SpringBean - AvcEventConsumer acvEventConsumer = new AvcEventConsumer(eventsPublisher) - - @Autowired - JsonObjectMapper jsonObjectMapper - - def cloudEventKafkaConsumer = new KafkaConsumer<>(eventConsumerConfigProperties('ncmp-group', CloudEventDeserializer)) - - def 'Consume and forward valid message'() { - given: 'consumer has a subscription on a topic' - def cmEventsTopicName = 'cm-events' - acvEventConsumer.cmEventsTopicName = cmEventsTopicName - cloudEventKafkaConsumer.subscribe([cmEventsTopicName] as List) - and: 'an event is sent' - def jsonData = TestUtils.getResourceFileContent('sampleAvcInputEvent.json') - def testEventSent = jsonObjectMapper.convertJsonString(jsonData, AvcEvent.class) - def testCloudEventSent = CloudEventBuilder.v1() - .withData(jsonObjectMapper.asJsonBytes(testEventSent)) - .withId('sample-eventid') - .withType('sample-test-type') - .withSource(URI.create('sample-test-source')) - .withExtension('correlationid', 'test-cmhandle1').build() - and: 'event has header information' - def consumerRecord = new ConsumerRecord(cmEventsTopicName, 0, 0, 'sample-eventid', testCloudEventSent) - when: 'the event is consumed' - acvEventConsumer.consumeAndForward(consumerRecord) - and: 'the topic is polled' - def records = cloudEventKafkaConsumer.poll(Duration.ofMillis(1500)) - then: 'poll returns one record' - assert records.size() == 1 - and: 'record can be converted to AVC event' - def record = records.iterator().next() - def cloudEvent = record.value() as CloudEvent - def convertedAvcEvent = toTargetEvent(cloudEvent, AvcEvent.class) - and: 'we have correct headers forwarded where correlation id matches' - assert KafkaHeaders.getParsedKafkaHeader(record.headers(), 'ce_correlationid') == 'test-cmhandle1' - and: 'event id differs(as per requirement) between consumed and forwarded' - assert KafkaHeaders.getParsedKafkaHeader(record.headers(), 'ce_id') != 'sample-eventid' - and: 'the event payload still matches' - assert testEventSent == convertedAvcEvent - } - -} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDeltaSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDeltaSpec.groovy deleted file mode 100644 index 89ccc7e7dc..0000000000 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDeltaSpec.groovy +++ /dev/null @@ -1,60 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (c) 2024 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.events.cmsubscription - -import org.onap.cps.ncmp.api.data.models.DatastoreType -import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.DmiCmNotificationSubscriptionPredicate -import org.onap.cps.ncmp.api.impl.events.cmsubscription.service.CmNotificationSubscriptionPersistenceService -import spock.lang.Specification - -class CmNotificationSubscriptionDeltaSpec extends Specification { - - def mockCmNotificationSubscriptionPersistenceService = Mock(CmNotificationSubscriptionPersistenceService) - def objectUnderTest = new CmNotificationSubscriptionDelta(mockCmNotificationSubscriptionPersistenceService) - - def 'Find Delta of given list of predicates'() { - given: 'A list of predicates' - def predicateList = [new DmiCmNotificationSubscriptionPredicate(['ch-1','ch-2'].toSet(), DatastoreType.PASSTHROUGH_OPERATIONAL, ['a/1/','b/2'].toSet())] - and: '3 positive responses and 1 negative.' - mockCmNotificationSubscriptionPersistenceService.isOngoingCmNotificationSubscription(DatastoreType.PASSTHROUGH_OPERATIONAL, 'ch-1', 'a/1/') >>> true - mockCmNotificationSubscriptionPersistenceService.isOngoingCmNotificationSubscription(DatastoreType.PASSTHROUGH_OPERATIONAL, 'ch-1', 'b/2') >>> true - mockCmNotificationSubscriptionPersistenceService.isOngoingCmNotificationSubscription(DatastoreType.PASSTHROUGH_OPERATIONAL, 'ch-2', 'a/1/') >>> true - mockCmNotificationSubscriptionPersistenceService.isOngoingCmNotificationSubscription(DatastoreType.PASSTHROUGH_OPERATIONAL, 'ch-2', 'b/2') >>> false - when: 'getDelta is called' - def result = objectUnderTest.getDelta(predicateList) - then: 'verify correct delta is returned' - assert result.size() == 1 - assert result[0].targetCmHandleIds[0] == 'ch-2' - assert result[0].xpaths[0] == 'b/2' - - } - - def 'Find Delta of given list of predicates when it is an ongoing Cm Subscription'() { - given: 'A list of predicates' - def predicateList = [new DmiCmNotificationSubscriptionPredicate(['ch-1'].toSet(), DatastoreType.PASSTHROUGH_OPERATIONAL, ['a/1/'].toSet())] - and: 'its already present' - mockCmNotificationSubscriptionPersistenceService.isOngoingCmNotificationSubscription(DatastoreType.PASSTHROUGH_OPERATIONAL, 'ch-1', 'a/1/') >>> true - when: 'getDelta is called' - def result = objectUnderTest.getDelta(predicateList) - then: 'verify correct delta is returned' - assert result.size() == 0 - } - -} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDmiInEventProducerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDmiInEventProducerSpec.groovy deleted file mode 100644 index 253763b13b..0000000000 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDmiInEventProducerSpec.groovy +++ /dev/null @@ -1,65 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (c) 2024 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.events.cmsubscription - -import com.fasterxml.jackson.databind.ObjectMapper -import io.cloudevents.CloudEvent -import org.onap.cps.events.EventsPublisher -import org.onap.cps.ncmp.api.impl.events.cmsubscription.producer.CmNotificationSubscriptionDmiInEventProducer -import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.CmHandle -import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.CmNotificationSubscriptionDmiInEvent -import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.Data -import org.onap.cps.ncmp.utils.events.CloudEventMapper -import org.onap.cps.utils.JsonObjectMapper -import spock.lang.Specification - -class CmNotificationSubscriptionDmiInEventProducerSpec extends Specification { - - def mockEventsPublisher = Mock(EventsPublisher) - def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) - - def objectUnderTest = new CmNotificationSubscriptionDmiInEventProducer(mockEventsPublisher, jsonObjectMapper) - - def 'Create and Publish Cm Notification Subscription DMI In Event'() { - given: 'a cm subscription for a dmi plugin' - def subscriptionId = 'test-subscription-id' - def dmiPluginName = 'test-dmiplugin' - def eventType = 'subscriptionCreateRequest' - def cmNotificationSubscriptionDmiInEvent = new CmNotificationSubscriptionDmiInEvent(data: new Data(cmHandles: [new CmHandle(cmhandleId: 'test-1', privateProperties: [:])])) - and: 'also we have target topic for dmiPlugin' - objectUnderTest.cmNotificationSubscriptionDmiInEventTopic = 'dmiplugin-test-topic' - when: 'the event is published' - objectUnderTest.publishCmNotificationSubscriptionDmiInEvent(subscriptionId, dmiPluginName, eventType, cmNotificationSubscriptionDmiInEvent) - then: 'the event contains the required attributes' - 1 * mockEventsPublisher.publishCloudEvent(_, _, _) >> { - args -> - { - assert args[0] == 'dmiplugin-test-topic' - assert args[1] == subscriptionId - def cmNotificationSubscriptionDmiInEventAsCloudEvent = (args[2] as CloudEvent) - assert cmNotificationSubscriptionDmiInEventAsCloudEvent.getExtension('correlationid') == subscriptionId + '#' + dmiPluginName - assert cmNotificationSubscriptionDmiInEventAsCloudEvent.type == 'subscriptionCreateRequest' - assert cmNotificationSubscriptionDmiInEventAsCloudEvent.source.toString() == 'NCMP' - assert CloudEventMapper.toTargetEvent(cmNotificationSubscriptionDmiInEventAsCloudEvent, CmNotificationSubscriptionDmiInEvent) == cmNotificationSubscriptionDmiInEvent - } - } - } -} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDmiOutEventConsumerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDmiOutEventConsumerSpec.groovy deleted file mode 100644 index 9b0a48d93e..0000000000 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionDmiOutEventConsumerSpec.groovy +++ /dev/null @@ -1,119 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (c) 2024 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.events.cmsubscription - -import ch.qos.logback.classic.Level -import ch.qos.logback.classic.Logger -import ch.qos.logback.classic.spi.ILoggingEvent -import ch.qos.logback.core.read.ListAppender -import com.fasterxml.jackson.databind.ObjectMapper -import io.cloudevents.CloudEvent -import io.cloudevents.core.builder.CloudEventBuilder -import org.apache.kafka.clients.consumer.ConsumerRecord -import org.onap.cps.ncmp.api.impl.events.cmsubscription.consumer.CmNotificationSubscriptionDmiOutEventConsumer -import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.CmNotificationSubscriptionStatus -import org.onap.cps.ncmp.api.kafka.MessagingBaseSpec -import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.dmi_to_ncmp.CmNotificationSubscriptionDmiOutEvent -import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.dmi_to_ncmp.Data -import org.onap.cps.ncmp.utils.TestUtils -import org.onap.cps.utils.JsonObjectMapper -import org.slf4j.LoggerFactory -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.context.SpringBootTest - -@SpringBootTest(classes = [ObjectMapper, JsonObjectMapper]) -class CmNotificationSubscriptionDmiOutEventConsumerSpec extends MessagingBaseSpec { - - @Autowired - JsonObjectMapper jsonObjectMapper - - @Autowired - ObjectMapper objectMapper - - def mockDmiCmNotificationSubscriptionCacheHandler = Mock(DmiCmNotificationSubscriptionCacheHandler) - def mockCmNotificationSubscriptionEventsHandler = Mock(CmNotificationSubscriptionEventsHandler) - def mockCmNotificationSubscriptionMappersHandler = Mock(CmNotificationSubscriptionMappersHandler) - - def objectUnderTest = new CmNotificationSubscriptionDmiOutEventConsumer(mockDmiCmNotificationSubscriptionCacheHandler, mockCmNotificationSubscriptionEventsHandler, mockCmNotificationSubscriptionMappersHandler) - def logger = Spy(ListAppender) - - void setup() { - ((Logger) LoggerFactory.getLogger(CmNotificationSubscriptionDmiOutEventConsumer.class)).addAppender(logger) - logger.start() - } - - void cleanup() { - ((Logger) LoggerFactory.getLogger(CmNotificationSubscriptionDmiOutEventConsumer.class)).detachAndStopAllAppenders() - } - - - def 'Consume valid CM Subscription response from DMI Plugin'() { - given: 'a cmsubscription event' - def jsonData = TestUtils.getResourceFileContent('cmSubscription/cmNotificationSubscriptionDmiOutEvent.json') - def testEventSent = jsonObjectMapper.convertJsonString(jsonData, CmNotificationSubscriptionDmiOutEvent.class) - def testCloudEventSent = CloudEventBuilder.v1() - .withData(objectMapper.writeValueAsBytes(testEventSent)) - .withId('random-uuid') - .withType('subscriptionCreateResponse') - .withSource(URI.create('test-dmi-plugin-name')) - .withExtension('correlationid', 'sub-1#test-dmi-plugin-name').build() - def consumerRecord = new ConsumerRecord('topic-name', 0, 0, 'event-key', testCloudEventSent) - when: 'the valid event is consumed' - objectUnderTest.consumeCmNotificationSubscriptionDmiOutEvent(consumerRecord) - then: 'an event is logged with level INFO' - def loggingEvent = getLoggingEvent() - assert loggingEvent.level == Level.INFO - and: 'the log indicates the task completed successfully' - assert loggingEvent.formattedMessage == 'Cm Subscription with id : sub-1 handled by the dmi-plugin : test-dmi-plugin-name has the status : accepted' - } - - def 'Consume a valid CM Notification Subscription Event and perform correct actions base on status'() { - given: 'a cmNotificationSubscription event' - def dmiOutEventData = new Data(statusCode: statusCode, statusMessage: subscriptionStatus.toString()) - def cmNotificationSubscriptionDmiOutEvent = new CmNotificationSubscriptionDmiOutEvent().withData(dmiOutEventData) - def testCloudEventSent = CloudEventBuilder.v1() - .withData(objectMapper.writeValueAsBytes(cmNotificationSubscriptionDmiOutEvent)) - .withId('random-uuid') - .withType('subscriptionCreateResponse') - .withSource(URI.create('test-dmi-plugin-name')) - .withExtension('correlationid', 'sub-1#test-dmi-plugin-name').build() - def consumerRecord = new ConsumerRecord('topic-name', 0, 0, 'event-key', testCloudEventSent) - when: 'the event is consumed' - objectUnderTest.consumeCmNotificationSubscriptionDmiOutEvent(consumerRecord) - then: 'correct number of calls to cache' - expectedCacheCalls * mockDmiCmNotificationSubscriptionCacheHandler.updateDmiCmNotificationSubscriptionStatusPerDmi('sub-1','test-dmi-plugin-name', subscriptionStatus) - and: 'correct number of calls to persist cache' - expectedPersistenceCalls * mockDmiCmNotificationSubscriptionCacheHandler.persistIntoDatabasePerDmi('sub-1','test-dmi-plugin-name') - and: 'correct number of calls to map the ncmp out event' - 1 * mockCmNotificationSubscriptionMappersHandler.toCmNotificationSubscriptionNcmpOutEvent('sub-1', _) - and: 'correct number of calls to publish the ncmp out event to client' - 1 * mockCmNotificationSubscriptionEventsHandler.publishCmNotificationSubscriptionNcmpOutEvent('sub-1', 'subscriptionCreateResponse', _, false) - where: 'the following parameters are used' - scenario | subscriptionStatus | statusCode || expectedCacheCalls | expectedPersistenceCalls - 'Accepted Status' | CmNotificationSubscriptionStatus.ACCEPTED | '1' || 1 | 1 - 'Rejected Status' | CmNotificationSubscriptionStatus.REJECTED | '104' || 1 | 0 - } - - def getLoggingEvent() { - return logger.list[0] - } - -} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionEventsHandlerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionEventsHandlerSpec.groovy deleted file mode 100644 index 788a7a7da5..0000000000 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionEventsHandlerSpec.groovy +++ /dev/null @@ -1,58 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (c) 2024 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.events.cmsubscription - -import org.onap.cps.ncmp.api.impl.events.cmsubscription.producer.CmNotificationSubscriptionNcmpOutEventProducer -import org.onap.cps.ncmp.api.impl.events.cmsubscription.producer.CmNotificationSubscriptionDmiInEventProducer -import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.CmNotificationSubscriptionDmiInEvent -import org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.ncmp_to_client.CmNotificationSubscriptionNcmpOutEvent -import spock.lang.Specification - -class CmNotificationSubscriptionEventsHandlerSpec extends Specification { - - def mockCmNotificationSubscriptionNcmpOutEventProducer = Mock(CmNotificationSubscriptionNcmpOutEventProducer) - def mockCmNotificationSubscriptionDmiInEventProducer = Mock(CmNotificationSubscriptionDmiInEventProducer) - - def objectUnderTest = new CmNotificationSubscriptionEventsHandler(mockCmNotificationSubscriptionNcmpOutEventProducer, - mockCmNotificationSubscriptionDmiInEventProducer) - - def 'Publish cm notification subscription ncmp out event'() { - given: 'an ncmp out event' - def cmNotificationSubscriptionNcmpOutEvent = new CmNotificationSubscriptionNcmpOutEvent() - when: 'the method to publish cm notification subscription ncmp out event is called' - objectUnderTest.publishCmNotificationSubscriptionNcmpOutEvent("some-id", - "some-event", cmNotificationSubscriptionNcmpOutEvent, true) - then: 'the parameters is delegated to the correct method once' - 1 * mockCmNotificationSubscriptionNcmpOutEventProducer.publishCmNotificationSubscriptionNcmpOutEvent( - "some-id", "some-event", cmNotificationSubscriptionNcmpOutEvent, true) - } - - def 'Publish cm notification subscription dmi in event'() { - given: 'a dmi in event' - def cmNotificationSubscriptionDmiInEvent = new CmNotificationSubscriptionDmiInEvent() - when: 'the method to publish cm notification subscription ncmp out event is called' - objectUnderTest.publishCmNotificationSubscriptionDmiInEvent("some-id", - "some-dmi", "some-event", cmNotificationSubscriptionDmiInEvent) - then: 'the parameters is delegated to the correct method once' - 1 * mockCmNotificationSubscriptionDmiInEventProducer.publishCmNotificationSubscriptionDmiInEvent("some-id", - "some-dmi", "some-event", cmNotificationSubscriptionDmiInEvent) - } -} \ No newline at end of file diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionMappersHandlerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionMappersHandlerSpec.groovy deleted file mode 100644 index bdc54bd1ee..0000000000 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionMappersHandlerSpec.groovy +++ /dev/null @@ -1,64 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2024 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.events.cmsubscription - -import org.onap.cps.ncmp.api.impl.events.cmsubscription.mapper.CmNotificationSubscriptionDmiInEventMapper -import org.onap.cps.ncmp.api.impl.events.cmsubscription.mapper.CmNotificationSubscriptionNcmpOutEventMapper -import spock.lang.Specification - -class CmNotificationSubscriptionMappersHandlerSpec extends Specification{ - - def mockCmNotificationDmiInEventMapper = Mock(CmNotificationSubscriptionDmiInEventMapper) - def mockCmNotificationNcmpOutEventMapper = Mock(CmNotificationSubscriptionNcmpOutEventMapper) - - def objectUnderTest = new CmNotificationSubscriptionMappersHandler(mockCmNotificationDmiInEventMapper, - mockCmNotificationNcmpOutEventMapper) - - def 'Get cm notification subscription DMI in event'() { - given: 'a list of predicates' - def testListOfPredicates = [] - when: 'method to create a cm notification subscription dmi in event is called with predicates' - objectUnderTest.toCmNotificationSubscriptionDmiInEvent(testListOfPredicates) - then: 'the parameters is delegated to the correct dmi in event mapper method' - 1 * mockCmNotificationDmiInEventMapper.toCmNotificationSubscriptionDmiInEvent(testListOfPredicates) - } - - def 'Get cm notification subscription ncmp out event'() { - given: 'a subscription details map' - def testSubscriptionDetailsMap = [:] - when: 'method to create cm notification subscription ncmp out event is called with the following parameters' - objectUnderTest.toCmNotificationSubscriptionNcmpOutEvent("test-id", testSubscriptionDetailsMap) - then: 'the parameters is delegated to the correct ncmp out event mapper method' - 1 * mockCmNotificationNcmpOutEventMapper.toCmNotificationSubscriptionNcmpOutEvent("test-id", - testSubscriptionDetailsMap) - } - - def 'Get cm notification subscription ncmp out event for a rejected request'() { - given: 'a list of target filters' - def testRejectedTargetFilters = [] - when: 'method to create cm notification subscription ncmp out event is called with the following parameters' - objectUnderTest.toCmNotificationSubscriptionNcmpOutEventForRejectedRequest( - "test-id", testRejectedTargetFilters) - then: 'the parameters is delegated to the correct ncmp out event mapper method' - 1 * mockCmNotificationNcmpOutEventMapper.toCmNotificationSubscriptionNcmpOutEventForRejectedRequest( - "test-id", testRejectedTargetFilters) - } -} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpInEventConsumerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpInEventConsumerSpec.groovy deleted file mode 100644 index 01a92c02fa..0000000000 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpInEventConsumerSpec.groovy +++ /dev/null @@ -1,113 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (c) 2024 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.events.cmsubscription - -import ch.qos.logback.classic.Level -import ch.qos.logback.classic.Logger -import ch.qos.logback.classic.spi.ILoggingEvent -import ch.qos.logback.core.read.ListAppender -import com.fasterxml.jackson.databind.ObjectMapper -import io.cloudevents.CloudEvent -import io.cloudevents.core.builder.CloudEventBuilder -import org.apache.kafka.clients.consumer.ConsumerRecord -import org.onap.cps.ncmp.api.impl.events.cmsubscription.consumer.CmNotificationSubscriptionNcmpInEventConsumer -import org.onap.cps.ncmp.api.impl.events.cmsubscription.service.CmNotificationSubscriptionHandlerService -import org.onap.cps.ncmp.api.kafka.MessagingBaseSpec -import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.client_to_ncmp.CmNotificationSubscriptionNcmpInEvent -import org.onap.cps.ncmp.utils.TestUtils -import org.onap.cps.utils.JsonObjectMapper -import org.slf4j.LoggerFactory -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.context.SpringBootTest - -@SpringBootTest(classes = [ObjectMapper, JsonObjectMapper]) -class CmNotificationSubscriptionNcmpInEventConsumerSpec extends MessagingBaseSpec { - - def mockCmNotificationSubscriptionHandlerService = Mock(CmNotificationSubscriptionHandlerService) - def objectUnderTest = new CmNotificationSubscriptionNcmpInEventConsumer(mockCmNotificationSubscriptionHandlerService) - def logger = Spy(ListAppender) - - @Autowired - JsonObjectMapper jsonObjectMapper - - @Autowired - ObjectMapper objectMapper - - void setup() { - ((Logger) LoggerFactory.getLogger(CmNotificationSubscriptionNcmpInEventConsumer.class)).addAppender(logger) - logger.start() - } - - void cleanup() { - ((Logger) LoggerFactory.getLogger(CmNotificationSubscriptionNcmpInEventConsumer.class)).detachAndStopAllAppenders() - } - - - def 'Consume valid CmNotificationSubscriptionNcmpInEvent create message'() { - given: 'a cmNotificationSubscription event' - def jsonData = TestUtils.getResourceFileContent('cmSubscription/cmNotificationSubscriptionNcmpInEvent.json') - def testEventSent = jsonObjectMapper.convertJsonString(jsonData, CmNotificationSubscriptionNcmpInEvent.class) - def testCloudEventSent = CloudEventBuilder.v1() - .withData(objectMapper.writeValueAsBytes(testEventSent)) - .withId('subscriptionCreated') - .withType('subscriptionCreateRequest') - .withSource(URI.create('some-resource')) - .withExtension('correlationid', 'test-cmhandle1').build() - def consumerRecord = new ConsumerRecord('topic-name', 0, 0, 'event-key', testCloudEventSent) - when: 'the valid event is consumed' - objectUnderTest.consumeSubscriptionEvent(consumerRecord) - then: 'an event is logged with level INFO' - def loggingEvent = getLoggingEvent() - assert loggingEvent.level == Level.INFO - and: 'the log indicates the task completed successfully' - assert loggingEvent.formattedMessage == 'Subscription create request for source some-resource with subscription id test-id ...' - and: 'the subscription handler service is called once' - 1 * mockCmNotificationSubscriptionHandlerService.processSubscriptionCreateRequest('test-id',_) - } - - def 'Consume valid CmNotificationSubscriptionNcmpInEvent delete message'() { - given: 'a cmNotificationSubscription event' - def jsonData = TestUtils.getResourceFileContent('cmSubscription/cmNotificationSubscriptionNcmpInEvent.json') - def testEventSent = jsonObjectMapper.convertJsonString(jsonData, CmNotificationSubscriptionNcmpInEvent.class) - def testCloudEventSent = CloudEventBuilder.v1() - .withData(objectMapper.writeValueAsBytes(testEventSent)) - .withId('sub-id') - .withType('subscriptionDeleteRequest') - .withSource(URI.create('some-resource')) - .withExtension('correlationid', 'test-cmhandle1').build() - def consumerRecord = new ConsumerRecord('topic-name', 0, 0, 'event-key', testCloudEventSent) - when: 'the valid event is consumed' - objectUnderTest.consumeSubscriptionEvent(consumerRecord) - then: 'an event is logged with level INFO' - def loggingEvent = getLoggingEvent() - assert loggingEvent.level == Level.INFO - and: 'the log indicates the task completed successfully' - assert loggingEvent.formattedMessage == 'Subscription delete request for source some-resource with subscription id test-id ...' - and: 'the subscription handler service is called once' - 1 * mockCmNotificationSubscriptionHandlerService.processSubscriptionDeleteRequest('test-id',_) - } - - - def getLoggingEvent() { - return logger.list[1] - } - -} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpOutEventProducerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpOutEventProducerSpec.groovy deleted file mode 100644 index 1fb5837eb3..0000000000 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/CmNotificationSubscriptionNcmpOutEventProducerSpec.groovy +++ /dev/null @@ -1,88 +0,0 @@ -package org.onap.cps.ncmp.api.impl.events.cmsubscription - -import com.fasterxml.jackson.databind.ObjectMapper -import io.cloudevents.CloudEvent -import org.onap.cps.events.EventsPublisher -import org.onap.cps.ncmp.api.impl.events.cmsubscription.producer.CmNotificationSubscriptionNcmpOutEventProducer -import org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.ncmp_to_client.CmNotificationSubscriptionNcmpOutEvent -import org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.ncmp_to_client.Data -import org.onap.cps.ncmp.utils.events.CloudEventMapper -import org.onap.cps.utils.JsonObjectMapper -import spock.lang.Specification - -class CmNotificationSubscriptionNcmpOutEventProducerSpec extends Specification { - - def mockEventsPublisher = Mock(EventsPublisher) - def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) - def mockCmNotificationSubscriptionMappersHandler = Mock(CmNotificationSubscriptionMappersHandler) - def mockDmiCmNotificationSubscriptionCacheHandler = Mock(DmiCmNotificationSubscriptionCacheHandler) - - def objectUnderTest = new CmNotificationSubscriptionNcmpOutEventProducer(mockEventsPublisher, jsonObjectMapper, - mockCmNotificationSubscriptionMappersHandler, mockDmiCmNotificationSubscriptionCacheHandler) - - def 'Create and #scenario Cm Notification Subscription NCMP out event'() { - given: 'a cm subscription response for the client' - def subscriptionId = 'test-subscription-id-2' - def eventType = 'subscriptionCreateResponse' - def cmNotificationSubscriptionNcmpOutEvent = new CmNotificationSubscriptionNcmpOutEvent(data: new Data(subscriptionId: 'test-subscription-id-2', acceptedTargets: ['ch-1', 'ch-2'])) - and: 'also we have target topic for publishing to client' - objectUnderTest.cmNotificationSubscriptionNcmpOutEventTopic = 'client-test-topic' - and: 'a deadline to an event' - objectUnderTest.cmNotificationSubscriptionDmiOutEventTimeoutInMs = 1000 - when: 'the event is published' - objectUnderTest.publishCmNotificationSubscriptionNcmpOutEvent(subscriptionId, eventType, cmNotificationSubscriptionNcmpOutEvent, eventPublishingTaskToBeScheduled) - then: 'we conditionally wait for a while' - Thread.sleep(delayInMs) - then: 'the event contains the required attributes' - 1 * mockEventsPublisher.publishCloudEvent(_, _, _) >> { - args -> - { - assert args[0] == 'client-test-topic' - assert args[1] == subscriptionId - def cmNotificationSubscriptionNcmpOutEventAsCloudEvent = (args[2] as CloudEvent) - assert cmNotificationSubscriptionNcmpOutEventAsCloudEvent.getExtension('correlationid') == subscriptionId - assert cmNotificationSubscriptionNcmpOutEventAsCloudEvent.type == 'subscriptionCreateResponse' - assert cmNotificationSubscriptionNcmpOutEventAsCloudEvent.source.toString() == 'NCMP' - assert CloudEventMapper.toTargetEvent(cmNotificationSubscriptionNcmpOutEventAsCloudEvent, CmNotificationSubscriptionNcmpOutEvent) == cmNotificationSubscriptionNcmpOutEvent - } - } - where: 'following scenarios are considered' - scenario | delayInMs | eventPublishingTaskToBeScheduled - 'publish event now' | 0 | false - 'schedule and publish after the configured time ' | 1500 | true - } - - def 'Schedule Cm Notification Subscription NCMP out event but later publish it on demand'() { - given: 'a cm subscription response for the client' - def subscriptionId = 'test-subscription-id-3' - def eventType = 'subscriptionCreateResponse' - def cmNotificationSubscriptionNcmpOutEvent = new CmNotificationSubscriptionNcmpOutEvent(data: new Data(subscriptionId: 'test-subscription-id-3', acceptedTargets: ['ch-2', 'ch-3'])) - and: 'also we have target topic for publishing to client' - objectUnderTest.cmNotificationSubscriptionNcmpOutEventTopic = 'client-test-topic' - and: 'a deadline to an event' - objectUnderTest.cmNotificationSubscriptionDmiOutEventTimeoutInMs = 1000 - when: 'the event is scheduled to be published' - objectUnderTest.publishCmNotificationSubscriptionNcmpOutEvent(subscriptionId, eventType, cmNotificationSubscriptionNcmpOutEvent, true) - then: 'we wait for 10ms and then we receive response from DMI' - Thread.sleep(10) - and: 'we receive response from DMI so we publish the message on demand' - objectUnderTest.publishCmNotificationSubscriptionNcmpOutEvent(subscriptionId, eventType, cmNotificationSubscriptionNcmpOutEvent, false) - then: 'the event contains the required attributes' - 1 * mockEventsPublisher.publishCloudEvent(_, _, _) >> { - args -> - { - assert args[0] == 'client-test-topic' - assert args[1] == subscriptionId - def cmNotificationSubscriptionNcmpOutEventAsCloudEvent = (args[2] as CloudEvent) - assert cmNotificationSubscriptionNcmpOutEventAsCloudEvent.getExtension('correlationid') == subscriptionId - assert cmNotificationSubscriptionNcmpOutEventAsCloudEvent.type == 'subscriptionCreateResponse' - assert cmNotificationSubscriptionNcmpOutEventAsCloudEvent.source.toString() == 'NCMP' - assert CloudEventMapper.toTargetEvent(cmNotificationSubscriptionNcmpOutEventAsCloudEvent, CmNotificationSubscriptionNcmpOutEvent) == cmNotificationSubscriptionNcmpOutEvent - } - } - then: 'the cache handler is called once to remove accepted and rejected entries in cache' - 1 * mockDmiCmNotificationSubscriptionCacheHandler.removeAcceptedAndRejectedDmiCmNotificationSubscriptionEntries(subscriptionId) - } - - -} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/DmiCmNotificationSubscriptionCacheHandlerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/DmiCmNotificationSubscriptionCacheHandlerSpec.groovy deleted file mode 100644 index 7d4bd73aa1..0000000000 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/DmiCmNotificationSubscriptionCacheHandlerSpec.groovy +++ /dev/null @@ -1,211 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2024 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.events.cmsubscription - -import com.fasterxml.jackson.databind.ObjectMapper -import io.cloudevents.CloudEvent -import io.cloudevents.core.builder.CloudEventBuilder -import org.apache.kafka.clients.consumer.ConsumerRecord -import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.CmNotificationSubscriptionStatus -import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.DmiCmNotificationSubscriptionDetails -import org.onap.cps.ncmp.api.impl.events.cmsubscription.service.CmNotificationSubscriptionPersistenceService -import org.onap.cps.ncmp.api.kafka.MessagingBaseSpec -import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.client_to_ncmp.CmNotificationSubscriptionNcmpInEvent -import org.onap.cps.ncmp.impl.inventory.InventoryPersistence -import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle -import org.onap.cps.ncmp.utils.TestUtils -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 static org.onap.cps.ncmp.utils.events.CloudEventMapper.toTargetEvent - -@SpringBootTest(classes = [ObjectMapper, JsonObjectMapper]) -class DmiCmNotificationSubscriptionCacheHandlerSpec extends MessagingBaseSpec { - - @Autowired - JsonObjectMapper jsonObjectMapper - @Autowired - ObjectMapper objectMapper - @SpringBean - InventoryPersistence mockInventoryPersistence = Mock(InventoryPersistence) - @SpringBean - CmNotificationSubscriptionPersistenceService mockCmNotificationSubscriptionPersistenceService = Mock(CmNotificationSubscriptionPersistenceService) - - def testCache = [:] - def objectUnderTest = new DmiCmNotificationSubscriptionCacheHandler(mockCmNotificationSubscriptionPersistenceService, testCache, mockInventoryPersistence) - - CmNotificationSubscriptionNcmpInEvent cmNotificationSubscriptionNcmpInEvent - def yangModelCmHandle1 = new YangModelCmHandle(id:'ch1',dmiServiceName:'dmi-1') - def yangModelCmHandle2 = new YangModelCmHandle(id:'ch2',dmiServiceName:'dmi-2') - def yangModelCmHandle3 = new YangModelCmHandle(id:'ch3',dmiServiceName:'dmi-1') - def yangModelCmHandle4 = new YangModelCmHandle(id:'ch4',dmiServiceName:'dmi-2') - - def setup() { - setUpTestEvent() - initialiseMockInventoryPersistenceResponses() - } - - def 'Load CM subscription event to cache'() { - given: 'a valid subscription event with Id' - def subscriptionId = cmNotificationSubscriptionNcmpInEvent.getData().getSubscriptionId() - and: 'list of predicates' - def predicates = cmNotificationSubscriptionNcmpInEvent.getData().getPredicates() - when: 'a valid event object loaded in cache' - objectUnderTest.add(subscriptionId, predicates) - then: 'the cache contains the correct entry with #subscriptionId subscription ID' - assert testCache.containsKey(subscriptionId) - } - - def 'Get cache entry via subscription id'() { - given: 'the cache contains value for some-id' - testCache.put('some-id',[:]) - when: 'the get method is called' - def result = objectUnderTest.get('some-id') - then: 'correct value is returned as expected' - assert result == [:] - } - - def 'Remove accepted and rejected entries from cache via subscription id'() { - given: 'a map as the value for cache entry for some-id' - def testMap = [:] - testMap.put("dmi-1", - new DmiCmNotificationSubscriptionDetails([],CmNotificationSubscriptionStatus.ACCEPTED)) - testMap.put("dmi-2", - new DmiCmNotificationSubscriptionDetails([],CmNotificationSubscriptionStatus.REJECTED)) - testMap.put("dmi-3", - new DmiCmNotificationSubscriptionDetails([],CmNotificationSubscriptionStatus.PENDING)) - testCache.put("test-id", testMap) - assert testCache.get("test-id").size() == 3 - when: 'the method to remove accepted and rejected entries for test-id is called' - objectUnderTest.removeAcceptedAndRejectedDmiCmNotificationSubscriptionEntries("test-id") - then: 'all entries with status accepted/rejected are no longer present for test-id' - testCache.get("test-id").each { key, testResultMap -> - assert testResultMap.cmNotificationSubscriptionStatus != CmNotificationSubscriptionStatus.ACCEPTED - || testResultMap.cmNotificationSubscriptionStatus != CmNotificationSubscriptionStatus.REJECTED - } - and: 'the size of the map for cache entry test-id is as expected' - assert testCache.get("test-id").size() == 1 - } - - def 'Create map for DMI cm notification subscription per DMI service name'() { - given: 'list of predicates from the create subscription event' - def predicates = cmNotificationSubscriptionNcmpInEvent.getData().getPredicates() - when: 'method to create map of DMI cm notification subscription per DMI service name is called' - def result = objectUnderTest.createDmiCmNotificationSubscriptionsPerDmi(predicates) - then: 'the result size of resulting map is correct to the number of DMIs' - assert result.size() == 2 - and: 'the cache objects per DMI exists' - def resultMapForDmi1 = result.get('dmi-1') - def resultMapForDmi2 = result.get('dmi-2') - assert resultMapForDmi1 != null - assert resultMapForDmi2 != null - and: 'the size of predicates in each object is correct' - assert resultMapForDmi1.dmiCmNotificationSubscriptionPredicates.size() == 2 - assert resultMapForDmi2.dmiCmNotificationSubscriptionPredicates.size() == 2 - and: 'the subscription status in each object is correct' - assert resultMapForDmi1.cmNotificationSubscriptionStatus.toString() == 'PENDING' - assert resultMapForDmi2.cmNotificationSubscriptionStatus.toString() == 'PENDING' - and: 'the target cmHandles for each predicate is correct' - assert resultMapForDmi1.dmiCmNotificationSubscriptionPredicates[0].targetCmHandleIds == ['ch1'].toSet() - assert resultMapForDmi1.dmiCmNotificationSubscriptionPredicates[1].targetCmHandleIds == ['ch3'].toSet() - - assert resultMapForDmi2.dmiCmNotificationSubscriptionPredicates[0].targetCmHandleIds == ['ch2'].toSet() - assert resultMapForDmi2.dmiCmNotificationSubscriptionPredicates[1].targetCmHandleIds == ['ch4'].toSet() - and: 'the list of xpath for each is correct' - assert resultMapForDmi1.dmiCmNotificationSubscriptionPredicates[0].xpaths - && resultMapForDmi2.dmiCmNotificationSubscriptionPredicates[0].xpaths == ['/x1/y1','x2/y2'].toSet() - - assert resultMapForDmi1.dmiCmNotificationSubscriptionPredicates[1].xpaths - && resultMapForDmi2.dmiCmNotificationSubscriptionPredicates[1].xpaths == ['/x3/y3','x4/y4'].toSet() - } - - def 'Get map for cm handle IDs by DMI service name'() { - given: 'the predicate from the test request CM subscription event' - def targetFilter = cmNotificationSubscriptionNcmpInEvent.getData().getPredicates().get(0).getTargetFilter() - when: 'the method to group all target CM handles by DMI service name is called' - def mapOfCMHandleIDsByDmi = objectUnderTest.groupTargetCmHandleIdsByDmi(targetFilter) - then: 'the size of the resulting map is correct' - assert mapOfCMHandleIDsByDmi.size() == 2 - and: 'the values in the map is as expected' - assert mapOfCMHandleIDsByDmi.get('dmi-1') == ['ch1'].toSet() - assert mapOfCMHandleIDsByDmi.get('dmi-2') == ['ch2'].toSet() - } - - def 'Update subscription status in cache per DMI service name'() { - given: 'populated cache' - def predicates = cmNotificationSubscriptionNcmpInEvent.getData().getPredicates() - def subscriptionId = cmNotificationSubscriptionNcmpInEvent.getData().getSubscriptionId() - objectUnderTest.add(subscriptionId, predicates) - when: 'subscription status per dmi is updated in cache' - objectUnderTest.updateDmiCmNotificationSubscriptionStatusPerDmi(subscriptionId,'dmi-1', CmNotificationSubscriptionStatus.ACCEPTED) - then: 'verify status has been updated in cache' - def predicate = testCache.get(subscriptionId) - assert predicate.get('dmi-1').cmNotificationSubscriptionStatus == CmNotificationSubscriptionStatus.ACCEPTED - } - - def 'Persist Cache into database per dmi'() { - given: 'populated cache' - def predicates = cmNotificationSubscriptionNcmpInEvent.getData().getPredicates() - def subscriptionId = cmNotificationSubscriptionNcmpInEvent.getData().getSubscriptionId() - objectUnderTest.add(subscriptionId, predicates) - when: 'subscription is persisted in database' - objectUnderTest.persistIntoDatabasePerDmi(subscriptionId,'dmi-1') - then: 'persistence service is called the correct number of times per dmi' - 4 * mockCmNotificationSubscriptionPersistenceService.addCmNotificationSubscription(_,_,_,subscriptionId) - } - - def 'Remove subscription from database per dmi'() { - given: 'populated cache' - def predicates = cmNotificationSubscriptionNcmpInEvent.getData().getPredicates() - def subscriptionId = cmNotificationSubscriptionNcmpInEvent.getData().getSubscriptionId() - objectUnderTest.add(subscriptionId, predicates) - when: 'subscription is persisted in database' - objectUnderTest.removeFromDatabasePerDmi(subscriptionId,'dmi-1') - then: 'persistence service is called the correct number of times per dmi' - 4 * mockCmNotificationSubscriptionPersistenceService.removeCmNotificationSubscription(_,_,_,subscriptionId) - } - - def setUpTestEvent(){ - def jsonData = TestUtils.getResourceFileContent('cmSubscription/cmNotificationSubscriptionNcmpInEvent.json') - def testEventSent = jsonObjectMapper.convertJsonString(jsonData, CmNotificationSubscriptionNcmpInEvent.class) - def testCloudEventSent = CloudEventBuilder.v1() - .withData(objectMapper.writeValueAsBytes(testEventSent)) - .withId('subscriptionCreated') - .withType('subscriptionCreated') - .withSource(URI.create('some-resource')) - .withExtension('correlationid', 'test-cmhandle1').build() - def consumerRecord = new ConsumerRecord('topic-name', 0, 0, 'event-key', testCloudEventSent) - def cloudEvent = consumerRecord.value() - - cmNotificationSubscriptionNcmpInEvent = toTargetEvent(cloudEvent, CmNotificationSubscriptionNcmpInEvent.class); - } - - def initialiseMockInventoryPersistenceResponses(){ - mockInventoryPersistence.getYangModelCmHandles(['ch1','ch2']) - >> [yangModelCmHandle1, yangModelCmHandle2] - - mockInventoryPersistence.getYangModelCmHandles(['ch3','ch4']) - >> [yangModelCmHandle3, yangModelCmHandle4] - } - -} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/mapper/CmNotificationSubscriptionDmiInEventMapperSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/mapper/CmNotificationSubscriptionDmiInEventMapperSpec.groovy deleted file mode 100644 index ebbaf95578..0000000000 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/mapper/CmNotificationSubscriptionDmiInEventMapperSpec.groovy +++ /dev/null @@ -1,57 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2024 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.events.cmsubscription.mapper - - -import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.DmiCmNotificationSubscriptionPredicate -import org.onap.cps.ncmp.impl.inventory.InventoryPersistence -import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle -import spock.lang.Specification - -import static org.onap.cps.ncmp.api.data.models.DatastoreType.PASSTHROUGH_OPERATIONAL -import static org.onap.cps.ncmp.api.data.models.DatastoreType.PASSTHROUGH_RUNNING - -class CmNotificationSubscriptionDmiInEventMapperSpec extends Specification { - - def mockInventoryPersistence = Mock(InventoryPersistence) - - def objectUnderTest = new CmNotificationSubscriptionDmiInEventMapper(mockInventoryPersistence) - - def setup() { - def yangModelCmHandles = [new YangModelCmHandle(id: 'ch-1', dmiProperties: [new YangModelCmHandle.Property('k1', 'v1')], publicProperties: []), - new YangModelCmHandle(id: 'ch-2', dmiProperties: [new YangModelCmHandle.Property('k2', 'v2')], publicProperties: [])] - mockInventoryPersistence.getYangModelCmHandles(['ch-1', 'ch-2'] as Set) >> yangModelCmHandles - } - - def 'Check for Cm Notification Subscription DMI In Event mapping'() { - given: 'a collection of cm subscription predicates' - def dmiCmNotificationSubscriptionPredicates = [new DmiCmNotificationSubscriptionPredicate(['ch-1'].toSet(), PASSTHROUGH_RUNNING, ['/ch-1'].toSet()), - new DmiCmNotificationSubscriptionPredicate(['ch-2'].toSet(), PASSTHROUGH_OPERATIONAL, ['/ch-2'].toSet())] - when: 'we try to map the values' - def result = objectUnderTest.toCmNotificationSubscriptionDmiInEvent(dmiCmNotificationSubscriptionPredicates) - then: 'it contains correct cm notification subscription cmhandle object' - assert result.data.cmHandles.cmhandleId.containsAll(['ch-1', 'ch-2']) - assert result.data.cmHandles.privateProperties.containsAll([['k1': 'v1'], ['k2': 'v2']]) - and: 'also has the correct dmi cm notification subscription predicates' - assert result.data.predicates.targetFilter.containsAll([['ch-1'], ['ch-2']]) - - } -} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/mapper/CmNotificationSubscriptionNcmpOutEventMapperSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/mapper/CmNotificationSubscriptionNcmpOutEventMapperSpec.groovy deleted file mode 100644 index 179cf361d9..0000000000 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/mapper/CmNotificationSubscriptionNcmpOutEventMapperSpec.groovy +++ /dev/null @@ -1,64 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2024 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.events.cmsubscription.mapper - -import org.onap.cps.ncmp.api.data.models.DatastoreType -import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.CmNotificationSubscriptionStatus -import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.DmiCmNotificationSubscriptionDetails -import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.DmiCmNotificationSubscriptionPredicate -import spock.lang.Specification - -class CmNotificationSubscriptionNcmpOutEventMapperSpec extends Specification { - - static Map dmiCmNotificationSubscriptionDetailsMap - - def objectUnderTest = new CmNotificationSubscriptionNcmpOutEventMapper() - - def setup() { - def dmiCmNotificationSubscriptionPredicateA = new DmiCmNotificationSubscriptionPredicate(['ch-A'] as Set, DatastoreType.PASSTHROUGH_RUNNING, ['/a'] as Set) - def dmiCmNotificationSubscriptionPredicateB = new DmiCmNotificationSubscriptionPredicate(['ch-B'] as Set, DatastoreType.PASSTHROUGH_OPERATIONAL, ['/b'] as Set) - def dmiCmNotificationSubscriptionPredicateC = new DmiCmNotificationSubscriptionPredicate(['ch-C'] as Set, DatastoreType.PASSTHROUGH_OPERATIONAL, ['/c'] as Set) - dmiCmNotificationSubscriptionDetailsMap = ['dmi-1': new DmiCmNotificationSubscriptionDetails([dmiCmNotificationSubscriptionPredicateA], CmNotificationSubscriptionStatus.PENDING), - 'dmi-2': new DmiCmNotificationSubscriptionDetails([dmiCmNotificationSubscriptionPredicateB], CmNotificationSubscriptionStatus.ACCEPTED), - 'dmi-3': new DmiCmNotificationSubscriptionDetails([dmiCmNotificationSubscriptionPredicateC], CmNotificationSubscriptionStatus.REJECTED) - ] - } - - def 'Check for Cm Notification Subscription Outgoing event mapping'() { - when: 'we try to map the event to send it to client' - def result = objectUnderTest.toCmNotificationSubscriptionNcmpOutEvent('test-subscription', dmiCmNotificationSubscriptionDetailsMap) - then: 'event is mapped correctly for the subscription' - result.data.subscriptionId == 'test-subscription' - and: 'the cm handle ids are part of correct list' - result.data.pendingTargets == ['ch-A'] - result.data.acceptedTargets == ['ch-B'] - result.data.rejectedTargets == ['ch-C'] - } - - def 'Check for Cm Notification Rejected Subscription Outgoing event mapping'() { - when: 'we try to map the event to send it to client' - def result = objectUnderTest.toCmNotificationSubscriptionNcmpOutEventForRejectedRequest('test-subscription', ['ch-1', 'ch-2']) - then: 'event is mapped correctly for the subscription id' - result.data.subscriptionId == 'test-subscription' - and: 'the cm handle ids are part of correct list' - result.data.withRejectedTargets(['ch-1', 'ch-2']) - } -} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerServiceImplSpec.groovy deleted file mode 100644 index 55a817ed67..0000000000 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionHandlerServiceImplSpec.groovy +++ /dev/null @@ -1,147 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (c) 2024 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.events.cmsubscription.service - -import com.fasterxml.jackson.databind.ObjectMapper -import org.onap.cps.ncmp.api.data.models.DatastoreType -import org.onap.cps.ncmp.api.impl.events.cmsubscription.CmNotificationSubscriptionDelta -import org.onap.cps.ncmp.api.impl.events.cmsubscription.CmNotificationSubscriptionEventsHandler -import org.onap.cps.ncmp.api.impl.events.cmsubscription.CmNotificationSubscriptionMappersHandler -import org.onap.cps.ncmp.api.impl.events.cmsubscription.DmiCmNotificationSubscriptionCacheHandler -import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.CmNotificationSubscriptionStatus -import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.DmiCmNotificationSubscriptionDetails -import org.onap.cps.ncmp.api.impl.events.cmsubscription.model.DmiCmNotificationSubscriptionPredicate -import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.client_to_ncmp.CmNotificationSubscriptionNcmpInEvent -import org.onap.cps.ncmp.events.cmnotificationsubscription_merge1_0_0.ncmp_to_dmi.CmNotificationSubscriptionDmiInEvent -import org.onap.cps.ncmp.events.cmsubscription_merge1_0_0.ncmp_to_client.CmNotificationSubscriptionNcmpOutEvent -import org.onap.cps.ncmp.utils.TestUtils -import org.onap.cps.utils.JsonObjectMapper -import spock.lang.Specification - -class CmNotificationSubscriptionHandlerServiceImplSpec extends Specification{ - - def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) - def mockCmNotificationSubscriptionPersistenceService = Mock(CmNotificationSubscriptionPersistenceService); - def mockCmNotificationSubscriptionDelta = Mock(CmNotificationSubscriptionDelta); - def mockCmNotificationSubscriptionMappersHandler = Mock(CmNotificationSubscriptionMappersHandler); - def mockCmNotificationSubscriptionEventsHandler = Mock(CmNotificationSubscriptionEventsHandler); - def mockDmiCmNotificationSubscriptionCacheHandler = Mock(DmiCmNotificationSubscriptionCacheHandler); - - def objectUnderTest = new CmNotificationSubscriptionHandlerServiceImpl(mockCmNotificationSubscriptionPersistenceService, - mockCmNotificationSubscriptionDelta, mockCmNotificationSubscriptionMappersHandler, - mockCmNotificationSubscriptionEventsHandler, mockDmiCmNotificationSubscriptionCacheHandler) - - def testSubscriptionDetailsMap = ["dmi-1":new DmiCmNotificationSubscriptionDetails([], CmNotificationSubscriptionStatus.PENDING)] - - def 'Consume valid and unique CmNotificationSubscriptionNcmpInEvent create message'() { - given: 'a cmNotificationSubscriptionNcmp in event with unique subscription id' - def jsonData = TestUtils.getResourceFileContent('cmSubscription/cmNotificationSubscriptionNcmpInEvent.json') - def testEventConsumed = jsonObjectMapper.convertJsonString(jsonData, CmNotificationSubscriptionNcmpInEvent.class) - def testListOfDeltaPredicates = [new DmiCmNotificationSubscriptionPredicate(['ch1'].toSet(), DatastoreType.PASSTHROUGH_OPERATIONAL, ['/a/b'].toSet())] - mockCmNotificationSubscriptionPersistenceService.isUniqueSubscriptionId("test-id") >> true - and: 'relevant details is extracted from the event' - def subscriptionId = testEventConsumed.getData().getSubscriptionId() - def predicates = testEventConsumed.getData().getPredicates() - and: 'the cache handler returns for relevant subscription id' - 1 * mockDmiCmNotificationSubscriptionCacheHandler.get("test-id") >> testSubscriptionDetailsMap - and: 'the delta predicates is returned' - 1 * mockCmNotificationSubscriptionDelta.getDelta(_) >> testListOfDeltaPredicates - and: 'the DMI in event mapper returns cm notification subscription event' - def testDmiInEvent = new CmNotificationSubscriptionDmiInEvent() - 1 * mockCmNotificationSubscriptionMappersHandler - .toCmNotificationSubscriptionDmiInEvent(testListOfDeltaPredicates) >> testDmiInEvent - when: 'the valid and unique event is consumed' - objectUnderTest.processSubscriptionCreateRequest(subscriptionId, predicates) - then: 'the subscription cache handler is called once' - 1 * mockDmiCmNotificationSubscriptionCacheHandler.add('test-id',_) - and: 'the events handler method to publish DMI event is called correct number of times with the correct parameters' - testSubscriptionDetailsMap.size() * mockCmNotificationSubscriptionEventsHandler.publishCmNotificationSubscriptionDmiInEvent( - "test-id", "dmi-1", "subscriptionCreateRequest", testDmiInEvent) - and: 'we schedule to send the response after configured time from the cache' - 1 * mockCmNotificationSubscriptionEventsHandler.publishCmNotificationSubscriptionNcmpOutEvent( - "test-id", "subscriptionCreateResponse", null, true) - } - - def 'Consume valid and Overlapping Cm Notification Subscription NcmpIn Event'() { - given: 'a cmNotificationSubscriptionNcmp in event with unique subscription id' - def jsonData = TestUtils.getResourceFileContent('cmSubscription/cmNotificationSubscriptionNcmpInEvent.json') - def testEventConsumed = jsonObjectMapper.convertJsonString(jsonData, CmNotificationSubscriptionNcmpInEvent.class) - def noDeltaPredicates = [] - mockCmNotificationSubscriptionPersistenceService.isUniqueSubscriptionId("test-id") >> true - and: 'the cache handler returns for relevant subscription id' - 1 * mockDmiCmNotificationSubscriptionCacheHandler.get("test-id") >> testSubscriptionDetailsMap - and: 'the delta predicates is returned' - 1 * mockCmNotificationSubscriptionDelta.getDelta(_) >> noDeltaPredicates - when: 'the valid and unique event is consumed' - objectUnderTest.processSubscriptionCreateRequest('test-id', noDeltaPredicates) - then: 'the subscription cache handler is called once' - 1 * mockDmiCmNotificationSubscriptionCacheHandler.add('test-id', _) - and: 'the subscription details are updated in the cache' - 1 * mockDmiCmNotificationSubscriptionCacheHandler.updateDmiCmNotificationSubscriptionStatusPerDmi('test-id', _, CmNotificationSubscriptionStatus.ACCEPTED) - and: 'we schedule to send the response after configured time from the cache' - 1 * mockCmNotificationSubscriptionEventsHandler.publishCmNotificationSubscriptionNcmpOutEvent( - "test-id", "subscriptionCreateResponse", null, true) - } - - def 'Consume valid and but non-unique CmNotificationSubscription create message'() { - given: 'a cmNotificationSubscriptionNcmp in event' - def jsonData = TestUtils.getResourceFileContent('cmSubscription/cmNotificationSubscriptionNcmpInEvent.json') - def testEventConsumed = jsonObjectMapper.convertJsonString(jsonData, CmNotificationSubscriptionNcmpInEvent.class) - mockCmNotificationSubscriptionPersistenceService.isUniqueSubscriptionId('test-id') >> false - and: 'relevant details is extracted from the event' - def subscriptionId = testEventConsumed.getData().getSubscriptionId() - def predicates = testEventConsumed.getData().getPredicates() - and: 'the NCMP out in event mapper returns an event for rejected request' - def testNcmpOutEvent = new CmNotificationSubscriptionNcmpOutEvent() - 1 * mockCmNotificationSubscriptionMappersHandler.toCmNotificationSubscriptionNcmpOutEventForRejectedRequest( - "test-id",_) >> testNcmpOutEvent - when: 'the valid but non-unique event is consumed' - objectUnderTest.processSubscriptionCreateRequest(subscriptionId, predicates) - then: 'the events handler method to publish DMI event is never called' - 0 * mockCmNotificationSubscriptionEventsHandler.publishCmNotificationSubscriptionDmiInEvent(_,_,_,_) - and: 'the events handler method to publish NCMP out event is called once' - 1 * mockCmNotificationSubscriptionEventsHandler.publishCmNotificationSubscriptionNcmpOutEvent( - 'test-id', 'subscriptionCreateResponse', testNcmpOutEvent, false) - } - - def 'Consume valid CmNotificationSubscriptionNcmpInEvent delete message'() { - given: 'a cmNotificationSubscriptionNcmp in event for delete' - def jsonData = TestUtils.getResourceFileContent('cmSubscription/cmNotificationSubscriptionNcmpInEvent.json') - def testEventConsumed = jsonObjectMapper.convertJsonString(jsonData, CmNotificationSubscriptionNcmpInEvent.class) - and: 'relevant details is extracted from the event' - def subscriptionId = testEventConsumed.getData().getSubscriptionId() - def predicates = testEventConsumed.getData().getPredicates() - and: 'the cache handler returns for relevant subscription id' - 1 * mockDmiCmNotificationSubscriptionCacheHandler.get('test-id') >> testSubscriptionDetailsMap - when: 'the valid and unique event is consumed' - objectUnderTest.processSubscriptionDeleteRequest(subscriptionId, predicates) - then: 'the subscription cache handler is called once' - 1 * mockDmiCmNotificationSubscriptionCacheHandler.add('test-id', predicates) - and: 'the mapper handler to get DMI in event is called once' - 1 * mockCmNotificationSubscriptionMappersHandler.toCmNotificationSubscriptionDmiInEvent(_) - and: 'the events handler method to publish DMI event is called correct number of times with the correct parameters' - testSubscriptionDetailsMap.size() * mockCmNotificationSubscriptionEventsHandler.publishCmNotificationSubscriptionDmiInEvent( - 'test-id', 'dmi-1', 'subscriptionDeleteRequest', _) - and: 'we schedule to send the response after configured time from the cache' - 1 * mockCmNotificationSubscriptionEventsHandler.publishCmNotificationSubscriptionNcmpOutEvent( - 'test-id', 'subscriptionDeleteResponse', null, true) - } -} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImplSpec.groovy deleted file mode 100644 index ef735fd82a..0000000000 --- a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/api/impl/events/cmsubscription/service/CmNotificationSubscriptionPersistenceServiceImplSpec.groovy +++ /dev/null @@ -1,192 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (c) 2024 Nordix Foundation. - * Modifications Copyright (C) 2024 TechMahindra Ltd. - * ================================================================================ - * 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.events.cmsubscription.service - -import com.fasterxml.jackson.databind.ObjectMapper -import org.onap.cps.api.CpsDataService -import org.onap.cps.api.CpsQueryService -import org.onap.cps.ncmp.api.data.models.DatastoreType -import org.onap.cps.spi.FetchDescendantsOption -import org.onap.cps.spi.model.DataNode -import org.onap.cps.utils.ContentType -import org.onap.cps.utils.JsonObjectMapper -import spock.lang.Specification - -import static org.onap.cps.ncmp.api.impl.events.cmsubscription.service.CmNotificationSubscriptionPersistenceServiceImpl.CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE -import static org.onap.cps.ncmp.api.impl.events.cmsubscription.service.CmNotificationSubscriptionPersistenceServiceImpl.CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH -import static org.onap.cps.ncmp.api.impl.events.cmsubscription.service.CmNotificationSubscriptionPersistenceServiceImpl.CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_ID - -class CmNotificationSubscriptionPersistenceServiceImplSpec extends Specification { - - def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) - def mockCpsQueryService = Mock(CpsQueryService) - def mockCpsDataService = Mock(CpsDataService) - - def objectUnderTest = new CmNotificationSubscriptionPersistenceServiceImpl(jsonObjectMapper, mockCpsQueryService, mockCpsDataService) - - def 'Check ongoing cm subscription #scenario'() { - given: 'a valid cm subscription query' - def cpsPathQuery = "/datastores/datastore[@name='ncmp-datastore:passthrough-running']/cm-handles/cm-handle[@id='ch-1']/filters/filter[@xpath='/cps/path']"; - and: 'datanodes optionally returned' - 1 * mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions', - cpsPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >> dataNode - when: 'we check for an ongoing cm subscription' - def response = objectUnderTest.isOngoingCmNotificationSubscription(DatastoreType.PASSTHROUGH_RUNNING, 'ch-1', '/cps/path') - then: 'we get expected response' - assert response == isOngoingCmSubscription - where: 'following scenarios are used' - scenario | dataNode || isOngoingCmSubscription - 'valid datanodes present' | [new DataNode(xpath: '/cps/path', leaves: ['subscriptionIds': ['sub-1', 'sub-2']])] || true - 'no datanodes present' | [] || false - } - - def 'Checking uniqueness of incoming subscription ID'() { - given: 'a cps path with a subscription ID for querying' - def cpsPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_ID.formatted('some-sub') - and: 'relevant datanodes are returned' - 1 * mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions', cpsPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >> - dataNodes - when: 'a subscription ID is tested for uniqueness' - def result = objectUnderTest.isUniqueSubscriptionId('some-sub') - then: 'result is as expected' - assert result == isValidSubscriptionId - where: 'following scenarios are used' - scenario | dataNodes || isValidSubscriptionId - 'datanodes present' | [new DataNode()] || false - 'no datanodes present' | [] || true - } - - def 'Add new subscriber to an ongoing cm notification subscription'() { - given: 'a valid cm subscription path query' - def cpsPathQuery =CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted('ncmp-datastore:passthrough-running', 'ch-1', '/x/y') - and: 'a dataNode exists for the given cps path query' - mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions', - cpsPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >> [new DataNode(xpath: cpsPathQuery, leaves: ['xpath': '/x/y','subscriptionIds': ['sub-1']])] - when: 'the method to add/update cm notification subscription is called' - objectUnderTest.addCmNotificationSubscription(DatastoreType.PASSTHROUGH_RUNNING, 'ch-1','/x/y', 'newSubId') - then: 'data service method to update list of subscribers is called once' - 1 * mockCpsDataService.updateNodeLeaves( - 'NCMP-Admin', - 'cm-data-subscriptions', - '/datastores/datastore[@name=\'ncmp-datastore:passthrough-running\']/cm-handles/cm-handle[@id=\'ch-1\']/filters', - objectUnderTest.getSubscriptionDetailsAsJson('/x/y', ['sub-1','newSubId']), _,ContentType.JSON) - } - - def 'Add new cm notification subscription for #datastoreType'() { - given: 'a valid cm subscription path query' - def cmSubscriptionCpsPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted(datastoreName, 'ch-1', '/x/y') - def cmHandleForSubscriptionPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE.formatted(datastoreName, 'ch-1') - and: 'a parent node xpath for the cm subscription path above' - def parentNodeXpath = '/datastores/datastore[@name=\'%s\']/cm-handles' - and: 'a datanode does not exist for cm subscription path query' - mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions', - cmSubscriptionCpsPathQuery, - FetchDescendantsOption.OMIT_DESCENDANTS) >> [] - and: 'a datanode does not exist for the given cm handle subscription path query' - mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions', - cmHandleForSubscriptionPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >> [] - and: 'subscription is mapped as JSON' - def subscriptionAsJson = '{"cm-handle":[{"id":"ch-1","filters":' + - objectUnderTest.getSubscriptionDetailsAsJson('/x/y', ['newSubId']) + '}]}' - when: 'the method to add/update cm notification subscription is called' - objectUnderTest.addCmNotificationSubscription(datastoreType, 'ch-1','/x/y', 'newSubId') - then: 'data service method to create new subscription for given subscriber is called once with the correct parameters' - 1 * mockCpsDataService.saveData( - 'NCMP-Admin', - 'cm-data-subscriptions', - parentNodeXpath.formatted(datastoreName), - subscriptionAsJson,_, ContentType.JSON) - where: - scenario | datastoreType || datastoreName - 'passthrough_running' | DatastoreType.PASSTHROUGH_RUNNING || "ncmp-datastore:passthrough-running" - 'passthrough_operational' | DatastoreType.PASSTHROUGH_OPERATIONAL || "ncmp-datastore:passthrough-operational" - } - - def 'Add new cm notification subscription when xpath does not exist for existing subscription cm handle'() { - given: 'a valid cm subscription path query' - def cmSubscriptionCpsPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted(datastoreName, 'ch-1', '/x/y') - def cmHandleForSubscriptionPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE.formatted(datastoreName, 'ch-1') - and: 'a parent node xpath for given cm handle for subscription path above' - def parentNodeXpath = '/datastores/datastore[@name=\'%s\']/cm-handles/cm-handle[@id=\'%s\']/filters' - and: 'a datanode does not exist for cm subscription path query' - mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions', - cmSubscriptionCpsPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >> [] - and: 'a datanode exists for the given cm handle subscription path query' - mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions', - cmHandleForSubscriptionPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >> [new DataNode()] - when: 'the method to add/update cm notification subscription is called' - objectUnderTest.addCmNotificationSubscription(datastoreType, 'ch-1','/x/y', 'newSubId') - then: 'data service method to create new subscription for given subscriber is called once with the correct parameters' - 1 * mockCpsDataService.saveListElements( - 'NCMP-Admin', - 'cm-data-subscriptions', - parentNodeXpath.formatted(datastoreName, 'ch-1'), - objectUnderTest.getSubscriptionDetailsAsJson('/x/y', ['newSubId']),_) - where: - scenario | datastoreType || datastoreName - 'passthrough_running' | DatastoreType.PASSTHROUGH_RUNNING || "ncmp-datastore:passthrough-running" - 'passthrough_operational' | DatastoreType.PASSTHROUGH_OPERATIONAL || "ncmp-datastore:passthrough-operational" - } - - def 'Remove subscriber from a list of an ongoing cm notification subscription'() { - given: 'a subscription exists when queried' - def cpsPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted('ncmp-datastore:passthrough-running', 'ch-1', '/x/y') - mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions', - cpsPathQuery, FetchDescendantsOption.OMIT_DESCENDANTS) >> [new DataNode(xpath: cpsPathQuery, leaves: ['xpath': '/x/y','subscriptionIds': ['sub-1', 'sub-2']])] - when: 'the subscriber is removed' - objectUnderTest.removeCmNotificationSubscription(DatastoreType.PASSTHROUGH_RUNNING, 'ch-1', '/x/y', 'sub-1') - then: 'the list of subscribers is updated' - 1 * mockCpsDataService.updateNodeLeaves('NCMP-Admin', 'cm-data-subscriptions', - '/datastores/datastore[@name=\'ncmp-datastore:passthrough-running\']/cm-handles/cm-handle[@id=\'ch-1\']/filters', - objectUnderTest.getSubscriptionDetailsAsJson('/x/y', ['sub-2']), _, ContentType.JSON) - } - - def 'Removing last ongoing subscription for datastore and cmhandle and xpath'(){ - given: 'a subscription exists when queried but has only 1 subscriber' - mockCpsQueryService.queryDataNodes( - 'NCMP-Admin', - 'cm-data-subscriptions', - CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted('ncmp-datastore:passthrough-running', 'ch-1', '/x/y'), - FetchDescendantsOption.OMIT_DESCENDANTS) >> [new DataNode(leaves: ['xpath': '/x/y','subscriptionIds': ['sub-1']])] - and: 'the #scenario' - mockCpsQueryService.queryDataNodes( - 'NCMP-Admin', - 'cm-data-subscriptions', - CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE.formatted('ncmp-datastore:passthrough-running', 'ch-1'), - FetchDescendantsOption.DIRECT_CHILDREN_ONLY) >> [new DataNode(childDataNodes: listOfChildNodes)] - when: 'that last ongoing subscription is removed' - objectUnderTest.removeCmNotificationSubscription(DatastoreType.PASSTHROUGH_RUNNING, 'ch-1', '/x/y', 'sub-1') - then: 'the subscription with empty subscriber list is removed' - 1 * mockCpsDataService.deleteDataNode('NCMP-Admin', 'cm-data-subscriptions', - '/datastores/datastore[@name=\'ncmp-datastore:passthrough-running\']/cm-handles/cm-handle[@id=\'ch-1\']/filters/filter[@xpath=\'/x/y\']', - _) - and: 'method call to delete the cm handle is called the correct number of times' - numberOfCallsToDeleteCmHandle * mockCpsDataService.deleteDataNode('NCMP-Admin', 'cm-data-subscriptions', - '/datastores/datastore[@name=\'ncmp-datastore:passthrough-running\']/cm-handles/cm-handle[@id=\'ch-1\']', - _) - where: - scenario | listOfChildNodes || numberOfCallsToDeleteCmHandle - 'cm handle in same datastore is used for other subscriptions' | [new DataNode()] || 0 - 'cm handle in same datastore is NOT used for other subscriptions' | [] || 1 - } - -} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/EventsFacadeSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/EventsFacadeSpec.groovy new file mode 100644 index 0000000000..bc2df10ce3 --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/EventsFacadeSpec.groovy @@ -0,0 +1,58 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (c) 2024 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.impl.cmnotificationsubscription + +import org.onap.cps.ncmp.impl.cmnotificationsubscription.dmi.DmiInEventProducer +import org.onap.cps.ncmp.impl.cmnotificationsubscription.ncmp.NcmpOutEventProducer +import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.ncmp_to_client.NcmpOutEvent +import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.ncmp_to_dmi.DmiInEvent +import spock.lang.Specification + +class EventsFacadeSpec extends Specification { + + def mockCmNotificationSubscriptionNcmpOutEventProducer = Mock(NcmpOutEventProducer) + def mockCmNotificationSubscriptionDmiInEventProducer = Mock(DmiInEventProducer) + + def objectUnderTest = new EventsFacade(mockCmNotificationSubscriptionNcmpOutEventProducer, + mockCmNotificationSubscriptionDmiInEventProducer) + + def 'Publish cm notification subscription ncmp out event'() { + given: 'an ncmp out event' + def ncmpOutEvent = new NcmpOutEvent() + when: 'the method to publish cm notification subscription ncmp out event is called' + objectUnderTest.publishNcmpOutEvent("some-id", + "some-event", ncmpOutEvent, true) + then: 'the parameters is delegated to the correct method once' + 1 * mockCmNotificationSubscriptionNcmpOutEventProducer.publishNcmpOutEvent( + "some-id", "some-event", ncmpOutEvent, true) + } + + def 'Publish cm notification subscription dmi in event'() { + given: 'a dmi in event' + def dmiInEvent = new DmiInEvent() + when: 'the method to publish cm notification subscription ncmp out event is called' + objectUnderTest.publishDmiInEvent("some-id", + "some-dmi", "some-event", dmiInEvent) + then: 'the parameters is delegated to the correct method once' + 1 * mockCmNotificationSubscriptionDmiInEventProducer.publishDmiInEvent("some-id", + "some-dmi", "some-event", dmiInEvent) + } +} \ No newline at end of file diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/MappersFacadeSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/MappersFacadeSpec.groovy new file mode 100644 index 0000000000..79b9ad578f --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/MappersFacadeSpec.groovy @@ -0,0 +1,65 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 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.impl.cmnotificationsubscription + + +import org.onap.cps.ncmp.impl.cmnotificationsubscription.dmi.DmiInEventMapper +import org.onap.cps.ncmp.impl.cmnotificationsubscription.ncmp.NcmpOutEventMapper +import spock.lang.Specification + +class MappersFacadeSpec extends Specification{ + + def mockCmNotificationDmiInEventMapper = Mock(DmiInEventMapper) + def mockCmNotificationNcmpOutEventMapper = Mock(NcmpOutEventMapper) + + def objectUnderTest = new MappersFacade(mockCmNotificationDmiInEventMapper, + mockCmNotificationNcmpOutEventMapper) + + def 'Get cm notification subscription DMI in event'() { + given: 'a list of predicates' + def testListOfPredicates = [] + when: 'method to create a cm notification subscription dmi in event is called with predicates' + objectUnderTest.toDmiInEvent(testListOfPredicates) + then: 'the parameters is delegated to the correct dmi in event mapper method' + 1 * mockCmNotificationDmiInEventMapper.toDmiInEvent(testListOfPredicates) + } + + def 'Get cm notification subscription ncmp out event'() { + given: 'a subscription details map' + def testSubscriptionDetailsMap = [:] + when: 'method to create cm notification subscription ncmp out event is called with the following parameters' + objectUnderTest.toNcmpOutEvent("test-id", testSubscriptionDetailsMap) + then: 'the parameters is delegated to the correct ncmp out event mapper method' + 1 * mockCmNotificationNcmpOutEventMapper.toNcmpOutEvent("test-id", + testSubscriptionDetailsMap) + } + + def 'Get cm notification subscription ncmp out event for a rejected request'() { + given: 'a list of target filters' + def testRejectedTargetFilters = [] + when: 'method to create cm notification subscription ncmp out event is called with the following parameters' + objectUnderTest.toNcmpOutEventForRejectedRequest( + "test-id", testRejectedTargetFilters) + then: 'the parameters is delegated to the correct ncmp out event mapper method' + 1 * mockCmNotificationNcmpOutEventMapper.toNcmpOutEventForRejectedRequest( + "test-id", testRejectedTargetFilters) + } +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/CmSubscriptionConfigSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/CmSubscriptionConfigSpec.groovy new file mode 100644 index 0000000000..915bccb53d --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/CmSubscriptionConfigSpec.groovy @@ -0,0 +1,63 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 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.impl.cmnotificationsubscription.cache + +import com.hazelcast.core.Hazelcast +import com.hazelcast.map.IMap +import org.onap.cps.ncmp.api.data.models.DatastoreType +import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.CmSubscriptionStatus +import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionDetails +import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionPredicate +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest +import spock.lang.Specification + +@SpringBootTest(classes = [CmSubscriptionConfig]) +class CmSubscriptionConfigSpec extends Specification { + + @Autowired + IMap> cmNotificationSubscriptionCache; + + def 'Embedded (hazelcast) cache for Cm Notification Subscription Cache.'() { + expect: 'system is able to create an instance of the Cm Notification Subscription Cache' + assert null != cmNotificationSubscriptionCache + and: 'there is at least 1 instance' + assert Hazelcast.allHazelcastInstances.size() > 0 + and: 'Cm Notification Subscription Cache is present' + assert Hazelcast.allHazelcastInstances.name.contains('hazelCastInstanceCmNotificationSubscription') + } + + def 'Provided CM Subscription data'() { + given: 'a cm subscription properties' + def subscriptionId = 'sub123' + def dmiPluginName = 'dummydmi' + def cmSubscriptionPredicates = new DmiCmSubscriptionPredicate(['cmhandle1', 'cmhandle2'].toSet(), DatastoreType.PASSTHROUGH_RUNNING, ['/a/b/c'].toSet()) + def cmSubscriptionCacheObject = new DmiCmSubscriptionDetails([cmSubscriptionPredicates], CmSubscriptionStatus.PENDING) + when: 'the cache is populated' + cmNotificationSubscriptionCache.put(subscriptionId, [(dmiPluginName): cmSubscriptionCacheObject]) + then: 'the values are present in memory' + assert cmNotificationSubscriptionCache.get(subscriptionId) != null + and: 'properties match' + assert dmiPluginName == cmNotificationSubscriptionCache.get(subscriptionId).keySet()[0] + assert cmSubscriptionCacheObject.cmSubscriptionStatus == cmNotificationSubscriptionCache.get(subscriptionId).values().cmSubscriptionStatus[0] + assert cmSubscriptionCacheObject.dmiCmSubscriptionPredicates[0].targetCmHandleIds == cmNotificationSubscriptionCache.get(subscriptionId).values().dmiCmSubscriptionPredicates[0].targetCmHandleIds[0] + } +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/DmiCacheHandlerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/DmiCacheHandlerSpec.groovy new file mode 100644 index 0000000000..b335843bb3 --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/cache/DmiCacheHandlerSpec.groovy @@ -0,0 +1,211 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 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.impl.cmnotificationsubscription.cache + +import com.fasterxml.jackson.databind.ObjectMapper +import io.cloudevents.CloudEvent +import io.cloudevents.core.builder.CloudEventBuilder +import org.apache.kafka.clients.consumer.ConsumerRecord +import org.onap.cps.ncmp.api.kafka.MessagingBaseSpec +import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.CmSubscriptionStatus +import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionDetails +import org.onap.cps.ncmp.impl.cmnotificationsubscription.utils.CmSubscriptionPersistenceService +import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.client_to_ncmp.NcmpInEvent +import org.onap.cps.ncmp.impl.inventory.InventoryPersistence +import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle +import org.onap.cps.ncmp.utils.TestUtils +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 static org.onap.cps.ncmp.utils.events.CloudEventMapper.toTargetEvent + +@SpringBootTest(classes = [ObjectMapper, JsonObjectMapper]) +class DmiCacheHandlerSpec extends MessagingBaseSpec { + + @Autowired + JsonObjectMapper jsonObjectMapper + @Autowired + ObjectMapper objectMapper + @SpringBean + InventoryPersistence mockInventoryPersistence = Mock(InventoryPersistence) + @SpringBean + CmSubscriptionPersistenceService mockCmSubscriptionPersistenceService = Mock(CmSubscriptionPersistenceService) + + def testCache = [:] + def objectUnderTest = new DmiCacheHandler(mockCmSubscriptionPersistenceService, testCache, mockInventoryPersistence) + + NcmpInEvent ncmpInEvent + def yangModelCmHandle1 = new YangModelCmHandle(id:'ch1',dmiServiceName:'dmi-1') + def yangModelCmHandle2 = new YangModelCmHandle(id:'ch2',dmiServiceName:'dmi-2') + def yangModelCmHandle3 = new YangModelCmHandle(id:'ch3',dmiServiceName:'dmi-1') + def yangModelCmHandle4 = new YangModelCmHandle(id:'ch4',dmiServiceName:'dmi-2') + + def setup() { + setUpTestEvent() + initialiseMockInventoryPersistenceResponses() + } + + def 'Load CM subscription event to cache'() { + given: 'a valid subscription event with Id' + def subscriptionId = ncmpInEvent.getData().getSubscriptionId() + and: 'list of predicates' + def predicates = ncmpInEvent.getData().getPredicates() + when: 'a valid event object loaded in cache' + objectUnderTest.add(subscriptionId, predicates) + then: 'the cache contains the correct entry with #subscriptionId subscription ID' + assert testCache.containsKey(subscriptionId) + } + + def 'Get cache entry via subscription id'() { + given: 'the cache contains value for some-id' + testCache.put('some-id',[:]) + when: 'the get method is called' + def result = objectUnderTest.get('some-id') + then: 'correct value is returned as expected' + assert result == [:] + } + + def 'Remove accepted and rejected entries from cache via subscription id'() { + given: 'a map as the value for cache entry for some-id' + def testMap = [:] + testMap.put("dmi-1", + new DmiCmSubscriptionDetails([],CmSubscriptionStatus.ACCEPTED)) + testMap.put("dmi-2", + new DmiCmSubscriptionDetails([],CmSubscriptionStatus.REJECTED)) + testMap.put("dmi-3", + new DmiCmSubscriptionDetails([],CmSubscriptionStatus.PENDING)) + testCache.put("test-id", testMap) + assert testCache.get("test-id").size() == 3 + when: 'the method to remove accepted and rejected entries for test-id is called' + objectUnderTest.removeAcceptedAndRejectedDmiSubscriptionEntries("test-id") + then: 'all entries with status accepted/rejected are no longer present for test-id' + testCache.get("test-id").each { key, testResultMap -> + assert testResultMap.cmSubscriptionStatus != CmSubscriptionStatus.ACCEPTED + || testResultMap.cmSubscriptionStatus != CmSubscriptionStatus.REJECTED + } + and: 'the size of the map for cache entry test-id is as expected' + assert testCache.get("test-id").size() == 1 + } + + def 'Create map for DMI cm notification subscription per DMI service name'() { + given: 'list of predicates from the create subscription event' + def predicates = ncmpInEvent.getData().getPredicates() + when: 'method to create map of DMI cm notification subscription per DMI service name is called' + def result = objectUnderTest.createDmiSubscriptionsPerDmi(predicates) + then: 'the result size of resulting map is correct to the number of DMIs' + assert result.size() == 2 + and: 'the cache objects per DMI exists' + def resultMapForDmi1 = result.get('dmi-1') + def resultMapForDmi2 = result.get('dmi-2') + assert resultMapForDmi1 != null + assert resultMapForDmi2 != null + and: 'the size of predicates in each object is correct' + assert resultMapForDmi1.dmiCmSubscriptionPredicates.size() == 2 + assert resultMapForDmi2.dmiCmSubscriptionPredicates.size() == 2 + and: 'the subscription status in each object is correct' + assert resultMapForDmi1.cmSubscriptionStatus.toString() == 'PENDING' + assert resultMapForDmi2.cmSubscriptionStatus.toString() == 'PENDING' + and: 'the target cmHandles for each predicate is correct' + assert resultMapForDmi1.dmiCmSubscriptionPredicates[0].targetCmHandleIds == ['ch1'].toSet() + assert resultMapForDmi1.dmiCmSubscriptionPredicates[1].targetCmHandleIds == ['ch3'].toSet() + + assert resultMapForDmi2.dmiCmSubscriptionPredicates[0].targetCmHandleIds == ['ch2'].toSet() + assert resultMapForDmi2.dmiCmSubscriptionPredicates[1].targetCmHandleIds == ['ch4'].toSet() + and: 'the list of xpath for each is correct' + assert resultMapForDmi1.dmiCmSubscriptionPredicates[0].xpaths + && resultMapForDmi2.dmiCmSubscriptionPredicates[0].xpaths == ['/x1/y1', 'x2/y2'].toSet() + + assert resultMapForDmi1.dmiCmSubscriptionPredicates[1].xpaths + && resultMapForDmi2.dmiCmSubscriptionPredicates[1].xpaths == ['/x3/y3', 'x4/y4'].toSet() + } + + def 'Get map for cm handle IDs by DMI service name'() { + given: 'the predicate from the test request CM subscription event' + def targetFilter = ncmpInEvent.getData().getPredicates().get(0).getTargetFilter() + when: 'the method to group all target CM handles by DMI service name is called' + def mapOfCMHandleIDsByDmi = objectUnderTest.groupTargetCmHandleIdsByDmi(targetFilter) + then: 'the size of the resulting map is correct' + assert mapOfCMHandleIDsByDmi.size() == 2 + and: 'the values in the map is as expected' + assert mapOfCMHandleIDsByDmi.get('dmi-1') == ['ch1'].toSet() + assert mapOfCMHandleIDsByDmi.get('dmi-2') == ['ch2'].toSet() + } + + def 'Update subscription status in cache per DMI service name'() { + given: 'populated cache' + def predicates = ncmpInEvent.getData().getPredicates() + def subscriptionId = ncmpInEvent.getData().getSubscriptionId() + objectUnderTest.add(subscriptionId, predicates) + when: 'subscription status per dmi is updated in cache' + objectUnderTest.updateDmiSubscriptionStatusPerDmi(subscriptionId,'dmi-1', CmSubscriptionStatus.ACCEPTED) + then: 'verify status has been updated in cache' + def predicate = testCache.get(subscriptionId) + assert predicate.get('dmi-1').cmSubscriptionStatus == CmSubscriptionStatus.ACCEPTED + } + + def 'Persist Cache into database per dmi'() { + given: 'populated cache' + def predicates = ncmpInEvent.getData().getPredicates() + def subscriptionId = ncmpInEvent.getData().getSubscriptionId() + objectUnderTest.add(subscriptionId, predicates) + when: 'subscription is persisted in database' + objectUnderTest.persistIntoDatabasePerDmi(subscriptionId,'dmi-1') + then: 'persistence service is called the correct number of times per dmi' + 4 * mockCmSubscriptionPersistenceService.addCmSubscription(_,_,_,subscriptionId) + } + + def 'Remove subscription from database per dmi'() { + given: 'populated cache' + def predicates = ncmpInEvent.getData().getPredicates() + def subscriptionId = ncmpInEvent.getData().getSubscriptionId() + objectUnderTest.add(subscriptionId, predicates) + when: 'subscription is persisted in database' + objectUnderTest.removeFromDatabasePerDmi(subscriptionId,'dmi-1') + then: 'persistence service is called the correct number of times per dmi' + 4 * mockCmSubscriptionPersistenceService.removeCmSubscription(_,_,_,subscriptionId) + } + + def setUpTestEvent(){ + def jsonData = TestUtils.getResourceFileContent('cmSubscription/cmNotificationSubscriptionNcmpInEvent.json') + def testEventSent = jsonObjectMapper.convertJsonString(jsonData, NcmpInEvent.class) + def testCloudEventSent = CloudEventBuilder.v1() + .withData(objectMapper.writeValueAsBytes(testEventSent)) + .withId('subscriptionCreated') + .withType('subscriptionCreated') + .withSource(URI.create('some-resource')) + .withExtension('correlationid', 'test-cmhandle1').build() + def consumerRecord = new ConsumerRecord('topic-name', 0, 0, 'event-key', testCloudEventSent) + def cloudEvent = consumerRecord.value() + + ncmpInEvent = toTargetEvent(cloudEvent, NcmpInEvent.class); + } + + def initialiseMockInventoryPersistenceResponses(){ + mockInventoryPersistence.getYangModelCmHandles(['ch1','ch2']) + >> [yangModelCmHandle1, yangModelCmHandle2] + + mockInventoryPersistence.getYangModelCmHandles(['ch3','ch4']) + >> [yangModelCmHandle3, yangModelCmHandle4] + } + +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/cmavc/CmAvcEventConsumerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/cmavc/CmAvcEventConsumerSpec.groovy new file mode 100644 index 0000000000..98cc383e7f --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/cmavc/CmAvcEventConsumerSpec.groovy @@ -0,0 +1,95 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (c) 2023-2024 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.impl.cmnotificationsubscription.cmavc + +import com.fasterxml.jackson.databind.ObjectMapper +import io.cloudevents.CloudEvent +import io.cloudevents.core.builder.CloudEventBuilder +import io.cloudevents.kafka.CloudEventDeserializer +import io.cloudevents.kafka.impl.KafkaHeaders +import org.apache.kafka.clients.consumer.ConsumerRecord +import org.apache.kafka.clients.consumer.KafkaConsumer +import org.onap.cps.events.EventsPublisher +import org.onap.cps.ncmp.api.kafka.MessagingBaseSpec +import org.onap.cps.ncmp.events.avc1_0_0.AvcEvent +import org.onap.cps.ncmp.utils.TestUtils +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 org.springframework.test.annotation.DirtiesContext +import org.testcontainers.spock.Testcontainers + +import java.time.Duration + +import static org.onap.cps.ncmp.utils.events.CloudEventMapper.toTargetEvent + +@SpringBootTest(classes = [EventsPublisher, CmAvcEventConsumer, ObjectMapper, JsonObjectMapper]) +@Testcontainers +@DirtiesContext +class CmAvcEventConsumerSpec extends MessagingBaseSpec { + + @SpringBean + EventsPublisher eventsPublisher = new EventsPublisher(legacyEventKafkaTemplate, cloudEventKafkaTemplate) + + @SpringBean + CmAvcEventConsumer acvEventConsumer = new CmAvcEventConsumer(eventsPublisher) + + @Autowired + JsonObjectMapper jsonObjectMapper + + def cloudEventKafkaConsumer = new KafkaConsumer<>(eventConsumerConfigProperties('ncmp-group', CloudEventDeserializer)) + + def 'Consume and forward valid message'() { + given: 'consumer has a subscription on a topic' + def cmEventsTopicName = 'cm-events' + acvEventConsumer.cmEventsTopicName = cmEventsTopicName + cloudEventKafkaConsumer.subscribe([cmEventsTopicName] as List) + and: 'an event is sent' + def jsonData = TestUtils.getResourceFileContent('sampleAvcInputEvent.json') + def testEventSent = jsonObjectMapper.convertJsonString(jsonData, AvcEvent.class) + def testCloudEventSent = CloudEventBuilder.v1() + .withData(jsonObjectMapper.asJsonBytes(testEventSent)) + .withId('sample-eventid') + .withType('sample-test-type') + .withSource(URI.create('sample-test-source')) + .withExtension('correlationid', 'test-cmhandle1').build() + and: 'event has header information' + def consumerRecord = new ConsumerRecord(cmEventsTopicName, 0, 0, 'sample-eventid', testCloudEventSent) + when: 'the event is consumed' + acvEventConsumer.consumeAndForward(consumerRecord) + and: 'the topic is polled' + def records = cloudEventKafkaConsumer.poll(Duration.ofMillis(1500)) + then: 'poll returns one record' + assert records.size() == 1 + and: 'record can be converted to AVC event' + def record = records.iterator().next() + def cloudEvent = record.value() as CloudEvent + def convertedAvcEvent = toTargetEvent(cloudEvent, AvcEvent.class) + and: 'we have correct headers forwarded where correlation id matches' + assert KafkaHeaders.getParsedKafkaHeader(record.headers(), 'ce_correlationid') == 'test-cmhandle1' + and: 'event id differs(as per requirement) between consumed and forwarded' + assert KafkaHeaders.getParsedKafkaHeader(record.headers(), 'ce_id') != 'sample-eventid' + and: 'the event payload still matches' + assert testEventSent == convertedAvcEvent + } + +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiInEventMapperSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiInEventMapperSpec.groovy new file mode 100644 index 0000000000..62d1572502 --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiInEventMapperSpec.groovy @@ -0,0 +1,57 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 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.impl.cmnotificationsubscription.dmi + + +import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionPredicate +import org.onap.cps.ncmp.impl.inventory.InventoryPersistence +import org.onap.cps.ncmp.impl.inventory.models.YangModelCmHandle +import spock.lang.Specification + +import static org.onap.cps.ncmp.api.data.models.DatastoreType.PASSTHROUGH_OPERATIONAL +import static org.onap.cps.ncmp.api.data.models.DatastoreType.PASSTHROUGH_RUNNING + +class DmiInEventMapperSpec extends Specification { + + def mockInventoryPersistence = Mock(InventoryPersistence) + + def objectUnderTest = new DmiInEventMapper(mockInventoryPersistence) + + def setup() { + def yangModelCmHandles = [new YangModelCmHandle(id: 'ch-1', dmiProperties: [new YangModelCmHandle.Property('k1', 'v1')], publicProperties: []), + new YangModelCmHandle(id: 'ch-2', dmiProperties: [new YangModelCmHandle.Property('k2', 'v2')], publicProperties: [])] + mockInventoryPersistence.getYangModelCmHandles(['ch-1', 'ch-2'] as Set) >> yangModelCmHandles + } + + def 'Check for Cm Notification Subscription DMI In Event mapping'() { + given: 'a collection of cm subscription predicates' + def dmiSubscriptionPredicates = [new DmiCmSubscriptionPredicate(['ch-1'].toSet(), PASSTHROUGH_RUNNING, ['/ch-1'].toSet()), + new DmiCmSubscriptionPredicate(['ch-2'].toSet(), PASSTHROUGH_OPERATIONAL, ['/ch-2'].toSet())] + when: 'we try to map the values' + def result = objectUnderTest.toDmiInEvent(dmiSubscriptionPredicates) + then: 'it contains correct cm notification subscription cmhandle object' + assert result.data.cmHandles.cmhandleId.containsAll(['ch-1', 'ch-2']) + assert result.data.cmHandles.privateProperties.containsAll([['k1': 'v1'], ['k2': 'v2']]) + and: 'also has the correct dmi cm notification subscription predicates' + assert result.data.predicates.targetFilter.containsAll([['ch-1'], ['ch-2']]) + + } +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiInEventProducerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiInEventProducerSpec.groovy new file mode 100644 index 0000000000..34fa4549f5 --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiInEventProducerSpec.groovy @@ -0,0 +1,64 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (c) 2024 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.impl.cmnotificationsubscription.dmi + +import com.fasterxml.jackson.databind.ObjectMapper +import io.cloudevents.CloudEvent +import org.onap.cps.events.EventsPublisher +import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.ncmp_to_dmi.CmHandle +import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.ncmp_to_dmi.Data +import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.ncmp_to_dmi.DmiInEvent +import org.onap.cps.ncmp.utils.events.CloudEventMapper +import org.onap.cps.utils.JsonObjectMapper +import spock.lang.Specification + +class DmiInEventProducerSpec extends Specification { + + def mockEventsPublisher = Mock(EventsPublisher) + def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) + + def objectUnderTest = new DmiInEventProducer(mockEventsPublisher, jsonObjectMapper) + + def 'Create and Publish Cm Notification Subscription DMI In Event'() { + given: 'a cm subscription for a dmi plugin' + def subscriptionId = 'test-subscription-id' + def dmiPluginName = 'test-dmiplugin' + def eventType = 'subscriptionCreateRequest' + def dmiInEvent = new DmiInEvent(data: new Data(cmHandles: [new CmHandle(cmhandleId: 'test-1', privateProperties: [:])])) + and: 'also we have target topic for dmiPlugin' + objectUnderTest.dmiInEventTopic = 'dmiplugin-test-topic' + when: 'the event is published' + objectUnderTest.publishDmiInEvent(subscriptionId, dmiPluginName, eventType, dmiInEvent) + then: 'the event contains the required attributes' + 1 * mockEventsPublisher.publishCloudEvent(_, _, _) >> { + args -> + { + assert args[0] == 'dmiplugin-test-topic' + assert args[1] == subscriptionId + def dmiInEventAsCloudEvent = (args[2] as CloudEvent) + assert dmiInEventAsCloudEvent.getExtension('correlationid') == subscriptionId + '#' + dmiPluginName + assert dmiInEventAsCloudEvent.type == 'subscriptionCreateRequest' + assert dmiInEventAsCloudEvent.source.toString() == 'NCMP' + assert CloudEventMapper.toTargetEvent(dmiInEventAsCloudEvent, DmiInEvent) == dmiInEvent + } + } + } +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiOutEventConsumerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiOutEventConsumerSpec.groovy new file mode 100644 index 0000000000..44e24042a8 --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/dmi/DmiOutEventConsumerSpec.groovy @@ -0,0 +1,123 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (c) 2024 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.impl.cmnotificationsubscription.dmi + +import ch.qos.logback.classic.Level +import ch.qos.logback.classic.Logger +import ch.qos.logback.classic.spi.ILoggingEvent +import ch.qos.logback.core.read.ListAppender +import com.fasterxml.jackson.databind.ObjectMapper +import io.cloudevents.CloudEvent +import io.cloudevents.core.builder.CloudEventBuilder +import org.apache.kafka.clients.consumer.ConsumerRecord +import org.onap.cps.ncmp.api.kafka.MessagingBaseSpec +import org.onap.cps.ncmp.impl.cmnotificationsubscription.EventsFacade +import org.onap.cps.ncmp.impl.cmnotificationsubscription.MappersFacade +import org.onap.cps.ncmp.impl.cmnotificationsubscription.cache.DmiCacheHandler +import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.dmi_to_ncmp.Data +import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.dmi_to_ncmp.DmiOutEvent +import org.onap.cps.ncmp.utils.TestUtils +import org.onap.cps.utils.JsonObjectMapper +import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest + +import static org.onap.cps.ncmp.impl.cmnotificationsubscription.models.CmSubscriptionStatus.ACCEPTED +import static org.onap.cps.ncmp.impl.cmnotificationsubscription.models.CmSubscriptionStatus.REJECTED + +@SpringBootTest(classes = [ObjectMapper, JsonObjectMapper]) +class DmiOutEventConsumerSpec extends MessagingBaseSpec { + + @Autowired + JsonObjectMapper jsonObjectMapper + + @Autowired + ObjectMapper objectMapper + + def mockDmiCacheHandler = Mock(DmiCacheHandler) + def mockEventsHandler = Mock(EventsFacade) + def mockMappersHandler = Mock(MappersFacade) + + def objectUnderTest = new DmiOutEventConsumer(mockDmiCacheHandler, mockEventsHandler, mockMappersHandler) + def logger = Spy(ListAppender) + + void setup() { + ((Logger) LoggerFactory.getLogger(DmiOutEventConsumer.class)).addAppender(logger) + logger.start() + } + + void cleanup() { + ((Logger) LoggerFactory.getLogger(DmiOutEventConsumer.class)).detachAndStopAllAppenders() + } + + + def 'Consume valid CM Subscription response from DMI Plugin'() { + given: 'a cmsubscription event' + def jsonData = TestUtils.getResourceFileContent('cmSubscription/cmNotificationSubscriptionDmiOutEvent.json') + def testEventSent = jsonObjectMapper.convertJsonString(jsonData, DmiOutEvent.class) + def testCloudEventSent = CloudEventBuilder.v1() + .withData(objectMapper.writeValueAsBytes(testEventSent)) + .withId('random-uuid') + .withType('subscriptionCreateResponse') + .withSource(URI.create('test-dmi-plugin-name')) + .withExtension('correlationid', 'sub-1#test-dmi-plugin-name').build() + def consumerRecord = new ConsumerRecord('topic-name', 0, 0, 'event-key', testCloudEventSent) + when: 'the valid event is consumed' + objectUnderTest.consumeDmiOutEvent(consumerRecord) + then: 'an event is logged with level INFO' + def loggingEvent = getLoggingEvent() + assert loggingEvent.level == Level.INFO + and: 'the log indicates the task completed successfully' + assert loggingEvent.formattedMessage == 'Cm Subscription with id : sub-1 handled by the dmi-plugin : test-dmi-plugin-name has the status : accepted' + } + + def 'Consume a valid CM Notification Subscription Event and perform correct actions base on status'() { + given: 'a cmNotificationSubscription event' + def dmiOutEventData = new Data(statusCode: statusCode, statusMessage: subscriptionStatus.toString()) + def dmiOutEvent = new DmiOutEvent().withData(dmiOutEventData) + def testCloudEventSent = CloudEventBuilder.v1() + .withData(objectMapper.writeValueAsBytes(dmiOutEvent)) + .withId('random-uuid') + .withType('subscriptionCreateResponse') + .withSource(URI.create('test-dmi-plugin-name')) + .withExtension('correlationid', 'sub-1#test-dmi-plugin-name').build() + def consumerRecord = new ConsumerRecord('topic-name', 0, 0, 'event-key', testCloudEventSent) + when: 'the event is consumed' + objectUnderTest.consumeDmiOutEvent(consumerRecord) + then: 'correct number of calls to cache' + expectedCacheCalls * mockDmiCacheHandler.updateDmiSubscriptionStatusPerDmi('sub-1','test-dmi-plugin-name', subscriptionStatus) + and: 'correct number of calls to persist cache' + expectedPersistenceCalls * mockDmiCacheHandler.persistIntoDatabasePerDmi('sub-1','test-dmi-plugin-name') + and: 'correct number of calls to map the ncmp out event' + 1 * mockMappersHandler.toNcmpOutEvent('sub-1', _) + and: 'correct number of calls to publish the ncmp out event to client' + 1 * mockEventsHandler.publishNcmpOutEvent('sub-1', 'subscriptionCreateResponse', _, false) + where: 'the following parameters are used' + scenario | subscriptionStatus | statusCode || expectedCacheCalls | expectedPersistenceCalls + 'Accepted Status' | ACCEPTED | '1' || 1 | 1 + 'Rejected Status' | REJECTED | '104' || 1 | 0 + } + + def getLoggingEvent() { + return logger.list[0] + } + +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/CmSubscriptionComparatorSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/CmSubscriptionComparatorSpec.groovy new file mode 100644 index 0000000000..0ebf9a6aed --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/CmSubscriptionComparatorSpec.groovy @@ -0,0 +1,62 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (c) 2024 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.impl.cmnotificationsubscription.ncmp + + +import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionPredicate +import org.onap.cps.ncmp.impl.cmnotificationsubscription.utils.CmSubscriptionPersistenceService +import spock.lang.Specification + +import static org.onap.cps.ncmp.api.data.models.DatastoreType.PASSTHROUGH_OPERATIONAL + +class CmSubscriptionComparatorSpec extends Specification { + + def mockCmSubscriptionPersistenceService = Mock(CmSubscriptionPersistenceService) + def objectUnderTest = new CmSubscriptionComparator(mockCmSubscriptionPersistenceService) + + def 'Find Delta of given list of predicates'() { + given: 'A list of predicates' + def predicates = [new DmiCmSubscriptionPredicate(['ch-1', 'ch-2'].toSet(), PASSTHROUGH_OPERATIONAL, ['a/1/', 'b/2'].toSet())] + and: '3 positive responses and 1 negative.' + mockCmSubscriptionPersistenceService.isOngoingCmSubscription(PASSTHROUGH_OPERATIONAL, 'ch-1', 'a/1/') >>> true + mockCmSubscriptionPersistenceService.isOngoingCmSubscription(PASSTHROUGH_OPERATIONAL, 'ch-1', 'b/2') >>> true + mockCmSubscriptionPersistenceService.isOngoingCmSubscription(PASSTHROUGH_OPERATIONAL, 'ch-2', 'a/1/') >>> true + mockCmSubscriptionPersistenceService.isOngoingCmSubscription(PASSTHROUGH_OPERATIONAL, 'ch-2', 'b/2') >>> false + when: 'getDelta is called' + def result = objectUnderTest.getNewDmiSubscriptionPredicates(predicates) + then: 'verify correct delta is returned' + assert result.size() == 1 + assert result[0].targetCmHandleIds[0] == 'ch-2' + assert result[0].xpaths[0] == 'b/2' + + } + + def 'Find Delta of given list of predicates when it is an ongoing Cm Subscription'() { + given: 'A list of predicates' + def predicates = [new DmiCmSubscriptionPredicate(['ch-1'].toSet(), PASSTHROUGH_OPERATIONAL, ['a/1/'].toSet())] + and: 'its already present' + mockCmSubscriptionPersistenceService.isOngoingCmSubscription(PASSTHROUGH_OPERATIONAL, 'ch-1', 'a/1/') >>> true + when: 'getDelta is called' + def result = objectUnderTest.getNewDmiSubscriptionPredicates(predicates) + then: 'verify correct delta is returned' + assert result.size() == 0 + } + +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/CmSubscriptionHandlerImplSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/CmSubscriptionHandlerImplSpec.groovy new file mode 100644 index 0000000000..caefdeea1d --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/CmSubscriptionHandlerImplSpec.groovy @@ -0,0 +1,145 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (c) 2024 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.impl.cmnotificationsubscription.ncmp + +import com.fasterxml.jackson.databind.ObjectMapper +import org.onap.cps.ncmp.impl.cmnotificationsubscription.EventsFacade +import org.onap.cps.ncmp.impl.cmnotificationsubscription.MappersFacade +import org.onap.cps.ncmp.impl.cmnotificationsubscription.cache.DmiCacheHandler +import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionDetails +import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionPredicate +import org.onap.cps.ncmp.impl.cmnotificationsubscription.utils.CmSubscriptionPersistenceService +import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.client_to_ncmp.NcmpInEvent +import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.ncmp_to_client.NcmpOutEvent +import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.ncmp_to_dmi.DmiInEvent +import org.onap.cps.ncmp.utils.TestUtils +import org.onap.cps.utils.JsonObjectMapper +import spock.lang.Specification + +import static org.onap.cps.ncmp.api.data.models.DatastoreType.PASSTHROUGH_OPERATIONAL +import static org.onap.cps.ncmp.impl.cmnotificationsubscription.models.CmSubscriptionStatus.ACCEPTED +import static org.onap.cps.ncmp.impl.cmnotificationsubscription.models.CmSubscriptionStatus.PENDING + +class CmSubscriptionHandlerImplSpec extends Specification { + + def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) + def mockCmSubscriptionPersistenceService = Mock(CmSubscriptionPersistenceService); + def mockCmSubscriptionComparator = Mock(CmSubscriptionComparator); + def mockMappersFacade = Mock(MappersFacade); + def mockEventsFacade = Mock(EventsFacade); + def mockDmiCacheHandler = Mock(DmiCacheHandler); + + def objectUnderTest = new CmSubscriptionHandlerImpl(mockCmSubscriptionPersistenceService, + mockCmSubscriptionComparator, mockMappersFacade, + mockEventsFacade, mockDmiCacheHandler) + + def testDmiSubscriptionsPerDmi = ["dmi-1": new DmiCmSubscriptionDetails([], PENDING)] + + def 'Consume valid and unique CmNotificationSubscriptionNcmpInEvent create message'() { + given: 'a cmNotificationSubscriptionNcmp in event with unique subscription id' + def jsonData = TestUtils.getResourceFileContent('cmSubscription/cmNotificationSubscriptionNcmpInEvent.json') + def testEventConsumed = jsonObjectMapper.convertJsonString(jsonData, NcmpInEvent.class) + def testListOfDeltaPredicates = [new DmiCmSubscriptionPredicate(['ch1'].toSet(), PASSTHROUGH_OPERATIONAL, ['/a/b'].toSet())] + mockCmSubscriptionPersistenceService.isUniqueSubscriptionId("test-id") >> true + and: 'relevant details is extracted from the event' + def subscriptionId = testEventConsumed.getData().getSubscriptionId() + def predicates = testEventConsumed.getData().getPredicates() + and: 'the cache handler returns for relevant subscription id' + 1 * mockDmiCacheHandler.get("test-id") >> testDmiSubscriptionsPerDmi + and: 'the delta predicates is returned' + 1 * mockCmSubscriptionComparator.getNewDmiSubscriptionPredicates(_) >> testListOfDeltaPredicates + and: 'the DMI in event mapper returns cm notification subscription event' + def testDmiInEvent = new DmiInEvent() + 1 * mockMappersFacade + .toDmiInEvent(testListOfDeltaPredicates) >> testDmiInEvent + when: 'the valid and unique event is consumed' + objectUnderTest.processSubscriptionCreateRequest(subscriptionId, predicates) + then: 'the subscription cache handler is called once' + 1 * mockDmiCacheHandler.add('test-id', _) + and: 'the events handler method to publish DMI event is called correct number of times with the correct parameters' + testDmiSubscriptionsPerDmi.size() * mockEventsFacade.publishDmiInEvent( + "test-id", "dmi-1", "subscriptionCreateRequest", testDmiInEvent) + and: 'we schedule to send the response after configured time from the cache' + 1 * mockEventsFacade.publishNcmpOutEvent('test-id', 'subscriptionCreateResponse', null, true) + } + + def 'Consume valid and Overlapping Cm Notification Subscription NcmpIn Event'() { + given: 'a cmNotificationSubscriptionNcmp in event with unique subscription id' + def jsonData = TestUtils.getResourceFileContent('cmSubscription/cmNotificationSubscriptionNcmpInEvent.json') + def testEventConsumed = jsonObjectMapper.convertJsonString(jsonData, NcmpInEvent.class) + def noDeltaPredicates = [] + mockCmSubscriptionPersistenceService.isUniqueSubscriptionId("test-id") >> true + and: 'the cache handler returns for relevant subscription id' + 1 * mockDmiCacheHandler.get('test-id') >> testDmiSubscriptionsPerDmi + and: 'the delta predicates is returned' + 1 * mockCmSubscriptionComparator.getNewDmiSubscriptionPredicates(_) >> noDeltaPredicates + when: 'the valid and unique event is consumed' + objectUnderTest.processSubscriptionCreateRequest('test-id', noDeltaPredicates) + then: 'the subscription cache handler is called once' + 1 * mockDmiCacheHandler.add('test-id', _) + and: 'the subscription details are updated in the cache' + 1 * mockDmiCacheHandler.updateDmiSubscriptionStatusPerDmi('test-id', _, ACCEPTED) + and: 'we schedule to send the response after configured time from the cache' + 1 * mockEventsFacade.publishNcmpOutEvent('test-id', 'subscriptionCreateResponse', null, true) + } + + def 'Consume valid and but non-unique CmNotificationSubscription create message'() { + given: 'a cmNotificationSubscriptionNcmp in event' + def jsonData = TestUtils.getResourceFileContent('cmSubscription/cmNotificationSubscriptionNcmpInEvent.json') + def testEventConsumed = jsonObjectMapper.convertJsonString(jsonData, NcmpInEvent.class) + mockCmSubscriptionPersistenceService.isUniqueSubscriptionId('test-id') >> false + and: 'relevant details is extracted from the event' + def subscriptionId = testEventConsumed.getData().getSubscriptionId() + def predicates = testEventConsumed.getData().getPredicates() + and: 'the NCMP out in event mapper returns an event for rejected request' + def testNcmpOutEvent = new NcmpOutEvent() + 1 * mockMappersFacade.toNcmpOutEventForRejectedRequest( + "test-id", _) >> testNcmpOutEvent + when: 'the valid but non-unique event is consumed' + objectUnderTest.processSubscriptionCreateRequest(subscriptionId, predicates) + then: 'the events handler method to publish DMI event is never called' + 0 * mockEventsFacade.publishDmiInEvent(_, _, _, _) + and: 'the events handler method to publish NCMP out event is called once' + 1 * mockEventsFacade.publishNcmpOutEvent('test-id', 'subscriptionCreateResponse', testNcmpOutEvent, false) + } + + def 'Consume valid CmNotificationSubscriptionNcmpInEvent delete message'() { + given: 'a cmNotificationSubscriptionNcmp in event for delete' + def jsonData = TestUtils.getResourceFileContent('cmSubscription/cmNotificationSubscriptionNcmpInEvent.json') + def testEventConsumed = jsonObjectMapper.convertJsonString(jsonData, NcmpInEvent.class) + and: 'relevant details is extracted from the event' + def subscriptionId = testEventConsumed.getData().getSubscriptionId() + def predicates = testEventConsumed.getData().getPredicates() + and: 'the cache handler returns for relevant subscription id' + 1 * mockDmiCacheHandler.get('test-id') >> testDmiSubscriptionsPerDmi + when: 'the valid and unique event is consumed' + objectUnderTest.processSubscriptionDeleteRequest(subscriptionId, predicates) + then: 'the subscription cache handler is called once' + 1 * mockDmiCacheHandler.add('test-id', predicates) + and: 'the mapper handler to get DMI in event is called once' + 1 * mockMappersFacade.toDmiInEvent(_) + and: 'the events handler method to publish DMI event is called correct number of times with the correct parameters' + testDmiSubscriptionsPerDmi.size() * mockEventsFacade.publishDmiInEvent( + 'test-id', 'dmi-1', 'subscriptionDeleteRequest', _) + and: 'we schedule to send the response after configured time from the cache' + 1 * mockEventsFacade.publishNcmpOutEvent('test-id', 'subscriptionDeleteResponse', null, true) + } +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpInEventConsumerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpInEventConsumerSpec.groovy new file mode 100644 index 0000000000..637e9ebfec --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpInEventConsumerSpec.groovy @@ -0,0 +1,111 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (c) 2024 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.impl.cmnotificationsubscription.ncmp + +import ch.qos.logback.classic.Level +import ch.qos.logback.classic.Logger +import ch.qos.logback.classic.spi.ILoggingEvent +import ch.qos.logback.core.read.ListAppender +import com.fasterxml.jackson.databind.ObjectMapper +import io.cloudevents.CloudEvent +import io.cloudevents.core.builder.CloudEventBuilder +import org.apache.kafka.clients.consumer.ConsumerRecord +import org.onap.cps.ncmp.api.kafka.MessagingBaseSpec +import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.client_to_ncmp.NcmpInEvent +import org.onap.cps.ncmp.utils.TestUtils +import org.onap.cps.utils.JsonObjectMapper +import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.SpringBootTest + +@SpringBootTest(classes = [ObjectMapper, JsonObjectMapper]) +class NcmpInEventConsumerSpec extends MessagingBaseSpec { + + def mockCmSubscriptionHandler = Mock(CmSubscriptionHandler) + def objectUnderTest = new NcmpInEventConsumer(mockCmSubscriptionHandler) + def logger = Spy(ListAppender) + + @Autowired + JsonObjectMapper jsonObjectMapper + + @Autowired + ObjectMapper objectMapper + + void setup() { + ((Logger) LoggerFactory.getLogger(NcmpInEventConsumer.class)).addAppender(logger) + logger.start() + } + + void cleanup() { + ((Logger) LoggerFactory.getLogger(NcmpInEventConsumer.class)).detachAndStopAllAppenders() + } + + + def 'Consume valid CmNotificationSubscriptionNcmpInEvent create message'() { + given: 'a cmNotificationSubscription event' + def jsonData = TestUtils.getResourceFileContent('cmSubscription/cmNotificationSubscriptionNcmpInEvent.json') + def testEventSent = jsonObjectMapper.convertJsonString(jsonData, NcmpInEvent.class) + def testCloudEventSent = CloudEventBuilder.v1() + .withData(objectMapper.writeValueAsBytes(testEventSent)) + .withId('subscriptionCreated') + .withType('subscriptionCreateRequest') + .withSource(URI.create('some-resource')) + .withExtension('correlationid', 'test-cmhandle1').build() + def consumerRecord = new ConsumerRecord('topic-name', 0, 0, 'event-key', testCloudEventSent) + when: 'the valid event is consumed' + objectUnderTest.consumeSubscriptionEvent(consumerRecord) + then: 'an event is logged with level INFO' + def loggingEvent = getLoggingEvent() + assert loggingEvent.level == Level.INFO + and: 'the log indicates the task completed successfully' + assert loggingEvent.formattedMessage == 'Subscription create request for source some-resource with subscription id test-id ...' + and: 'the subscription handler service is called once' + 1 * mockCmSubscriptionHandler.processSubscriptionCreateRequest('test-id',_) + } + + def 'Consume valid CmNotificationSubscriptionNcmpInEvent delete message'() { + given: 'a cmNotificationSubscription event' + def jsonData = TestUtils.getResourceFileContent('cmSubscription/cmNotificationSubscriptionNcmpInEvent.json') + def testEventSent = jsonObjectMapper.convertJsonString(jsonData, NcmpInEvent.class) + def testCloudEventSent = CloudEventBuilder.v1() + .withData(objectMapper.writeValueAsBytes(testEventSent)) + .withId('sub-id') + .withType('subscriptionDeleteRequest') + .withSource(URI.create('some-resource')) + .withExtension('correlationid', 'test-cmhandle1').build() + def consumerRecord = new ConsumerRecord('topic-name', 0, 0, 'event-key', testCloudEventSent) + when: 'the valid event is consumed' + objectUnderTest.consumeSubscriptionEvent(consumerRecord) + then: 'an event is logged with level INFO' + def loggingEvent = getLoggingEvent() + assert loggingEvent.level == Level.INFO + and: 'the log indicates the task completed successfully' + assert loggingEvent.formattedMessage == 'Subscription delete request for source some-resource with subscription id test-id ...' + and: 'the subscription handler service is called once' + 1 * mockCmSubscriptionHandler.processSubscriptionDeleteRequest('test-id',_) + } + + + def getLoggingEvent() { + return logger.list[1] + } + +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventMapperSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventMapperSpec.groovy new file mode 100644 index 0000000000..2251a33466 --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventMapperSpec.groovy @@ -0,0 +1,69 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2024 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.impl.cmnotificationsubscription.ncmp + + +import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionDetails +import org.onap.cps.ncmp.impl.cmnotificationsubscription.models.DmiCmSubscriptionPredicate +import spock.lang.Specification + +import static org.onap.cps.ncmp.api.data.models.DatastoreType.PASSTHROUGH_OPERATIONAL +import static org.onap.cps.ncmp.api.data.models.DatastoreType.PASSTHROUGH_RUNNING +import static org.onap.cps.ncmp.impl.cmnotificationsubscription.models.CmSubscriptionStatus.ACCEPTED +import static org.onap.cps.ncmp.impl.cmnotificationsubscription.models.CmSubscriptionStatus.PENDING +import static org.onap.cps.ncmp.impl.cmnotificationsubscription.models.CmSubscriptionStatus.REJECTED + +class NcmpOutEventMapperSpec extends Specification { + + static Map dmiSubscriptionsPerDmi + + def objectUnderTest = new NcmpOutEventMapper() + + def setup() { + def dmiSubscriptionPredicateA = new DmiCmSubscriptionPredicate(['ch-A'] as Set, PASSTHROUGH_RUNNING, ['/a'] as Set) + def dmiSubscriptionPredicateB = new DmiCmSubscriptionPredicate(['ch-B'] as Set, PASSTHROUGH_OPERATIONAL, ['/b'] as Set) + def dmiSubscriptionPredicateC = new DmiCmSubscriptionPredicate(['ch-C'] as Set, PASSTHROUGH_OPERATIONAL, ['/c'] as Set) + dmiSubscriptionsPerDmi = ['dmi-1': new DmiCmSubscriptionDetails([dmiSubscriptionPredicateA], PENDING), + 'dmi-2': new DmiCmSubscriptionDetails([dmiSubscriptionPredicateB], ACCEPTED), + 'dmi-3': new DmiCmSubscriptionDetails([dmiSubscriptionPredicateC], REJECTED) + ] + } + + def 'Check for Cm Notification Subscription Outgoing event mapping'() { + when: 'we try to map the event to send it to client' + def result = objectUnderTest.toNcmpOutEvent('test-subscription', dmiSubscriptionsPerDmi) + then: 'event is mapped correctly for the subscription' + result.data.subscriptionId == 'test-subscription' + and: 'the cm handle ids are part of correct list' + result.data.pendingTargets == ['ch-A'] + result.data.acceptedTargets == ['ch-B'] + result.data.rejectedTargets == ['ch-C'] + } + + def 'Check for Cm Notification Rejected Subscription Outgoing event mapping'() { + when: 'we try to map the event to send it to client' + def result = objectUnderTest.toNcmpOutEventForRejectedRequest('test-subscription', ['ch-1', 'ch-2']) + then: 'event is mapped correctly for the subscription id' + result.data.subscriptionId == 'test-subscription' + and: 'the cm handle ids are part of correct list' + result.data.withRejectedTargets(['ch-1', 'ch-2']) + } +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventProducerSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventProducerSpec.groovy new file mode 100644 index 0000000000..f96f3df786 --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/ncmp/NcmpOutEventProducerSpec.groovy @@ -0,0 +1,89 @@ +package org.onap.cps.ncmp.impl.cmnotificationsubscription.ncmp + +import com.fasterxml.jackson.databind.ObjectMapper +import io.cloudevents.CloudEvent +import org.onap.cps.events.EventsPublisher +import org.onap.cps.ncmp.impl.cmnotificationsubscription.MappersFacade +import org.onap.cps.ncmp.impl.cmnotificationsubscription.cache.DmiCacheHandler +import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.ncmp_to_client.Data +import org.onap.cps.ncmp.impl.cmnotificationsubscription_1_0_0.ncmp_to_client.NcmpOutEvent +import org.onap.cps.ncmp.utils.events.CloudEventMapper +import org.onap.cps.utils.JsonObjectMapper +import spock.lang.Specification + +class NcmpOutEventProducerSpec extends Specification { + + def mockEventsPublisher = Mock(EventsPublisher) + def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) + def mockCmNotificationSubscriptionMappersHandler = Mock(MappersFacade) + def mockDmiCmNotificationSubscriptionCacheHandler = Mock(DmiCacheHandler) + + def objectUnderTest = new NcmpOutEventProducer(mockEventsPublisher, jsonObjectMapper, + mockCmNotificationSubscriptionMappersHandler, mockDmiCmNotificationSubscriptionCacheHandler) + + def 'Create and #scenario Cm Notification Subscription NCMP out event'() { + given: 'a cm subscription response for the client' + def subscriptionId = 'test-subscription-id-2' + def eventType = 'subscriptionCreateResponse' + def ncmpOutEvent = new NcmpOutEvent(data: new Data(subscriptionId: 'test-subscription-id-2', acceptedTargets: ['ch-1', 'ch-2'])) + and: 'also we have target topic for publishing to client' + objectUnderTest.ncmpOutEventTopic = 'client-test-topic' + and: 'a deadline to an event' + objectUnderTest.dmiOutEventTimeoutInMs = 1000 + when: 'the event is published' + objectUnderTest.publishNcmpOutEvent(subscriptionId, eventType, ncmpOutEvent, eventPublishingTaskToBeScheduled) + then: 'we conditionally wait for a while' + Thread.sleep(delayInMs) + then: 'the event contains the required attributes' + 1 * mockEventsPublisher.publishCloudEvent(_, _, _) >> { + args -> + { + assert args[0] == 'client-test-topic' + assert args[1] == subscriptionId + def ncmpOutEventAsCloudEvent = (args[2] as CloudEvent) + assert ncmpOutEventAsCloudEvent.getExtension('correlationid') == subscriptionId + assert ncmpOutEventAsCloudEvent.type == 'subscriptionCreateResponse' + assert ncmpOutEventAsCloudEvent.source.toString() == 'NCMP' + assert CloudEventMapper.toTargetEvent(ncmpOutEventAsCloudEvent, NcmpOutEvent) == ncmpOutEvent + } + } + where: 'following scenarios are considered' + scenario | delayInMs | eventPublishingTaskToBeScheduled + 'publish event now' | 0 | false + 'schedule and publish after the configured time ' | 1500 | true + } + + def 'Schedule Cm Notification Subscription NCMP out event but later publish it on demand'() { + given: 'a cm subscription response for the client' + def subscriptionId = 'test-subscription-id-3' + def eventType = 'subscriptionCreateResponse' + def ncmpOutEvent = new NcmpOutEvent(data: new Data(subscriptionId: 'test-subscription-id-3', acceptedTargets: ['ch-2', 'ch-3'])) + and: 'also we have target topic for publishing to client' + objectUnderTest.ncmpOutEventTopic = 'client-test-topic' + and: 'a deadline to an event' + objectUnderTest.dmiOutEventTimeoutInMs = 1000 + when: 'the event is scheduled to be published' + objectUnderTest.publishNcmpOutEvent(subscriptionId, eventType, ncmpOutEvent, true) + then: 'we wait for 10ms and then we receive response from DMI' + Thread.sleep(10) + and: 'we receive response from DMI so we publish the message on demand' + objectUnderTest.publishNcmpOutEvent(subscriptionId, eventType, ncmpOutEvent, false) + then: 'the event contains the required attributes' + 1 * mockEventsPublisher.publishCloudEvent(_, _, _) >> { + args -> + { + assert args[0] == 'client-test-topic' + assert args[1] == subscriptionId + def ncmpOutEventAsCloudEvent = (args[2] as CloudEvent) + assert ncmpOutEventAsCloudEvent.getExtension('correlationid') == subscriptionId + assert ncmpOutEventAsCloudEvent.type == 'subscriptionCreateResponse' + assert ncmpOutEventAsCloudEvent.source.toString() == 'NCMP' + assert CloudEventMapper.toTargetEvent(ncmpOutEventAsCloudEvent, NcmpOutEvent) == ncmpOutEvent + } + } + then: 'the cache handler is called once to remove accepted and rejected entries in cache' + 1 * mockDmiCmNotificationSubscriptionCacheHandler.removeAcceptedAndRejectedDmiSubscriptionEntries(subscriptionId) + } + + +} diff --git a/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/utils/CmSubscriptionPersistenceServiceSpec.groovy b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/utils/CmSubscriptionPersistenceServiceSpec.groovy new file mode 100644 index 0000000000..d32d143ade --- /dev/null +++ b/cps-ncmp-service/src/test/groovy/org/onap/cps/ncmp/impl/cmnotificationsubscription/utils/CmSubscriptionPersistenceServiceSpec.groovy @@ -0,0 +1,191 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (c) 2024 Nordix Foundation. + * Modifications Copyright (C) 2024 TechMahindra Ltd. + * ================================================================================ + * 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.impl.cmnotificationsubscription.utils + +import com.fasterxml.jackson.databind.ObjectMapper +import org.onap.cps.api.CpsDataService +import org.onap.cps.api.CpsQueryService +import org.onap.cps.spi.model.DataNode +import org.onap.cps.utils.ContentType +import org.onap.cps.utils.JsonObjectMapper +import spock.lang.Specification + +import static CmSubscriptionPersistenceService.CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE +import static CmSubscriptionPersistenceService.CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH +import static CmSubscriptionPersistenceService.CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_ID +import static org.onap.cps.ncmp.api.data.models.DatastoreType.PASSTHROUGH_OPERATIONAL +import static org.onap.cps.ncmp.api.data.models.DatastoreType.PASSTHROUGH_RUNNING +import static org.onap.cps.spi.FetchDescendantsOption.DIRECT_CHILDREN_ONLY +import static org.onap.cps.spi.FetchDescendantsOption.OMIT_DESCENDANTS + +class CmSubscriptionPersistenceServiceSpec extends Specification { + + def jsonObjectMapper = new JsonObjectMapper(new ObjectMapper()) + def mockCpsQueryService = Mock(CpsQueryService) + def mockCpsDataService = Mock(CpsDataService) + + def objectUnderTest = new CmSubscriptionPersistenceService(jsonObjectMapper, mockCpsQueryService, mockCpsDataService) + + def 'Check ongoing cm subscription #scenario'() { + given: 'a valid cm subscription query' + def cpsPathQuery = "/datastores/datastore[@name='ncmp-datastore:passthrough-running']/cm-handles/cm-handle[@id='ch-1']/filters/filter[@xpath='/cps/path']" + and: 'datanodes optionally returned' + 1 * mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions', + cpsPathQuery, OMIT_DESCENDANTS) >> dataNode + when: 'we check for an ongoing cm subscription' + def response = objectUnderTest.isOngoingCmSubscription(PASSTHROUGH_RUNNING, 'ch-1', '/cps/path') + then: 'we get expected response' + assert response == isOngoingCmSubscription + where: 'following scenarios are used' + scenario | dataNode || isOngoingCmSubscription + 'valid datanodes present' | [new DataNode(xpath: '/cps/path', leaves: ['subscriptionIds': ['sub-1', 'sub-2']])] || true + 'no datanodes present' | [] || false + } + + def 'Checking uniqueness of incoming subscription ID'() { + given: 'a cps path with a subscription ID for querying' + def cpsPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_ID.formatted('some-sub') + and: 'relevant datanodes are returned' + 1 * mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions', cpsPathQuery, OMIT_DESCENDANTS) >> + dataNodes + when: 'a subscription ID is tested for uniqueness' + def result = objectUnderTest.isUniqueSubscriptionId('some-sub') + then: 'result is as expected' + assert result == isValidSubscriptionId + where: 'following scenarios are used' + scenario | dataNodes || isValidSubscriptionId + 'datanodes present' | [new DataNode()] || false + 'no datanodes present' | [] || true + } + + def 'Add new subscriber to an ongoing cm notification subscription'() { + given: 'a valid cm subscription path query' + def cpsPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted('ncmp-datastore:passthrough-running', 'ch-1', '/x/y') + and: 'a dataNode exists for the given cps path query' + mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions', + cpsPathQuery, OMIT_DESCENDANTS) >> [new DataNode(xpath: cpsPathQuery, leaves: ['xpath': '/x/y', 'subscriptionIds': ['sub-1']])] + when: 'the method to add/update cm notification subscription is called' + objectUnderTest.addCmSubscription(PASSTHROUGH_RUNNING, 'ch-1', '/x/y', 'newSubId') + then: 'data service method to update list of subscribers is called once' + 1 * mockCpsDataService.updateNodeLeaves( + 'NCMP-Admin', + 'cm-data-subscriptions', + '/datastores/datastore[@name=\'ncmp-datastore:passthrough-running\']/cm-handles/cm-handle[@id=\'ch-1\']/filters', + objectUnderTest.getSubscriptionDetailsAsJson('/x/y', ['sub-1', 'newSubId']), _, ContentType.JSON) + } + + def 'Add new cm notification subscription for #datastoreType'() { + given: 'a valid cm subscription path query' + def cmSubscriptionCpsPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted(datastoreName, 'ch-1', '/x/y') + def cmHandleForSubscriptionPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE.formatted(datastoreName, 'ch-1') + and: 'a parent node xpath for the cm subscription path above' + def parentNodeXpath = '/datastores/datastore[@name=\'%s\']/cm-handles' + and: 'a datanode does not exist for cm subscription path query' + mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions', + cmSubscriptionCpsPathQuery, + OMIT_DESCENDANTS) >> [] + and: 'a datanode does not exist for the given cm handle subscription path query' + mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions', + cmHandleForSubscriptionPathQuery, OMIT_DESCENDANTS) >> [] + and: 'subscription is mapped as JSON' + def subscriptionAsJson = '{"cm-handle":[{"id":"ch-1","filters":' + + objectUnderTest.getSubscriptionDetailsAsJson('/x/y', ['newSubId']) + '}]}' + when: 'the method to add/update cm notification subscription is called' + objectUnderTest.addCmSubscription(datastoreType, 'ch-1', '/x/y', 'newSubId') + then: 'data service method to create new subscription for given subscriber is called once with the correct parameters' + 1 * mockCpsDataService.saveData( + 'NCMP-Admin', + 'cm-data-subscriptions', + parentNodeXpath.formatted(datastoreName), + subscriptionAsJson, _, ContentType.JSON) + where: + scenario | datastoreType || datastoreName + 'passthrough_running' | PASSTHROUGH_RUNNING || 'ncmp-datastore:passthrough-running' + 'passthrough_operational' | PASSTHROUGH_OPERATIONAL || 'ncmp-datastore:passthrough-operational' + } + + def 'Add new cm notification subscription when xpath does not exist for existing subscription cm handle'() { + given: 'a valid cm subscription path query' + def cmSubscriptionCpsPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted(datastoreName, 'ch-1', '/x/y') + def cmHandleForSubscriptionPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE.formatted(datastoreName, 'ch-1') + and: 'a parent node xpath for given cm handle for subscription path above' + def parentNodeXpath = '/datastores/datastore[@name=\'%s\']/cm-handles/cm-handle[@id=\'%s\']/filters' + and: 'a datanode does not exist for cm subscription path query' + mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions', + cmSubscriptionCpsPathQuery, OMIT_DESCENDANTS) >> [] + and: 'a datanode exists for the given cm handle subscription path query' + mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions', + cmHandleForSubscriptionPathQuery, OMIT_DESCENDANTS) >> [new DataNode()] + when: 'the method to add/update cm notification subscription is called' + objectUnderTest.addCmSubscription(datastoreType, 'ch-1', '/x/y', 'newSubId') + then: 'data service method to create new subscription for given subscriber is called once with the correct parameters' + 1 * mockCpsDataService.saveListElements( + 'NCMP-Admin', + 'cm-data-subscriptions', + parentNodeXpath.formatted(datastoreName, 'ch-1'), + objectUnderTest.getSubscriptionDetailsAsJson('/x/y', ['newSubId']), _) + where: + scenario | datastoreType || datastoreName + 'passthrough_running' | PASSTHROUGH_RUNNING || 'ncmp-datastore:passthrough-running' + 'passthrough_operational' | PASSTHROUGH_OPERATIONAL || 'ncmp-datastore:passthrough-operational' + } + + def 'Remove subscriber from a list of an ongoing cm notification subscription'() { + given: 'a subscription exists when queried' + def cpsPathQuery = CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted('ncmp-datastore:passthrough-running', 'ch-1', '/x/y') + mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions', + cpsPathQuery, OMIT_DESCENDANTS) >> [new DataNode(xpath: cpsPathQuery, leaves: ['xpath': '/x/y', 'subscriptionIds': ['sub-1', 'sub-2']])] + when: 'the subscriber is removed' + objectUnderTest.removeCmSubscription(PASSTHROUGH_RUNNING, 'ch-1', '/x/y', 'sub-1') + then: 'the list of subscribers is updated' + 1 * mockCpsDataService.updateNodeLeaves('NCMP-Admin', 'cm-data-subscriptions', + '/datastores/datastore[@name=\'ncmp-datastore:passthrough-running\']/cm-handles/cm-handle[@id=\'ch-1\']/filters', + objectUnderTest.getSubscriptionDetailsAsJson('/x/y', ['sub-2']), _, ContentType.JSON) + } + + def 'Removing last ongoing subscription for datastore and cmhandle and xpath'() { + given: 'a subscription exists when queried but has only 1 subscriber' + mockCpsQueryService.queryDataNodes( + 'NCMP-Admin', 'cm-data-subscriptions', + CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_WITH_DATASTORE_CMHANDLE_AND_XPATH.formatted('ncmp-datastore:passthrough-running', 'ch-1', '/x/y'), + OMIT_DESCENDANTS) >> [new DataNode(leaves: ['xpath': '/x/y', 'subscriptionIds': ['sub-1']])] + and: 'the #scenario' + mockCpsQueryService.queryDataNodes('NCMP-Admin', 'cm-data-subscriptions', + CPS_PATH_QUERY_FOR_CM_SUBSCRIPTION_FILTERS_WITH_DATASTORE_AND_CMHANDLE.formatted('ncmp-datastore:passthrough-running', 'ch-1'), + DIRECT_CHILDREN_ONLY) >> [new DataNode(childDataNodes: listOfChildNodes)] + when: 'that last ongoing subscription is removed' + objectUnderTest.removeCmSubscription(PASSTHROUGH_RUNNING, 'ch-1', '/x/y', 'sub-1') + then: 'the subscription with empty subscriber list is removed' + 1 * mockCpsDataService.deleteDataNode('NCMP-Admin', 'cm-data-subscriptions', + '/datastores/datastore[@name=\'ncmp-datastore:passthrough-running\']/cm-handles/cm-handle[@id=\'ch-1\']/filters/filter[@xpath=\'/x/y\']', + _) + and: 'method call to delete the cm handle is called the correct number of times' + numberOfCallsToDeleteCmHandle * mockCpsDataService.deleteDataNode('NCMP-Admin', 'cm-data-subscriptions', + '/datastores/datastore[@name=\'ncmp-datastore:passthrough-running\']/cm-handles/cm-handle[@id=\'ch-1\']', + _) + where: + scenario | listOfChildNodes || numberOfCallsToDeleteCmHandle + 'cm handle in same datastore is used for other subscriptions' | [new DataNode()] || 0 + 'cm handle in same datastore is NOT used for other subscriptions' | [] || 1 + } + +} -- cgit 1.2.3-korg