From f2cf03287b48c2529aa0d8af1e2ab3ae8fb0b8d7 Mon Sep 17 00:00:00 2001
From: Nikolay Stanchev <ns17@it-innovation.soton.ac.uk>
Date: Tue, 21 Aug 2018 14:57:31 +0100
Subject: [PATCH] Implements tests for alerts API

---
 src/service/clmcservice/alertsapi/tests.py | 121 +++++++++++++++++++--
 src/service/clmcservice/alertsapi/views.py |   7 +-
 2 files changed, 112 insertions(+), 16 deletions(-)

diff --git a/src/service/clmcservice/alertsapi/tests.py b/src/service/clmcservice/alertsapi/tests.py
index 11984dc..9707a70 100644
--- a/src/service/clmcservice/alertsapi/tests.py
+++ b/src/service/clmcservice/alertsapi/tests.py
@@ -26,12 +26,13 @@
 # Python standard libs
 from os import listdir
 from os.path import isfile, join
-
+from urllib.parse import urlparse
 
 # PIP installed libs
 import pytest
 from yaml import load
 from pyramid import testing
+from requests import get, delete
 from toscaparser.tosca_template import ToscaTemplate
 
 # CLMC-service imports
@@ -129,27 +130,67 @@ class TestAlertsConfigurationAPI(object):
         """
         Tests the POST API endpoint of the alerts configuration API responsible for receiving alerts specifications.
 
-        Unit test consists of:
-            * Traverse all valid TOSCA Alerts Specifications in the src/service/clmcservice/resources/tosca/test-data/clmc-validator/valid
+        Test steps are:
+            * Traverse all valid TOSCA Alerts Specifications in the
+                src/service/clmcservice/resources/tosca/test-data/clmc-validator/valid and src/service/clmcservice/resources/tosca/test-data/tosca-parser/valid
             * Sending a valid TOSCA Alert Specification to the view responsible for configuring Kapacitor
             * Check that Kapacitor alerts, topics and handlers are created with the correct identifier and arguments
 
         :param app_config: fixture for setUp/tearDown of the web service registry
         """
 
-        test_data_path = join(ROOT_DIR, *["resources", "tosca", "test-data", "clmc-validator", "valid"])
+        for test_folder in ("clmc-validator", "tosca-parser"):
+            test_data_path = join(ROOT_DIR, *["resources", "tosca", "test-data", test_folder, "valid"])
+
+            for test_file_path in listdir(test_data_path):
+                alert_spec_abs_path = join(test_data_path, test_file_path)
+
+                if not isfile(alert_spec_abs_path):
+                    continue  # skip directories
+
+                if not test_file_path.lower().endswith('.yaml'):
+                    continue  # non-yaml files are not intended for being tested
+
+                print("Testing file {0} in folder {1}".format(test_file_path, test_folder))
+
+                request = testing.DummyRequest()
+
+                with open(alert_spec_abs_path) as alert_spec:
+                    sfc, sfc_instance, alert_ids, topic_handlers = extract_alert_spec_data(alert_spec)
+                    alert_spec.seek(0)
+                    request.POST['alert-spec'] = FieldStorageMock(test_file_path, alert_spec)  # a simple mock class is used to mimic the FieldStorage class
+                    clmc_service_response = AlertsConfigurationAPI(request).post_alerts_specification()
+
+                assert (sfc, sfc_instance) == (clmc_service_response["service_function_chain_id"], clmc_service_response["service_function_chain_instance_id"]), \
+                    "Incorrect extraction of metadata for file {0}". format(test_file_path)
 
-        for test_file_path in listdir(test_data_path):
+                # traverse through all alert IDs and check that they are created within Kapacitor
+                for alert_id in alert_ids:
+                    kapacitor_response = get("http://localhost:9092/kapacitor/v1/tasks/{0}".format(alert_id))
+                    assert kapacitor_response.status_code == 200, "Alert with ID {0} was not created - test file {1}.".format(alert_id, test_file_path)
+                    kapacitor_response_json = kapacitor_response.json()
+                    assert "link" in kapacitor_response_json, "Incorrect response from kapacitor for alert with ID {0} - test file {1}".format(alert_id, test_file_path)
+                    assert kapacitor_response_json["status"] == "enabled", "Alert with ID {0} was created but is disabled - test file {1}".format(alert_id, test_file_path)
 
-            request = testing.DummyRequest()
-            alert_spec_abs_path = join(test_data_path, test_file_path)
-            print(alert_spec_abs_path)
-            with open(alert_spec_abs_path) as alert_spec:
-                request.POST['alert-spec'] = FieldStorageMock(test_file_path, alert_spec)  # a simple mock class is used to mimic the FieldStorage class
+                # check that all topic IDs were registered within Kapacitor
+                topic_ids = list(topic_handlers.keys())
+                kapacitor_response = get("http://localhost:9092/kapacitor/v1/alerts/topics")
+                assert kapacitor_response.status_code == 200, "Kapacitor couldn't return the list of created topics - test file {0}".format(test_file_path)
+                kapacitor_response_json = kapacitor_response.json()
+                kapacitor_defined_topics = [topic["id"] for topic in kapacitor_response_json["topics"]]
+                assert set(topic_ids).issubset(kapacitor_defined_topics), "Not all topic IDs were created within kapacitor - test file {0}".format(test_file_path)
 
-                print(AlertsConfigurationAPI(request).post_alerts_specification())
+                # check that all handler IDs were created and each of them is subscribed to the correct topic ID
+                for topic_id in topic_handlers:
+                    for handler_id, handler_url in topic_handlers[topic_id]:
+                        kapacitor_response = get("http://localhost:9092/kapacitor/v1/alerts/topics/{0}/handlers/{1}".format(topic_id, handler_id))
+                        assert kapacitor_response.status_code == 200, "Handler with ID {0} for topic with ID {1} doesn't exist - test file {2}".format(handler_id, topic_id, test_file_path)
+                        kapacitor_response_json = kapacitor_response.json()
+                        assert kapacitor_response_json["id"] == handler_id, "Incorrect ID of handler {0} in the Kapacitor response - test file {1}".format(handler_id, test_file_path)
+                        assert kapacitor_response_json["kind"] == "post", "Incorrect kind of handler {0} in the Kapacitor response - test file {1}".format(handler_id, test_file_path)
+                        assert kapacitor_response_json["options"]["url"], "Incorrect url of handler {0} in the Kapacitor response - test file {1}".format(handler_id, test_file_path)
 
-            break
+                clear_kapacitor_alerts(alert_ids, topic_handlers)
 
 
 class FieldStorageMock(object):
@@ -164,3 +205,59 @@ class FieldStorageMock(object):
 
         self.filename = filename
         self.file = file
+
+
+def extract_alert_spec_data(alert_spec):
+    """
+    A utility function to extract the expected alert, handler and topic identifiers from a given alert specification.
+
+    :param alert_spec: the alert specification file (file object)
+    :return: a tuple containing sfc_id and sfc_instance_id along with a list and a dictionary of generated IDs (alert IDs (list), topic IDs linked to handler IDs (dict))
+    """
+
+    yaml_alert_spec = load(alert_spec)
+    adjust_tosca_definitions_import(yaml_alert_spec)
+    tosca_tpl = ToscaTemplate(yaml_dict_tpl=yaml_alert_spec)
+    sfc, sfc_instance = tosca_tpl.tpl["metadata"]["sfc"], tosca_tpl.tpl["metadata"]["sfci"]
+
+    alert_ids = []  # saves all alert IDs in a list
+    topic_handlers = {}  # saves all topics in a dictionary, each topic is linked to a list of handler pairs (a handler pair consists of handler id and handler url)
+
+    for policy in tosca_tpl.policies:
+        policy_id = policy.name
+        for trigger in policy.triggers:
+            trigger_id = trigger.name
+
+            topic_id = "{0}.{1}.{2}".format(sfc, sfc_instance, trigger_id)
+            topic_handlers[topic_id] = []
+
+            alert_id = "{0}.{1}.{2}.{3}".format(sfc, sfc_instance, policy_id, trigger_id)
+            alert_ids.append(alert_id)
+
+            for handler_url in trigger.trigger_tpl["action"]["implementation"]:
+                handler_host = urlparse(handler_url).hostname
+                handler_id = "{0}.{1}.{2}".format(policy_id, trigger_id, handler_host)
+                topic_handlers[topic_id].append((handler_id, handler_url))
+
+    return sfc, sfc_instance, alert_ids, topic_handlers
+
+
+def clear_kapacitor_alerts(alert_ids, topic_handlers):
+    """
+    A utility function to clean up Kapacitor from the configured alerts, topics and handlers.
+
+    :param alert_ids: the list of alert IDs to delete
+    :param topic_handlers: the dictionary of topic and handlers to delete
+    """
+
+    for alert_id in alert_ids:
+        kapacitor_response = delete("http://localhost:9092/kapacitor/v1/tasks/{0}".format(alert_id))  # delete alert
+        assert kapacitor_response.status_code == 204
+
+    for topic_id in topic_handlers:
+        for handler_id, handler_url in topic_handlers[topic_id]:
+            kapacitor_response = delete("http://localhost:9092/kapacitor/v1/alerts/topics/{0}/handlers/{1}".format(topic_id, handler_id))  # delete handler
+            assert kapacitor_response.status_code == 204
+
+        kapacitor_response = delete("http://localhost:9092/kapacitor/v1/alerts/topics/{0}".format(topic_id))  # delete topic
+        assert kapacitor_response.status_code == 204
diff --git a/src/service/clmcservice/alertsapi/views.py b/src/service/clmcservice/alertsapi/views.py
index 70cbf7f..5f9e24e 100644
--- a/src/service/clmcservice/alertsapi/views.py
+++ b/src/service/clmcservice/alertsapi/views.py
@@ -24,7 +24,6 @@
 
 # Python standard libs
 import logging
-from json import dumps
 from urllib.parse import urlparse
 
 # PIP installed libs
@@ -136,7 +135,7 @@ class AlertsConfigurationAPI(object):
                 }
 
                 # send the request and receive a response
-                response = post(kapacitor_api_tasks_url, data=dumps(kapacitor_http_request_body))
+                response = post(kapacitor_api_tasks_url, json=kapacitor_http_request_body)
                 response_content = response.json()
                 # log the response
                 log.info(response_content, response.status_code)
@@ -147,10 +146,10 @@ class AlertsConfigurationAPI(object):
                 # subscribe all http handlers to the created topic
                 kapacitor_api_handlers_url = "http://localhost:9092/kapacitor/v1/alerts/topics/{0}/handlers".format(topic_id)
                 for http_handler_url in http_handlers:
-                    http_handler_host = urlparse(http_handler_url).netloc
+                    http_handler_host = urlparse(http_handler_url).hostname
                     handler_id = "{0}.{1}.{2}".format(policy.name, event_id, http_handler_host)
                     kapacitor_http_request_body = fill_http_post_handler_vars(handler_id, http_handler_url)
-                    response = post(kapacitor_api_handlers_url, data=dumps(kapacitor_http_request_body))
+                    response = post(kapacitor_api_handlers_url, json=kapacitor_http_request_body)
                     response_content = response.json()
                     log.info(response_content, response.status_code)
 
-- 
GitLab