From 61a6bda63c24c0c65a650f617556dc9c17b2742f Mon Sep 17 00:00:00 2001
From: Andrew Melton <andrew.melton@rackspace.com>
Date: Thu, 24 Jan 2013 14:27:30 -0500
Subject: [PATCH] More lifecycle/KPI unit tests and some cleanup

---
 stacktach/db.py                        |  6 ++
 stacktach/tests.py                     | 21 ------
 stacktach/views.py                     | 23 ++++---
 tests/__init__.py                      |  6 --
 tests/integration/__init__.py          |  0
 tests/integration/settings.py          |  1 +
 tests/unit/test_datetime_to_decimal.py | 21 ++++++
 tests/unit/test_stacktach.py           | 90 ++++++++++++++++++++++----
 tests/unit/{test_utils.py => utils.py} | 39 ++++++++++-
 tox.ini                                | 28 --------
 10 files changed, 152 insertions(+), 83 deletions(-)
 create mode 100644 tests/integration/__init__.py
 create mode 120000 tests/integration/settings.py
 create mode 100644 tests/unit/test_datetime_to_decimal.py
 rename tests/unit/{test_utils.py => utils.py} (51%)
 delete mode 100644 tox.ini

diff --git a/stacktach/db.py b/stacktach/db.py
index 386ac40..9d5290b 100644
--- a/stacktach/db.py
+++ b/stacktach/db.py
@@ -12,5 +12,11 @@ def create_timing(**kwargs):
 def find_timings(**kwargs):
     return models.Timing.objects.select_related().filter(**kwargs)
 
+def create_request_tracker(**kwargs):
+    return models.RequestTracker(**kwargs)
+
+def find_request_trackers(**kwargs):
+    return models.RequestTracker.objects.filter(**kwargs)
+
 def save(obj):
     obj.save()
\ No newline at end of file
diff --git a/stacktach/tests.py b/stacktach/tests.py
index 370a828..0b55f71 100644
--- a/stacktach/tests.py
+++ b/stacktach/tests.py
@@ -17,27 +17,6 @@ from test_utils import create_raw
 import views
 
 
-class DatetimeToDecimalTestCase(unittest.TestCase):
-
-    def test_datetime_to_and_from_decimal(self):
-        now = datetime.datetime.utcnow()
-        d = datetime_to_decimal.dt_to_decimal(now)
-        daittyme = datetime_to_decimal.dt_from_decimal(d)
-        self.assertEqual(now, daittyme)
-
-    def test_datetime_to_decimal(self):
-        expected_decimal = decimal.Decimal('1356093296.123')
-        utc_datetime = datetime.datetime.utcfromtimestamp(expected_decimal)
-        actual_decimal = datetime_to_decimal.dt_to_decimal(utc_datetime)
-        self.assertEqual(actual_decimal, expected_decimal)
-
-    def test_decimal_to_datetime(self):
-        expected_decimal = decimal.Decimal('1356093296.123')
-        expected_datetime = datetime.datetime.utcfromtimestamp(expected_decimal)
-        actual_datetime = datetime_to_decimal.dt_from_decimal(expected_decimal)
-        self.assertEqual(actual_datetime, expected_datetime)
-
-
 class ViewsUtilsTestCase(unittest.TestCase):
 
     def test_srt_time_to_unix(self):
diff --git a/stacktach/views.py b/stacktach/views.py
index f8eb847..b53755f 100644
--- a/stacktach/views.py
+++ b/stacktach/views.py
@@ -67,7 +67,7 @@ def _compute_update_message(routing_key, body):
     resp = dict(host=host, instance=instance, publisher=publisher,
                 service=service, event=event, tenant=tenant,
                 request_id=request_id)
-    payload = data.get('payload', {})
+    payload = body.get('payload', {})
     resp.update(_extract_states(payload))
     return resp
 
@@ -87,15 +87,15 @@ def start_kpi_tracking(lifecycle, raw):
     if "api" not in raw.host:
         return
 
-    tracker = models.RequestTracker(request_id=raw.request_id,
-                                    start=raw.when,
-                                    lifecycle=lifecycle,
-                                    last_timing=None,
-                                    duration=str(0.0))
-    tracker.save()
+    tracker = STACKDB.create_request_tracker(request_id=raw.request_id,
+                                             start=raw.when,
+                                             lifecycle=lifecycle,
+                                             last_timing=None,
+                                             duration=str(0.0))
+    STACKDB.save(tracker)
 
 
-def update_kpi(lifecycle, timing, raw):
+def update_kpi(timing, raw):
     """Whenever we get a .end event, use the Timing object to
     compute our current end-to-end duration.
 
@@ -106,15 +106,14 @@ def update_kpi(lifecycle, timing, raw):
 
     Until then, we'll take the lazy route and be aware of these
     potential fence-post issues."""
-    trackers = models.RequestTracker.objects.\
-                                        filter(request_id=raw.request_id)
+    trackers = STACKDB.find_request_trackers(request_id=raw.request_id)
     if len(trackers) == 0:
         return
 
     tracker = trackers[0]
     tracker.last_timing = timing
     tracker.duration = timing.end_when - tracker.start
-    tracker.save()
+    STACKDB.save(tracker)
 
 
 def aggregate_lifecycle(raw):
@@ -198,7 +197,7 @@ def aggregate_lifecycle(raw):
         if timing.start_when:
             timing.diff = timing.end_when - timing.start_when
             # Looks like a valid pair ...
-            update_kpi(lifecycle, timing, raw)
+            update_kpi(timing, raw)
     STACKDB.save(timing)
 
 
diff --git a/tests/__init__.py b/tests/__init__.py
index d90f00b..e69de29 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -1,6 +0,0 @@
-import os
-import sys
-
-print "!!!!!!! %s" %__package__
-
-#sys.path = [os.path.abspath(os.path.dirname(__package__))] + sys.path
\ No newline at end of file
diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/integration/settings.py b/tests/integration/settings.py
new file mode 120000
index 0000000..fff7c2e
--- /dev/null
+++ b/tests/integration/settings.py
@@ -0,0 +1 @@
+settings.py
\ No newline at end of file
diff --git a/tests/unit/test_datetime_to_decimal.py b/tests/unit/test_datetime_to_decimal.py
new file mode 100644
index 0000000..8632640
--- /dev/null
+++ b/tests/unit/test_datetime_to_decimal.py
@@ -0,0 +1,21 @@
+import datetime
+import decimal
+import unittest
+
+import utils
+utils.setup_sys_path()
+from stacktach import datetime_to_decimal
+
+class DatetimeToDecimalTestCase(unittest.TestCase):
+
+    def test_datetime_to_decimal(self):
+        expected_decimal = decimal.Decimal('1356093296.123')
+        utc_datetime = datetime.datetime.utcfromtimestamp(expected_decimal)
+        actual_decimal = datetime_to_decimal.dt_to_decimal(utc_datetime)
+        self.assertEqual(actual_decimal, expected_decimal)
+
+    def test_decimal_to_datetime(self):
+        expected_decimal = decimal.Decimal('1356093296.123')
+        expected_datetime = datetime.datetime.utcfromtimestamp(expected_decimal)
+        actual_datetime = datetime_to_decimal.dt_from_decimal(expected_decimal)
+        self.assertEqual(actual_datetime, expected_datetime)
diff --git a/tests/unit/test_stacktach.py b/tests/unit/test_stacktach.py
index 850ea93..2d093a1 100644
--- a/tests/unit/test_stacktach.py
+++ b/tests/unit/test_stacktach.py
@@ -5,14 +5,16 @@ import unittest
 
 import mox
 
-INSTANCE_ID_1 = 'testinstanceid1'
-INSTANCE_ID_2 = 'testinstanceid2'
-
-os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
-sys.path = [os.path.abspath(os.path.dirname('stacktach'))] + sys.path
-
+import utils
+utils.setup_sys_path()
+from utils import INSTANCE_ID_1
+from utils import INSTANCE_ID_2
+from utils import MESSAGE_ID_1
+from utils import MESSAGE_ID_2
+from utils import REQUEST_ID_1
+from utils import REQUEST_ID_2
+from utils import REQUEST_ID_3
 from stacktach import views
-import test_utils as utils
 
 class StacktachLifecycleTestCase(unittest.TestCase):
     def setUp(self):
@@ -20,15 +22,77 @@ class StacktachLifecycleTestCase(unittest.TestCase):
         views.STACKDB = self.mox.CreateMockAnything()
 
     def tearDown(self):
-        pass
+        self.mox.UnsetStubs()
+
+    def test_start_kpi_tracking_not_update(self):
+        raw = self.mox.CreateMockAnything()
+        raw.event = 'compute.instance.create.start'
+        self.mox.ReplayAll()
+        views.start_kpi_tracking(None, raw)
+        self.mox.VerifyAll()
+
+    def test_start_kpi_tracking_not_from_api(self):
+        raw = self.mox.CreateMockAnything()
+        raw.event = 'compute.instance.update'
+        raw.host = 'compute'
+        self.mox.ReplayAll()
+        views.start_kpi_tracking(None, raw)
+        self.mox.VerifyAll()
+
+    def test_start_kpi_tracking(self):
+        lifecycle = self.mox.CreateMockAnything()
+        tracker = self.mox.CreateMockAnything()
+        when = utils.decimal_utcnow()
+        raw = utils.create_raw(self.mox, when, 'compute.instance.update',
+                               host='api')
+        views.STACKDB.create_request_tracker(lifecycle=lifecycle,
+                                             request_id=REQUEST_ID_1,
+                                             start=when,
+                                             last_timing=None,
+                                             duration=str(0.0))\
+                                             .AndReturn(tracker)
+        views.STACKDB.save(tracker)
+        self.mox.ReplayAll()
+        views.start_kpi_tracking(lifecycle, raw)
+        self.mox.VerifyAll()
+
+    def test_update_kpi_no_trackers(self):
+        raw = self.mox.CreateMockAnything()
+        raw.request_id = REQUEST_ID_1
+        views.STACKDB.find_request_trackers(request_id=REQUEST_ID_1)\
+                                            .AndReturn([])
+        self.mox.ReplayAll()
+        views.update_kpi(None, raw)
+        self.mox.VerifyAll()
+
+    def test_update_kpi(self):
+        lifecycle = self.mox.CreateMockAnything()
+        end = utils.decimal_utcnow()
+        raw = self.mox.CreateMockAnything()
+        raw.request_id = REQUEST_ID_1
+        raw.when=end
+        timing = utils.create_timing(self.mox, 'compute.instance.create',
+                                     lifecycle, end_when=end)
+        start = utils.decimal_utcnow()
+        tracker = utils.create_tracker(self.mox, REQUEST_ID_1, lifecycle,
+                                       start)
+        views.STACKDB.find_request_trackers(request_id=REQUEST_ID_1)\
+                                            .AndReturn([tracker])
+        views.STACKDB.save(tracker)
+        self.mox.ReplayAll()
+        views.update_kpi(timing, raw)
+        self.assertEqual(tracker.request_id, REQUEST_ID_1)
+        self.assertEqual(tracker.lifecycle, lifecycle)
+        self.assertEqual(tracker.last_timing, timing)
+        self.assertEqual(tracker.start, start)
+        self.assertEqual(tracker.duration, end-start)
+        self.mox.VerifyAll()
 
     def test_aggregate_lifecycle_no_instance(self):
         raw = self.mox.CreateMockAnything()
         raw.instance = None
-
-        views.aggregate_lifecycle(raw)
-
         self.mox.ReplayAll()
+        views.aggregate_lifecycle(raw)
         self.mox.VerifyAll()
 
     def test_aggregate_lifecycle_start(self):
@@ -82,7 +146,7 @@ class StacktachLifecycleTestCase(unittest.TestCase):
         views.STACKDB.find_timings(name=event_name, lifecycle=lifecycle).AndReturn([timing])
 
         self.mox.StubOutWithMock(views, "update_kpi")
-        views.update_kpi(lifecycle, timing, end_raw)
+        views.update_kpi(timing, end_raw)
         views.STACKDB.save(timing)
 
         self.mox.ReplayAll()
@@ -98,7 +162,6 @@ class StacktachLifecycleTestCase(unittest.TestCase):
         self.assertEqual(timing.end_when, end_when)
         self.assertEqual(timing.diff, end_when-start_when)
 
-        self.mox.UnsetStubs()
         self.mox.VerifyAll()
 
 
@@ -122,5 +185,4 @@ class StacktachLifecycleTestCase(unittest.TestCase):
         self.assertEqual(lifecycle.last_state, 'active')
         self.assertEqual(lifecycle.last_task_state, 'reboot')
 
-        self.mox.UnsetStubs()
         self.mox.VerifyAll()
diff --git a/tests/unit/test_utils.py b/tests/unit/utils.py
similarity index 51%
rename from tests/unit/test_utils.py
rename to tests/unit/utils.py
index 82fa2e3..742285e 100644
--- a/tests/unit/test_utils.py
+++ b/tests/unit/utils.py
@@ -1,3 +1,7 @@
+import datetime
+import os
+import sys
+import unittest
 
 INSTANCE_ID_1 = 'testinstanceid1'
 INSTANCE_ID_2 = 'testinstanceid2'
@@ -9,6 +13,27 @@ REQUEST_ID_1 = 'testrequestid1'
 REQUEST_ID_2 = 'testrequestid2'
 REQUEST_ID_3 = 'testrequestid3'
 
+def setup_sys_path():
+    sys.path = [os.path.abspath(os.path.dirname('stacktach'))] + sys.path
+
+def setup_environment():
+    os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
+    os.environ['STACKTACH_DB_ENGINE'] = 'django.db.backends.sqlite3'
+    when = str(datetime.datetime.utcnow())
+    os.environ['STACKTACH_DB_NAME'] = '/tmp/stacktach.%s.sqlite' % when
+    os.environ['STACKTACH_DB_HOST'] = ''
+    os.environ['STACKTACH_DB_USERNAME'] = ''
+    os.environ['STACKTACH_DB_PASSWORD'] = ''
+    install_dir = os.path.abspath(os.path.dirname('stacktach'))
+    os.environ['STACKTACH_INSTALL_DIR'] = install_dir
+
+setup_sys_path()
+setup_environment()
+from stacktach import datetime_to_decimal as dt
+
+def decimal_utcnow():
+    return dt.dt_to_decimal(datetime.datetime.utcnow())
+
 def create_raw(mox, when, event, instance=INSTANCE_ID_1,
                request_id=REQUEST_ID_1, state='active', old_task='',
                host='compute', json=''):
@@ -19,7 +44,7 @@ def create_raw(mox, when, event, instance=INSTANCE_ID_1,
     raw.when = when
     raw.state = state
     raw.old_task = old_task
-    raw.request_id = request_id,
+    raw.request_id = request_id
     raw.json = json
     return raw
 
@@ -41,4 +66,14 @@ def create_timing(mox, name, lifecycle, start_raw=None, start_when=None,
     timing.end_raw = end_raw
     timing.end_when = end_when
     timing.diff = diff
-    return timing
\ No newline at end of file
+    return timing
+
+def create_tracker(mox, request_id, lifecycle, start, last_timing=None,
+                   duration=str(0.0)):
+    tracker = mox.CreateMockAnything()
+    tracker.request_id=request_id
+    tracker.lifecycle=lifecycle
+    tracker.start=start
+    tracker.last_timing=last_timing
+    tracker.duration=duration
+    return tracker
\ No newline at end of file
diff --git a/tox.ini b/tox.ini
deleted file mode 100644
index 630adc5..0000000
--- a/tox.ini
+++ /dev/null
@@ -1,28 +0,0 @@
-[tox]
-envlist = py26,py27,pep8
-
-[testenv]
-setenv = VIRTUAL_ENV={envdir}
-#         NOSE_WITH_OPENSTACK=1
-#         NOSE_OPENSTACK_COLOR=1
-#         NOSE_OPENSTACK_RED=0.05
-#         NOSE_OPENSTACK_YELLOW=0.025
-#         NOSE_OPENSTACK_SHOW_ELAPSED=1
-#         NOSE_OPENSTACK_STDOUT=1
-#deps = -r{toxinidir}/tools/pip-requires
-#       -r{toxinidir}/tools/test-requires
-#commands = nosetests {posargs}
-
-[tox:jenkins]
-downloadcache = ~/cache/pip
-
-[testenv:pep8]
-deps = pep8==1.3.3
-commands =
-  pep8 --ignore=E125,E126,E711,E712 --repeat --show-source --exclude=.venv,.tox,dist,doc,openstack .
-
-[testenv:cover]
-setenv = NOSE_WITH_COVERAGE=1
-
-[testenv:venv]
-commands = {posargs}