Statistics
| Branch: | Tag: | Revision:

root / astakosclient / astakosclient / tests.py @ 25a04cdd

History | View | Annotate | Download (42.5 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 sys
44
import socket
45
import simplejson
46

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

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

    
61

    
62
# --------------------------------------------------------------------
63
# Helper functions
64

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

    
71

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

    
81

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

    
92

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

    
100

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

    
108

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

    
116

    
117
def _request_ok(conn, method, url, **kwargs):
118
    """This request behaves like original Astakos does"""
119
    if url.startswith(astakosclient.API_AUTHENTICATE):
120
        return _req_authenticate(conn, method, url, **kwargs)
121
    elif url.startswith(astakosclient.API_USERCATALOGS):
122
        return _req_catalogs(conn, method, url, **kwargs)
123
    elif url.startswith(astakosclient.API_RESOURCES):
124
        return _req_resources(conn, method, url, **kwargs)
125
    elif url.startswith(astakosclient.API_QUOTAS):
126
        return _req_quotas(conn, method, url, **kwargs)
127
    elif url.startswith(astakosclient.API_COMMISSIONS):
128
        return _req_commission(conn, method, url, **kwargs)
129
    elif url.startswith(astakosclient.API_TOKENS):
130
        return _req_endpoints(conn, method, url, **kwargs)
131
    else:
132
        return _request_status_404(conn, method, url, **kwargs)
133

    
134

    
135
def _req_authenticate(conn, method, url, **kwargs):
136
    """Check if user exists and return his profile"""
137
    global user_1, user_2, token_1, token_2
138

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

    
153
    # Return
154
    if "usage=1" not in url:
155
        # Strip `usage' key from `user'
156
        del user['usage']
157
    return ("", simplejson.dumps(user), 200)
158

    
159

    
160
def _req_catalogs(conn, method, url, **kwargs):
161
    """Return user catalogs"""
162
    global token_1, token_2, user_1, user_2
163

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

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

    
197

    
198
def _req_resources(conn, method, url, **kwargs):
199
    """Return quota resources"""
200
    global resources
201

    
202
    # Check input
203
    if conn.__class__.__name__ != "HTTPSConnection":
204
        return _request_status_302(conn, method, url, **kwargs)
205
    if method != "GET":
206
        return _request_status_400(conn, method, url, **kwargs)
207

    
208
    # Return
209
    return ("", simplejson.dumps(resources), 200)
210

    
211

    
212
def _req_quotas(conn, method, url, **kwargs):
213
    """Return quotas for user_1"""
214
    global token_1, quotas
215

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

    
225
    # Return
226
    return ("", simplejson.dumps(quotas), 200)
227

    
228

    
229
def _req_commission(conn, method, url, **kwargs):
230
    """Perform a commission for user_1"""
231
    global token_1, pending_commissions, \
232
        commission_successful_response, commission_failure_response
233

    
234
    # Check input
235
    if conn.__class__.__name__ != "HTTPSConnection":
236
        return _request_status_302(conn, method, url, **kwargs)
237
    token = kwargs['headers'].get('X-Auth-Token')
238
    if token != token_1:
239
        return _request_status_401(conn, method, url, **kwargs)
240

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

    
272
    elif method == "GET":
273
        if url == astakosclient.API_COMMISSIONS:
274
            # Return pending commission
275
            return ("", simplejson.dumps(pending_commissions), 200)
276
        else:
277
            # Return commissions's description
278
            serial = url[25:]
279
            if serial == str(57):
280
                return ("", simplejson.dumps(commission_description), 200)
281
            else:
282
                return _request_status_404(conn, method, url, **kwargs)
283
    else:
284
        return _request_status_400(conn, method, url, **kwargs)
285

    
286

    
287
def _req_endpoints(conn, method, url, **kwargs):
288
    """Request endpoints"""
289
    global token_1, endpoints
290

    
291
    # Check input
292
    if conn.__class__.__name__ != "HTTPSConnection":
293
        return _request_status_302(conn, method, url, **kwargs)
294

    
295
    token_head = kwargs['headers'].get('X-Auth-Token')
296
    if url == astakosclient.API_TOKENS:
297
        if method != "POST":
298
            return _request_status_400(conn, method, url, **kwargs)
299
        body = simplejson.loads(kwargs['body'])
300
        token = body['auth']['token']['id']
301
        if token != token_1:
302
            return _request_status_401(conn, method, url, **kwargs)
303
        # Return
304
        return ("", simplejson.dumps(user_info_endpoints), 200)
305

    
306
    else:
307
        if method != "GET":
308
            return _request_status_400(conn, method, url, **kwargs)
309
        url_split = url[len(astakosclient.API_TOKENS):].split('/')
310
        token_url = url_split[1]
311
        if token_head != token_url:
312
            return _request_status_403(conn, method, url, **kwargs)
313
        if token_url != token_1:
314
            return _request_status_401(conn, method, url, **kwargs)
315
        # Return
316
        return ("", simplejson.dumps(endpoints), 200)
317

    
318

    
319
# ----------------------------
320
# Mock the actual _doRequest
321
def _mock_request(new_requests):
322
    """Mock the actual request
323

324
    Given a list of requests to use (in rotation),
325
    replace the original _doRequest function with
326
    a new one
327

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

    
337
    _mock.requests = new_requests
338
    # Replace `_doRequest' with our `_mock'
339
    astakosclient._do_request = _mock
340

    
341

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

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

    
402
resources = {
403
    "cyclades.vm": {
404
        "unit": None,
405
        "description": "Number of virtual machines",
406
        "service": "cyclades"},
407
    "cyclades.ram": {
408
        "unit": "bytes",
409
        "description": "Virtual machine memory",
410
        "service": "cyclades"}}
411

    
412
endpoints = {
413
    "endpoints": [
414
        {"name": "cyclades",
415
         "region": "cyclades",
416
         "internalURL": "https://node1.example.com/ui/",
417
         "adminURL": "https://node1.example.com/v1/",
418
         "type": None,
419
         "id": 5,
420
         "publicURL": "https://node1.example.com/ui/"},
421
        {"name": "pithos",
422
         "region": "pithos",
423
         "internalURL": "https://node2.example.com/ui/",
424
         "adminURL": "https://node2.example.com/v1",
425
         "type": None,
426
         "id": 6,
427
         "publicURL": "https://node2.example.com/ui/"}],
428
    "endpoint_links": [
429
        {"href": "/astakos/api/tokens/0000/endpoints?marker=4&limit=10000",
430
         "rel": "next"}]}
431

    
432
user_info_endpoints = \
433
    {'serviceCatalog': [
434
        {'endpoints': [{
435
            'SNF:uiURL': 'https://node1.example.com/ui/',
436
            'adminURL': 'https://node1.example.com/v1',
437
            'internalUrl': 'https://node1.example.com/v1',
438
            'publicURL': 'https://node1.example.com/v1',
439
            'region': 'cyclades'}],
440
         'name': 'cyclades',
441
         'type': 'compute'},
442
        {'endpoints': [{
443
            'SNF:uiURL': 'https://node2.example.com/ui/',
444
            'adminURL': 'https://node2.example.com/v1',
445
            'internalUrl': 'https://node2.example.com/v1',
446
            'publicURL': 'https://node2.example.com/v1',
447
            'region': 'pithos'}],
448
         'name': 'pithos',
449
         'type': 'storage'}],
450
     'token': {
451
         'expires': '2013-06-19T15:23:59.975572+00:00',
452
         'id': token_1,
453
         'tenant': {
454
             'id': user_1,
455
             'name': 'Firstname Lastname'}},
456
     'user': {
457
         'id': user_1,
458
         'name': 'Firstname Lastname',
459
         'roles': [{'id': 1, 'name': 'default'}],
460
         'roles_links': []}}
461

    
462
quotas = {
463
    "system": {
464
        "cyclades.ram": {
465
            "pending": 0,
466
            "limit": 1073741824,
467
            "usage": 536870912},
468
        "cyclades.vm": {
469
            "pending": 0,
470
            "limit": 2,
471
            "usage": 2}},
472
    "project:1": {
473
        "cyclades.ram": {
474
            "pending": 0,
475
            "limit": 2147483648,
476
            "usage": 2147483648},
477
        "cyclades.vm": {
478
            "pending": 1,
479
            "limit": 5,
480
            "usage": 2}}}
481

    
482
commission_request = {
483
    "force": False,
484
    "auto_accept": False,
485
    "name": "my commission",
486
    "provisions": [
487
        {
488
            "holder": "c02f315b-7d84-45bc-a383-552a3f97d2ad",
489
            "source": "system",
490
            "resource": "cyclades.vm",
491
            "quantity": 1
492
        },
493
        {
494
            "holder": "c02f315b-7d84-45bc-a383-552a3f97d2ad",
495
            "source": "system",
496
            "resource": "cyclades.ram",
497
            "quantity": 30000
498
        }]}
499

    
500
commission_successful_response = {"serial": 57}
501

    
502
commission_failure_response = {
503
    "overLimit": {
504
        "message": "a human-readable error message",
505
        "code": 413,
506
        "data": {
507
            "provision": {
508
                "holder": "c02f315b-7d84-45bc-a383-552a3f97d2ad",
509
                "source": "system",
510
                "resource": "cyclades.ram",
511
                "quantity": 520000000},
512
            "name": "NoCapacityError",
513
            "limit": 600000000,
514
            "usage": 180000000}}}
515

    
516
pending_commissions = [100, 200]
517

    
518
commission_description = {
519
    "serial": 57,
520
    "issue_time": "2013-04-08T10:19:15.0373+00:00",
521
    "name": "a commission",
522
    "provisions": [
523
        {
524
            "holder": "c02f315b-7d84-45bc-a383-552a3f97d2ad",
525
            "source": "system",
526
            "resource": "cyclades.vm",
527
            "quantity": 1
528
        },
529
        {
530
            "holder": "c02f315b-7d84-45bc-a383-552a3f97d2ad",
531
            "source": "system",
532
            "resource": "cyclades.ram",
533
            "quantity": 536870912
534
        }]}
535

    
536
resolve_commissions_req = {
537
    "accept": [56, 57],
538
    "reject": [56, 58, 59]}
539

    
540
resolve_commissions_rep = {
541
    "accepted": [57],
542
    "rejected": [59],
543
    "failed": [
544
        [56, {
545
            "badRequest": {
546
                "message": "cannot both accept and reject serial 56",
547
                "code": 400}}],
548
        [58, {
549
            "itemNotFound": {
550
                "message": "serial 58 does not exist",
551
                "code": 404}}]]}
552

    
553

    
554
# --------------------------------------------------------------------
555
# The actual tests
556

    
557
class TestCallAstakos(unittest.TestCase):
558
    """Test cases for function _callAstakos"""
559

    
560
    # ----------------------------------
561
    # Test the response we get if we don't have internet access
562
    def _offline(self, pool):
563
        global token_1
564
        _mock_request([_request_offline])
565
        try:
566
            client = AstakosClient("https://example.com", use_pool=pool)
567
            client._call_astakos(token_1, astakosclient.API_AUTHENTICATE)
568
        except AstakosClientException:
569
            pass
570
        else:
571
            self.fail("Should have raised AstakosClientException")
572

    
573
    def test_offline(self):
574
        """Test _offline without pool"""
575
        self._offline(False)
576

    
577
    def test_offline_pool(self):
578
        """Test _offline using pool"""
579
        self._offline(True)
580

    
581
    # ----------------------------------
582
    # Test the response we get if we send invalid token
583
    def _invalid_token(self, pool):
584
        token = "skaksaFlBl+fasFdaf24sx=="
585
        _mock_request([_request_ok])
586
        try:
587
            client = AstakosClient("https://example.com", use_pool=pool)
588
            client._call_astakos(token, astakosclient.API_AUTHENTICATE)
589
        except Unauthorized:
590
            pass
591
        except Exception:
592
            self.fail("Should have returned 401 (Invalid X-Auth-Token)")
593
        else:
594
            self.fail("Should have returned 401 (Invalid X-Auth-Token)")
595

    
596
    def test_invalid_token(self):
597
        """Test _invalid_token without pool"""
598
        self._invalid_token(False)
599

    
600
    def test_invalid_token_pool(self):
601
        """Test _invalid_token using pool"""
602
        self._invalid_token(True)
603

    
604
    # ----------------------------------
605
    # Test the response we get if we send invalid url
606
    def _invalid_url(self, pool):
607
        global token_1
608
        _mock_request([_request_ok])
609
        try:
610
            client = AstakosClient("https://example.com", use_pool=pool)
611
            client._call_astakos(token_1, "/astakos/api/misspelled")
612
        except NotFound:
613
            pass
614
        except Exception:
615
            self.fail("Should have returned 404 (Not Found)")
616
        else:
617
            self.fail("Should have returned 404 (Not Found)")
618

    
619
    def test_invalid_url(self):
620
        """Test _invalid_url without pool"""
621
        self._invalid_url(False)
622

    
623
    def test_invalid_url_pool(self):
624
        """Test _invalid_url using pool"""
625
        self._invalid_url(True)
626

    
627
    # ----------------------------------
628
    # Test the response we get if we use an unsupported scheme
629
    def _unsupported_scheme(self, pool):
630
        global token_1
631
        _mock_request([_request_ok])
632
        try:
633
            client = AstakosClient("ftp://example.com", use_pool=pool)
634
            client._call_astakos(token_1, astakosclient.API_AUTHENTICATE)
635
        except BadValue:
636
            pass
637
        except Exception:
638
            self.fail("Should have raise BadValue Exception")
639
        else:
640
            self.fail("Should have raise BadValue Exception")
641

    
642
    def test_unsupported_scheme(self):
643
        """Test _unsupported_scheme without pool"""
644
        self._unsupported_scheme(False)
645

    
646
    def test_unsupported_scheme_pool(self):
647
        """Test _unsupported_scheme using pool"""
648
        self._unsupported_scheme(True)
649

    
650
    # ----------------------------------
651
    # Test the response we get if we use http instead of https
652
    def _http_scheme(self, pool):
653
        global token_1
654
        _mock_request([_request_ok])
655
        try:
656
            client = AstakosClient("http://example.com", use_pool=pool)
657
            client._call_astakos(token_1, astakosclient.API_AUTHENTICATE)
658
        except AstakosClientException as err:
659
            if err.status != 302:
660
                self.fail("Should have returned 302 (Found)")
661
        else:
662
            self.fail("Should have returned 302 (Found)")
663

    
664
    def test_http_scheme(self):
665
        """Test _http_scheme without pool"""
666
        self._http_scheme(False)
667

    
668
    def test_http_scheme_pool(self):
669
        """Test _http_scheme using pool"""
670
        self._http_scheme(True)
671

    
672
    # ----------------------------------
673
    # Test the response we get if we use authenticate with POST
674
    def _post_authenticate(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(
680
                token_1, astakosclient.API_AUTHENTICATE, method="POST")
681
        except BadRequest:
682
            pass
683
        except Exception:
684
            self.fail("Should have returned 400 (Method not allowed)")
685
        else:
686
            self.fail("Should have returned 400 (Method not allowed)")
687

    
688
    def test_post_authenticate(self):
689
        """Test _post_authenticate without pool"""
690
        self._post_authenticate(False)
691

    
692
    def test_post_authenticate_pool(self):
693
        """Test _post_authenticate using pool"""
694
        self._post_authenticate(True)
695

    
696
    # ----------------------------------
697
    # Test the response if we request user_catalogs with GET
698
    def _get_user_catalogs(self, pool):
699
        global token_1
700
        _mock_request([_request_ok])
701
        try:
702
            client = AstakosClient("https://example.com", use_pool=pool)
703
            client._call_astakos(token_1, astakosclient.API_USERCATALOGS)
704
        except BadRequest:
705
            pass
706
        except Exception:
707
            self.fail("Should have returned 400 (Method not allowed)")
708
        else:
709
            self.fail("Should have returned 400 (Method not allowed)")
710

    
711
    def test_get_user_catalogs(self):
712
        """Test _get_user_catalogs without pool"""
713
        self._get_user_catalogs(False)
714

    
715
    def test_get_user_catalogs_pool(self):
716
        """Test _get_user_catalogs using pool"""
717
        self._get_user_catalogs(True)
718

    
719

    
720
class TestAuthenticate(unittest.TestCase):
721
    """Test cases for function getUserInfo"""
722

    
723
    # ----------------------------------
724
    # Test the response we get if we don't have internet access
725
    def test_offline(self):
726
        """Test offline after 3 retries"""
727
        global token_1
728
        _mock_request([_request_offline])
729
        try:
730
            client = AstakosClient("https://example.com", retry=3)
731
            client.get_user_info(token_1)
732
        except AstakosClientException:
733
            pass
734
        else:
735
            self.fail("Should have raised AstakosClientException exception")
736

    
737
    # ----------------------------------
738
    # Test the response we get for invalid token
739
    def _invalid_token(self, pool):
740
        token = "skaksaFlBl+fasFdaf24sx=="
741
        _mock_request([_request_ok])
742
        try:
743
            client = AstakosClient("https://example.com", use_pool=pool)
744
            client.get_user_info(token)
745
        except Unauthorized:
746
            pass
747
        except Exception:
748
            self.fail("Should have returned 401 (Invalid X-Auth-Token)")
749
        else:
750
            self.fail("Should have returned 401 (Invalid X-Auth-Token)")
751

    
752
    def test_invalid_token(self):
753
        """Test _invalid_token without pool"""
754
        self._invalid_token(False)
755

    
756
    def test_invalid_token_pool(self):
757
        """Test _invalid_token using pool"""
758
        self._invalid_token(True)
759

    
760
    #- ---------------------------------
761
    # Test response for user 1
762
    def _auth_user(self, token, user_info, usage, pool):
763
        _mock_request([_request_ok])
764
        try:
765
            client = AstakosClient("https://example.com", use_pool=pool)
766
            auth_info = client.get_user_info(token, usage=usage)
767
        except:
768
            self.fail("Shouldn't raise an Exception")
769
        self.assertEqual(user_info, auth_info)
770

    
771
    def test_auth_user_one(self):
772
        """Test _auth_user for User 1 without pool, without usage"""
773
        global token_1, user_1
774
        user_info = dict(user_1)
775
        del user_info['usage']
776
        self._auth_user(token_1, user_info, False, False)
777

    
778
    def test_auth_user_one_usage(self):
779
        """Test _auth_user for User 1 without pool, with usage"""
780
        global token_1, user_1
781
        self._auth_user(token_1, user_1, True, False)
782

    
783
    def test_auth_user_one_usage_pool(self):
784
        """Test _auth_user for User 1 using pool, with usage"""
785
        global token_1, user_1
786
        self._auth_user(token_1, user_1, True, True)
787

    
788
    def test_auth_user_two(self):
789
        """Test _auth_user for User 2 without pool, without usage"""
790
        global token_2, user_2
791
        user_info = dict(user_2)
792
        del user_info['usage']
793
        self._auth_user(token_2, user_info, False, False)
794

    
795
    def test_auth_user_two_usage(self):
796
        """Test _auth_user for User 2 without pool, with usage"""
797
        global token_2, user_2
798
        self._auth_user(token_2, user_2, True, False)
799

    
800
    def test_auth_user_two_usage_pool(self):
801
        """Test _auth_user for User 2 using pool, with usage"""
802
        global token_2, user_2
803
        self._auth_user(token_2, user_2, True, True)
804

    
805
    # ----------------------------------
806
    # Test retry functionality
807
    def test_offline_retry(self):
808
        """Test retry functionality for getUserInfo"""
809
        global token_1, user_1
810
        _mock_request([_request_offline, _request_offline, _request_ok])
811
        try:
812
            client = AstakosClient("https://example.com", retry=2)
813
            auth_info = client.get_user_info(token_1, usage=True)
814
        except:
815
            self.fail("Shouldn't raise an Exception")
816
        self.assertEqual(user_1, auth_info)
817

    
818

    
819
class TestDisplayNames(unittest.TestCase):
820
    """Test cases for functions getDisplayNames/getDisplayName"""
821

    
822
    # ----------------------------------
823
    # Test the response we get for invalid token
824
    def test_invalid_token(self):
825
        """Test the response we get for invalid token (without pool)"""
826
        global user_1
827
        token = "skaksaFlBl+fasFdaf24sx=="
828
        _mock_request([_request_ok])
829
        try:
830
            client = AstakosClient("https://example.com")
831
            client.get_usernames(token, [user_1['uuid']])
832
        except Unauthorized:
833
            pass
834
        except Exception:
835
            self.fail("Should have returned 401 (Invalid X-Auth-Token)")
836
        else:
837
            self.fail("Should have returned 401 (Invalid X-Auth-Token)")
838

    
839
    # ----------------------------------
840
    # Get Info for both users
841
    def test_usernames(self):
842
        """Test get_usernames with both users"""
843
        global token_1, user_1, user_2
844
        _mock_request([_request_ok])
845
        try:
846
            client = AstakosClient("https://example.com")
847
            catalog = client.get_usernames(
848
                token_1, [user_1['uuid'], user_2['uuid']])
849
        except:
850
            self.fail("Shouldn't raise an Exception")
851
        self.assertEqual(catalog[user_1['uuid']], user_1['username'])
852
        self.assertEqual(catalog[user_2['uuid']], user_2['username'])
853

    
854
    # ----------------------------------
855
    # Get info for user 1
856
    def test_username_user_one(self):
857
        """Test get_username for User One"""
858
        global token_2, user_1
859
        _mock_request([_request_offline, _request_ok])
860
        try:
861
            client = AstakosClient(
862
                "https://example.com", use_pool=True, retry=2)
863
            info = client.get_username(token_2, user_1['uuid'])
864
        except:
865
            self.fail("Shouldn't raise an Exception")
866
        self.assertEqual(info, user_1['username'])
867

    
868
    # ----------------------------------
869
    # Get info with wrong uuid
870
    def test_no_username(self):
871
        global token_1
872
        _mock_request([_request_ok])
873
        try:
874
            client = AstakosClient("https://example.com")
875
            client.get_username(token_1, "1234")
876
        except NoUserName:
877
            pass
878
        except:
879
            self.fail("Should have raised NoDisplayName exception")
880
        else:
881
            self.fail("Should have raised NoDisplayName exception")
882

    
883

    
884
class TestGetUUIDs(unittest.TestCase):
885
    """Test cases for functions getUUIDs/getUUID"""
886

    
887
    # ----------------------------------
888
    # Test the response we get for invalid token
889
    def test_invalid_token(self):
890
        """Test the response we get for invalid token (using pool)"""
891
        global user_1
892
        token = "skaksaFlBl+fasFdaf24sx=="
893
        _mock_request([_request_ok])
894
        try:
895
            client = AstakosClient("https://example.com")
896
            client.get_uuids(token, [user_1['username']])
897
        except Unauthorized:
898
            pass
899
        except Exception:
900
            self.fail("Should have returned 401 (Invalid X-Auth-Token)")
901
        else:
902
            self.fail("Should have returned 401 (Invalid X-Auth-Token)")
903

    
904
    # ----------------------------------
905
    # Get info for both users
906
    def test_uuids(self):
907
        """Test get_uuids with both users"""
908
        global token_1, user_1, user_2
909
        _mock_request([_request_ok])
910
        try:
911
            client = AstakosClient("https://example.com")
912
            catalog = client.get_uuids(
913
                token_1, [user_1['username'], user_2['username']])
914
        except:
915
            self.fail("Shouldn't raise an Exception")
916
        self.assertEqual(catalog[user_1['username']], user_1['uuid'])
917
        self.assertEqual(catalog[user_2['username']], user_2['uuid'])
918

    
919
    # ----------------------------------
920
    # Get uuid for user 2
921
    def test_get_uuid_user_two(self):
922
        """Test get_uuid for User Two"""
923
        global token_1, user_2
924
        _mock_request([_request_offline, _request_ok])
925
        try:
926
            client = AstakosClient("https://example.com", retry=1)
927
            info = client.get_uuid(token_2, user_1['username'])
928
        except:
929
            self.fail("Shouldn't raise an Exception")
930
        self.assertEqual(info, user_1['uuid'])
931

    
932
    # ----------------------------------
933
    # Get uuid with wrong username
934
    def test_no_uuid(self):
935
        global token_1
936
        _mock_request([_request_ok])
937
        try:
938
            client = AstakosClient("https://example.com")
939
            client.get_uuid(token_1, "1234")
940
        except NoUUID:
941
            pass
942
        except:
943
            self.fail("Should have raised NoUUID exception")
944
        else:
945
            self.fail("Should have raised NoUUID exception")
946

    
947

    
948
class TestResources(unittest.TestCase):
949
    """Test cases for function get_resources"""
950

    
951
    # ----------------------------------
952
    def test_get_resources(self):
953
        """Test function call of get_resources"""
954
        global resources
955
        _mock_request([_request_offline, _request_ok])
956
        try:
957
            client = AstakosClient("https://example.com", retry=1)
958
            result = client.get_resources()
959
        except Exception as err:
960
            self.fail("Shouldn't raise Exception %s" % err)
961
        self.assertEqual(resources, result)
962

    
963

    
964
class TestQuotas(unittest.TestCase):
965
    """Test cases for function get_quotas"""
966

    
967
    # ----------------------------------
968
    def test_get_quotas(self):
969
        """Test function call of get_quotas"""
970
        global quotas, token_1
971
        _mock_request([_request_ok])
972
        try:
973
            client = AstakosClient("https://example.com")
974
            result = client.get_quotas(token_1)
975
        except Exception as err:
976
            self.fail("Shouldn't raise Exception %s" % err)
977
        self.assertEqual(quotas, result)
978

    
979
    # -----------------------------------
980
    def test_get_quotas_unauthorized(self):
981
        """Test function call of get_quotas with wrong token"""
982
        global token_2
983
        _mock_request([_request_ok])
984
        try:
985
            client = AstakosClient("https://example.com")
986
            client.get_quotas(token_2)
987
        except Unauthorized:
988
            pass
989
        except Exception as err:
990
            self.fail("Shouldn't raise Exception %s" % err)
991
        else:
992
            self.fail("Should have raised Unauthorized Exception")
993

    
994
    # ----------------------------------
995
    def test_get_quotas_without_token(self):
996
        """Test function call of get_quotas without token"""
997
        _mock_request([_request_ok])
998
        try:
999
            client = AstakosClient("https://example.com")
1000
            client.get_quotas(None)
1001
        except Unauthorized:
1002
            pass
1003
        except Exception as err:
1004
            self.fail("Shouldn't raise Exception %s" % err)
1005
        else:
1006
            self.fail("Should have raised Unauthorized Exception")
1007

    
1008

    
1009
class TestCommissions(unittest.TestCase):
1010
    """Test cases for quota commissions"""
1011

    
1012
    # ----------------------------------
1013
    def test_issue_commission(self):
1014
        """Test function call of issue_commission"""
1015
        global token_1, commission_request, commission_successful_reqsponse
1016
        _mock_request([_request_ok])
1017
        try:
1018
            client = AstakosClient("https://example.com")
1019
            response = client.issue_commission(token_1, commission_request)
1020
        except Exception as err:
1021
            self.fail("Shouldn't raise Exception %s" % err)
1022
        self.assertEqual(response, commission_successful_response['serial'])
1023

    
1024
    # ----------------------------------
1025
    def test_issue_commission_quota_limit(self):
1026
        """Test function call of issue_commission with limit exceeded"""
1027
        global token_1, commission_request, commission_failure_response
1028
        _mock_request([_request_ok])
1029
        new_request = dict(commission_request)
1030
        new_request['provisions'][1]['quantity'] = 520000000
1031
        try:
1032
            client = AstakosClient("https://example.com")
1033
            client.issue_commission(token_1, new_request)
1034
        except QuotaLimit:
1035
            pass
1036
        except Exception as err:
1037
            self.fail("Shouldn't raise Exception %s" % err)
1038
        else:
1039
            self.fail("Should have raised QuotaLimit Exception")
1040

    
1041
    # ----------------------------------
1042
    def test_issue_one_commission(self):
1043
        """Test function call of issue_one_commission"""
1044
        global token_1, commission_successful_response
1045
        _mock_request([_request_ok])
1046
        try:
1047
            client = AstakosClient("https://example.com")
1048
            response = client.issue_one_commission(
1049
                token_1, "c02f315b-7d84-45bc-a383-552a3f97d2ad",
1050
                "system", {"cyclades.vm": 1, "cyclades.ram": 30000})
1051
        except Exception as err:
1052
            self.fail("Shouldn't have raised Exception %s" % err)
1053
        self.assertEqual(response, commission_successful_response['serial'])
1054

    
1055
    # ----------------------------------
1056
    def test_get_pending_commissions(self):
1057
        """Test function call of get_pending_commissions"""
1058
        global token_1, pending_commissions
1059
        _mock_request([_request_ok])
1060
        try:
1061
            client = AstakosClient("https://example.com")
1062
            response = client.get_pending_commissions(token_1)
1063
        except Exception as err:
1064
            self.fail("Shouldn't raise Exception %s" % err)
1065
        self.assertEqual(response, pending_commissions)
1066

    
1067
    # ----------------------------------
1068
    def test_get_commission_info(self):
1069
        """Test function call of get_commission_info"""
1070
        global token_1, commission_description
1071
        _mock_request([_request_ok])
1072
        try:
1073
            client = \
1074
                AstakosClient("https://example.com", use_pool=True, pool_size=2)
1075
            response = client.get_commission_info(token_1, 57)
1076
        except Exception as err:
1077
            self.fail("Shouldn't raise Exception %s" % err)
1078
        self.assertEqual(response, commission_description)
1079

    
1080
    # ----------------------------------
1081
    def test_get_commission_info_not_found(self):
1082
        """Test function call of get_commission_info with invalid serial"""
1083
        global token_1
1084
        _mock_request([_request_ok])
1085
        try:
1086
            client = AstakosClient("https://example.com")
1087
            client.get_commission_info(token_1, "57lala")
1088
        except NotFound:
1089
            pass
1090
        except Exception as err:
1091
            self.fail("Shouldn't raise Exception %s" % err)
1092
        else:
1093
            self.fail("Should have raised NotFound")
1094

    
1095
    # ----------------------------------
1096
    def test_get_commission_info_without_serial(self):
1097
        """Test function call of get_commission_info without serial"""
1098
        global token_1
1099
        _mock_request([_request_ok])
1100
        try:
1101
            client = AstakosClient("https://example.com")
1102
            client.get_commission_info(token_1, None)
1103
        except BadValue:
1104
            pass
1105
        except Exception as err:
1106
            self.fail("Shouldn't raise Exception %s" % err)
1107
        else:
1108
            self.fail("Should have raise BadValue")
1109

    
1110
    # ----------------------------------
1111
    def test_commision_action(self):
1112
        """Test function call of commision_action with wrong action"""
1113
        global token_1
1114
        _mock_request([_request_ok])
1115
        try:
1116
            client = AstakosClient("https://example.com")
1117
            client.commission_action(token_1, 57, "lala")
1118
        except BadRequest:
1119
            pass
1120
        except Exception as err:
1121
            self.fail("Shouldn't raise Exception %s" % err)
1122
        else:
1123
            self.fail("Should have raised BadRequest")
1124

    
1125
    # ----------------------------------
1126
    def test_accept_commission(self):
1127
        """Test function call of accept_commission"""
1128
        global token_1
1129
        _mock_request([_request_ok])
1130
        try:
1131
            client = AstakosClient("https://example.com")
1132
            client.accept_commission(token_1, 57)
1133
        except Exception as err:
1134
            self.fail("Shouldn't raise Exception %s" % err)
1135

    
1136
    # ----------------------------------
1137
    def test_reject_commission(self):
1138
        """Test function call of reject_commission"""
1139
        global token_1
1140
        _mock_request([_request_ok])
1141
        try:
1142
            client = AstakosClient("https://example.com")
1143
            client.reject_commission(token_1, 57)
1144
        except Exception as err:
1145
            self.fail("Shouldn't raise Exception %s" % err)
1146

    
1147
    # ----------------------------------
1148
    def test_accept_commission_not_found(self):
1149
        """Test function call of accept_commission with wrong serial"""
1150
        global token_1
1151
        _mock_request([_request_ok])
1152
        try:
1153
            client = AstakosClient("https://example.com")
1154
            client.reject_commission(token_1, 20)
1155
        except NotFound:
1156
            pass
1157
        except Exception as err:
1158
            self.fail("Shouldn't raise Exception %s" % err)
1159
        else:
1160
            self.fail("Should have raised NotFound")
1161

    
1162
    # ----------------------------------
1163
    def test_resolve_commissions(self):
1164
        """Test function call of resolve_commissions"""
1165
        global token_1
1166
        _mock_request([_request_ok])
1167
        try:
1168
            client = AstakosClient("https://example.com")
1169
            result = client.resolve_commissions(token_1, [56, 57], [56, 58, 59])
1170
        except Exception as err:
1171
            self.fail("Shouldn't raise Exception %s" % err)
1172
        self.assertEqual(result, resolve_commissions_rep)
1173

    
1174

    
1175
class TestEndPoints(unittest.TestCase):
1176
    """Test cases for endpoints requests"""
1177

    
1178
    # ----------------------------------
1179
    def test_get_endpoints(self):
1180
        """Test function call of get_endpoints"""
1181
        global token_1, endpoints
1182
        _mock_request([_request_ok])
1183
        try:
1184
            client = AstakosClient("https://example.com")
1185
            response = client.get_endpoints(token_1)
1186
        except Exception as err:
1187
            self.fail("Shouldn't raise Exception %s" % err)
1188
        self.assertEqual(response, endpoints)
1189

    
1190
    # ----------------------------------
1191
    def test_get_endpoints_wrong_token(self):
1192
        """Test function call of get_endpoints with wrong token"""
1193
        global token_2, endpoints
1194
        _mock_request([_request_ok])
1195
        try:
1196
            client = AstakosClient("https://example.com")
1197
            client.get_endpoints(token_2, marker=2, limit=100)
1198
        except Unauthorized:
1199
            pass
1200
        except Exception as err:
1201
            self.fail("Shouldn't raise Exception %s" % err)
1202
        else:
1203
            self.fail("Should have raised Unauthorized Exception")
1204

    
1205
    # ----------------------------------
1206
    def test_get_user_info_with_endpoints(self):
1207
        """Test function call of get_user_info_with_endpoints"""
1208
        global token_1, user_info_endpoints
1209
        _mock_request([_request_ok])
1210
        try:
1211
            client = AstakosClient("https://example.com")
1212
            response = client.get_user_info_with_endpoints(token_1)
1213
        except Exception as err:
1214
            self.fail("Shouldn't raise Exception %s" % err)
1215
        self.assertEqual(response, user_info_endpoints)
1216

    
1217

    
1218
# ----------------------------
1219
# Run tests
1220
if __name__ == "__main__":
1221
    unittest.main()