Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (10.1 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 f3787696 Sofia Papagiannaki
serial = 0
121 f3787696 Sofia Papagiannaki
122 d2b8ec7b Christos Stavrakakis
123 d2b8ec7b Christos Stavrakakis
@contextmanager
124 d2b8ec7b Christos Stavrakakis
def astakos_user(user):
125 d2b8ec7b Christos Stavrakakis
    """
126 d2b8ec7b Christos Stavrakakis
    Context manager to mock astakos response.
127 d2b8ec7b Christos Stavrakakis

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

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