From 0a6839c9f0c41598aa09bfc78d50635851f58423 Mon Sep 17 00:00:00 2001 From: Kirill Zaitsev Date: Wed, 30 Nov 2016 00:44:27 +0300 Subject: [PATCH] Modify api_fake_data's api_handler to be lazy Previously api_handler function returned a lambda, that could be used as a decorator and would return fake or real function, depending on a global variable. The decision was made at import time. This made it very difficult to test decorated functions behaviour, since these functions were decorated away before there was any chance to mock config or global variable. With this commit api_handler returns a decorator, that, when applied, returns a function, that proxies calls to fake or real api function, depending on global variable. This consequently means there will be an extra function call for every request. However this would potentially allow us to switch behaviour from/to fake functions on the fly. This commit also restores fake decorators around health API functions. --- ceagle/api/v1/status.py | 2 ++ ceagle/api_fake_data/base.py | 18 +++++++++++++++++- tests/unit/api/v1/test_status.py | 24 ++++++++++++++++++------ tests/unit/api_fake_data/test_base.py | 6 ++++-- 4 files changed, 41 insertions(+), 9 deletions(-) diff --git a/ceagle/api/v1/status.py b/ceagle/api/v1/status.py index 1089de1..6ea66ba 100644 --- a/ceagle/api/v1/status.py +++ b/ceagle/api/v1/status.py @@ -32,6 +32,7 @@ def get_status(period): @bp_status.route("/health/") +@fake_status.get_status_health def get_status_health(period): health_client = client.get_client("health") if not health_client: @@ -62,6 +63,7 @@ def get_region_status(region, period): @bp_region_status.route("//status/health/") +@fake_status.get_region_status_health def get_region_status_health(region, period): health_client = client.get_client("health") if not health_client: diff --git a/ceagle/api_fake_data/base.py b/ceagle/api_fake_data/base.py index 28b4c0e..87c4ebb 100644 --- a/ceagle/api_fake_data/base.py +++ b/ceagle/api_fake_data/base.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +import functools import random from ceagle import config @@ -23,7 +24,22 @@ def api_handler(fake): - return lambda real: USE_FAKE_DATA and fake or real + """Function that handles api_fake_data substitution functions + + It is intended to be used as a decorator around fake api functions. It + turns a function it decorates into a decorator, that accepts "real" api + function (real_function_handler). The resulting decorator returns a + (choice_maker) function, that calls real or fake function, depending on the + value of USE_FAKE_DATA global variable. + """ + def real_function_handler(real): + @functools.wraps(real) + def choice_maker(*args, **kwargs): + if USE_FAKE_DATA: + return fake(*args, **kwargs) + return real(*args, **kwargs) + return choice_maker + return real_function_handler def randnum(from_num, to_num, round_by=2): diff --git a/tests/unit/api/v1/test_status.py b/tests/unit/api/v1/test_status.py index 763c469..d4c0429 100644 --- a/tests/unit/api/v1/test_status.py +++ b/tests/unit/api/v1/test_status.py @@ -15,6 +15,7 @@ import mock +from ceagle.api_fake_data import base as fake_api_base from tests.unit import test @@ -23,9 +24,11 @@ class ApiTestCase(test.TestCase): def test_api_response_code(self): region = "north-2.piedpiper.net" for urlpath in ("/api/v1/status", + "/api/v1/status/health", "/api/v1/status/performance", "/api/v1/status/availability", "/api/v1/region/%s/status" % region, + "/api/v1/region/%s/status/health" % region, "/api/v1/region/%s/status/performance" % region, "/api/v1/region/%s/status/availability" % region): for suffix, code in ( @@ -45,17 +48,26 @@ class HealthApiTestCase(test.TestCase): def setUp(self): super(HealthApiTestCase, self).setUp() - self.health_config = {"health": { - "endpoint": "http://dummy.example.org/api/health" - }} + self.health_config = { + "services": { + "health": "http://dummy.example.org/api/health" + } + } + + self.old_USE_FAKE_DATA = fake_api_base.USE_FAKE_DATA + fake_api_base.USE_FAKE_DATA = False + + def tearDown(self): + fake_api_base.USE_FAKE_DATA = self.old_USE_FAKE_DATA + super(HealthApiTestCase, self).tearDown() @mock.patch("ceagle.api.client.Client") def test_status_health_api(self, mock_client, mock_get_config): mock_get_config.return_value = self.health_config mock_client.return_value.get.return_value = {"status_code": 200} code, resp = self.get("/api/v1/status/health/day") - mock_client.return_value.get.assert_called_with( - "/api/v1/health", params={"period": u"day"}) + + mock_client.return_value.get.assert_called_with("/api/v1/health/day") self.assertEqual(200, code) @mock.patch("ceagle.api.client.Client") @@ -65,7 +77,7 @@ def test_region_status_health_api(self, mock_client, mock_get_config): code, resp = self.get("/api/v1/region/test_region/status/health/day") mock_client.return_value.get.assert_called_with( - "/api/v1/health/test_region", params={"period": u"day"}) + "/api/v1/region/test_region/health/day") self.assertEqual(200, code) def test_health_api_no_endpoint(self, mock_get_config): diff --git a/tests/unit/api_fake_data/test_base.py b/tests/unit/api_fake_data/test_base.py index 8dd8e3e..b4044fe 100644 --- a/tests/unit/api_fake_data/test_base.py +++ b/tests/unit/api_fake_data/test_base.py @@ -24,10 +24,12 @@ class ModuleTestCase(test.TestCase): @mock.patch("ceagle.api_fake_data.base.config") def test_api_handler(self, mock_config): mock_config.get_config.return_value = {} - self.assertEqual("fake", base.api_handler("fake")("real")) + fake = lambda: "fake" + real = lambda: "real" + self.assertEqual("fake", base.api_handler(fake)(real)()) self.assertEqual(base.USE_FAKE_DATA, True) base.USE_FAKE_DATA = False - self.assertEqual("real", base.api_handler("fake")("real")) + self.assertEqual("real", base.api_handler(fake)(real)()) @mock.patch("ceagle.api_fake_data.base.random") def test_randnum(self, mock_random):