Skip to content

Commit

Permalink
Modify api_fake_data's api_handler to be lazy
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
teferi authored and boris-42 committed Dec 3, 2016
1 parent 389713a commit 0a6839c
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 9 deletions.
2 changes: 2 additions & 0 deletions ceagle/api/v1/status.py
Expand Up @@ -32,6 +32,7 @@ def get_status(period):


@bp_status.route("/health/<period>")
@fake_status.get_status_health
def get_status_health(period):
health_client = client.get_client("health")
if not health_client:
Expand Down Expand Up @@ -62,6 +63,7 @@ def get_region_status(region, period):


@bp_region_status.route("/<region>/status/health/<period>")
@fake_status.get_region_status_health
def get_region_status_health(region, period):
health_client = client.get_client("health")
if not health_client:
Expand Down
18 changes: 17 additions & 1 deletion ceagle/api_fake_data/base.py
Expand Up @@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.

import functools
import random

from ceagle import config
Expand All @@ -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):
Expand Down
24 changes: 18 additions & 6 deletions tests/unit/api/v1/test_status.py
Expand Up @@ -15,6 +15,7 @@

import mock

from ceagle.api_fake_data import base as fake_api_base
from tests.unit import test


Expand All @@ -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 (
Expand All @@ -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")
Expand All @@ -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):
Expand Down
6 changes: 4 additions & 2 deletions tests/unit/api_fake_data/test_base.py
Expand Up @@ -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):
Expand Down

0 comments on commit 0a6839c

Please sign in to comment.