Statistics
| Branch: | Tag: | Revision:

root / astakosclient / astakosclient / tests.py @ 164e64d5

History | View | Annotate | Download (40.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
    if method != "GET":
295
        return _request_status_400(conn, method, url, **kwargs)
296

    
297
    token_head = kwargs['headers'].get('X-Auth-Token')
298
    url_split = url[len(astakosclient.API_TOKENS):].split('/')
299
    token_url = url_split[1]
300
    if token_head != token_url:
301
        return _request_status_403(conn, method, url, **kwargs)
302
    if token_url != token_1:
303
        return _request_status_401(conn, method, url, **kwargs)
304

    
305
    # Return
306
    return ("", simplejson.dumps(endpoints), 200)
307

    
308

    
309
# ----------------------------
310
# Mock the actual _doRequest
311
def _mock_request(new_requests):
312
    """Mock the actual request
313

314
    Given a list of requests to use (in rotation),
315
    replace the original _doRequest function with
316
    a new one
317

318
    """
319
    def _mock(conn, method, url, **kwargs):
320
        # Get first request
321
        request = _mock.requests[0]
322
        # Rotate requests
323
        _mock.requests = _mock.requests[1:] + _mock.requests[:1]
324
        # Use first request
325
        return request(conn, method, url, **kwargs)
326

    
327
    _mock.requests = new_requests
328
    # Replace `_doRequest' with our `_mock'
329
    astakosclient._do_request = _mock
330

    
331

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

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

    
392
resources = {
393
    "cyclades.vm": {
394
        "unit": None,
395
        "description": "Number of virtual machines",
396
        "service": "cyclades"},
397
    "cyclades.ram": {
398
        "unit": "bytes",
399
        "description": "Virtual machine memory",
400
        "service": "cyclades"}}
401

    
402
endpoints = {
403
    "endpoints": [
404
        {"name": "cyclades",
405
         "region": "cyclades",
406
         "internalURL": "https://node1.example.com/ui/",
407
         "adminURL": "https://node1.example.com/v1/",
408
         "type": None,
409
         "id": 5,
410
         "publicURL": "https://node1.example.com/ui/"},
411
        {"name": "pithos",
412
         "region": "pithos",
413
         "internalURL": "https://node2.example.com/ui/",
414
         "adminURL": "https://node2.example.com/v1",
415
         "type": None,
416
         "id": 6,
417
         "publicURL": "https://node2.example.com/ui/"}],
418
    "endpoint_links": [
419
        {"href": "/astakos/api/tokens/0000/endpoints?marker=4&limit=10000",
420
         "rel": "next"}]}
421

    
422
quotas = {
423
    "system": {
424
        "cyclades.ram": {
425
            "pending": 0,
426
            "limit": 1073741824,
427
            "usage": 536870912},
428
        "cyclades.vm": {
429
            "pending": 0,
430
            "limit": 2,
431
            "usage": 2}},
432
    "project:1": {
433
        "cyclades.ram": {
434
            "pending": 0,
435
            "limit": 2147483648,
436
            "usage": 2147483648},
437
        "cyclades.vm": {
438
            "pending": 1,
439
            "limit": 5,
440
            "usage": 2}}}
441

    
442
commission_request = {
443
    "force": False,
444
    "auto_accept": False,
445
    "name": "my commission",
446
    "provisions": [
447
        {
448
            "holder": "c02f315b-7d84-45bc-a383-552a3f97d2ad",
449
            "source": "system",
450
            "resource": "cyclades.vm",
451
            "quantity": 1
452
        },
453
        {
454
            "holder": "c02f315b-7d84-45bc-a383-552a3f97d2ad",
455
            "source": "system",
456
            "resource": "cyclades.ram",
457
            "quantity": 30000
458
        }]}
459

    
460
commission_successful_response = {"serial": 57}
461

    
462
commission_failure_response = {
463
    "overLimit": {
464
        "message": "a human-readable error message",
465
        "code": 413,
466
        "data": {
467
            "provision": {
468
                "holder": "c02f315b-7d84-45bc-a383-552a3f97d2ad",
469
                "source": "system",
470
                "resource": "cyclades.ram",
471
                "quantity": 520000000},
472
            "name": "NoCapacityError",
473
            "limit": 600000000,
474
            "usage": 180000000}}}
475

    
476
pending_commissions = [100, 200]
477

    
478
commission_description = {
479
    "serial": 57,
480
    "issue_time": "2013-04-08T10:19:15.0373+00:00",
481
    "name": "a commission",
482
    "provisions": [
483
        {
484
            "holder": "c02f315b-7d84-45bc-a383-552a3f97d2ad",
485
            "source": "system",
486
            "resource": "cyclades.vm",
487
            "quantity": 1
488
        },
489
        {
490
            "holder": "c02f315b-7d84-45bc-a383-552a3f97d2ad",
491
            "source": "system",
492
            "resource": "cyclades.ram",
493
            "quantity": 536870912
494
        }]}
495

    
496
resolve_commissions_req = {
497
    "accept": [56, 57],
498
    "reject": [56, 58, 59]}
499

    
500
resolve_commissions_rep = {
501
    "accepted": [57],
502
    "rejected": [59],
503
    "failed": [
504
        [56, {
505
            "badRequest": {
506
                "message": "cannot both accept and reject serial 56",
507
                "code": 400}}],
508
        [58, {
509
            "itemNotFound": {
510
                "message": "serial 58 does not exist",
511
                "code": 404}}]]}
512

    
513

    
514
# --------------------------------------------------------------------
515
# The actual tests
516

    
517
class TestCallAstakos(unittest.TestCase):
518
    """Test cases for function _callAstakos"""
519

    
520
    # ----------------------------------
521
    # Test the response we get if we don't have internet access
522
    def _offline(self, pool):
523
        global token_1
524
        _mock_request([_request_offline])
525
        try:
526
            client = AstakosClient("https://example.com", use_pool=pool)
527
            client._call_astakos(token_1, astakosclient.API_AUTHENTICATE)
528
        except AstakosClientException:
529
            pass
530
        else:
531
            self.fail("Should have raised AstakosClientException")
532

    
533
    def test_offline(self):
534
        """Test _offline without pool"""
535
        self._offline(False)
536

    
537
    def test_offline_pool(self):
538
        """Test _offline using pool"""
539
        self._offline(True)
540

    
541
    # ----------------------------------
542
    # Test the response we get if we send invalid token
543
    def _invalid_token(self, pool):
544
        token = "skaksaFlBl+fasFdaf24sx=="
545
        _mock_request([_request_ok])
546
        try:
547
            client = AstakosClient("https://example.com", use_pool=pool)
548
            client._call_astakos(token, astakosclient.API_AUTHENTICATE)
549
        except Unauthorized:
550
            pass
551
        except Exception:
552
            self.fail("Should have returned 401 (Invalid X-Auth-Token)")
553
        else:
554
            self.fail("Should have returned 401 (Invalid X-Auth-Token)")
555

    
556
    def test_invalid_token(self):
557
        """Test _invalid_token without pool"""
558
        self._invalid_token(False)
559

    
560
    def test_invalid_token_pool(self):
561
        """Test _invalid_token using pool"""
562
        self._invalid_token(True)
563

    
564
    # ----------------------------------
565
    # Test the response we get if we send invalid url
566
    def _invalid_url(self, pool):
567
        global token_1
568
        _mock_request([_request_ok])
569
        try:
570
            client = AstakosClient("https://example.com", use_pool=pool)
571
            client._call_astakos(token_1, "/astakos/api/misspelled")
572
        except NotFound:
573
            pass
574
        except Exception:
575
            self.fail("Should have returned 404 (Not Found)")
576
        else:
577
            self.fail("Should have returned 404 (Not Found)")
578

    
579
    def test_invalid_url(self):
580
        """Test _invalid_url without pool"""
581
        self._invalid_url(False)
582

    
583
    def test_invalid_url_pool(self):
584
        """Test _invalid_url using pool"""
585
        self._invalid_url(True)
586

    
587
    # ----------------------------------
588
    # Test the response we get if we use an unsupported scheme
589
    def _unsupported_scheme(self, pool):
590
        global token_1
591
        _mock_request([_request_ok])
592
        try:
593
            client = AstakosClient("ftp://example.com", use_pool=pool)
594
            client._call_astakos(token_1, astakosclient.API_AUTHENTICATE)
595
        except BadValue:
596
            pass
597
        except Exception:
598
            self.fail("Should have raise BadValue Exception")
599
        else:
600
            self.fail("Should have raise BadValue Exception")
601

    
602
    def test_unsupported_scheme(self):
603
        """Test _unsupported_scheme without pool"""
604
        self._unsupported_scheme(False)
605

    
606
    def test_unsupported_scheme_pool(self):
607
        """Test _unsupported_scheme using pool"""
608
        self._unsupported_scheme(True)
609

    
610
    # ----------------------------------
611
    # Test the response we get if we use http instead of https
612
    def _http_scheme(self, pool):
613
        global token_1
614
        _mock_request([_request_ok])
615
        try:
616
            client = AstakosClient("http://example.com", use_pool=pool)
617
            client._call_astakos(token_1, astakosclient.API_AUTHENTICATE)
618
        except AstakosClientException as err:
619
            if err.status != 302:
620
                self.fail("Should have returned 302 (Found)")
621
        else:
622
            self.fail("Should have returned 302 (Found)")
623

    
624
    def test_http_scheme(self):
625
        """Test _http_scheme without pool"""
626
        self._http_scheme(False)
627

    
628
    def test_http_scheme_pool(self):
629
        """Test _http_scheme using pool"""
630
        self._http_scheme(True)
631

    
632
    # ----------------------------------
633
    # Test the response we get if we use authenticate with POST
634
    def _post_authenticate(self, pool):
635
        global token_1
636
        _mock_request([_request_ok])
637
        try:
638
            client = AstakosClient("https://example.com", use_pool=pool)
639
            client._call_astakos(
640
                token_1, astakosclient.API_AUTHENTICATE, method="POST")
641
        except BadRequest:
642
            pass
643
        except Exception:
644
            self.fail("Should have returned 400 (Method not allowed)")
645
        else:
646
            self.fail("Should have returned 400 (Method not allowed)")
647

    
648
    def test_post_authenticate(self):
649
        """Test _post_authenticate without pool"""
650
        self._post_authenticate(False)
651

    
652
    def test_post_authenticate_pool(self):
653
        """Test _post_authenticate using pool"""
654
        self._post_authenticate(True)
655

    
656
    # ----------------------------------
657
    # Test the response if we request user_catalogs with GET
658
    def _get_user_catalogs(self, pool):
659
        global token_1
660
        _mock_request([_request_ok])
661
        try:
662
            client = AstakosClient("https://example.com", use_pool=pool)
663
            client._call_astakos(token_1, astakosclient.API_USERCATALOGS)
664
        except BadRequest:
665
            pass
666
        except Exception:
667
            self.fail("Should have returned 400 (Method not allowed)")
668
        else:
669
            self.fail("Should have returned 400 (Method not allowed)")
670

    
671
    def test_get_user_catalogs(self):
672
        """Test _get_user_catalogs without pool"""
673
        self._get_user_catalogs(False)
674

    
675
    def test_get_user_catalogs_pool(self):
676
        """Test _get_user_catalogs using pool"""
677
        self._get_user_catalogs(True)
678

    
679

    
680
class TestAuthenticate(unittest.TestCase):
681
    """Test cases for function getUserInfo"""
682

    
683
    # ----------------------------------
684
    # Test the response we get if we don't have internet access
685
    def test_offline(self):
686
        """Test offline after 3 retries"""
687
        global token_1
688
        _mock_request([_request_offline])
689
        try:
690
            client = AstakosClient("https://example.com", retry=3)
691
            client.get_user_info(token_1)
692
        except AstakosClientException:
693
            pass
694
        else:
695
            self.fail("Should have raised AstakosClientException exception")
696

    
697
    # ----------------------------------
698
    # Test the response we get for invalid token
699
    def _invalid_token(self, pool):
700
        token = "skaksaFlBl+fasFdaf24sx=="
701
        _mock_request([_request_ok])
702
        try:
703
            client = AstakosClient("https://example.com", use_pool=pool)
704
            client.get_user_info(token)
705
        except Unauthorized:
706
            pass
707
        except Exception:
708
            self.fail("Should have returned 401 (Invalid X-Auth-Token)")
709
        else:
710
            self.fail("Should have returned 401 (Invalid X-Auth-Token)")
711

    
712
    def test_invalid_token(self):
713
        """Test _invalid_token without pool"""
714
        self._invalid_token(False)
715

    
716
    def test_invalid_token_pool(self):
717
        """Test _invalid_token using pool"""
718
        self._invalid_token(True)
719

    
720
    #- ---------------------------------
721
    # Test response for user 1
722
    def _auth_user(self, token, user_info, usage, pool):
723
        _mock_request([_request_ok])
724
        try:
725
            client = AstakosClient("https://example.com", use_pool=pool)
726
            auth_info = client.get_user_info(token, usage=usage)
727
        except:
728
            self.fail("Shouldn't raise an Exception")
729
        self.assertEqual(user_info, auth_info)
730

    
731
    def test_auth_user_one(self):
732
        """Test _auth_user for User 1 without pool, without usage"""
733
        global token_1, user_1
734
        user_info = dict(user_1)
735
        del user_info['usage']
736
        self._auth_user(token_1, user_info, False, False)
737

    
738
    def test_auth_user_one_usage(self):
739
        """Test _auth_user for User 1 without pool, with usage"""
740
        global token_1, user_1
741
        self._auth_user(token_1, user_1, True, False)
742

    
743
    def test_auth_user_one_usage_pool(self):
744
        """Test _auth_user for User 1 using pool, with usage"""
745
        global token_1, user_1
746
        self._auth_user(token_1, user_1, True, True)
747

    
748
    def test_auth_user_two(self):
749
        """Test _auth_user for User 2 without pool, without usage"""
750
        global token_2, user_2
751
        user_info = dict(user_2)
752
        del user_info['usage']
753
        self._auth_user(token_2, user_info, False, False)
754

    
755
    def test_auth_user_two_usage(self):
756
        """Test _auth_user for User 2 without pool, with usage"""
757
        global token_2, user_2
758
        self._auth_user(token_2, user_2, True, False)
759

    
760
    def test_auth_user_two_usage_pool(self):
761
        """Test _auth_user for User 2 using pool, with usage"""
762
        global token_2, user_2
763
        self._auth_user(token_2, user_2, True, True)
764

    
765
    # ----------------------------------
766
    # Test retry functionality
767
    def test_offline_retry(self):
768
        """Test retry functionality for getUserInfo"""
769
        global token_1, user_1
770
        _mock_request([_request_offline, _request_offline, _request_ok])
771
        try:
772
            client = AstakosClient("https://example.com", retry=2)
773
            auth_info = client.get_user_info(token_1, usage=True)
774
        except:
775
            self.fail("Shouldn't raise an Exception")
776
        self.assertEqual(user_1, auth_info)
777

    
778

    
779
class TestDisplayNames(unittest.TestCase):
780
    """Test cases for functions getDisplayNames/getDisplayName"""
781

    
782
    # ----------------------------------
783
    # Test the response we get for invalid token
784
    def test_invalid_token(self):
785
        """Test the response we get for invalid token (without pool)"""
786
        global user_1
787
        token = "skaksaFlBl+fasFdaf24sx=="
788
        _mock_request([_request_ok])
789
        try:
790
            client = AstakosClient("https://example.com")
791
            client.get_usernames(token, [user_1['uuid']])
792
        except Unauthorized:
793
            pass
794
        except Exception:
795
            self.fail("Should have returned 401 (Invalid X-Auth-Token)")
796
        else:
797
            self.fail("Should have returned 401 (Invalid X-Auth-Token)")
798

    
799
    # ----------------------------------
800
    # Get Info for both users
801
    def test_usernames(self):
802
        """Test get_usernames with both users"""
803
        global token_1, user_1, user_2
804
        _mock_request([_request_ok])
805
        try:
806
            client = AstakosClient("https://example.com")
807
            catalog = client.get_usernames(
808
                token_1, [user_1['uuid'], user_2['uuid']])
809
        except:
810
            self.fail("Shouldn't raise an Exception")
811
        self.assertEqual(catalog[user_1['uuid']], user_1['username'])
812
        self.assertEqual(catalog[user_2['uuid']], user_2['username'])
813

    
814
    # ----------------------------------
815
    # Get info for user 1
816
    def test_username_user_one(self):
817
        """Test get_username for User One"""
818
        global token_2, user_1
819
        _mock_request([_request_offline, _request_ok])
820
        try:
821
            client = AstakosClient(
822
                "https://example.com", use_pool=True, retry=2)
823
            info = client.get_username(token_2, user_1['uuid'])
824
        except:
825
            self.fail("Shouldn't raise an Exception")
826
        self.assertEqual(info, user_1['username'])
827

    
828
    # ----------------------------------
829
    # Get info with wrong uuid
830
    def test_no_username(self):
831
        global token_1
832
        _mock_request([_request_ok])
833
        try:
834
            client = AstakosClient("https://example.com")
835
            client.get_username(token_1, "1234")
836
        except NoUserName:
837
            pass
838
        except:
839
            self.fail("Should have raised NoDisplayName exception")
840
        else:
841
            self.fail("Should have raised NoDisplayName exception")
842

    
843

    
844
class TestGetUUIDs(unittest.TestCase):
845
    """Test cases for functions getUUIDs/getUUID"""
846

    
847
    # ----------------------------------
848
    # Test the response we get for invalid token
849
    def test_invalid_token(self):
850
        """Test the response we get for invalid token (using pool)"""
851
        global user_1
852
        token = "skaksaFlBl+fasFdaf24sx=="
853
        _mock_request([_request_ok])
854
        try:
855
            client = AstakosClient("https://example.com")
856
            client.get_uuids(token, [user_1['username']])
857
        except Unauthorized:
858
            pass
859
        except Exception:
860
            self.fail("Should have returned 401 (Invalid X-Auth-Token)")
861
        else:
862
            self.fail("Should have returned 401 (Invalid X-Auth-Token)")
863

    
864
    # ----------------------------------
865
    # Get info for both users
866
    def test_uuids(self):
867
        """Test get_uuids with both users"""
868
        global token_1, user_1, user_2
869
        _mock_request([_request_ok])
870
        try:
871
            client = AstakosClient("https://example.com")
872
            catalog = client.get_uuids(
873
                token_1, [user_1['username'], user_2['username']])
874
        except:
875
            self.fail("Shouldn't raise an Exception")
876
        self.assertEqual(catalog[user_1['username']], user_1['uuid'])
877
        self.assertEqual(catalog[user_2['username']], user_2['uuid'])
878

    
879
    # ----------------------------------
880
    # Get uuid for user 2
881
    def test_get_uuid_user_two(self):
882
        """Test get_uuid for User Two"""
883
        global token_1, user_2
884
        _mock_request([_request_offline, _request_ok])
885
        try:
886
            client = AstakosClient("https://example.com", retry=1)
887
            info = client.get_uuid(token_2, user_1['username'])
888
        except:
889
            self.fail("Shouldn't raise an Exception")
890
        self.assertEqual(info, user_1['uuid'])
891

    
892
    # ----------------------------------
893
    # Get uuid with wrong username
894
    def test_no_uuid(self):
895
        global token_1
896
        _mock_request([_request_ok])
897
        try:
898
            client = AstakosClient("https://example.com")
899
            client.get_uuid(token_1, "1234")
900
        except NoUUID:
901
            pass
902
        except:
903
            self.fail("Should have raised NoUUID exception")
904
        else:
905
            self.fail("Should have raised NoUUID exception")
906

    
907

    
908
class TestResources(unittest.TestCase):
909
    """Test cases for function get_resources"""
910

    
911
    # ----------------------------------
912
    def test_get_resources(self):
913
        """Test function call of get_resources"""
914
        global resources
915
        _mock_request([_request_offline, _request_ok])
916
        try:
917
            client = AstakosClient("https://example.com", retry=1)
918
            result = client.get_resources()
919
        except Exception as err:
920
            self.fail("Shouldn't raise Exception %s" % err)
921
        self.assertEqual(resources, result)
922

    
923

    
924
class TestQuotas(unittest.TestCase):
925
    """Test cases for function get_quotas"""
926

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

    
939
    # -----------------------------------
940
    def test_get_quotas_unauthorized(self):
941
        """Test function call of get_quotas with wrong token"""
942
        global token_2
943
        _mock_request([_request_ok])
944
        try:
945
            client = AstakosClient("https://example.com")
946
            client.get_quotas(token_2)
947
        except Unauthorized:
948
            pass
949
        except Exception as err:
950
            self.fail("Shouldn't raise Exception %s" % err)
951
        else:
952
            self.fail("Should have raised Unauthorized Exception")
953

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

    
968

    
969
class TestCommissions(unittest.TestCase):
970
    """Test cases for quota commissions"""
971

    
972
    # ----------------------------------
973
    def test_issue_commission(self):
974
        """Test function call of issue_commission"""
975
        global token_1, commission_request, commission_successful_reqsponse
976
        _mock_request([_request_ok])
977
        try:
978
            client = AstakosClient("https://example.com")
979
            response = client.issue_commission(token_1, commission_request)
980
        except Exception as err:
981
            self.fail("Shouldn't raise Exception %s" % err)
982
        self.assertEqual(response, commission_successful_response['serial'])
983

    
984
    # ----------------------------------
985
    def test_issue_commission_quota_limit(self):
986
        """Test function call of issue_commission with limit exceeded"""
987
        global token_1, commission_request, commission_failure_response
988
        _mock_request([_request_ok])
989
        new_request = dict(commission_request)
990
        new_request['provisions'][1]['quantity'] = 520000000
991
        try:
992
            client = AstakosClient("https://example.com")
993
            client.issue_commission(token_1, new_request)
994
        except QuotaLimit:
995
            pass
996
        except Exception as err:
997
            self.fail("Shouldn't raise Exception %s" % err)
998
        else:
999
            self.fail("Should have raised QuotaLimit Exception")
1000

    
1001
    # ----------------------------------
1002
    def test_issue_one_commission(self):
1003
        """Test function call of issue_one_commission"""
1004
        global token_1, commission_successful_response
1005
        _mock_request([_request_ok])
1006
        try:
1007
            client = AstakosClient("https://example.com")
1008
            response = client.issue_one_commission(
1009
                token_1, "c02f315b-7d84-45bc-a383-552a3f97d2ad",
1010
                "system", {"cyclades.vm": 1, "cyclades.ram": 30000})
1011
        except Exception as err:
1012
            self.fail("Shouldn't have raised Exception %s" % err)
1013
        self.assertEqual(response, commission_successful_response['serial'])
1014

    
1015
    # ----------------------------------
1016
    def test_get_pending_commissions(self):
1017
        """Test function call of get_pending_commissions"""
1018
        global token_1, pending_commissions
1019
        _mock_request([_request_ok])
1020
        try:
1021
            client = AstakosClient("https://example.com")
1022
            response = client.get_pending_commissions(token_1)
1023
        except Exception as err:
1024
            self.fail("Shouldn't raise Exception %s" % err)
1025
        self.assertEqual(response, pending_commissions)
1026

    
1027
    # ----------------------------------
1028
    def test_get_commission_info(self):
1029
        """Test function call of get_commission_info"""
1030
        global token_1, commission_description
1031
        _mock_request([_request_ok])
1032
        try:
1033
            client = \
1034
                AstakosClient("https://example.com", use_pool=True, pool_size=2)
1035
            response = client.get_commission_info(token_1, 57)
1036
        except Exception as err:
1037
            self.fail("Shouldn't raise Exception %s" % err)
1038
        self.assertEqual(response, commission_description)
1039

    
1040
    # ----------------------------------
1041
    def test_get_commission_info_not_found(self):
1042
        """Test function call of get_commission_info with invalid serial"""
1043
        global token_1
1044
        _mock_request([_request_ok])
1045
        try:
1046
            client = AstakosClient("https://example.com")
1047
            client.get_commission_info(token_1, "57lala")
1048
        except NotFound:
1049
            pass
1050
        except Exception as err:
1051
            self.fail("Shouldn't raise Exception %s" % err)
1052
        else:
1053
            self.fail("Should have raised NotFound")
1054

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

    
1070
    # ----------------------------------
1071
    def test_commision_action(self):
1072
        """Test function call of commision_action with wrong action"""
1073
        global token_1
1074
        _mock_request([_request_ok])
1075
        try:
1076
            client = AstakosClient("https://example.com")
1077
            client.commission_action(token_1, 57, "lala")
1078
        except BadRequest:
1079
            pass
1080
        except Exception as err:
1081
            self.fail("Shouldn't raise Exception %s" % err)
1082
        else:
1083
            self.fail("Should have raised BadRequest")
1084

    
1085
    # ----------------------------------
1086
    def test_accept_commission(self):
1087
        """Test function call of accept_commission"""
1088
        global token_1
1089
        _mock_request([_request_ok])
1090
        try:
1091
            client = AstakosClient("https://example.com")
1092
            client.accept_commission(token_1, 57)
1093
        except Exception as err:
1094
            self.fail("Shouldn't raise Exception %s" % err)
1095

    
1096
    # ----------------------------------
1097
    def test_reject_commission(self):
1098
        """Test function call of reject_commission"""
1099
        global token_1
1100
        _mock_request([_request_ok])
1101
        try:
1102
            client = AstakosClient("https://example.com")
1103
            client.reject_commission(token_1, 57)
1104
        except Exception as err:
1105
            self.fail("Shouldn't raise Exception %s" % err)
1106

    
1107
    # ----------------------------------
1108
    def test_accept_commission_not_found(self):
1109
        """Test function call of accept_commission with wrong serial"""
1110
        global token_1
1111
        _mock_request([_request_ok])
1112
        try:
1113
            client = AstakosClient("https://example.com")
1114
            client.reject_commission(token_1, 20)
1115
        except NotFound:
1116
            pass
1117
        except Exception as err:
1118
            self.fail("Shouldn't raise Exception %s" % err)
1119
        else:
1120
            self.fail("Should have raised NotFound")
1121

    
1122
    # ----------------------------------
1123
    def test_resolve_commissions(self):
1124
        """Test function call of resolve_commissions"""
1125
        global token_1
1126
        _mock_request([_request_ok])
1127
        try:
1128
            client = AstakosClient("https://example.com")
1129
            result = client.resolve_commissions(token_1, [56, 57], [56, 58, 59])
1130
        except Exception as err:
1131
            self.fail("Shouldn't raise Exception %s" % err)
1132
        self.assertEqual(result, resolve_commissions_rep)
1133

    
1134

    
1135
class TestEndPoints(unittest.TestCase):
1136
    """Test cases for endpoints requests"""
1137

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

    
1150
    # ----------------------------------
1151
    def test_get_endpoints_wrong_token(self):
1152
        """Test function call of get_endpoints with wrong token"""
1153
        global token_2, endpoints
1154
        _mock_request([_request_ok])
1155
        try:
1156
            client = AstakosClient("https://example.com")
1157
            client.get_endpoints(token_2, marker=2, limit=100)
1158
        except Unauthorized:
1159
            pass
1160
        except Exception as err:
1161
            self.fail("Shouldn't raise Exception %s" % err)
1162
        else:
1163
            self.fail("Should have raised Unauthorized Exception")
1164

    
1165

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