Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / connection / test.py @ b35e8daf

History | View | Annotate | Download (14 kB)

1
# Copyright 2013 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, self.list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, self.list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
from unittest import TestCase, TestSuite, makeSuite, TextTestRunner
35
from mock import Mock, patch, call
36
from random import randrange
37
from urllib2 import quote
38

    
39
from kamaki.clients import connection
40
from kamaki.clients.connection import errors, kamakicon
41

    
42

    
43
def _encode(v):
44
    if v and isinstance(v, unicode):
45
        return quote(v.encode('utf-8'))
46
    return v
47

    
48

    
49
class KamakiConnection(TestCase):
50
    v_samples = {'title': 'value', 5: 'value'}
51
    n_samples = {'title': None, 5: None}
52
    false_samples = {None: 'value', 0: 'value'}
53

    
54
    def setUp(self):
55
        from kamaki.clients.connection import KamakiConnection as HTTPC
56
        self.conn = HTTPC()
57
        self.conn.reset_headers()
58
        self.conn.reset_params()
59

    
60
    def test_poolsize(self):
61

    
62
        def set_poolsize(poolsize):
63
            self.conn.poolsize = poolsize
64

    
65
        from kamaki.clients.connection import KamakiConnection as HTTPC
66
        for poolsize in ('non integer', -10, 0):
67
            err = AssertionError
68
            self.assertRaises(err, set_poolsize, poolsize)
69
        for poolsize in (1, 100, 1024 * 1024 * 1024 * 1024):
70
            self.conn.poolsize = poolsize
71
            self.assertEquals(self.conn.poolsize, poolsize)
72
            self.assertEquals(HTTPC(poolsize=poolsize).poolsize, poolsize)
73

    
74
    def test_set_header(self):
75
        cnn = self.conn
76
        for k, v in self.v_samples.items():
77
            cnn.set_header(k, v)
78
            self.assertEquals(cnn.headers[unicode(k)], unicode(v))
79
        for k, v in self.n_samples.items():
80
            cnn.set_header(k, v)
81
            self.assertEquals(cnn.headers[unicode(k)], unicode(v))
82
        for k, v in self.false_samples.items():
83
            self.assertRaises(AssertionError, cnn.set_header, k, v)
84
        self.assertEquals(len(cnn.headers), 2)
85

    
86
    def test_set_param(self):
87
        cnn = self.conn
88
        for k, v in self.v_samples.items():
89
            cnn.set_param(k, v)
90
            self.assertEquals(cnn.params[unicode(k)], v)
91
        for k, v in self.n_samples.items():
92
            cnn.set_param(k, v)
93
            self.assertEquals(cnn.params[unicode(k)], v)
94
        for k, v in self.false_samples.items():
95
            self.assertRaises(AssertionError, cnn.set_param, k, v)
96
        self.assertEquals(len(cnn.params), 2)
97

    
98
    def test_remove_header(self):
99
        cnn = self.conn
100
        for k, v in self.v_samples.items():
101
            cnn.headers[unicode(k)] = unicode(v)
102
        for k in self.v_samples:
103
            cnn.remove_header(k)
104
            self.assertFalse(k in cnn.headers)
105

    
106
    def test_remove_param(self):
107
        cnn = self.conn
108
        for k, v in self.v_samples.items():
109
            cnn.params[unicode(k)] = unicode(v)
110
        for k in self.v_samples:
111
            cnn.remove_param(k)
112
            self.assertFalse(k in cnn.params)
113

    
114
    def test_replace_headers(self):
115
        cnn = self.conn
116
        cnn.headers = self.v_samples
117
        cnn.replace_headers({1: 'one', 2: 'two'})
118
        for k in self.v_samples:
119
            self.assertFalse(k in cnn.headers)
120

    
121
    def test_replace_params(self):
122
        cnn = self.conn
123
        cnn.params = self.v_samples
124
        cnn.replace_params({1: 'one', 2: 'two'})
125
        for k in self.v_samples:
126
            self.assertFalse(k in cnn.params)
127

    
128
    def test_reset_headers(self):
129
        cnn = self.conn
130
        cnn.headers = self.v_samples
131
        cnn.reset_headers()
132
        self.assertFalse(cnn.headers)
133

    
134
    def test_reset_params(self):
135
        cnn = self.conn
136
        cnn.params = self.v_samples
137
        cnn.reset_params()
138
        self.assertFalse(cnn.params)
139

    
140
    def test_set_url(self):
141
        self.assertFalse(self.conn.url)
142
        sample_url = 'http://example.com'
143
        self.conn.set_url(sample_url)
144
        self.assertEquals(self.conn.url, sample_url)
145

    
146
    def test_set_path(self):
147
        self.assertFalse(self.conn.path)
148
        sample_path = '/example/local/path'
149
        self.conn.set_path(sample_path)
150
        self.assertEquals(self.conn.path, sample_path)
151

    
152
    def test_set_method(self):
153
        self.assertFalse(self.conn.method)
154
        sample_method = 'GET'
155
        self.conn.set_method(sample_method)
156
        self.assertEquals(self.conn.method, sample_method)
157

    
158
    def test_perform_request(self):
159
        self.assertRaises(NotImplementedError, self.conn.perform_request)
160

    
161

    
162
class KamakiHTTPConnection(TestCase):
163

    
164
    def setUp(self):
165
        self.conn = kamakicon.KamakiHTTPConnection()
166
        self.conn.reset_params()
167
        self.conn.reset_headers()
168

    
169
    def test__retrieve_connection_info(self):
170
        async_params = dict(param1='val1', param2=None, param3=42)
171
        r = self.conn._retrieve_connection_info(async_params)
172
        self.assertEquals(r, ('http', '127.0.0.1'))
173
        expected = '?%s' % '&'.join([(
174
            '%s=%s' % (k, v)) if v else (
175
            '%s' % k) for k, v in async_params.items()])
176
        self.assertEquals('http://127.0.0.1%s' % expected, self.conn.url)
177

    
178
        for schnet in (
179
            ('http', 'www.example.com'), ('https', 'www.example.com'),
180
            ('ftp', 'www.example.com'), ('ftps', 'www.example.com'),
181
            ('http', 'www.example.com/v1'), ('https', 'www.example.com/v1')):
182
            self.conn = kamakicon.KamakiHTTPConnection(url='%s://%s' % schnet)
183
            self.conn.url = '%s://%s' % schnet
184
            r = self.conn._retrieve_connection_info(async_params)
185
            if schnet[1].endswith('v1'):
186
                self.assertEquals(r, (schnet[0], schnet[1][:-3]))
187
            else:
188
                self.assertEquals(r, schnet)
189
            self.assertEquals(
190
                '%s://%s/%s' % (schnet[0], schnet[1], expected),
191
                self.conn.url)
192

    
193
    def test_perform_request(self):
194
        from httplib import HTTPConnection
195
        from objpool import http
196
        pr = self.conn.perform_request
197
        kwargs = dict(
198
            data='',
199
            method='GET',
200
            async_headers=dict(),
201
            async_params=dict())
202
        utf_test = u'\u03a6\u03bf\u03cd\u03c4\u03c3\u03bf\u03c2'
203
        utf_dict = dict(utf=utf_test)
204
        ascii_dict = dict(ascii1='myAscii', ascii2=None)
205
        kwargs0 = dict(
206
            data='',
207
            method='get',
208
            async_headers=utf_dict,
209
            async_params=ascii_dict)
210

    
211
        def get_expected():
212
            expected = []
213
            for k, v in kwargs0['async_params'].items():
214
                v = _encode(v)
215
                expected.append(('%s=%s' % (k, v)) if v else ('%s' % k))
216
            return '&'.join(expected)
217

    
218
        KCError = errors.KamakiConnectionError
219
        fakecon = HTTPConnection('X', 'Y')
220

    
221
        with patch.object(http, 'get_http_connection', return_value=fakecon):
222
            with patch.object(HTTPConnection, 'request') as request:
223
                r = pr(**kwargs)
224
                self.assertTrue(isinstance(r, kamakicon.KamakiHTTPResponse))
225
                self.assertEquals(
226
                    request.mock_calls[-1],
227
                    call(body='', headers={}, url='/', method='GET'))
228

    
229
                pr(**kwargs0)
230

    
231
                exp_headers = dict(kwargs0['async_headers'])
232
                exp_headers['utf'] = _encode(exp_headers['utf'])
233

    
234
                self.assertEquals(
235
                    request.mock_calls[-1],
236
                    call(
237
                        body=kwargs0['data'],
238
                        headers=exp_headers,
239
                        url='/?%s' % get_expected(),
240
                        method=kwargs0['method'].upper()))
241

    
242
                self.conn = kamakicon.KamakiHTTPConnection()
243
                (kwargs0['async_params'], kwargs0['async_headers']) = (
244
                    kwargs0['async_headers'], kwargs0['async_params'])
245
                kwargs0['async_headers']['ascii2'] = 'None'
246
                self.conn.perform_request(**kwargs0)
247
                self.assertEquals(
248
                    request.mock_calls[-1],
249
                    call(
250
                        body=kwargs0['data'],
251
                        headers=kwargs0['async_headers'],
252
                        url='/?%s' % get_expected(),
253
                        method=kwargs0['method'].upper()))
254

    
255
            err = IOError('IO Error')
256
            with patch.object(HTTPConnection, 'request', side_effect=err):
257
                self.assertRaises(KCError, pr, **kwargs)
258

    
259
        err = ValueError('Cannot Establish connection')
260
        with patch.object(http, 'get_http_connection', side_effect=err):
261
            self.assertRaises(KCError, pr, **kwargs)
262

    
263
        err = Exception('Any other error')
264
        with patch.object(http, 'get_http_connection', side_effect=err):
265
            self.assertRaises(KCError, pr, **kwargs)
266

    
267

    
268
class KamakiHTTPResponse(TestCase):
269

    
270
    class fakeResponse(object):
271
        sample = 'sample string'
272
        getheaders = Mock(return_value={})
273
        read = Mock(return_value=sample)
274
        status = Mock(return_value=None)
275
        reason = Mock(return_value=None)
276

    
277
    def setUp(self):
278
        from httplib import HTTPConnection
279
        self.HTC = HTTPConnection
280
        self.FR = self.fakeResponse
281

    
282
    def test_text(self):
283
        with patch.object(self.HTC, 'getresponse', return_value=self.FR()):
284
            self.resp = kamakicon.KamakiHTTPResponse(self.HTC('X', 'Y'))
285
            self.assertEquals(self.resp.text, self.FR.sample)
286
            sample2 = 'some other string'
287
            self.resp.text = sample2
288
            self.assertNotEquals(self.resp.text, sample2)
289

    
290
    def test_json(self):
291
        with patch.object(self.HTC, 'getresponse', return_value=self.FR()):
292
            self.resp = kamakicon.KamakiHTTPResponse(self.HTC('X', 'Y'))
293
            self.assertRaises(errors.KamakiResponseError, self.resp.json)
294
            sample2 = '{"antoher":"sample", "formated":"in_json"}'
295
            with patch.object(self.FR, 'read', return_value=sample2):
296
                self.resp = kamakicon.KamakiHTTPResponse(self.HTC('X', 'Y'))
297
                from json import loads
298
                self.assertEquals(loads(sample2), self.resp.json)
299

    
300
    def test_pool_lock(self):
301
        exceptions_left = 100
302
        while exceptions_left:
303
            kre = errors.KamakiResponseError
304
            with patch.object(self.HTC, 'close', return_value=True):
305
                self.resp = kamakicon.KamakiHTTPResponse(self.HTC('X', 'Y'))
306
                if randrange(10):
307
                    with patch.object(
308
                            self.HTC,
309
                            'getresponse',
310
                            return_value=self.FR()):
311
                        self.assertEquals(self.resp.text, self.FR.sample)
312
                else:
313
                    with patch.object(
314
                            self.HTC,
315
                            'getresponse',
316
                            side_effect=kre('A random error')):
317
                        try:
318
                            self.resp.text
319
                        except kre:
320
                            exceptions_left -= 1
321
                        else:
322
                            self.assertTrue(False)
323
                self.HTC.close.assert_called_with()
324

    
325

    
326
class KamakiResponse(TestCase):
327

    
328
    def setUp(self):
329
        self.resp = connection.KamakiResponse(
330
            'Abstract class, so test with fake request (str)')
331

    
332
    def _mock_get_response(foo):
333
        def mocker(self):
334
            self.resp._get_response = Mock()
335
            foo(self)
336
        return mocker
337

    
338
    def test_release(self):
339
        self.assertRaises(NotImplementedError, self.resp.release)
340

    
341
    def test_prefetched(self):
342
        self.assertFalse(self.resp.prefetched)
343
        self.resp.prefetched = True
344
        self.assertTrue(self.resp.prefetched)
345

    
346
    @_mock_get_response
347
    def test_content(self):
348
        rsp = self.resp
349
        for cont in ('Sample Content', u'\u03c7\u03cd\u03bd\u03c9\x00'):
350
            rsp.content = cont
351
            self.assertEquals(rsp.content, cont)
352

    
353
    (
354
        test_text,
355
        test_json,
356
        test_headers,
357
        test_status,
358
        test_status_code) = 5 * (test_content,)
359

    
360
    def test_request(self):
361
        r = self.resp.request
362
        self.assertTrue(isinstance(r, str))
363

    
364

    
365
def get_test_classes(module=__import__(__name__), name=''):
366
    from inspect import getmembers, isclass
367
    for objname, obj in getmembers(module):
368
        if (objname == name or not name) and isclass(obj) and (
369
                issubclass(obj, TestCase)):
370
            yield (obj, objname)
371

    
372

    
373
def main(argv):
374
    for cls, name in get_test_classes(name=argv[1] if len(argv) > 1 else ''):
375
        args = argv[2:]
376
        suite = TestSuite()
377
        if args:
378
            suite.addTest(cls('_'.join(['test'] + args)))
379
        else:
380
            suite.addTest(makeSuite(cls))
381
        print('Test %s' % name)
382
        TextTestRunner(verbosity=2).run(suite)
383

    
384

    
385
if __name__ == '__main__':
386
    from sys import argv
387
    main(argv)