Statistics
| Branch: | Tag: | Revision:

root / snf-django-lib / snf_django / utils / testing.py @ dcb8545f

History | View | Annotate | Download (9.5 kB)

1 f09da76e Kostas Papadimitriou
# Copyright 2011-2013 GRNET S.A. All rights reserved.
2 f09da76e Kostas Papadimitriou
#
3 f09da76e Kostas Papadimitriou
# Redistribution and use in source and binary forms, with or
4 f09da76e Kostas Papadimitriou
# without modification, are permitted provided that the following
5 f09da76e Kostas Papadimitriou
# conditions are met:
6 f09da76e Kostas Papadimitriou
#
7 f09da76e Kostas Papadimitriou
#   1. Redistributions of source code must retain the above
8 f09da76e Kostas Papadimitriou
#      copyright notice, this list of conditions and the following
9 f09da76e Kostas Papadimitriou
#      disclaimer.
10 f09da76e Kostas Papadimitriou
#
11 f09da76e Kostas Papadimitriou
#   2. Redistributions in binary form must reproduce the above
12 f09da76e Kostas Papadimitriou
#      copyright notice, this list of conditions and the following
13 f09da76e Kostas Papadimitriou
#      disclaimer in the documentation and/or other materials
14 f09da76e Kostas Papadimitriou
#      provided with the distribution.
15 f09da76e Kostas Papadimitriou
#
16 f09da76e Kostas Papadimitriou
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 f09da76e Kostas Papadimitriou
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 f09da76e Kostas Papadimitriou
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 f09da76e Kostas Papadimitriou
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 f09da76e Kostas Papadimitriou
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 f09da76e Kostas Papadimitriou
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 f09da76e Kostas Papadimitriou
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 f09da76e Kostas Papadimitriou
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 f09da76e Kostas Papadimitriou
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 f09da76e Kostas Papadimitriou
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 f09da76e Kostas Papadimitriou
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 f09da76e Kostas Papadimitriou
# POSSIBILITY OF SUCH DAMAGE.
28 f09da76e Kostas Papadimitriou
#
29 f09da76e Kostas Papadimitriou
# The views and conclusions contained in the software and
30 f09da76e Kostas Papadimitriou
# documentation are those of the authors and should not be
31 f09da76e Kostas Papadimitriou
# interpreted as representing official policies, either expressed
32 f09da76e Kostas Papadimitriou
# or implied, of GRNET S.A.
33 f09da76e Kostas Papadimitriou
34 f09da76e Kostas Papadimitriou
35 f09da76e Kostas Papadimitriou
from contextlib import contextmanager
36 d2b8ec7b Christos Stavrakakis
from django.test import TestCase
37 d2b8ec7b Christos Stavrakakis
from django.utils import simplejson as json
38 dcb8545f Christos Stavrakakis
from synnefo.util import text
39 d2b8ec7b Christos Stavrakakis
from mock import patch
40 f09da76e Kostas Papadimitriou
41 f09da76e Kostas Papadimitriou
42 f09da76e Kostas Papadimitriou
@contextmanager
43 f09da76e Kostas Papadimitriou
def override_settings(settings, **kwargs):
44 f09da76e Kostas Papadimitriou
    """
45 f09da76e Kostas Papadimitriou
    Helper context manager to override django settings within the provided
46 f09da76e Kostas Papadimitriou
    context.
47 f09da76e Kostas Papadimitriou

48 f09da76e Kostas Papadimitriou
    All keyword arguments provided are set to the django settings object and
49 f09da76e Kostas Papadimitriou
    get reverted/removed when the manager exits.
50 f09da76e Kostas Papadimitriou

51 f09da76e Kostas Papadimitriou
    >>> from synnefo.util.testing import override_settings
52 f09da76e Kostas Papadimitriou
    >>> from django.conf import settings
53 f09da76e Kostas Papadimitriou
    >>> with override_settings(settings, DEBUG=True):
54 f09da76e Kostas Papadimitriou
    >>>     assert settings.DEBUG == True
55 f09da76e Kostas Papadimitriou

56 f09da76e Kostas Papadimitriou
    The special arguemnt ``prefix`` can be set to prefix all setting keys with
57 f09da76e Kostas Papadimitriou
    the provided value.
58 f09da76e Kostas Papadimitriou

59 f09da76e Kostas Papadimitriou
    >>> from django.conf import settings
60 f09da76e Kostas Papadimitriou
    >>> from django.core import mail
61 f09da76e Kostas Papadimitriou
    >>> with override_settings(settings, CONTACT_EMAILS=['kpap@grnet.gr'],
62 f09da76e Kostas Papadimitriou
    >>>                        prefix='MYAPP_'):
63 f09da76e Kostas Papadimitriou
    >>>     from django.core.mail import send_mail
64 f09da76e Kostas Papadimitriou
    >>>     send_mail("hello", "I love you kpap", settings.DEFAULT_FROM_EMAIL,
65 f09da76e Kostas Papadimitriou
    >>>               settings.MYAPP_CONTACT_EMAILS)
66 f09da76e Kostas Papadimitriou
    >>>     assert 'kpap@grnet.gr' in mail.mailbox[0].recipients()
67 f09da76e Kostas Papadimitriou

68 f09da76e Kostas Papadimitriou
    If you plan to reuse it
69 f09da76e Kostas Papadimitriou

70 f09da76e Kostas Papadimitriou
    >>> import functools
71 f09da76e Kostas Papadimitriou
    >>> from synnefo.util.testing import override_settings
72 f09da76e Kostas Papadimitriou
    >>> from django.conf import settings
73 f09da76e Kostas Papadimitriou
    >>> myapp_settings = functools.partial(override_settings, prefix='MYAPP_')
74 f09da76e Kostas Papadimitriou
    >>> with myapp_settings(CONTACT_EMAILS=['kpap@grnet.gr'])
75 f09da76e Kostas Papadimitriou
    >>>     assert settings.MYAPP_CONTACT_EMAILS == ['kpap@grnet.gr']
76 f09da76e Kostas Papadimitriou

77 f09da76e Kostas Papadimitriou
    """
78 f09da76e Kostas Papadimitriou
79 f09da76e Kostas Papadimitriou
    _prefix = kwargs.get('prefix', '')
80 f09da76e Kostas Papadimitriou
    prefix = lambda key: '%s%s' % (_prefix, key)
81 f09da76e Kostas Papadimitriou
82 f09da76e Kostas Papadimitriou
    oldkeys = [k for k in dir(settings) if k.upper() == k]
83 f09da76e Kostas Papadimitriou
    oldsettings = dict([(k, getattr(settings, k)) for k in oldkeys])
84 f09da76e Kostas Papadimitriou
85 f09da76e Kostas Papadimitriou
    toremove = []
86 f09da76e Kostas Papadimitriou
    for key, value in kwargs.iteritems():
87 f09da76e Kostas Papadimitriou
        key = prefix(key)
88 f09da76e Kostas Papadimitriou
        if not hasattr(settings, key):
89 f09da76e Kostas Papadimitriou
            toremove.append(key)
90 f09da76e Kostas Papadimitriou
        setattr(settings, key, value)
91 f09da76e Kostas Papadimitriou
92 f09da76e Kostas Papadimitriou
    yield
93 f09da76e Kostas Papadimitriou
94 f09da76e Kostas Papadimitriou
    # Remove keys that didn't exist
95 f09da76e Kostas Papadimitriou
    for key in toremove:
96 f09da76e Kostas Papadimitriou
        delattr(settings, key)
97 f09da76e Kostas Papadimitriou
98 f09da76e Kostas Papadimitriou
    # Remove keys that added during the execution of the context
99 f09da76e Kostas Papadimitriou
    if kwargs.get('reset_changes', True):
100 f09da76e Kostas Papadimitriou
        newkeys = [k for k in dir(settings) if k.upper() == k]
101 f09da76e Kostas Papadimitriou
        for key in newkeys:
102 f09da76e Kostas Papadimitriou
            if not key in oldkeys:
103 f09da76e Kostas Papadimitriou
                delattr(settings, key)
104 f09da76e Kostas Papadimitriou
105 f09da76e Kostas Papadimitriou
    # Revert old keys
106 f09da76e Kostas Papadimitriou
    for key in oldkeys:
107 f09da76e Kostas Papadimitriou
        if key == key.upper():
108 f09da76e Kostas Papadimitriou
            setattr(settings, key, oldsettings.get(key))
109 f09da76e Kostas Papadimitriou
110 f09da76e Kostas Papadimitriou
111 f09da76e Kostas Papadimitriou
def with_settings(settings, prefix='', **override):
112 f09da76e Kostas Papadimitriou
    def wrapper(func):
113 f09da76e Kostas Papadimitriou
        def inner(*args, **kwargs):
114 f09da76e Kostas Papadimitriou
            with override_settings(settings, prefix=prefix, **override):
115 f09da76e Kostas Papadimitriou
                ret = func(*args, **kwargs)
116 f09da76e Kostas Papadimitriou
            return ret
117 f09da76e Kostas Papadimitriou
        return inner
118 f09da76e Kostas Papadimitriou
    return wrapper
119 d2b8ec7b Christos Stavrakakis
120 d2b8ec7b Christos Stavrakakis
121 d2b8ec7b Christos Stavrakakis
@contextmanager
122 d2b8ec7b Christos Stavrakakis
def astakos_user(user):
123 d2b8ec7b Christos Stavrakakis
    """
124 d2b8ec7b Christos Stavrakakis
    Context manager to mock astakos response.
125 d2b8ec7b Christos Stavrakakis

126 d2b8ec7b Christos Stavrakakis
    usage:
127 d2b8ec7b Christos Stavrakakis
    with astakos_user("user@user.com"):
128 d2b8ec7b Christos Stavrakakis
        .... make api calls ....
129 d2b8ec7b Christos Stavrakakis

130 d2b8ec7b Christos Stavrakakis
    """
131 d2b8ec7b Christos Stavrakakis
    with patch("snf_django.lib.api.get_token") as get_token:
132 d2b8ec7b Christos Stavrakakis
        get_token.return_value = "DummyToken"
133 d2b8ec7b Christos Stavrakakis
        with patch('astakosclient.AstakosClient.get_user_info') as m:
134 dcb8545f Christos Stavrakakis
            m.return_value = {"uuid": text.uenc(user, 'utf8')}
135 3f9db536 Christos Stavrakakis
            with patch('astakosclient.AstakosClient.get_quotas') as m2:
136 3f9db536 Christos Stavrakakis
                m2.return_value = {
137 3f9db536 Christos Stavrakakis
                    "system": {
138 3f9db536 Christos Stavrakakis
                        "pithos.diskspace": {
139 3f9db536 Christos Stavrakakis
                            "usage": 0,
140 3f9db536 Christos Stavrakakis
                            "limit": 1073741824,  # 1GB
141 3f9db536 Christos Stavrakakis
                            "pending": 0
142 3f9db536 Christos Stavrakakis
                        }
143 3f9db536 Christos Stavrakakis
                    }
144 3f9db536 Christos Stavrakakis
                }
145 3f9db536 Christos Stavrakakis
                issue_fun = "astakosclient.AstakosClient.issue_one_commission"
146 3f9db536 Christos Stavrakakis
                with patch(issue_fun) as m3:
147 3f9db536 Christos Stavrakakis
                    serials = []
148 3f9db536 Christos Stavrakakis
                    append = serials.append
149 3f9db536 Christos Stavrakakis
150 3f9db536 Christos Stavrakakis
                    def get_serial(*args, **kwargs):
151 3f9db536 Christos Stavrakakis
                        global serial
152 3f9db536 Christos Stavrakakis
                        serial += 1
153 3f9db536 Christos Stavrakakis
                        append(serial)
154 3f9db536 Christos Stavrakakis
                        return serial
155 3f9db536 Christos Stavrakakis
156 3f9db536 Christos Stavrakakis
                    m3.side_effect = get_serial
157 3f9db536 Christos Stavrakakis
                    resolv_fun = \
158 3f9db536 Christos Stavrakakis
                        'astakosclient.AstakosClient.resolve_commissions'
159 3f9db536 Christos Stavrakakis
                    with patch(resolv_fun) as m4:
160 3f9db536 Christos Stavrakakis
                        m4.return_value = {'accepted': serials,
161 3f9db536 Christos Stavrakakis
                                           'rejected': [],
162 3f9db536 Christos Stavrakakis
                                           'failed': []}
163 3f9db536 Christos Stavrakakis
                        users_fun = 'astakosclient.AstakosClient.get_usernames'
164 3f9db536 Christos Stavrakakis
                        with patch(users_fun) as m5:
165 3f9db536 Christos Stavrakakis
166 3f9db536 Christos Stavrakakis
                            def get_usernames(*args, **kwargs):
167 3f9db536 Christos Stavrakakis
                                uuids = args[-1]
168 3f9db536 Christos Stavrakakis
                                return dict((uuid, uuid) for uuid in uuids)
169 3f9db536 Christos Stavrakakis
170 3f9db536 Christos Stavrakakis
                            m5.side_effect = get_usernames
171 3f9db536 Christos Stavrakakis
                            yield
172 184a2a8c Christos Stavrakakis
173 184a2a8c Christos Stavrakakis
174 184a2a8c Christos Stavrakakis
@contextmanager
175 184a2a8c Christos Stavrakakis
def mocked_quotaholder(success=True):
176 27899d6f Christos Stavrakakis
    with patch("synnefo.quotas.Quotaholder.get") as astakos:
177 184a2a8c Christos Stavrakakis
        global serial
178 f2080d16 Christos Stavrakakis
        serial += 10
179 f2080d16 Christos Stavrakakis
180 f2080d16 Christos Stavrakakis
        def foo(*args, **kwargs):
181 f2080d16 Christos Stavrakakis
            return (len(astakos.return_value.issue_one_commission.mock_calls) +
182 f2080d16 Christos Stavrakakis
                    serial)
183 f2080d16 Christos Stavrakakis
        astakos.return_value.issue_one_commission.side_effect = foo
184 27899d6f Christos Stavrakakis
        astakos.return_value.resolve_commissions.return_value = {"failed": []}
185 184a2a8c Christos Stavrakakis
        yield
186 184a2a8c Christos Stavrakakis
187 d2b8ec7b Christos Stavrakakis
188 d2b8ec7b Christos Stavrakakis
class BaseAPITest(TestCase):
189 d2b8ec7b Christos Stavrakakis
    def get(self, url, user='user', *args, **kwargs):
190 d2b8ec7b Christos Stavrakakis
        with astakos_user(user):
191 d2b8ec7b Christos Stavrakakis
            response = self.client.get(url, *args, **kwargs)
192 d2b8ec7b Christos Stavrakakis
        return response
193 d2b8ec7b Christos Stavrakakis
194 d2b8ec7b Christos Stavrakakis
    def delete(self, url, user='user'):
195 d2b8ec7b Christos Stavrakakis
        with astakos_user(user):
196 d2b8ec7b Christos Stavrakakis
            response = self.client.delete(url)
197 d2b8ec7b Christos Stavrakakis
        return response
198 d2b8ec7b Christos Stavrakakis
199 d2b8ec7b Christos Stavrakakis
    def post(self, url, user='user', params={}, ctype='json', *args, **kwargs):
200 d2b8ec7b Christos Stavrakakis
        if ctype == 'json':
201 d2b8ec7b Christos Stavrakakis
            content_type = 'application/json'
202 d2b8ec7b Christos Stavrakakis
        with astakos_user(user):
203 d2b8ec7b Christos Stavrakakis
            response = self.client.post(url, params, content_type=content_type,
204 d2b8ec7b Christos Stavrakakis
                                        *args, **kwargs)
205 d2b8ec7b Christos Stavrakakis
        return response
206 d2b8ec7b Christos Stavrakakis
207 d2b8ec7b Christos Stavrakakis
    def put(self, url, user='user', params={}, ctype='json', *args, **kwargs):
208 d2b8ec7b Christos Stavrakakis
        if ctype == 'json':
209 d2b8ec7b Christos Stavrakakis
            content_type = 'application/json'
210 d2b8ec7b Christos Stavrakakis
        with astakos_user(user):
211 d2b8ec7b Christos Stavrakakis
            response = self.client.put(url, params, content_type=content_type,
212 d2b8ec7b Christos Stavrakakis
                                       *args, **kwargs)
213 d2b8ec7b Christos Stavrakakis
        return response
214 d2b8ec7b Christos Stavrakakis
215 d2b8ec7b Christos Stavrakakis
    def assertSuccess(self, response):
216 d2b8ec7b Christos Stavrakakis
        self.assertTrue(response.status_code in [200, 203, 204])
217 d2b8ec7b Christos Stavrakakis
218 d2b8ec7b Christos Stavrakakis
    def assertFault(self, response, status_code, name):
219 d2b8ec7b Christos Stavrakakis
        self.assertEqual(response.status_code, status_code)
220 d2b8ec7b Christos Stavrakakis
        fault = json.loads(response.content)
221 d2b8ec7b Christos Stavrakakis
        self.assertEqual(fault.keys(), [name])
222 d2b8ec7b Christos Stavrakakis
223 d2b8ec7b Christos Stavrakakis
    def assertBadRequest(self, response):
224 d2b8ec7b Christos Stavrakakis
        self.assertFault(response, 400, 'badRequest')
225 d2b8ec7b Christos Stavrakakis
226 d2b8ec7b Christos Stavrakakis
    def assertItemNotFound(self, response):
227 d2b8ec7b Christos Stavrakakis
        self.assertFault(response, 404, 'itemNotFound')
228 aba462a2 Giorgos Korfiatis
229 2ad4eb80 Kostas Papadimitriou
    def assertMethodNotAllowed(self, response):
230 2ad4eb80 Kostas Papadimitriou
        self.assertFault(response, 400, 'badRequest')
231 2ad4eb80 Kostas Papadimitriou
        try:
232 2ad4eb80 Kostas Papadimitriou
            error = json.loads(response.content)
233 2ad4eb80 Kostas Papadimitriou
        except ValueError:
234 2ad4eb80 Kostas Papadimitriou
            self.assertTrue(False)
235 2ad4eb80 Kostas Papadimitriou
        self.assertEqual(error['badRequest']['message'], 'Method not allowed')
236 2ad4eb80 Kostas Papadimitriou
237 aba462a2 Giorgos Korfiatis
238 aba462a2 Giorgos Korfiatis
# Imitate unittest assertions new in Python 2.7
239 aba462a2 Giorgos Korfiatis
240 aba462a2 Giorgos Korfiatis
class _AssertRaisesContext(object):
241 aba462a2 Giorgos Korfiatis
    """
242 aba462a2 Giorgos Korfiatis
    A context manager used to implement TestCase.assertRaises* methods.
243 aba462a2 Giorgos Korfiatis
    Adapted from unittest2.
244 aba462a2 Giorgos Korfiatis
    """
245 aba462a2 Giorgos Korfiatis
246 aba462a2 Giorgos Korfiatis
    def __init__(self, expected):
247 aba462a2 Giorgos Korfiatis
        self.expected = expected
248 aba462a2 Giorgos Korfiatis
249 aba462a2 Giorgos Korfiatis
    def __enter__(self):
250 aba462a2 Giorgos Korfiatis
        return self
251 aba462a2 Giorgos Korfiatis
252 aba462a2 Giorgos Korfiatis
    def __exit__(self, exc_type, exc_value, tb):
253 aba462a2 Giorgos Korfiatis
        if exc_type is None:
254 aba462a2 Giorgos Korfiatis
            try:
255 aba462a2 Giorgos Korfiatis
                exc_name = self.expected.__name__
256 aba462a2 Giorgos Korfiatis
            except AttributeError:
257 aba462a2 Giorgos Korfiatis
                exc_name = str(self.expected)
258 aba462a2 Giorgos Korfiatis
            raise AssertionError(
259 aba462a2 Giorgos Korfiatis
                "%s not raised" % (exc_name,))
260 aba462a2 Giorgos Korfiatis
        if not issubclass(exc_type, self.expected):
261 aba462a2 Giorgos Korfiatis
            # let unexpected exceptions pass through
262 aba462a2 Giorgos Korfiatis
            return False
263 aba462a2 Giorgos Korfiatis
        self.exception = exc_value  # store for later retrieval
264 aba462a2 Giorgos Korfiatis
        return True
265 aba462a2 Giorgos Korfiatis
266 aba462a2 Giorgos Korfiatis
267 aba462a2 Giorgos Korfiatis
def assertRaises(excClass):
268 aba462a2 Giorgos Korfiatis
    return _AssertRaisesContext(excClass)
269 aba462a2 Giorgos Korfiatis
270 aba462a2 Giorgos Korfiatis
271 aba462a2 Giorgos Korfiatis
def assertGreater(x, y):
272 aba462a2 Giorgos Korfiatis
    assert x > y
273 aba462a2 Giorgos Korfiatis
274 aba462a2 Giorgos Korfiatis
275 aba462a2 Giorgos Korfiatis
def assertIn(x, y):
276 aba462a2 Giorgos Korfiatis
    assert x in y