From ea55695715ce5e8d78319a4b49f333ce556ce130 Mon Sep 17 00:00:00 2001
From: Nikolay Stanchev <ns17@it-innovation.soton.ac.uk>
Date: Tue, 22 May 2018 09:54:02 +0100
Subject: [PATCH] Added tests for the aggregator starter.

---
 src/clmc-webservice/clmcservice/tests.py | 91 ++++++++++++++++++++----
 src/clmc-webservice/clmcservice/views.py | 12 +++-
 2 files changed, 86 insertions(+), 17 deletions(-)

diff --git a/src/clmc-webservice/clmcservice/tests.py b/src/clmc-webservice/clmcservice/tests.py
index a0212fb..00aa9e2 100644
--- a/src/clmc-webservice/clmcservice/tests.py
+++ b/src/clmc-webservice/clmcservice/tests.py
@@ -24,13 +24,15 @@
 import pytest
 from pyramid import testing
 from pyramid.httpexceptions import HTTPBadRequest
-
+from time import sleep
 from clmcservice.utilities import CONFIG_ATTRIBUTES
+import os
+import signal
 
 
-class TestAggregatorConfig(object):
+class TestAggregator(object):
     """
-    A pytest-implementation test for the aggregator configuration API calls
+    A pytest-implementation test for the aggregator API calls
     """
 
     @pytest.fixture(autouse=True)
@@ -123,20 +125,81 @@ class TestAggregatorConfig(object):
 
             assert error_raised, "Error must be raised in case of an invalid argument."
 
+    def test_start(self):
+        """
+        Tests the case of starting the aggregator through an API call.
+        """
 
-class TestAggregatorStarter(object):
-    """
-    A pytest-implementation test for the aggregator starter API calls
-    """
+        from clmcservice.views import aggregator_starter # nested import so that importing the class view is part of the test itself
 
-    def test_start(self):
-        #TODO
-        pass
+        assert not self.config.get_settings().get('aggregator_running'), "Initially aggregator is not running."
+
+        request = testing.DummyRequest()
+        input_body = '{"action": "start"}'
+        request.body = input_body.encode(request.charset)
+
+        response = aggregator_starter(request)
+        assert response == {'aggregator_running': True}, "The aggregator should have been started."
+        assert self.config.get_settings().get('aggregator_running'), "The aggregator should have been started."
+
+        # kill the started process after the test is over
+        pid = request.registry.settings["aggregator_pid"]
+        os.kill(pid, signal.SIGTERM)
 
     def test_stop(self):
-        #TODO
-        pass
+        from clmcservice.views import aggregator_starter  # nested import so that importing the class view is part of the test itself
+
+        assert not self.config.get_settings().get('aggregator_running'), "Initially aggregator is not running."
+
+        # send a start request to trigger the aggregator
+        request = testing.DummyRequest()
+        input_body = '{"action": "start"}'
+        request.body = input_body.encode(request.charset)
+        aggregator_starter(request)
+
+        # test stopping the aggregator process when it is running
+        request = testing.DummyRequest()
+        input_body = '{"action": "stop"}'
+        request.body = input_body.encode(request.charset)
+
+        response = aggregator_starter(request)
+        assert response == {'aggregator_running': False}, "The aggregator should have been started."
+        assert not self.config.get_settings().get('aggregator_running'), "The aggregator should have been started."
+
+        sleep(2)
+
+        # test stopping the aggregator process when it is not running
+        request = testing.DummyRequest()
+        input_body = '{"action": "stop"}'
+        request.body = input_body.encode(request.charset)
+
+        response = aggregator_starter(request)
+        assert response == {'aggregator_running': False}, "The aggregator should have been started."
+        assert not self.config.get_settings().get('aggregator_running'), "The aggregator should have been started."
 
     def test_restart(self):
-        #TODO
-        pass
+        from clmcservice.views import aggregator_starter  # nested import so that importing the class view is part of the test itself
+
+        assert not self.config.get_settings().get('aggregator_running'), "Initially aggregator is not running."
+
+        # test restarting the aggregator process when it is stopped
+        request = testing.DummyRequest()
+        input_body = '{"action": "restart"}'
+        request.body = input_body.encode(request.charset)
+
+        response = aggregator_starter(request)
+        assert response == {'aggregator_running': True}, "The aggregator should have been started."
+        assert self.config.get_settings().get('aggregator_running'), "The aggregator should have been started."
+
+        # test restarting the aggregator process when it is running
+        request = testing.DummyRequest()
+        input_body = '{"action": "restart"}'
+        request.body = input_body.encode(request.charset)
+
+        response = aggregator_starter(request)
+        assert response == {'aggregator_running': True}, "The aggregator should have been started."
+        assert self.config.get_settings().get('aggregator_running'), "The aggregator should have been started."
+
+        # kill the started process after the test is over
+        pid = request.registry.settings["aggregator_pid"]
+        os.kill(pid, signal.SIGTERM)
diff --git a/src/clmc-webservice/clmcservice/views.py b/src/clmc-webservice/clmcservice/views.py
index 70e4838..4fc47ff 100644
--- a/src/clmc-webservice/clmcservice/views.py
+++ b/src/clmc-webservice/clmcservice/views.py
@@ -36,6 +36,7 @@ def start_aggregator(config):
     An auxiliary method to start the aggregator.
 
     :param config: the configuration containing the arguments for the aggregator
+    :return: the process ID of the started aggregator script
     """
 
     global _process
@@ -44,7 +45,9 @@ def start_aggregator(config):
     command = ['python', 'aggregator.py', '--period', str(config.get('aggregator_report_period')), '--database',
                config.get('aggregator_database_name'), '--url', config.get('aggregator_database_url')]
     _process = Popen(command, cwd=dir_path, stdout=DEVNULL, stderr=DEVNULL, stdin=DEVNULL)
-    print("Started aggregator process with ID: {0}".format(_process.pid))
+    print("\nStarted aggregator process with PID: {0}\n".format(_process.pid))
+
+    return _process.pid
 
 
 def stop_aggregator():
@@ -57,6 +60,7 @@ def stop_aggregator():
     # check if the process is started before trying to terminate it - _process.poll() only returns something if the process has terminated, hence we check for a None value
     if _process is not None and _process.poll() is None:
         _process.terminate()
+        print("\nStopped aggregator process with PID: {0}\n".format(_process.pid))
 
 
 @view_defaults(route_name='aggregator_config', renderer='json')
@@ -133,15 +137,17 @@ def aggregator_starter(request):
         action = content['action']
 
         if action == 'start':
-            start_aggregator(config)
+            pid = start_aggregator(config)
             request.registry.settings[STATUS_ATTRIBUTE] = True
+            request.registry.settings["aggregator_pid"] = pid
         elif action == 'stop':
             stop_aggregator()
             request.registry.settings[STATUS_ATTRIBUTE] = False
         elif action == 'restart':
             stop_aggregator()
-            start_aggregator(config)
+            pid = start_aggregator(config)
             request.registry.settings[STATUS_ATTRIBUTE] = True
+            request.registry.settings["aggregator_pid"] = pid
 
         return {STATUS_ATTRIBUTE: request.registry.settings.get(STATUS_ATTRIBUTE)}
 
-- 
GitLab