Statistics
| Branch: | Tag: | Revision:

root / astakosclient / astakosclient / tests.py @ 8975f6f6

History | View | Annotate | Download (40.8 kB)

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

    
36
"""Unit Tests for the astakos-client module
37

38
Provides unit tests for the code implementing
39
the astakos client library
40

41
"""
42

    
43
import re
44
import sys
45
import socket
46
import simplejson
47

    
48
import astakosclient
49
from astakosclient import AstakosClient
50
from astakosclient.errors import \
51
    AstakosClientException, Unauthorized, BadRequest, NotFound, \
52
    NoUserName, NoUUID, BadValue, QuotaLimit
53

    
54
# Use backported unittest functionality if Python < 2.7
55
try:
56
    import unittest2 as unittest
57
except ImportError:
58
    if sys.version_info < (2, 7):
59
        raise Exception("The unittest2 package is required for Python < 2.7")
60
    import unittest
61

    
62

    
63
# --------------------------------------------------------------------
64
# Helper functions
65

    
66
# ----------------------------
67
# This functions will be used as mocked requests
68
def _request_offline(conn, method, url, **kwargs):
69
    """This request behaves as we were offline"""
70
    raise socket.gaierror
71

    
72

    
73
def _request_status_302(conn, method, url, **kwargs):
74
    """This request returns 302"""
75
    message = "FOUND"
76
    status = 302
77
    data = '<html>\r\n<head><title>302 Found</title></head>\r\n' \
78
        '<body bgcolor="white">\r\n<center><h1>302 Found</h1></center>\r\n' \
79
        '<hr><center>nginx/0.7.67</center>\r\n</body>\r\n</html>\r\n'
80
    return (message, data, status)
81

    
82

    
83
def _request_status_404(conn, method, url, **kwargs):
84
    """This request returns 404"""
85
    message = "Not Found"
86
    status = 404
87
    data = '<html><head><title>404 Not Found</title></head>' \
88
        '<body><h1>Not Found</h1><p>The requested URL /foo was ' \
89
        'not found on this server.</p><hr><address>Apache Server ' \
90
        'at example.com Port 80</address></body></html>'
91
    return (message, data, status)
92

    
93

    
94
def _request_status_403(conn, method, url, **kwargs):
95
    """This request returns 403"""
96
    message = "UNAUTHORIZED"
97
    status = 403
98
    data = "Forbidden"
99
    return (message, data, status)
100

    
101

    
102
def _request_status_401(conn, method, url, **kwargs):
103
    """This request returns 401"""
104
    message = "UNAUTHORIZED"
105
    status = 401
106
    data = "Invalid X-Auth-Token\n"
107
    return (message, data, status)
108

    
109

    
110
def _request_status_400(conn, method, url, **kwargs):
111
    """This request returns 400"""
112
    message = "BAD REQUEST"
113
    status = 400
114
    data = "Method not allowed.\n"
115
    return (message, data, status)
116

    
117

    
118
def _request_ok(conn, method, url, **kwargs):
119
    """This request behaves like original Astakos does"""
120
    if re.match('/?' + astakosclient.API_AUTHENTICATE, url) is not None:
121
        print "here 1"
122
        return _req_authenticate(conn, method, url, **kwargs)
123
    elif re.match('/?' + astakosclient.API_USERCATALOGS, url) is not None:
124
        print "here 2"
125
        return _req_catalogs(conn, method, url, **kwargs)
126
    elif re.match('/?' + astakosclient.API_RESOURCES, url) is not None:
127
        print "here 3"
128
        return _req_resources(conn, method, url, **kwargs)
129
    elif re.match('/?' + astakosclient.API_QUOTAS, url) is not None:
130
        return _req_quotas(conn, method, url, **kwargs)
131
    elif re.match('/?' + astakosclient.API_COMMISSIONS, url) is not None:
132
        return _req_commission(conn, method, url, **kwargs)
133
    elif re.match('/?' + astakosclient.API_TOKENS, url) is not None:
134
        return _req_endpoints(conn, method, url, **kwargs)
135
    else:
136
        print "here 4"
137
        return _request_status_404(conn, method, url, **kwargs)
138

    
139

    
140
def _req_authenticate(conn, method, url, **kwargs):
141
    """Check if user exists and return his profile"""
142
    global user_1, user_2, token_1, token_2
143

    
144
    # Check input
145
    if conn.__class__.__name__ != "HTTPSConnection":
146
        return _request_status_302(conn, method, url, **kwargs)
147
    if method != "GET":
148
        return _request_status_400(conn, method, url, **kwargs)
149
    token = kwargs['headers'].get('X-Auth-Token')
150
    if token == token_1:
151
        user = dict(user_1)
152
    elif token == token_2:
153
        user = dict(user_2)
154
    else:
155
        # No user found
156
        return _request_status_401(conn, method, url, **kwargs)
157

    
158
    # Return
159
    if "usage=1" not in url:
160
        # Strip `usage' key from `user'
161
        del user['usage']
162
    return ("", simplejson.dumps(user), 200)
163

    
164

    
165
def _req_catalogs(conn, method, url, **kwargs):
166
    """Return user catalogs"""
167
    global token_1, token_2, user_1, user_2
168

    
169
    # Check input
170
    if conn.__class__.__name__ != "HTTPSConnection":
171
        return _request_status_302(conn, method, url, **kwargs)
172
    if method != "POST":
173
        return _request_status_400(conn, method, url, **kwargs)
174
    token = kwargs['headers'].get('X-Auth-Token')
175
    if token != token_1 and token != token_2:
176
        return _request_status_401(conn, method, url, **kwargs)
177

    
178
    # Return
179
    body = simplejson.loads(kwargs['body'])
180
    if 'uuids' in body:
181
        # Return uuid_catalog
182
        uuids = body['uuids']
183
        catalogs = {}
184
        if user_1['uuid'] in uuids:
185
            catalogs[user_1['uuid']] = user_1['username']
186
        if user_2['uuid'] in uuids:
187
            catalogs[user_2['uuid']] = user_2['username']
188
        return_catalog = {"displayname_catalog": {}, "uuid_catalog": catalogs}
189
    elif 'displaynames' in body:
190
        # Return displayname_catalog
191
        names = body['displaynames']
192
        catalogs = {}
193
        if user_1['username'] in names:
194
            catalogs[user_1['username']] = user_1['uuid']
195
        if user_2['username'] in names:
196
            catalogs[user_2['username']] = user_2['uuid']
197
        return_catalog = {"displayname_catalog": catalogs, "uuid_catalog": {}}
198
    else:
199
        return_catalog = {"displayname_catalog": {}, "uuid_catalog": {}}
200
    return ("", simplejson.dumps(return_catalog), 200)
201

    
202

    
203
def _req_resources(conn, method, url, **kwargs):
204
    """Return quota resources"""
205
    global resources
206

    
207
    # Check input
208
    if conn.__class__.__name__ != "HTTPSConnection":
209
        return _request_status_302(conn, method, url, **kwargs)
210
    if method != "GET":
211
        return _request_status_400(conn, method, url, **kwargs)
212

    
213
    # Return
214
    return ("", simplejson.dumps(resources), 200)
215

    
216

    
217
def _req_quotas(conn, method, url, **kwargs):
218
    """Return quotas for user_1"""
219
    global token_1, quotas
220

    
221
    # Check input
222
    if conn.__class__.__name__ != "HTTPSConnection":
223
        return _request_status_302(conn, method, url, **kwargs)
224
    if method != "GET":
225
        return _request_status_400(conn, method, url, **kwargs)
226
    token = kwargs['headers'].get('X-Auth-Token')
227
    if token != token_1:
228
        return _request_status_401(conn, method, url, **kwargs)
229

    
230
    # Return
231
    return ("", simplejson.dumps(quotas), 200)
232

    
233

    
234
def _req_commission(conn, method, url, **kwargs):
235
    """Perform a commission for user_1"""
236
    global token_1, pending_commissions, \
237
        commission_successful_response, commission_failure_response
238

    
239
    # Check input
240
    if conn.__class__.__name__ != "HTTPSConnection":
241
        return _request_status_302(conn, method, url, **kwargs)
242
    token = kwargs['headers'].get('X-Auth-Token')
243
    if token != token_1:
244
        return _request_status_401(conn, method, url, **kwargs)
245

    
246
    if method == "POST":
247
        if 'body' not in kwargs:
248
            return _request_status_400(conn, method, url, **kwargs)
249
        body = simplejson.loads(unicode(kwargs['body']))
250
        if re.match('/?'+astakosclient.API_COMMISSIONS+'$', url) is not None:
251
            # Issue Commission
252
            # Check if we have enough resources to give
253
            if body['provisions'][1]['quantity'] > 420000000:
254
                return ("", simplejson.dumps(commission_failure_response), 413)
255
            else:
256
                return \
257
                    ("", simplejson.dumps(commission_successful_response), 200)
258
        else:
259
            # Issue commission action
260
            serial = url.split('/')[4]
261
            if serial == "action":
262
                # Resolve multiple actions
263
                if body == resolve_commissions_req:
264
                    return ("", simplejson.dumps(resolve_commissions_rep), 200)
265
                else:
266
                    return _request_status_400(conn, method, url, **kwargs)
267
            else:
268
                # Issue action for one commission
269
                if serial != str(57):
270
                    return _request_status_404(conn, method, url, **kwargs)
271
                if len(body) != 1:
272
                    return _request_status_400(conn, method, url, **kwargs)
273
                if "accept" not in body.keys() and "reject" not in body.keys():
274
                    return _request_status_400(conn, method, url, **kwargs)
275
                return ("", "", 200)
276

    
277
    elif method == "GET":
278
        if re.match('/?'+astakosclient.API_COMMISSIONS+'$', url) is not None:
279
            # Return pending commission
280
            return ("", simplejson.dumps(pending_commissions), 200)
281
        else:
282
            # Return commissions's description
283
            serial = re.sub('/?' + astakosclient.API_COMMISSIONS, '', url)[1:]
284
            if serial == str(57):
285
                return ("", simplejson.dumps(commission_description), 200)
286
            else:
287
                return _request_status_404(conn, method, url, **kwargs)
288
    else:
289
        return _request_status_400(conn, method, url, **kwargs)
290

    
291

    
292
def _req_endpoints(conn, method, url, **kwargs):
293
    """Request endpoints"""
294
    global token_1, endpoints
295

    
296
    # Check input
297
    if conn.__class__.__name__ != "HTTPSConnection":
298
        return _request_status_302(conn, method, url, **kwargs)
299
    if method != "POST":
300
        return _request_status_400(conn, method, url, **kwargs)
301

    
302
    token_head = kwargs['headers'].get('X-Auth-Token')
303
    if method != "POST":
304
        return _request_status_400(conn, method, url, **kwargs)
305
    body = simplejson.loads(kwargs['body'])
306
    token_body = body['auth']['token']['id']
307
    if token_head != token_body:
308
        return _request_status_403(conn, method, url, **kwargs)
309
    if token_body != token_1:
310
        return _request_status_401(conn, method, url, **kwargs)
311
    # Return
312
    return ("", simplejson.dumps(user_info_endpoints), 200)
313

    
314

    
315
# ----------------------------
316
# Mock the actual _doRequest
317
def _mock_request(new_requests):
318
    """Mock the actual request
319

320
    Given a list of requests to use (in rotation),
321
    replace the original _doRequest function with
322
    a new one
323

324
    """
325
    def _mock(conn, method, url, **kwargs):
326
        # Get first request
327
        request = _mock.requests[0]
328
        # Rotate requests
329
        _mock.requests = _mock.requests[1:] + _mock.requests[:1]
330
        # Use first request
331
        return request(conn, method, url, **kwargs)
332

    
333
    _mock.requests = new_requests
334
    # Replace `_doRequest' with our `_mock'
335
    astakosclient._do_request = _mock
336

    
337

    
338
# ----------------------------
339
# Local users
340
token_1 = "skzleaFlBl+fasFdaf24sx"
341
user_1 = \
342
    {"username": "user1@example.com",
343
     "auth_token_created": 1359386939000,
344
     "name": "Example User One",
345
     "email": ["user1@example.com"],
346
     "auth_token_expires": 1361978939000,
347
     "id": 108,
348
     "uuid": "73917abc-abcd-477e-a1f1-1763abcdefab",
349
     "usage": [
350
         {"currValue": 42949672960,
351
          "display_name": "System Disk",
352
          "name": "cyclades.disk"},
353
         {"currValue": 4,
354
          "display_name": "CPU",
355
          "name": "cyclades.cpu"},
356
         {"currValue": 4294967296,
357
          "display_name": "RAM",
358
          "name": "cyclades.ram"},
359
         {"currValue": 3,
360
          "display_name": "VM",
361
          "name": "cyclades.vm"},
362
         {"currValue": 0,
363
          "display_name": "private network",
364
          "name": "cyclades.network.private"},
365
         {"currValue": 152,
366
          "display_name": "Storage Space",
367
          "name": "pithos+.diskspace"}]}
368

    
369
token_2 = "fasdfDSFdf98923DF+sdfk"
370
user_2 = \
371
    {"username": "user2@example.com",
372
     "auth_token_created": 1358386938997,
373
     "name": "Example User Two",
374
     "email": ["user1@example.com"],
375
     "auth_token_expires": 1461998939000,
376
     "id": 109,
377
     "uuid": "73917bca-1234-5678-a1f1-1763abcdefab",
378
     "usage": [
379
         {"currValue": 68719476736,
380
          "display_name": "System Disk",
381
          "name": "cyclades.disk"},
382
         {"currValue": 1,
383
          "display_name": "CPU",
384
          "name": "cyclades.cpu"},
385
         {"currValue": 1073741824,
386
          "display_name": "RAM",
387
          "name": "cyclades.ram"},
388
         {"currValue": 2,
389
          "display_name": "VM",
390
          "name": "cyclades.vm"},
391
         {"currValue": 1,
392
          "display_name": "private network",
393
          "name": "cyclades.network.private"},
394
         {"currValue": 2341634510,
395
          "display_name": "Storage Space",
396
          "name": "pithos+.diskspace"}]}
397

    
398
resources = {
399
    "cyclades.vm": {
400
        "unit": None,
401
        "description": "Number of virtual machines",
402
        "service": "cyclades"},
403
    "cyclades.ram": {
404
        "unit": "bytes",
405
        "description": "Virtual machine memory",
406
        "service": "cyclades"}}
407

    
408
user_info_endpoints = \
409
    {'serviceCatalog': [
410
        {'endpoints': [{
411
            'SNF:uiURL': 'https://node1.example.com/ui/',
412
            'adminURL': 'https://node1.example.com/v1',
413
            'internalUrl': 'https://node1.example.com/v1',
414
            'publicURL': 'https://node1.example.com/v1',
415
            'region': 'cyclades'}],
416
         'name': 'cyclades',
417
         'type': 'compute'},
418
        {'endpoints': [{
419
            'SNF:uiURL': 'https://node2.example.com/ui/',
420
            'adminURL': 'https://node2.example.com/v1',
421
            'internalUrl': 'https://node2.example.com/v1',
422
            'publicURL': 'https://node2.example.com/v1',
423
            'region': 'pithos'}],
424
         'name': 'pithos',
425
         'type': 'storage'}],
426
     'token': {
427
         'expires': '2013-06-19T15:23:59.975572+00:00',
428
         'id': token_1,
429
         'tenant': {
430
             'id': user_1,
431
             'name': 'Firstname Lastname'}},
432
     'user': {
433
         'id': user_1,
434
         'name': 'Firstname Lastname',
435
         'roles': [{'id': 1, 'name': 'default'}],
436
         'roles_links': []}}
437

    
438
quotas = {
439
    "system": {
440
        "cyclades.ram": {
441
            "pending": 0,
442
            "limit": 1073741824,
443
            "usage": 536870912},
444
        "cyclades.vm": {
445
            "pending": 0,
446
            "limit": 2,
447
            "usage": 2}},
448
    "project:1": {
449
        "cyclades.ram": {
450
            "pending": 0,
451
            "limit": 2147483648,
452
            "usage": 2147483648},
453
        "cyclades.vm": {
454
            "pending": 1,
455
            "limit": 5,
456
            "usage": 2}}}
457

    
458
commission_request = {
459
    "force": False,
460
    "auto_accept": False,
461
    "name": "my commission",
462
    "provisions": [
463
        {
464
            "holder": "c02f315b-7d84-45bc-a383-552a3f97d2ad",
465
            "source": "system",
466
            "resource": "cyclades.vm",
467
            "quantity": 1
468
        },
469
        {
470
            "holder": "c02f315b-7d84-45bc-a383-552a3f97d2ad",
471
            "source": "system",
472
            "resource": "cyclades.ram",
473
            "quantity": 30000
474
        }]}
475

    
476
commission_successful_response = {"serial": 57}
477

    
478
commission_failure_response = {
479
    "overLimit": {
480
        "message": "a human-readable error message",
481
        "code": 413,
482
        "data": {
483
            "provision": {
484
                "holder": "c02f315b-7d84-45bc-a383-552a3f97d2ad",
485
                "source": "system",
486
                "resource": "cyclades.ram",
487
                "quantity": 520000000},
488
            "name": "NoCapacityError",
489
            "limit": 600000000,
490
            "usage": 180000000}}}
491

    
492
pending_commissions = [100, 200]
493

    
494
commission_description = {
495
    "serial": 57,
496
    "issue_time": "2013-04-08T10:19:15.0373+00:00",
497
    "name": "a commission",
498
    "provisions": [
499
        {
500
            "holder": "c02f315b-7d84-45bc-a383-552a3f97d2ad",
501
            "source": "system",
502
            "resource": "cyclades.vm",
503
            "quantity": 1
504
        },
505
        {
506
            "holder": "c02f315b-7d84-45bc-a383-552a3f97d2ad",
507
            "source": "system",
508
            "resource": "cyclades.ram",
509
            "quantity": 536870912
510
        }]}
511

    
512
resolve_commissions_req = {
513
    "accept": [56, 57],
514
    "reject": [56, 58, 59]}
515

    
516
resolve_commissions_rep = {
517
    "accepted": [57],
518
    "rejected": [59],
519
    "failed": [
520
        [56, {
521
            "badRequest": {
522
                "message": "cannot both accept and reject serial 56",
523
                "code": 400}}],
524
        [58, {
525
            "itemNotFound": {
526
                "message": "serial 58 does not exist",
527
                "code": 404}}]]}
528

    
529

    
530
# --------------------------------------------------------------------
531
# The actual tests
532

    
533
class TestCallAstakos(unittest.TestCase):
534
    """Test cases for function _callAstakos"""
535

    
536
    # ----------------------------------
537
    # Test the response we get if we don't have internet access
538
    def _offline(self, pool):
539
        global token_1
540
        _mock_request([_request_offline])
541
        try:
542
            client = AstakosClient("https://example.com", use_pool=pool)
543
            client._call_astakos(token_1, astakosclient.API_AUTHENTICATE)
544
        except AstakosClientException:
545
            pass
546
        else:
547
            self.fail("Should have raised AstakosClientException")
548

    
549
    def test_offline(self):
550
        """Test _offline without pool"""
551
        self._offline(False)
552

    
553
    def test_offline_pool(self):
554
        """Test _offline using pool"""
555
        self._offline(True)
556

    
557
    # ----------------------------------
558
    # Test the response we get if we send invalid token
559
    def _invalid_token(self, pool):
560
        token = "skaksaFlBl+fasFdaf24sx"
561
        _mock_request([_request_ok])
562
        try:
563
            client = AstakosClient("https://example.com", use_pool=pool)
564
            client._call_astakos(token, astakosclient.API_AUTHENTICATE)
565
        except Unauthorized:
566
            pass
567
        except Exception:
568
            self.fail("Should have returned 401 (Invalid X-Auth-Token)")
569
        else:
570
            self.fail("Should have returned 401 (Invalid X-Auth-Token)")
571

    
572
    def test_invalid_token(self):
573
        """Test _invalid_token without pool"""
574
        self._invalid_token(False)
575

    
576
    def test_invalid_token_pool(self):
577
        """Test _invalid_token using pool"""
578
        self._invalid_token(True)
579

    
580
    # ----------------------------------
581
    # Test the response we get if we send invalid url
582
    def _invalid_url(self, pool):
583
        global token_1
584
        _mock_request([_request_ok])
585
        try:
586
            client = AstakosClient("https://example.com", use_pool=pool)
587
            client._call_astakos(token_1, "/astakos/api/misspelled")
588
        except NotFound:
589
            pass
590
        except Exception:
591
            self.fail("Should have returned 404 (Not Found)")
592
        else:
593
            self.fail("Should have returned 404 (Not Found)")
594

    
595
    def test_invalid_url(self):
596
        """Test _invalid_url without pool"""
597
        self._invalid_url(False)
598

    
599
    def test_invalid_url_pool(self):
600
        """Test _invalid_url using pool"""
601
        self._invalid_url(True)
602

    
603
    # ----------------------------------
604
    # Test the response we get if we use an unsupported scheme
605
    def _unsupported_scheme(self, pool):
606
        global token_1
607
        _mock_request([_request_ok])
608
        try:
609
            client = AstakosClient("ftp://example.com", use_pool=pool)
610
            client._call_astakos(token_1, astakosclient.API_AUTHENTICATE)
611
        except BadValue:
612
            pass
613
        except Exception:
614
            self.fail("Should have raise BadValue Exception")
615
        else:
616
            self.fail("Should have raise BadValue Exception")
617

    
618
    def test_unsupported_scheme(self):
619
        """Test _unsupported_scheme without pool"""
620
        self._unsupported_scheme(False)
621

    
622
    def test_unsupported_scheme_pool(self):
623
        """Test _unsupported_scheme using pool"""
624
        self._unsupported_scheme(True)
625

    
626
    # ----------------------------------
627
    # Test the response we get if we use http instead of https
628
    def _http_scheme(self, pool):
629
        global token_1
630
        _mock_request([_request_ok])
631
        try:
632
            client = AstakosClient("http://example.com", use_pool=pool)
633
            client._call_astakos(token_1, astakosclient.API_AUTHENTICATE)
634
        except AstakosClientException as err:
635
            if err.status != 302:
636
                self.fail("Should have returned 302 (Found)")
637
        else:
638
            self.fail("Should have returned 302 (Found)")
639

    
640
    def test_http_scheme(self):
641
        """Test _http_scheme without pool"""
642
        self._http_scheme(False)
643

    
644
    def test_http_scheme_pool(self):
645
        """Test _http_scheme using pool"""
646
        self._http_scheme(True)
647

    
648
    # ----------------------------------
649
    # Test the response we get if we use authenticate with POST
650
    def _post_authenticate(self, pool):
651
        global token_1
652
        _mock_request([_request_ok])
653
        try:
654
            client = AstakosClient("https://example.com", use_pool=pool)
655
            client._call_astakos(
656
                token_1, astakosclient.API_AUTHENTICATE, method="POST")
657
        except BadRequest:
658
            pass
659
        except Exception:
660
            self.fail("Should have returned 400 (Method not allowed)")
661
        else:
662
            self.fail("Should have returned 400 (Method not allowed)")
663

    
664
    def test_post_authenticate(self):
665
        """Test _post_authenticate without pool"""
666
        self._post_authenticate(False)
667

    
668
    def test_post_authenticate_pool(self):
669
        """Test _post_authenticate using pool"""
670
        self._post_authenticate(True)
671

    
672
    # ----------------------------------
673
    # Test the response if we request user_catalogs with GET
674
    def _get_user_catalogs(self, pool):
675
        global token_1
676
        _mock_request([_request_ok])
677
        try:
678
            client = AstakosClient("https://example.com", use_pool=pool)
679
            client._call_astakos(token_1, astakosclient.API_USERCATALOGS)
680
        except BadRequest:
681
            pass
682
        except Exception:
683
            self.fail("Should have returned 400 (Method not allowed)")
684
        else:
685
            self.fail("Should have returned 400 (Method not allowed)")
686

    
687
    def test_get_user_catalogs(self):
688
        """Test _get_user_catalogs without pool"""
689
        self._get_user_catalogs(False)
690

    
691
    def test_get_user_catalogs_pool(self):
692
        """Test _get_user_catalogs using pool"""
693
        self._get_user_catalogs(True)
694

    
695

    
696
class TestAuthenticate(unittest.TestCase):
697
    """Test cases for function getUserInfo"""
698

    
699
    # ----------------------------------
700
    # Test the response we get if we don't have internet access
701
    def test_offline(self):
702
        """Test offline after 3 retries"""
703
        global token_1
704
        _mock_request([_request_offline])
705
        try:
706
            client = AstakosClient("https://example.com", retry=3)
707
            client.get_user_info(token_1)
708
        except AstakosClientException:
709
            pass
710
        else:
711
            self.fail("Should have raised AstakosClientException exception")
712

    
713
    # ----------------------------------
714
    # Test the response we get for invalid token
715
    def _invalid_token(self, pool):
716
        token = "skaksaFlBl+fasFdaf24sx"
717
        _mock_request([_request_ok])
718
        try:
719
            client = AstakosClient("https://example.com", use_pool=pool)
720
            client.get_user_info(token)
721
        except Unauthorized:
722
            pass
723
        except Exception:
724
            self.fail("Should have returned 401 (Invalid X-Auth-Token)")
725
        else:
726
            self.fail("Should have returned 401 (Invalid X-Auth-Token)")
727

    
728
    def test_invalid_token(self):
729
        """Test _invalid_token without pool"""
730
        self._invalid_token(False)
731

    
732
    def test_invalid_token_pool(self):
733
        """Test _invalid_token using pool"""
734
        self._invalid_token(True)
735

    
736
    #- ---------------------------------
737
    # Test response for user 1
738
    def _auth_user(self, token, user_info, usage, pool):
739
        _mock_request([_request_ok])
740
        try:
741
            client = AstakosClient("https://example.com", use_pool=pool)
742
            auth_info = client.get_user_info(token, usage=usage)
743
        except:
744
            self.fail("Shouldn't raise an Exception")
745
        self.assertEqual(user_info, auth_info)
746

    
747
    def test_auth_user_one(self):
748
        """Test _auth_user for User 1 without pool, without usage"""
749
        global token_1, user_1
750
        user_info = dict(user_1)
751
        del user_info['usage']
752
        self._auth_user(token_1, user_info, False, False)
753

    
754
    def test_auth_user_one_usage(self):
755
        """Test _auth_user for User 1 without pool, with usage"""
756
        global token_1, user_1
757
        self._auth_user(token_1, user_1, True, False)
758

    
759
    def test_auth_user_one_usage_pool(self):
760
        """Test _auth_user for User 1 using pool, with usage"""
761
        global token_1, user_1
762
        self._auth_user(token_1, user_1, True, True)
763

    
764
    def test_auth_user_two(self):
765
        """Test _auth_user for User 2 without pool, without usage"""
766
        global token_2, user_2
767
        user_info = dict(user_2)
768
        del user_info['usage']
769
        self._auth_user(token_2, user_info, False, False)
770

    
771
    def test_auth_user_two_usage(self):
772
        """Test _auth_user for User 2 without pool, with usage"""
773
        global token_2, user_2
774
        self._auth_user(token_2, user_2, True, False)
775

    
776
    def test_auth_user_two_usage_pool(self):
777
        """Test _auth_user for User 2 using pool, with usage"""
778
        global token_2, user_2
779
        self._auth_user(token_2, user_2, True, True)
780

    
781
    # ----------------------------------
782
    # Test retry functionality
783
    def test_offline_retry(self):
784
        """Test retry functionality for getUserInfo"""
785
        global token_1, user_1
786
        _mock_request([_request_offline, _request_offline, _request_ok])
787
        try:
788
            client = AstakosClient("https://example.com", retry=2)
789
            auth_info = client.get_user_info(token_1, usage=True)
790
        except:
791
            self.fail("Shouldn't raise an Exception")
792
        self.assertEqual(user_1, auth_info)
793

    
794

    
795
class TestDisplayNames(unittest.TestCase):
796
    """Test cases for functions getDisplayNames/getDisplayName"""
797

    
798
    # ----------------------------------
799
    # Test the response we get for invalid token
800
    def test_invalid_token(self):
801
        """Test the response we get for invalid token (without pool)"""
802
        global user_1
803
        token = "skaksaFlBl+fasFdaf24sx"
804
        _mock_request([_request_ok])
805
        try:
806
            client = AstakosClient("https://example.com")
807
            client.get_usernames(token, [user_1['uuid']])
808
        except Unauthorized:
809
            pass
810
        except Exception:
811
            self.fail("Should have returned 401 (Invalid X-Auth-Token)")
812
        else:
813
            self.fail("Should have returned 401 (Invalid X-Auth-Token)")
814

    
815
    # ----------------------------------
816
    # Get Info for both users
817
    def test_usernames(self):
818
        """Test get_usernames with both users"""
819
        global token_1, user_1, user_2
820
        _mock_request([_request_ok])
821
        try:
822
            client = AstakosClient("https://example.com")
823
            catalog = client.get_usernames(
824
                token_1, [user_1['uuid'], user_2['uuid']])
825
        except:
826
            self.fail("Shouldn't raise an Exception")
827
        self.assertEqual(catalog[user_1['uuid']], user_1['username'])
828
        self.assertEqual(catalog[user_2['uuid']], user_2['username'])
829

    
830
    # ----------------------------------
831
    # Get info for user 1
832
    def test_username_user_one(self):
833
        """Test get_username for User One"""
834
        global token_2, user_1
835
        _mock_request([_request_offline, _request_ok])
836
        try:
837
            client = AstakosClient(
838
                "https://example.com", use_pool=True, retry=2)
839
            info = client.get_username(token_2, user_1['uuid'])
840
        except:
841
            self.fail("Shouldn't raise an Exception")
842
        self.assertEqual(info, user_1['username'])
843

    
844
    # ----------------------------------
845
    # Get info with wrong uuid
846
    def test_no_username(self):
847
        global token_1
848
        _mock_request([_request_ok])
849
        try:
850
            client = AstakosClient("https://example.com")
851
            client.get_username(token_1, "1234")
852
        except NoUserName:
853
            pass
854
        except:
855
            self.fail("Should have raised NoDisplayName exception")
856
        else:
857
            self.fail("Should have raised NoDisplayName exception")
858

    
859

    
860
class TestGetUUIDs(unittest.TestCase):
861
    """Test cases for functions getUUIDs/getUUID"""
862

    
863
    # ----------------------------------
864
    # Test the response we get for invalid token
865
    def test_invalid_token(self):
866
        """Test the response we get for invalid token (using pool)"""
867
        global user_1
868
        token = "skaksaFlBl+fasFdaf24sx"
869
        _mock_request([_request_ok])
870
        try:
871
            client = AstakosClient("https://example.com")
872
            client.get_uuids(token, [user_1['username']])
873
        except Unauthorized:
874
            pass
875
        except Exception:
876
            self.fail("Should have returned 401 (Invalid X-Auth-Token)")
877
        else:
878
            self.fail("Should have returned 401 (Invalid X-Auth-Token)")
879

    
880
    # ----------------------------------
881
    # Get info for both users
882
    def test_uuids(self):
883
        """Test get_uuids with both users"""
884
        global token_1, user_1, user_2
885
        _mock_request([_request_ok])
886
        try:
887
            client = AstakosClient("https://example.com")
888
            catalog = client.get_uuids(
889
                token_1, [user_1['username'], user_2['username']])
890
        except:
891
            self.fail("Shouldn't raise an Exception")
892
        self.assertEqual(catalog[user_1['username']], user_1['uuid'])
893
        self.assertEqual(catalog[user_2['username']], user_2['uuid'])
894

    
895
    # ----------------------------------
896
    # Get uuid for user 2
897
    def test_get_uuid_user_two(self):
898
        """Test get_uuid for User Two"""
899
        global token_1, user_2
900
        _mock_request([_request_offline, _request_ok])
901
        try:
902
            client = AstakosClient("https://example.com", retry=1)
903
            info = client.get_uuid(token_2, user_1['username'])
904
        except:
905
            self.fail("Shouldn't raise an Exception")
906
        self.assertEqual(info, user_1['uuid'])
907

    
908
    # ----------------------------------
909
    # Get uuid with wrong username
910
    def test_no_uuid(self):
911
        global token_1
912
        _mock_request([_request_ok])
913
        try:
914
            client = AstakosClient("https://example.com")
915
            client.get_uuid(token_1, "1234")
916
        except NoUUID:
917
            pass
918
        except:
919
            self.fail("Should have raised NoUUID exception")
920
        else:
921
            self.fail("Should have raised NoUUID exception")
922

    
923

    
924
class TestResources(unittest.TestCase):
925
    """Test cases for function get_resources"""
926

    
927
    # ----------------------------------
928
    def test_get_resources(self):
929
        """Test function call of get_resources"""
930
        global resources
931
        _mock_request([_request_offline, _request_ok])
932
        try:
933
            client = AstakosClient("https://example.com", retry=1)
934
            result = client.get_resources()
935
        except Exception as err:
936
            self.fail("Shouldn't raise Exception %s" % err)
937
        self.assertEqual(resources, result)
938

    
939

    
940
class TestQuotas(unittest.TestCase):
941
    """Test cases for function get_quotas"""
942

    
943
    # ----------------------------------
944
    def test_get_quotas(self):
945
        """Test function call of get_quotas"""
946
        global quotas, token_1
947
        _mock_request([_request_ok])
948
        try:
949
            client = AstakosClient("https://example.com")
950
            result = client.get_quotas(token_1)
951
        except Exception as err:
952
            self.fail("Shouldn't raise Exception %s" % err)
953
        self.assertEqual(quotas, result)
954

    
955
    # -----------------------------------
956
    def test_get_quotas_unauthorized(self):
957
        """Test function call of get_quotas with wrong token"""
958
        global token_2
959
        _mock_request([_request_ok])
960
        try:
961
            client = AstakosClient("https://example.com")
962
            client.get_quotas(token_2)
963
        except Unauthorized:
964
            pass
965
        except Exception as err:
966
            self.fail("Shouldn't raise Exception %s" % err)
967
        else:
968
            self.fail("Should have raised Unauthorized Exception")
969

    
970
    # ----------------------------------
971
    def test_get_quotas_without_token(self):
972
        """Test function call of get_quotas without token"""
973
        _mock_request([_request_ok])
974
        try:
975
            client = AstakosClient("https://example.com")
976
            client.get_quotas(None)
977
        except Unauthorized:
978
            pass
979
        except Exception as err:
980
            self.fail("Shouldn't raise Exception %s" % err)
981
        else:
982
            self.fail("Should have raised Unauthorized Exception")
983

    
984

    
985
class TestCommissions(unittest.TestCase):
986
    """Test cases for quota commissions"""
987

    
988
    # ----------------------------------
989
    def test_issue_commission(self):
990
        """Test function call of issue_commission"""
991
        global token_1, commission_request, commission_successful_reqsponse
992
        _mock_request([_request_ok])
993
        try:
994
            client = AstakosClient("https://example.com")
995
            response = client.issue_commission(token_1, commission_request)
996
        except Exception as err:
997
            self.fail("Shouldn't raise Exception %s" % err)
998
        self.assertEqual(response, commission_successful_response['serial'])
999

    
1000
    # ----------------------------------
1001
    def test_issue_commission_quota_limit(self):
1002
        """Test function call of issue_commission with limit exceeded"""
1003
        global token_1, commission_request, commission_failure_response
1004
        _mock_request([_request_ok])
1005
        new_request = dict(commission_request)
1006
        new_request['provisions'][1]['quantity'] = 520000000
1007
        try:
1008
            client = AstakosClient("https://example.com")
1009
            client.issue_commission(token_1, new_request)
1010
        except QuotaLimit:
1011
            pass
1012
        except Exception as err:
1013
            self.fail("Shouldn't raise Exception %s" % err)
1014
        else:
1015
            self.fail("Should have raised QuotaLimit Exception")
1016

    
1017
    # ----------------------------------
1018
    def test_issue_one_commission(self):
1019
        """Test function call of issue_one_commission"""
1020
        global token_1, commission_successful_response
1021
        _mock_request([_request_ok])
1022
        try:
1023
            client = AstakosClient("https://example.com")
1024
            response = client.issue_one_commission(
1025
                token_1, "c02f315b-7d84-45bc-a383-552a3f97d2ad",
1026
                "system", {"cyclades.vm": 1, "cyclades.ram": 30000})
1027
        except Exception as err:
1028
            self.fail("Shouldn't have raised Exception %s" % err)
1029
        self.assertEqual(response, commission_successful_response['serial'])
1030

    
1031
    # ----------------------------------
1032
    def test_get_pending_commissions(self):
1033
        """Test function call of get_pending_commissions"""
1034
        global token_1, pending_commissions
1035
        _mock_request([_request_ok])
1036
        try:
1037
            client = AstakosClient("https://example.com")
1038
            response = client.get_pending_commissions(token_1)
1039
        except Exception as err:
1040
            self.fail("Shouldn't raise Exception %s" % err)
1041
        self.assertEqual(response, pending_commissions)
1042

    
1043
    # ----------------------------------
1044
    def test_get_commission_info(self):
1045
        """Test function call of get_commission_info"""
1046
        global token_1, commission_description
1047
        _mock_request([_request_ok])
1048
        try:
1049
            client = AstakosClient("https://example.com", use_pool=True,
1050
                                   pool_size=2)
1051
            response = client.get_commission_info(token_1, 57)
1052
        except Exception as err:
1053
            self.fail("Shouldn't raise Exception %s" % err)
1054
        self.assertEqual(response, commission_description)
1055

    
1056
    # ----------------------------------
1057
    def test_get_commission_info_not_found(self):
1058
        """Test function call of get_commission_info with invalid serial"""
1059
        global token_1
1060
        _mock_request([_request_ok])
1061
        try:
1062
            client = AstakosClient("https://example.com")
1063
            client.get_commission_info(token_1, "57lala")
1064
        except NotFound:
1065
            pass
1066
        except Exception as err:
1067
            self.fail("Shouldn't raise Exception %s" % err)
1068
        else:
1069
            self.fail("Should have raised NotFound")
1070

    
1071
    # ----------------------------------
1072
    def test_get_commission_info_without_serial(self):
1073
        """Test function call of get_commission_info without serial"""
1074
        global token_1
1075
        _mock_request([_request_ok])
1076
        try:
1077
            client = AstakosClient("https://example.com")
1078
            client.get_commission_info(token_1, None)
1079
        except BadValue:
1080
            pass
1081
        except Exception as err:
1082
            self.fail("Shouldn't raise Exception %s" % err)
1083
        else:
1084
            self.fail("Should have raise BadValue")
1085

    
1086
    # ----------------------------------
1087
    def test_commision_action(self):
1088
        """Test function call of commision_action with wrong action"""
1089
        global token_1
1090
        _mock_request([_request_ok])
1091
        try:
1092
            client = AstakosClient("https://example.com")
1093
            client.commission_action(token_1, 57, "lala")
1094
        except BadRequest:
1095
            pass
1096
        except Exception as err:
1097
            self.fail("Shouldn't raise Exception %s" % err)
1098
        else:
1099
            self.fail("Should have raised BadRequest")
1100

    
1101
    # ----------------------------------
1102
    def test_accept_commission(self):
1103
        """Test function call of accept_commission"""
1104
        global token_1
1105
        _mock_request([_request_ok])
1106
        try:
1107
            client = AstakosClient("https://example.com")
1108
            client.accept_commission(token_1, 57)
1109
        except Exception as err:
1110
            self.fail("Shouldn't raise Exception %s" % err)
1111

    
1112
    # ----------------------------------
1113
    def test_reject_commission(self):
1114
        """Test function call of reject_commission"""
1115
        global token_1
1116
        _mock_request([_request_ok])
1117
        try:
1118
            client = AstakosClient("https://example.com")
1119
            client.reject_commission(token_1, 57)
1120
        except Exception as err:
1121
            self.fail("Shouldn't raise Exception %s" % err)
1122

    
1123
    # ----------------------------------
1124
    def test_accept_commission_not_found(self):
1125
        """Test function call of accept_commission with wrong serial"""
1126
        global token_1
1127
        _mock_request([_request_ok])
1128
        try:
1129
            client = AstakosClient("https://example.com")
1130
            client.reject_commission(token_1, 20)
1131
        except NotFound:
1132
            pass
1133
        except Exception as err:
1134
            self.fail("Shouldn't raise Exception %s" % err)
1135
        else:
1136
            self.fail("Should have raised NotFound")
1137

    
1138
    # ----------------------------------
1139
    def test_resolve_commissions(self):
1140
        """Test function call of resolve_commissions"""
1141
        global token_1
1142
        _mock_request([_request_ok])
1143
        try:
1144
            client = AstakosClient("https://example.com")
1145
            result = client.resolve_commissions(token_1, [56, 57],
1146
                                                [56, 58, 59])
1147
        except Exception as err:
1148
            self.fail("Shouldn't raise Exception %s" % err)
1149
        self.assertEqual(result, resolve_commissions_rep)
1150

    
1151

    
1152
class TestEndPoints(unittest.TestCase):
1153
    """Test cases for endpoints requests"""
1154

    
1155
    # ----------------------------------
1156
    def test_get_endpoints(self):
1157
        """Test function call of get_endpoints"""
1158
        global token_1, user_info_endpoints
1159
        _mock_request([_request_ok])
1160
        try:
1161
            client = AstakosClient("https://example.com")
1162
            response = client.get_endpoints(token_1)
1163
        except Exception as err:
1164
            self.fail("Shouldn't raise Exception %s" % err)
1165
        self.assertEqual(response, user_info_endpoints)
1166

    
1167

    
1168
# ----------------------------
1169
# Run tests
1170
if __name__ == "__main__":
1171
    unittest.main()