Handle AAI Update and Delete events for PMSH

Change-Id: I7f84e4429011bbaea4de23077ce23629b897fd7d
Issue-ID: DCAEGEN2-1846
Signed-off-by: emartin <ephraim.martin@est.tech>
diff --git a/components/pm-subscription-handler/pmsh_service/mod/aai_event_handler.py b/components/pm-subscription-handler/pmsh_service/mod/aai_event_handler.py
new file mode 100755
index 0000000..f8254e5
--- /dev/null
+++ b/components/pm-subscription-handler/pmsh_service/mod/aai_event_handler.py
@@ -0,0 +1,90 @@
+# ============LICENSE_START===================================================
+#  Copyright (C) 2020 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=====================================================
+
+import json
+from enum import Enum
+
+from mod import pmsh_logging as logger
+from mod.network_function import NetworkFunction
+from mod.subscription import NetworkFunctionFilter
+
+
+class XNFType(Enum):
+    PNF = 'pnf'
+    VNF = 'vnf'
+
+
+class AAIEvent(Enum):
+    DELETE = 'DELETE'
+    UPDATE = 'UPDATE'
+
+
+class OrchestrationStatus(Enum):
+    ACTIVE = 'Active'
+    INVENTORIED = 'Inventoried'
+
+
+def process_aai_events(mr_sub, subscription, mr_pub, app):
+    """
+    Processes AAI UPDATE events for each filtered xNFs where orchestration status is set to Active.
+
+    Args:
+        mr_sub (_MrSub): MR subscriber
+        subscription (Subscription): The current subscription object
+        mr_pub (_MrPub): MR publisher
+        app (db): DB application
+    """
+    app.app_context().push()
+    aai_events = mr_sub.get_from_topic('AAI-EVENT')
+
+    if _aai_event_exists(aai_events):
+        for entry in aai_events:
+            logger.debug(f'AAI-EVENT entry: {entry}')
+            entry = json.loads(entry)
+            event_header = entry['event-header']
+            aai_xnf = entry['entity']
+            action = event_header['action']
+            entity_type = event_header['entity-type']
+            xnf_name = aai_xnf['pnf-name'] if entity_type == XNFType.PNF.value else aai_xnf[
+                'vnf-name']
+            new_status = aai_xnf['orchestration-status']
+
+            if NetworkFunctionFilter(**subscription.nfFilter).is_nf_in_filter(xnf_name):
+                _process_event(action, new_status, xnf_name, subscription, mr_pub)
+
+
+def _process_event(action, new_status, xnf_name, subscription, mr_pub):
+    if action == AAIEvent.UPDATE.value:
+        logger.debug(f'Update event found for network function {xnf_name}')
+        local_xnf = NetworkFunction.get(xnf_name)
+
+        if local_xnf is None:
+            logger.debug(f'Activating subscription for network function {xnf_name}')
+            subscription.process_subscription([NetworkFunction(
+                nf_name=xnf_name, orchestration_status=new_status)], mr_pub)
+        else:
+            logger.debug(f"Update Event for network function {xnf_name} will not be processed "
+                         f" as it's state is set to {local_xnf.orchestration_status}.")
+    elif action == AAIEvent.DELETE.value:
+        logger.debug(f'Delete event found for network function {xnf_name}')
+        NetworkFunction.delete(nf_name=xnf_name)
+        logger.debug(f'{xnf_name} successfully deleted.')
+
+
+def _aai_event_exists(aai_events):
+    return aai_events is not None and len(aai_events) != 0
diff --git a/components/pm-subscription-handler/pmsh_service/mod/db_models.py b/components/pm-subscription-handler/pmsh_service/mod/db_models.py
index 479d40e..d183676 100755
--- a/components/pm-subscription-handler/pmsh_service/mod/db_models.py
+++ b/components/pm-subscription-handler/pmsh_service/mod/db_models.py
@@ -67,6 +67,9 @@
 
 class NfSubRelationalModel(db.Model):
     __tablename__ = 'nf_to_sub_rel'
+    __mapper_args__ = {
+        'confirm_deleted_rows': False
+    }
     id = Column(Integer, primary_key=True, autoincrement=True)
     subscription_name = Column(
         String,
diff --git a/components/pm-subscription-handler/pmsh_service/mod/network_function.py b/components/pm-subscription-handler/pmsh_service/mod/network_function.py
index 9f21cc6..c4b9b56 100755
--- a/components/pm-subscription-handler/pmsh_service/mod/network_function.py
+++ b/components/pm-subscription-handler/pmsh_service/mod/network_function.py
@@ -32,6 +32,13 @@
     def __str__(self):
         return f'nf-name: {self.nf_name}, orchestration-status: {self.orchestration_status}'
 
+    def __eq__(self, other):
+        return self.nf_name == other.nf_name and \
+            self.orchestration_status == other.orchestration_status
+
+    def __hash__(self):
+        return hash((self.nf_name, self.orchestration_status))
+
     def create(self):
         """ Creates a NetworkFunction database entry """
         existing_nf = NetworkFunctionModel.query.filter(
@@ -72,8 +79,7 @@
     def delete(**kwargs):
         """ Deletes a network function from the database """
         nf_name = kwargs['nf_name']
-        NetworkFunctionModel.query.filter(
-            NetworkFunctionModel.nf_name == nf_name). \
-            delete(synchronize_session='evaluate')
+        nf = NetworkFunctionModel.query.filter(
+            NetworkFunctionModel.nf_name == nf_name).one_or_none()
 
-        db.session.commit()
+        db.session.delete(nf) if nf else None
diff --git a/components/pm-subscription-handler/pmsh_service/mod/pmsh_utils.py b/components/pm-subscription-handler/pmsh_service/mod/pmsh_utils.py
index 4a77543..9ff0c65 100755
--- a/components/pm-subscription-handler/pmsh_service/mod/pmsh_utils.py
+++ b/components/pm-subscription-handler/pmsh_service/mod/pmsh_utils.py
@@ -18,14 +18,15 @@
 import json
 import threading
 import uuid
+from threading import Timer
 
 import requests
 from requests.auth import HTTPBasicAuth
 from tenacity import retry, wait_fixed, retry_if_exception_type
 
 import mod.pmsh_logging as logger
-from mod.subscription import Subscription, SubNfState, AdministrativeState
 from mod.network_function import NetworkFunction
+from mod.subscription import Subscription, SubNfState, AdministrativeState
 
 
 class AppConfig:
@@ -163,6 +164,7 @@
         try:
             session = requests.Session()
             headers = {'accept': 'application/json', 'content-type': 'application/json'}
+            logger.debug(f'Request sent to MR topic: {self.topic_url}')
             response = session.get(f'{self.topic_url}/{consumer_group}/{consumer_id}'
                                    f'?timeout={timeout}',
                                    auth=HTTPBasicAuth(self.aaf_id, self.aaf_pass), headers=headers,
@@ -241,3 +243,12 @@
         'failed': Subscription.update_sub_nf_status
     }
 }
+
+
+class PeriodicTask(Timer):
+    """
+    See :class:`Timer`.
+    """
+    def run(self):
+        while not self.finished.wait(self.interval):
+            self.function(*self.args, **self.kwargs)
diff --git a/components/pm-subscription-handler/pmsh_service/mod/subscription.py b/components/pm-subscription-handler/pmsh_service/mod/subscription.py
index 031609a..7a0b88c 100755
--- a/components/pm-subscription-handler/pmsh_service/mod/subscription.py
+++ b/components/pm-subscription-handler/pmsh_service/mod/subscription.py
@@ -143,12 +143,19 @@
         db.session.commit()
 
     def delete_subscription(self):
-        """ Deletes a subscription from the database """
-        SubscriptionModel.query.filter(
-            SubscriptionModel.subscription_name == self.subscriptionName). \
-            delete(synchronize_session='evaluate')
-
-        db.session.commit()
+        """ Deletes a subscription and all its association from the database. A network function
+        that is only associated with the subscription being removed will also be deleted."""
+        subscription = SubscriptionModel.query.filter(
+            SubscriptionModel.subscription_name == self.subscriptionName).one_or_none()
+        if subscription:
+            for nf_relationship in subscription.nfs:
+                other_nf_relationship = NfSubRelationalModel.query.filter(
+                    NfSubRelationalModel.subscription_name != self.subscriptionName,
+                    NfSubRelationalModel.nf_name == nf_relationship.nf_name).one_or_none()
+                if not other_nf_relationship:
+                    db.session.delete(nf_relationship.nf)
+            db.session.delete(subscription)
+            db.session.commit()
 
     @retry(wait=wait_exponential(multiplier=1, min=30, max=120), stop=stop_after_attempt(3),
            retry=retry_if_exception_type(Exception))
diff --git a/components/pm-subscription-handler/pmsh_service/pmsh_service_main.py b/components/pm-subscription-handler/pmsh_service/pmsh_service_main.py
index 5c81250..31d1d07 100755
--- a/components/pm-subscription-handler/pmsh_service/pmsh_service_main.py
+++ b/components/pm-subscription-handler/pmsh_service/pmsh_service_main.py
@@ -21,12 +21,14 @@
 import mod.aai_client as aai
 import mod.pmsh_logging as logger
 from mod import db, create_app, launch_api_server
+from mod.aai_event_handler import process_aai_events
 from mod.config_handler import ConfigHandler
-from mod.pmsh_utils import AppConfig
+from mod.pmsh_utils import AppConfig, PeriodicTask
 from mod.subscription import Subscription, AdministrativeState
 
 
-def subscription_processor(config_handler, administrative_state, mr_pub, app):
+def subscription_processor(config_handler, administrative_state, mr_pub, app,
+                           mr_aai_event_subscriber):
     """
     Checks for changes of administrative state in config and proceeds to process
     the Subscription if a change has occurred
@@ -36,10 +38,10 @@
         administrative_state (str): The administrative state
         mr_pub (_MrPub): MR publisher
         app (db): DB application
+        mr_aai_event_subscriber (_MrSub): AAI events MR subscriber
     """
     app.app_context().push()
     config = config_handler.get_config()
-    sub, nfs = aai.get_pmsh_subscription_data(config)
     new_administrative_state = config['policy']['subscription']['administrativeState']
     polling_period = 30.0
 
@@ -47,17 +49,29 @@
         if administrative_state == new_administrative_state:
             logger.debug('Administrative State did not change in the Config')
         else:
+            logger.debug(f'Administrative State changed from "{administrative_state}" "to '
+                         f'"{new_administrative_state}".')
+            sub, nfs = aai.get_pmsh_subscription_data(config)
             sub.process_subscription(nfs, mr_pub)
+            aai_event_thread = PeriodicTask(10, process_aai_events, args=(mr_aai_event_subscriber,
+                                                                          sub, mr_pub, app))
+
+            if new_administrative_state == AdministrativeState.UNLOCKED.value:
+                logger.debug('Listening to AAI-EVENT topic in MR.')
+                aai_event_thread.start()
+            else:
+                logger.debug('Stopping to listen to AAI-EVENT topic in MR.')
+                aai_event_thread.cancel()
 
     except Exception as err:
         logger.debug(f'Error occurred during the activation/deactivation process {err}')
 
     threading.Timer(polling_period, subscription_processor,
-                    [config_handler, new_administrative_state, mr_pub, app]).start()
+                    [config_handler, new_administrative_state, mr_pub, app,
+                     mr_aai_event_subscriber]).start()
 
 
 def main():
-
     try:
         config_handler = ConfigHandler()
         config = config_handler.get_config()
@@ -68,6 +82,7 @@
         sub, nfs = aai.get_pmsh_subscription_data(config)
         mr_pub = app_conf.get_mr_pub('policy_pm_publisher')
         mr_sub = app_conf.get_mr_sub('policy_pm_subscriber')
+        mr_aai_event_subscriber = app_conf.get_mr_sub('aai_subscriber')
         initial_start_delay = 5.0
 
         administrative_state = AdministrativeState.LOCKED.value
@@ -76,7 +91,8 @@
             administrative_state = subscription_in_db.status
 
         threading.Timer(initial_start_delay, subscription_processor,
-                        [config_handler, administrative_state, mr_pub, app]).start()
+                        [config_handler, administrative_state, mr_pub,
+                         app, mr_aai_event_subscriber]).start()
 
         threading.Timer(20.0, mr_sub.poll_policy_topic, [sub.subscriptionName, app]).start()
 
diff --git a/components/pm-subscription-handler/tests/data/mr_aai_events.json b/components/pm-subscription-handler/tests/data/mr_aai_events.json
new file mode 100755
index 0000000..202d133
--- /dev/null
+++ b/components/pm-subscription-handler/tests/data/mr_aai_events.json
@@ -0,0 +1,6 @@
+{

+  "mr_response": [

+    "{\"cambria.partition\":\"AAI\",\"event-header\":{\"severity\":\"NORMAL\",\"entity-type\":\"pnf\",\"top-entity-type\":\"pnf\",\"entity-link\":\"/aai/v16/network/pnfs/pnf/pnf_newly_discovered\",\"event-type\":\"AAI-EVENT\",\"domain\":\"dev\",\"action\":\"UPDATE\",\"sequence-number\":\"0\",\"id\":\"db09e090-196e-4f84-9645-e449b1cd3640\",\"source-name\":\"dcae-curl\",\"version\":\"v16\",\"timestamp\":\"20200203-15:14:08:807\"},\"entity\":{\"ipaddress-v4-oam\":\"10.10.10.37\",\"nf-role\":\"gNB\",\"equip-type\":\"val8\",\"relationship-list\":{\"relationship\":[{\"related-to\":\"service-instance\",\"relationship-data\":[{\"relationship-value\":\"Demonstration\",\"relationship-key\":\"customer.global-customer-id\"},{\"relationship-value\":\"vCPE\",\"relationship-key\":\"service-subscription.service-type\"},{\"relationship-value\":\"2c03b2a8-e31a-4749-9e99-3089ab441400\",\"relationship-key\":\"service-instance.service-instance-id\"}],\"related-link\":\"/aai/v16/business/customers/customer/Demonstration/service-subscriptions/service-subscription/vCPE/service-instances/service-instance/2c03b2a8-e31a-4749-9e99-3089ab441400\",\"relationship-label\":\"org.onap.relationships.inventory.ComposedOf\",\"related-to-property\":[{\"property-key\":\"service-instance.service-instance-name\",\"property-value\":\"Svc6_1\"}]}]},\"equip-vendor\":\"Ericsson\",\"serial-number\":\"6061ZW3\",\"ipaddress-v6-oam\":\"2001:0db8:0:0:0:0:1428:57ab\",\"equip-model\":\"val6\",\"in-maint\":false,\"resource-version\":\"1578668956804\",\"sw-version\":\"val7\",\"pnf-id\":\"eabcfaf7-b7f3-45fb-94e7-e6112fb3e8b8\",\"pnf-name\":\"pnf_newly_discovered\",\"orchestration-status\":\"Active\"}}",

+    "{\"cambria.partition\":\"AAI\",\"event-header\":{\"severity\":\"NORMAL\",\"entity-type\":\"pnf\",\"top-entity-type\":\"pnf\",\"entity-link\":\"/aai/v16/network/pnfs/pnf/pnf_newly_discovered\",\"event-type\":\"AAI-EVENT\",\"domain\":\"dev\",\"action\":\"UPDATE\",\"sequence-number\":\"0\",\"id\":\"db09e090-196e-4f84-9645-e449b1cd3640\",\"source-name\":\"dcae-curl\",\"version\":\"v16\",\"timestamp\":\"20200203-15:14:08:807\"},\"entity\":{\"orchestration-status\":\"Active\",\"ipaddress-v4-oam\":\"10.10.10.37\",\"nf-role\":\"gNB\",\"equip-type\":\"val8\",\"relationship-list\":{\"relationship\":[{\"related-to\":\"service-instance\",\"relationship-data\":[{\"relationship-value\":\"Demonstration\",\"relationship-key\":\"customer.global-customer-id\"},{\"relationship-value\":\"vCPE\",\"relationship-key\":\"service-subscription.service-type\"},{\"relationship-value\":\"2c03b2a8-e31a-4749-9e99-3089ab441400\",\"relationship-key\":\"service-instance.service-instance-id\"}],\"related-link\":\"/aai/v16/business/customers/customer/Demonstration/service-subscriptions/service-subscription/vCPE/service-instances/service-instance/2c03b2a8-e31a-4749-9e99-3089ab441400\",\"relationship-label\":\"org.onap.relationships.inventory.ComposedOf\",\"related-to-property\":[{\"property-key\":\"service-instance.service-instance-name\",\"property-value\":\"Svc6_1\"}]}]},\"equip-vendor\":\"Ericsson\",\"serial-number\":\"6061ZW3\",\"ipaddress-v6-oam\":\"2001:0db8:0:0:0:0:1428:57ab\",\"equip-model\":\"val6\",\"in-maint\":false,\"resource-version\":\"1578668956804\",\"sw-version\":\"val7\",\"pnf-id\":\"eabcfaf7-b7f3-45fb-94e7-e6112fb3e8b8\",\"pnf-name\":\"pnf_already_active\",\"orchestration-status\":\"Active\"}}",

+    "{\"cambria.partition\":\"AAI\",\"event-header\":{\"severity\":\"NORMAL\",\"entity-type\":\"pnf\",\"top-entity-type\":\"pnf\",\"entity-link\":\"/aai/v16/network/pnfs/pnf/pnf_newly_discovered\",\"event-type\":\"AAI-EVENT\",\"domain\":\"dev\",\"action\":\"DELETE\",\"sequence-number\":\"0\",\"id\":\"db09e090-196e-4f84-9645-e449b1cd3640\",\"source-name\":\"dcae-curl\",\"version\":\"v16\",\"timestamp\":\"20200203-15:14:08:807\"},\"entity\":{\"ipaddress-v4-oam\":\"10.10.10.37\",\"nf-role\":\"gNB\",\"equip-type\":\"val8\",\"relationship-list\":{\"relationship\":[{\"related-to\":\"service-instance\",\"relationship-data\":[{\"relationship-value\":\"Demonstration\",\"relationship-key\":\"customer.global-customer-id\"},{\"relationship-value\":\"vCPE\",\"relationship-key\":\"service-subscription.service-type\"},{\"relationship-value\":\"2c03b2a8-e31a-4749-9e99-3089ab441400\",\"relationship-key\":\"service-instance.service-instance-id\"}],\"related-link\":\"/aai/v16/business/customers/customer/Demonstration/service-subscriptions/service-subscription/vCPE/service-instances/service-instance/2c03b2a8-e31a-4749-9e99-3089ab441400\",\"relationship-label\":\"org.onap.relationships.inventory.ComposedOf\",\"related-to-property\":[{\"property-key\":\"service-instance.service-instance-name\",\"property-value\":\"Svc6_1\"}]}]},\"equip-vendor\":\"Ericsson\",\"serial-number\":\"6061ZW3\",\"ipaddress-v6-oam\":\"2001:0db8:0:0:0:0:1428:57ab\",\"equip-model\":\"val6\",\"in-maint\":false,\"resource-version\":\"1578668956804\",\"sw-version\":\"val7\",\"pnf-id\":\"eabcfaf7-b7f3-45fb-94e7-e6112fb3e8b8\",\"pnf-name\":\"pnf_to_be_deleted\",\"orchestration-status\":\"Active\"}}"]

+}

diff --git a/components/pm-subscription-handler/tests/test_aai_event_handler.py b/components/pm-subscription-handler/tests/test_aai_event_handler.py
new file mode 100755
index 0000000..add7b3f
--- /dev/null
+++ b/components/pm-subscription-handler/tests/test_aai_event_handler.py
@@ -0,0 +1,53 @@
+# ============LICENSE_START===================================================
+#  Copyright (C) 2020 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=====================================================
+import json
+from os import path
+from unittest import TestCase
+from unittest.mock import patch, Mock
+
+from mod.aai_event_handler import OrchestrationStatus, process_aai_events
+from mod.network_function import NetworkFunction
+
+
+class AAIEventHandlerTest(TestCase):
+
+    def setUp(self):
+        with open(path.join(path.dirname(__file__), 'data/cbs_data_1.json'), 'r') as data:
+            self.cbs_data_1 = json.load(data)
+        with open(path.join(path.dirname(__file__), 'data/mr_aai_events.json'), 'r') as data:
+            self.mr_aai_events = json.load(data)["mr_response"]
+        self.mock_sub = Mock(nfFilter={'swVersions': ['1.0.0', '1.0.1'],
+                                       'nfNames': ['^pnf.*', '^vnf.*']})
+        self.mock_mr_sub = Mock(get_from_topic=Mock(return_value=self.mr_aai_events))
+        self.mock_mr_pub = Mock()
+        self.mock_app = Mock()
+
+    @patch('mod.aai_event_handler.NetworkFunction.delete')
+    @patch('mod.aai_event_handler.NetworkFunction.get')
+    def test_process_aai_update_and_delete_events(self, mock_nf_get, mock_nf_delete):
+        pnf_already_active = NetworkFunction(nf_name='pnf_already_active',
+                                             orchestration_status=OrchestrationStatus.ACTIVE.value)
+        mock_nf_get.side_effect = [None, pnf_already_active]
+        expected_nf_for_processing = NetworkFunction(
+            nf_name='pnf_newly_discovered', orchestration_status=OrchestrationStatus.ACTIVE.value)
+
+        process_aai_events(self.mock_mr_sub, self.mock_sub, self.mock_mr_pub, self.mock_app)
+
+        self.mock_sub.process_subscription.assert_called_once_with([expected_nf_for_processing],
+                                                                   self.mock_mr_pub)
+        mock_nf_delete.assert_called_once_with(nf_name='pnf_to_be_deleted')
diff --git a/components/pm-subscription-handler/tests/test_network_function.py b/components/pm-subscription-handler/tests/test_network_function.py
index e9394b4..4fca077 100755
--- a/components/pm-subscription-handler/tests/test_network_function.py
+++ b/components/pm-subscription-handler/tests/test_network_function.py
@@ -21,6 +21,7 @@
 
 from mod import db, create_app
 from mod.network_function import NetworkFunction
+from mod.subscription import Subscription
 
 
 class NetworkFunctionTests(TestCase):
@@ -69,7 +70,13 @@
     def test_delete_network_function(self):
         self.nf_1.create()
         self.nf_2.create()
-        self.nf_1.delete(nf_name='pnf_1')
-        nfs = NetworkFunction.get_all()
+        sub = Subscription(**{"subscriptionName": "sub"})
+        sub.add_network_functions_to_subscription([self.nf_1, self.nf_2])
 
+        NetworkFunction.delete(nf_name=self.nf_1.nf_name)
+
+        nfs = NetworkFunction.get_all()
         self.assertEqual(1, len(nfs))
+        self.assertEqual(1, len(Subscription.get_all_nfs_subscription_relations()))
+        pnf_1_deleted = [nf for nf in nfs if nf.nf_name != self.nf_1.nf_name]
+        self.assertTrue(pnf_1_deleted)
diff --git a/components/pm-subscription-handler/tests/test_pmsh_service.py b/components/pm-subscription-handler/tests/test_pmsh_service.py
index 4a6032b..b722567 100644
--- a/components/pm-subscription-handler/tests/test_pmsh_service.py
+++ b/components/pm-subscription-handler/tests/test_pmsh_service.py
@@ -38,19 +38,22 @@
         self.mock_sub = mock_sub
         self.mock_mr_pub = mock_mr_pub
         self.mock_config_handler = mock_config_handler
+        self.mock_aai_sub = mock_sub
         self.nf_1 = NetworkFunction(nf_name='pnf_1')
         self.nf_2 = NetworkFunction(nf_name='pnf_2')
         self.nfs = [self.nf_1, self.nf_2]
 
     @patch('threading.Timer')
     @patch('mod.aai_client.get_pmsh_subscription_data')
-    def test_subscription_processor_changed_state(self, mock_get_aai, mock_thread):
+    @patch('pmsh_service_main.PeriodicTask')
+    def test_subscription_processor_changed_state(self, periodic_task, mock_get_aai, mock_thread):
         self.mock_config_handler.get_config.return_value = self.cbs_data_1
         mock_get_aai.return_value = self.mock_sub, self.nfs
         mock_thread.start.return_value = 1
+        periodic_task.start.return_value = 1
 
         pmsh_service.subscription_processor(self.mock_config_handler, 'LOCKED',
-                                            self.mock_mr_pub, self.mock_app)
+                                            self.mock_mr_pub, self.mock_app, self.mock_aai_sub)
 
         self.mock_sub.process_subscription.assert_called_with(self.nfs, self.mock_mr_pub)
 
@@ -63,7 +66,7 @@
         mock_thread.start.return_value = 1
 
         pmsh_service.subscription_processor(self.mock_config_handler, 'UNLOCKED', self.mock_mr_pub,
-                                            self.mock_app)
+                                            self.mock_app, self.mock_aai_sub)
 
         mock_logger.assert_called_with('Administrative State did not change in the Config')
 
@@ -77,6 +80,6 @@
         self.mock_sub.process_subscription.side_effect = Exception
 
         pmsh_service.subscription_processor(self.mock_config_handler, 'LOCKED', self.mock_mr_pub,
-                                            self.mock_app)
+                                            self.mock_app, self.mock_aai_sub)
         mock_logger.assert_called_with(f'Error occurred during the '
                                        f'activation/deactivation process ')
diff --git a/components/pm-subscription-handler/tests/test_subscription.py b/components/pm-subscription-handler/tests/test_subscription.py
index 8fe233e..c357ad7 100755
--- a/components/pm-subscription-handler/tests/test_subscription.py
+++ b/components/pm-subscription-handler/tests/test_subscription.py
@@ -134,13 +134,16 @@
         self.assertEqual('new_status', sub.status)
 
     def test_delete_subscription(self):
-        self.sub_1.create()
-        subs = self.sub_1.get_all()
-        self.assertEqual(1, len(subs))
+        self.sub_1.add_network_functions_to_subscription([self.nf_1, self.nf_2])
+        self.sub_2.add_network_functions_to_subscription([self.nf_2])
 
         self.sub_1.delete_subscription()
-        new_subs = self.sub_1.get_all()
-        self.assertEqual(0, len(new_subs))
+
+        self.assertEqual(1, len(Subscription.get_all()))
+        self.assertEqual(None, Subscription.get(self.sub_1.subscriptionName))
+        self.assertEqual(1, len(Subscription.get_all_nfs_subscription_relations()))
+        self.assertEqual(1, len(NetworkFunction.get_all()))
+        self.assertEqual(None, NetworkFunction.get(nf_name=self.nf_1.nf_name))
 
     def test_update_sub_nf_status(self):
         sub_name = 'ExtraPM-All-gNB-R2B'