Commit 3d711cd6 authored by Ilias Tsitsimpis's avatar Ilias Tsitsimpis

astakosclient: Fix astakosclient tests

parent 6e3e3c40
......@@ -44,9 +44,12 @@ import re
import sys
import socket
import simplejson
from mock import patch
from contextlib import contextmanager
import astakosclient
from astakosclient import AstakosClient
from astakosclient.utils import join_urls
from astakosclient.errors import \
AstakosClientException, Unauthorized, BadRequest, NotFound, \
NoUserName, NoUUID, BadValue, QuotaLimit
......@@ -62,9 +65,138 @@ except ImportError:
# --------------------------------------------------------------------
# Helper functions
auth_url = "https://example.org/identity/v2.0"
account_prefix = "/account_prefix"
ui_prefix = "/ui_prefix"
api_authenticate = join_urls(account_prefix, "authenticate")
api_usercatalogs = join_urls(account_prefix, "user_catalogs")
api_resources = join_urls(account_prefix, "resources")
api_quotas = join_urls(account_prefix, "quotas")
api_commissions = join_urls(account_prefix, "commissions")
# --------------------------------------
# Local users
token_1 = "skzleaFlBl+fasFdaf24sx"
user_1 = \
{"username": "user1@example.com",
"name": "Example User One",
"email": ["user1@example.com"],
"uuid": "73917abc-abcd-477e-a1f1-1763abcdefab"}
resources = {
"cyclades.ram": {
"unit": "bytes",
"description": "Virtual machine memory",
"service": "cyclades"}}
endpoints = {
"access": {
"serviceCatalog": [{
"endpoints": [{"SNF:uiURL": join_urls("https://example.org/",
ui_prefix),
"publicURL": join_urls("https://example.org/",
account_prefix),
"region": "default",
"versionId": "v1.0"}],
"name": "astakos_account",
"type": "account"}]
}
}
quotas = {
"system": {
"cyclades.ram": {
"pending": 0,
"limit": 1073741824,
"usage": 536870912},
"cyclades.vm": {
"pending": 0,
"limit": 2,
"usage": 2}},
"project:1": {
"cyclades.ram": {
"pending": 0,
"limit": 2147483648,
"usage": 2147483648},
"cyclades.vm": {
"pending": 1,
"limit": 5,
"usage": 2}}}
commission_request = {
"force": False,
"auto_accept": False,
"name": "my commission",
"provisions": [
{
"holder": "c02f315b-7d84-45bc-a383-552a3f97d2ad",
"source": "system",
"resource": "cyclades.vm",
"quantity": 1
},
{
"holder": "c02f315b-7d84-45bc-a383-552a3f97d2ad",
"source": "system",
"resource": "cyclades.ram",
"quantity": 30000
}]}
commission_successful_response = {"serial": 57}
commission_failure_response = {
"overLimit": {
"message": "a human-readable error message",
"code": 413,
"data": {
"provision": {
"holder": "c02f315b-7d84-45bc-a383-552a3f97d2ad",
"source": "system",
"resource": "cyclades.ram",
"quantity": 520000000},
"name": "NoCapacityError",
"limit": 600000000,
"usage": 180000000}}}
pending_commissions = [100, 200]
commission_description = {
"serial": 57,
"issue_time": "2013-04-08T10:19:15.0373+00:00",
"name": "a commission",
"provisions": [
{
"holder": "c02f315b-7d84-45bc-a383-552a3f97d2ad",
"source": "system",
"resource": "cyclades.vm",
"quantity": 1
},
{
"holder": "c02f315b-7d84-45bc-a383-552a3f97d2ad",
"source": "system",
"resource": "cyclades.ram",
"quantity": 536870912
}]}
resolve_commissions_req = {
"accept": [56, 57],
"reject": [56, 58, 59]}
resolve_commissions_rep = {
"accepted": [57],
"rejected": [59],
"failed": [
[56, {
"badRequest": {
"message": "cannot both accept and reject serial 56",
"code": 400}}],
[58, {
"itemNotFound": {
"message": "serial 58 does not exist",
"code": 404}}]]}
# ----------------------------
# This functions will be used as mocked requests
# These functions will be used as mocked requests
def _request_offline(conn, method, url, **kwargs):
"""This request behaves as we were offline"""
raise socket.gaierror
......@@ -74,9 +206,7 @@ def _request_status_302(conn, method, url, **kwargs):
"""This request returns 302"""
message = "FOUND"
status = 302
data = '<html>\r\n<head><title>302 Found</title></head>\r\n' \
'<body bgcolor="white">\r\n<center><h1>302 Found</h1></center>\r\n' \
'<hr><center>nginx/0.7.67</center>\r\n</body>\r\n</html>\r\n'
data = "302 Found"
return (message, data, status)
......@@ -84,10 +214,7 @@ def _request_status_404(conn, method, url, **kwargs):
"""This request returns 404"""
message = "Not Found"
status = 404
data = '<html><head><title>404 Not Found</title></head>' \
'<body><h1>Not Found</h1><p>The requested URL /foo was ' \
'not found on this server.</p><hr><address>Apache Server ' \
'at example.com Port 80</address></body></html>'
data = "404 Not Found"
return (message, data, status)
......@@ -117,29 +244,23 @@ def _request_status_400(conn, method, url, **kwargs):
def _request_ok(conn, method, url, **kwargs):
"""This request behaves like original Astakos does"""
if re.match('/?' + astakosclient.API_AUTHENTICATE, url) is not None:
print "here 1"
if api_authenticate == url:
return _req_authenticate(conn, method, url, **kwargs)
elif re.match('/?' + astakosclient.API_USERCATALOGS, url) is not None:
print "here 2"
elif api_usercatalogs == url:
return _req_catalogs(conn, method, url, **kwargs)
elif re.match('/?' + astakosclient.API_RESOURCES, url) is not None:
print "here 3"
elif api_resources == url:
return _req_resources(conn, method, url, **kwargs)
elif re.match('/?' + astakosclient.API_QUOTAS, url) is not None:
elif api_quotas == url:
return _req_quotas(conn, method, url, **kwargs)
elif re.match('/?' + astakosclient.API_COMMISSIONS, url) is not None:
elif url.startswith(api_commissions):
return _req_commission(conn, method, url, **kwargs)
elif re.match('/?' + astakosclient.API_TOKENS, url) is not None:
return _req_endpoints(conn, method, url, **kwargs)
else:
print "here 4"
return _request_status_404(conn, method, url, **kwargs)
def _req_authenticate(conn, method, url, **kwargs):
"""Check if user exists and return his profile"""
global user_1, user_2, token_1, token_2
global user_1, token_1
# Check input
if conn.__class__.__name__ != "HTTPSConnection":
......@@ -149,18 +270,11 @@ def _req_authenticate(conn, method, url, **kwargs):
token = kwargs['headers'].get('X-Auth-Token')
if token == token_1:
user = dict(user_1)
elif token == token_2:
user = dict(user_2)
return ("", simplejson.dumps(user), 200)
else:
# No user found
return _request_status_401(conn, method, url, **kwargs)
# Return
if "usage=1" not in url:
# Strip `usage' key from `user'
del user['usage']
return ("", simplejson.dumps(user), 200)
def _req_catalogs(conn, method, url, **kwargs):
"""Return user catalogs"""
......@@ -172,7 +286,7 @@ def _req_catalogs(conn, method, url, **kwargs):
if method != "POST":
return _request_status_400(conn, method, url, **kwargs)
token = kwargs['headers'].get('X-Auth-Token')
if token != token_1 and token != token_2:
if token != token_1:
return _request_status_401(conn, method, url, **kwargs)
# Return
......@@ -183,8 +297,6 @@ def _req_catalogs(conn, method, url, **kwargs):
catalogs = {}
if user_1['uuid'] in uuids:
catalogs[user_1['uuid']] = user_1['username']
if user_2['uuid'] in uuids:
catalogs[user_2['uuid']] = user_2['username']
return_catalog = {"displayname_catalog": {}, "uuid_catalog": catalogs}
elif 'displaynames' in body:
# Return displayname_catalog
......@@ -192,8 +304,6 @@ def _req_catalogs(conn, method, url, **kwargs):
catalogs = {}
if user_1['username'] in names:
catalogs[user_1['username']] = user_1['uuid']
if user_2['username'] in names:
catalogs[user_2['username']] = user_2['uuid']
return_catalog = {"displayname_catalog": catalogs, "uuid_catalog": {}}
else:
return_catalog = {"displayname_catalog": {}, "uuid_catalog": {}}
......@@ -247,7 +357,7 @@ def _req_commission(conn, method, url, **kwargs):
if 'body' not in kwargs:
return _request_status_400(conn, method, url, **kwargs)
body = simplejson.loads(unicode(kwargs['body']))
if re.match('/?'+astakosclient.API_COMMISSIONS+'$', url) is not None:
if re.match('/?'+api_commissions+'$', url) is not None:
# Issue Commission
# Check if we have enough resources to give
if body['provisions'][1]['quantity'] > 420000000:
......@@ -257,7 +367,7 @@ def _req_commission(conn, method, url, **kwargs):
("", simplejson.dumps(commission_successful_response), 200)
else:
# Issue commission action
serial = url.split('/')[4]
serial = url.split('/')[3]
if serial == "action":
# Resolve multiple actions
if body == resolve_commissions_req:
......@@ -275,12 +385,12 @@ def _req_commission(conn, method, url, **kwargs):
return ("", "", 200)
elif method == "GET":
if re.match('/?'+astakosclient.API_COMMISSIONS+'$', url) is not None:
if re.match('/?'+api_commissions+'$', url) is not None:
# Return pending commission
return ("", simplejson.dumps(pending_commissions), 200)
else:
# Return commissions's description
serial = re.sub('/?' + astakosclient.API_COMMISSIONS, '', url)[1:]
serial = re.sub('/?' + api_commissions, '', url)[1:]
if serial == str(57):
return ("", simplejson.dumps(commission_description), 200)
else:
......@@ -289,29 +399,6 @@ def _req_commission(conn, method, url, **kwargs):
return _request_status_400(conn, method, url, **kwargs)
def _req_endpoints(conn, method, url, **kwargs):
"""Request endpoints"""
global token_1, endpoints
# Check input
if conn.__class__.__name__ != "HTTPSConnection":
return _request_status_302(conn, method, url, **kwargs)
if method != "POST":
return _request_status_400(conn, method, url, **kwargs)
token_head = kwargs['headers'].get('X-Auth-Token')
if method != "POST":
return _request_status_400(conn, method, url, **kwargs)
body = simplejson.loads(kwargs['body'])
token_body = body['auth']['token']['id']
if token_head != token_body:
return _request_status_403(conn, method, url, **kwargs)
if token_body != token_1:
return _request_status_401(conn, method, url, **kwargs)
# Return
return ("", simplejson.dumps(user_info_endpoints), 200)
# ----------------------------
# Mock the actual _doRequest
def _mock_request(new_requests):
......@@ -335,196 +422,14 @@ def _mock_request(new_requests):
astakosclient._do_request = _mock
# ----------------------------
# Local users
token_1 = "skzleaFlBl+fasFdaf24sx"
user_1 = \
{"username": "user1@example.com",
"auth_token_created": 1359386939000,
"name": "Example User One",
"email": ["user1@example.com"],
"auth_token_expires": 1361978939000,
"id": 108,
"uuid": "73917abc-abcd-477e-a1f1-1763abcdefab",
"usage": [
{"currValue": 42949672960,
"display_name": "System Disk",
"name": "cyclades.disk"},
{"currValue": 4,
"display_name": "CPU",
"name": "cyclades.cpu"},
{"currValue": 4294967296,
"display_name": "RAM",
"name": "cyclades.ram"},
{"currValue": 3,
"display_name": "VM",
"name": "cyclades.vm"},
{"currValue": 0,
"display_name": "private network",
"name": "cyclades.network.private"},
{"currValue": 152,
"display_name": "Storage Space",
"name": "pithos+.diskspace"}]}
token_2 = "fasdfDSFdf98923DF+sdfk"
user_2 = \
{"username": "user2@example.com",
"auth_token_created": 1358386938997,
"name": "Example User Two",
"email": ["user1@example.com"],
"auth_token_expires": 1461998939000,
"id": 109,
"uuid": "73917bca-1234-5678-a1f1-1763abcdefab",
"usage": [
{"currValue": 68719476736,
"display_name": "System Disk",
"name": "cyclades.disk"},
{"currValue": 1,
"display_name": "CPU",
"name": "cyclades.cpu"},
{"currValue": 1073741824,
"display_name": "RAM",
"name": "cyclades.ram"},
{"currValue": 2,
"display_name": "VM",
"name": "cyclades.vm"},
{"currValue": 1,
"display_name": "private network",
"name": "cyclades.network.private"},
{"currValue": 2341634510,
"display_name": "Storage Space",
"name": "pithos+.diskspace"}]}
resources = {
"cyclades.vm": {
"unit": None,
"description": "Number of virtual machines",
"service": "cyclades"},
"cyclades.ram": {
"unit": "bytes",
"description": "Virtual machine memory",
"service": "cyclades"}}
user_info_endpoints = \
{'serviceCatalog': [
{'endpoints': [{
'SNF:uiURL': 'https://node1.example.com/ui/',
'adminURL': 'https://node1.example.com/v1',
'internalUrl': 'https://node1.example.com/v1',
'publicURL': 'https://node1.example.com/v1',
'region': 'cyclades'}],
'name': 'cyclades',
'type': 'compute'},
{'endpoints': [{
'SNF:uiURL': 'https://node2.example.com/ui/',
'adminURL': 'https://node2.example.com/v1',
'internalUrl': 'https://node2.example.com/v1',
'publicURL': 'https://node2.example.com/v1',
'region': 'pithos'}],
'name': 'pithos',
'type': 'storage'}],
'token': {
'expires': '2013-06-19T15:23:59.975572+00:00',
'id': token_1,
'tenant': {
'id': user_1,
'name': 'Firstname Lastname'}},
'user': {
'id': user_1,
'name': 'Firstname Lastname',
'roles': [{'id': 1, 'name': 'default'}],
'roles_links': []}}
quotas = {
"system": {
"cyclades.ram": {
"pending": 0,
"limit": 1073741824,
"usage": 536870912},
"cyclades.vm": {
"pending": 0,
"limit": 2,
"usage": 2}},
"project:1": {
"cyclades.ram": {
"pending": 0,
"limit": 2147483648,
"usage": 2147483648},
"cyclades.vm": {
"pending": 1,
"limit": 5,
"usage": 2}}}
commission_request = {
"force": False,
"auto_accept": False,
"name": "my commission",
"provisions": [
{
"holder": "c02f315b-7d84-45bc-a383-552a3f97d2ad",
"source": "system",
"resource": "cyclades.vm",
"quantity": 1
},
{
"holder": "c02f315b-7d84-45bc-a383-552a3f97d2ad",
"source": "system",
"resource": "cyclades.ram",
"quantity": 30000
}]}
commission_successful_response = {"serial": 57}
commission_failure_response = {
"overLimit": {
"message": "a human-readable error message",
"code": 413,
"data": {
"provision": {
"holder": "c02f315b-7d84-45bc-a383-552a3f97d2ad",
"source": "system",
"resource": "cyclades.ram",
"quantity": 520000000},
"name": "NoCapacityError",
"limit": 600000000,
"usage": 180000000}}}
pending_commissions = [100, 200]
commission_description = {
"serial": 57,
"issue_time": "2013-04-08T10:19:15.0373+00:00",
"name": "a commission",
"provisions": [
{
"holder": "c02f315b-7d84-45bc-a383-552a3f97d2ad",
"source": "system",
"resource": "cyclades.vm",
"quantity": 1
},
{
"holder": "c02f315b-7d84-45bc-a383-552a3f97d2ad",
"source": "system",
"resource": "cyclades.ram",
"quantity": 536870912
}]}
resolve_commissions_req = {
"accept": [56, 57],
"reject": [56, 58, 59]}
resolve_commissions_rep = {
"accepted": [57],
"rejected": [59],
"failed": [
[56, {
"badRequest": {
"message": "cannot both accept and reject serial 56",
"code": 400}}],
[58, {
"itemNotFound": {
"message": "serial 58 does not exist",
"code": 404}}]]}
# --------------------------------------
# Mock the get_endpoints method
@contextmanager
def patch_astakosclient(new_requests):
_mock_request(new_requests)
with patch('astakosclient.AstakosClient.get_endpoints') as patcher:
patcher.return_value = endpoints
yield
# --------------------------------------------------------------------
......@@ -536,11 +441,11 @@ class TestCallAstakos(unittest.TestCase):
# ----------------------------------
# Test the response we get if we don't have internet access
def _offline(self, pool):
global token_1
global token_1, auth_url
_mock_request([_request_offline])
try:
client = AstakosClient("https://example.com", use_pool=pool)
client._call_astakos(token_1, astakosclient.API_AUTHENTICATE)
client = AstakosClient(token_1, auth_url, use_pool=pool)
client._call_astakos("offline")
except AstakosClientException:
pass
else:
......@@ -557,11 +462,12 @@ class TestCallAstakos(unittest.TestCase):
# ----------------------------------
# Test the response we get if we send invalid token
def _invalid_token(self, pool):
global auth_url
token = "skaksaFlBl+fasFdaf24sx"
_mock_request([_request_ok])
try:
client = AstakosClient("https://example.com", use_pool=pool)
client._call_astakos(token, astakosclient.API_AUTHENTICATE)
with patch_astakosclient([_request_ok]):
client = AstakosClient(token, auth_url, use_pool=pool)
client.get_user_info()
except Unauthorized:
pass
except Exception:
......@@ -580,15 +486,15 @@ class TestCallAstakos(unittest.TestCase):
# ----------------------------------
# Test the response we get if we send invalid url
def _invalid_url(self, pool):
global token_1
_mock_request([_request_ok])
global token_1, auth_url
try:
client = AstakosClient("https://example.com", use_pool=pool)
client._call_astakos(token_1, "/astakos/api/misspelled")
with patch_astakosclient([_request_ok]):
client = AstakosClient(token_1, auth_url, use_pool=pool)
client._call_astakos("/astakos/api/misspelled")
except NotFound:
pass
except Exception:
self.fail("Should have returned 404 (Not Found)")
except Exception, e:
self.fail("Got \"%s\" instead of 404" % e)
else:
self.fail("Should have returned 404 (Not Found)")
......@@ -603,11 +509,12 @@ class TestCallAstakos(unittest.TestCase):
# ----------------------------------
# Test the response we get if we use an unsupported scheme
def _unsupported_scheme(self, pool):
global token_1
_mock_request([_request_ok])
global token_1, auth_url
try:
client = AstakosClient("ftp://example.com", use_pool=pool)
client._call_astakos(token_1, astakosclient.API_AUTHENTICATE)
with patch_astakosclient([_request_ok]):
client = AstakosClient(token_1, "ftp://example.com",
use_pool=pool)
client.get_user_info()
except BadValue:
pass
except Exception:
......@@ -627,10 +534,11 @@ class TestCallAstakos(unittest.TestCase):
# Test the response we get if we use http instead of https
def _http_scheme(self, pool):
global token_1
_mock_request([_request_ok])
http_auth_url = "http://example.org/identity/v2.0"
try:
client = AstakosClient("http://example.com", use_pool=pool)
client._call_astakos(token_1, astakosclient.API_AUTHENTICATE)
with patch_astakosclient([_request_ok]):
client = AstakosClient(token_1, http_auth_url, use_pool=pool)
client.get_user_info()
except AstakosClientException as err:
if err.status != 302:
self.fail("Should have returned 302 (Found)")
......@@ -648,12 +556,11 @@ class TestCallAstakos(unittest.TestCase):
# ----------------------------------
# Test the response we get if we use authenticate with POST
def _post_authenticate(self, pool):
global token_1
_mock_request([_request_ok])
global token_1, auth_url
try:
client = AstakosClient("https://example.com", use_pool=pool)
client._call_astakos(
token_1, astakosclient.API_AUTHENTICATE, method="POST")
with patch_astakosclient([_request_ok]):
client = AstakosClient(token_1, auth_url, use_pool=pool)
client._call_astakos(api_authenticate, method="POST")
except BadRequest:
pass
except Exception:
......@@ -672,11 +579,11 @@ class TestCallAstakos(unittest.TestCase):
# ----------------------------------
# Test the response if we request user_catalogs with GET
def _get_user_catalogs(self, pool):
global token_1
_mock_request([_request_ok])
global token_1, auth_url, api_usercatalogs
try:
client = AstakosClient("https://example.com", use_pool=pool)
client._call_astakos(token_1, astakosclient.API_USERCATALOGS)
with patch_astakosclient([_request_ok]):
client = AstakosClient(token_1, auth_url, use_pool=pool)
client._call_astakos(api_usercatalogs)
except BadRequest:
pass
except Exception:
......@@ -700,11 +607,11 @@ class TestAuthenticate(unittest.TestCase):
# Test the response we get if we don't have internet access
def test_offline(self):
"""Test offline after 3 retries"""
global token_1
_mock_request([_request_offline])
global token_1, auth_url
try:
client = AstakosClient("https://example.com", retry=3)
client.get_user_info(token_1)
with patch_astakosclient([_request_offline]):
client = AstakosClient(token_1, auth_url, retry=3)
client.get_user_info()
except AstakosClientException:
pass
else:
......@@ -713,11 +620,12 @@ class TestAuthenticate(unittest.TestCase):
# ----------------------------------
# Test the response we get for invalid token
def _invalid_token(self, pool):
global auth_url
token = "skaksaFlBl+fasFdaf24sx"
_mock_request([_request_ok])
try:
client = AstakosClient("https://example.com", use_pool=pool)
client.get_user_info(token)
with patch_astakosclient([_request_ok]):
client = AstakosClient(token, auth_url, use_pool=pool)
client.get_user_info()
except Unauthorized:
pass
except Exception:
......@@ -734,61 +642,41 @@ class TestAuthenticate(unittest.TestCase):
self._invalid_token(True)
#- ---------------------------------
# Test response for user 1
def _auth_user(self, token, user_info, usage, pool):
_mock_request([_request_ok])
# Test response for user
def _auth_user(self, token, user_info, pool):
global auth_url