Skip to content

Commit

Permalink
Merge 4711f1c into a630b3c
Browse files Browse the repository at this point in the history
  • Loading branch information
darius committed Jun 2, 2017
2 parents a630b3c + 4711f1c commit 2a15d14
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 8 deletions.
21 changes: 17 additions & 4 deletions rest_models/backend/connexion.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@
from rest_models.backend.exceptions import FakeDatabaseDbAPI2
from rest_models.backend.utils import message_from_response

try:
from urllib.parse import urlparse, urlunparse
except ImportError: # pragma: no cover
from urlparse import urlparse, urlunparse

logger = logging.getLogger("django.db.backends")


Expand Down Expand Up @@ -232,6 +237,9 @@ def __init__(self, url, auth=None, retry=3, timeout=3, backend=None, middlewares
:param DatabaseWrapper backend: the backend
:param list[Middleware]|tuple[Middleware] middlewares:the list of middleware to execute for each query.
"""
if not url.endswith('/'):
# fix the miss configured url in the api (must end with a /)
url = url + '/'
self.session = requests.Session()
self.session.mount(LocalApiAdapter.SPECIAL_URL, LocalApiAdapter())
self.session.auth = self.auth = auth
Expand Down Expand Up @@ -351,16 +359,13 @@ def request(self, method, url, **kwargs):
kwargs.setdefault("allow_redirects", False)
kwargs.setdefault("timeout", self.get_timeout())
kwargs.setdefault('stream', False)
real_url = self.get_final_url(url)

assert not url.startswith("/"), "the url should not start with a «/»"
if url != '' and not self.url.endswith('/'):
url = '/' + url
error = 0
last_exception = None

while error <= self.retry:
try:
real_url = self.url + url
# to stay compatible with django_debug_toolbar, we must
# call execute on the cursor return by the backend, since this one is replaced
# by django-debug-toolbar to state the query.
Expand Down Expand Up @@ -391,3 +396,11 @@ def request(self, method, url, **kwargs):
raise FakeDatabaseDbAPI2.OperationalError(
"cound not connect to server: %s\nIs the API running on %s ? tried %d times" %
(last_exception, self.url, error))

def get_final_url(self, url):
if url.startswith("/"):
parsed_url = urlparse(url)
api_url = urlparse(self.url)
return urlunparse(api_url[:2] + parsed_url[2:])

return self.url + url
37 changes: 35 additions & 2 deletions rest_models/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,22 @@ def process_request(self, params, requestid, connection):
type(data))


class TrackRequestMiddleware(ApiMiddleware):
def __init__(self):
self.queries = {}

def process_request(self, params, requestid, connection):
self.queries[requestid] = {
'params': params,
}

def process_response(self, params, response, requestid):
self.queries[requestid]['response'] = response

def get_for_url(self, url):
return [q for q in self.queries.values() if q['params']['url'].endswith(url)]


class RestModelTestMixin(object):
"""
a test case mixin that add the feathure to mock the response from an api
Expand Down Expand Up @@ -149,7 +165,23 @@ def tearDown(self):
connections[db_name].connection.pop_middleware(middleware)

@contextmanager
def mock_api(self, url, result, params=None, using=None):
def track_query(self, using=None):
"""
:return: the middleware that tracked the queeries
:rtype: TrackRequestMiddleware
"""
connection = connections[using or get_default_api_database(settings.DATABASES)]
middleware = TrackRequestMiddleware()
cursor = connection.cursor()
try:

cursor.push_middleware(middleware, priority=6)
yield middleware
finally:
cursor.pop_middleware(middleware)

@contextmanager
def mock_api(self, url, result, params=None, using=None, status_code=200):
"""
assert that the eather one of the api have executed a query, with optionnaly the given params,
the query was made `count` times and by the given API
Expand All @@ -163,7 +195,8 @@ def mock_api(self, url, result, params=None, using=None):
mocked = {
url: [{
'filter': params or {},
'data': result
'data': result,
'status_code': status_code
}]
}
middleware = MockDataApiMiddleware(mocked, not_found=not_found_continue)
Expand Down
2 changes: 1 addition & 1 deletion rest_models/tests/test_introspection.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals

import six
from django.core.management import call_command
from django.test.testcases import TestCase
import six


class TestIntrospection(TestCase):
Expand Down
13 changes: 13 additions & 0 deletions rest_models/tests/test_restmodeltestcase.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,23 @@ def test_mock_api(self):
with self.mock_api('me/17', [1, 2, 3], using='api'):
self.assertEqual(self.client.get('me/17').json(), [1, 2, 3])

def test_mock_api_statuscode(self):
with self.mock_api('me/17', [1, 2, 3], using='api', status_code=201):
self.assertEqual(self.client.get('me/17').status_code, 201)
self.assertEqual(self.client.get('me/17').json(), [1, 2, 3])

def test_mock_api_pass(self):
with self.mock_api('lol', [1, 2, 3], using='api'):
self.assertEqual(self.client.get('b').status_code, 503)

def test_track_queries(self):
with self.track_query('api') as tracker:
self.assertEqual(self.client.get('c').json(), {'C': 'c'})
self.assertEqual(self.client.get('d').json(), {'A': 'a', 'B': 'b'})
self.assertEqual(len(tracker.get_for_url('c')), 1)
self.assertEqual(len(tracker.get_for_url('d')), 1)
self.assertEqual(len(tracker.get_for_url('b')), 0)


class TestMockDataSample(RestModelTestCase):
database_rest_fixtures = {'api': { # api => response mocker for databasen named «api»
Expand Down
27 changes: 27 additions & 0 deletions rest_models/tests/tests_connexion.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,33 @@ def test_auth_bad_cred(self):
c = ApiConnexion(self.live_server_url + "/api/v2/", auth=('user1', 'badpasswd'))
self.assertRaises(ProgrammingError, c.patch, 'authpizza/1', json={'pizza': {'price': 0}})

def test_url_resolving(self):
c = ApiConnexion(self.live_server_url + "/api/v2/", auth=('admin', 'admin'))
r = c.get('/other/view/', json={'result': 'ok'})
self.assertEqual(r.status_code, 200)


class TestApiConnexionUrlReslving(TestCase):

def setUp(self):
self.c = ApiConnexion("http://localhost/api/v2/", auth=('user1', 'badpasswd'))

def test_auto_fix_url(self):
c = ApiConnexion("http://localhost/api/v2", auth=('user1', 'badpasswd'))
self.assertEqual(c.url, "http://localhost/api/v2/") # plus final /

def test_url_relative(self):
self.assertEqual(self.c.get_final_url('pizza'), "http://localhost/api/v2/pizza")

def test_url_relative_end_slash(self):
self.assertEqual(self.c.get_final_url('pizza/'), "http://localhost/api/v2/pizza/")

def test_url_absolute(self):
self.assertEqual(self.c.get_final_url('/pizza'), "http://localhost/pizza")

def test_url_absolute_end_slash(self):
self.assertEqual(self.c.get_final_url('/pizza/'), "http://localhost/pizza/")


class TestOauth(TestCase):
def setUp(self):
Expand Down
3 changes: 2 additions & 1 deletion testapi/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from django.conf.urls import include, url
from django.contrib import admin
from django.http.response import HttpResponseForbidden
from django.http.response import HttpResponse, HttpResponseForbidden
from django.views.generic.base import RedirectView
from dynamic_rest.routers import DynamicRouter

Expand All @@ -24,6 +24,7 @@
url(r'^api/v2/view/$', fake_view),
url(r'^api/v2/', include(router.urls)),
url(r'^api/forbidden', lambda request: HttpResponseForbidden()),
url(r'^other/view/', lambda request: HttpResponse(b'{"result": "ok"}')),
url(r'admin/', include(admin.site.urls)),
url(r'^$', RedirectView.as_view(url='api/v2', permanent=False))
]

0 comments on commit 2a15d14

Please sign in to comment.