Skip to content

Commit 3f7e8b9

Browse files
author
Patryk Gałczyński
committed
first piece of tests, fix compare sign, minor fixes
1 parent 56c2447 commit 3f7e8b9

File tree

6 files changed

+177
-15
lines changed

6 files changed

+177
-15
lines changed

src/asyncopenstackclient/auth.py

+15-10
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ class AuthModel:
77
async def authenticate(self):
88
raise NotImplementedError()
99

10+
def verify(self):
11+
return self.token_expires_at - time() < 3600
12+
1013

1114
class AuthPassword:
1215

@@ -42,23 +45,25 @@ def __init__(self, auth_url, username, password, project_name, user_domain_name,
4245
}
4346
}
4447

45-
def verify(self):
46-
return self.token_expires_at - time() < 3600
47-
4848
async def get_token(self):
4949
async with aiohttp.ClientSession() as session:
5050
async with session.post(self.auth_url, json=self.auth_dict, headers=self.headers) as response:
5151
result = await response.json()
5252
return (
53-
response.headers['X-Subject-Token'], mktime(strptime(result['token']['expires_at'],
54-
"%Y-%m-%dT%H:%M:%S.000000Z")), result['token']['catalog']
53+
response.headers['X-Subject-Token'],
54+
mktime(strptime(result['token']['expires_at'], "%Y-%m-%dT%H:%M:%S.000000Z")),
55+
result['token']['catalog']
5556
)
5657

57-
async def get_ednpoint_url(self, endpoint_name):
58-
for endpoint in self.endpoints:
59-
if endpoint['name'] == endpoint_name:
60-
return endpoint['endpoints'][0]['url']
58+
def get_endpoint_url(self, endpoint_name, prefered_interface="public"):
59+
try:
60+
for endpoint in self.endpoints:
61+
if endpoint['name'] == endpoint_name:
62+
return [url['url'] for url in endpoint['endpoints'] if url['interface'] == prefered_interface][0]
63+
except IndexError:
64+
raise ValueError("could not find desired interface")
65+
raise ValueError("endpoint %s not found" % endpoint_name)
6166

6267
async def authenticate(self):
63-
if self.token is None or self.token_expires_at - time() > 0:
68+
if self.token is None or self.token_expires_at - time() < 0:
6469
self.token, self.token_expires_at, self.endpoints = await self.get_token()

src/asyncopenstackclient/client.py

+3-5
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
1-
import logging
21
from simple_rest_client.api import API
32
from simple_rest_client.resource import AsyncResource
43

54

65
class Client:
76

87
def __init__(self, api_name, resources, api_version=None, session=None):
9-
self.api_version = api_version or '2.26'
8+
self.api_version = api_version
109
self.api_name = api_name
1110
self.resources = resources
1211
self.session = session
1312
if self.session is None:
14-
logging.error("provided session object is None, probably auth error?")
15-
raise AttributeError()
13+
raise AttributeError("provided session object is None, probably auth error?")
1614

1715
async def init_api(self):
1816
await self.get_credentials()
@@ -26,4 +24,4 @@ async def init_api(self):
2624

2725
async def get_credentials(self):
2826
await self.session.authenticate()
29-
self.api_url = await self.session.get_ednpoint_url(self.api_name)
27+
self.api_url = await self.session.get_endpoint_url(self.api_name)

test-requirements.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
aiounittest
2+
aioresponses

tests/__init__.py

Whitespace-only changes.

tests/test_auth.py

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
from unittest.mock import patch
2+
from aiounittest import AsyncTestCase, futurized
3+
from aioresponses import aioresponses
4+
from asyncopenstackclient import AuthPassword
5+
6+
7+
class TestAuth(AsyncTestCase):
8+
9+
def setUp(self):
10+
self.auth_args = ('auth_url', 'username', 'password', 'mock_project_name',
11+
'mock_user_domain_name', 'mock_project_domain_name')
12+
self.auth = AuthPassword(*self.auth_args)
13+
14+
def tearDown(self):
15+
patch.stopall()
16+
17+
async def test_create_object(self):
18+
self.assertTrue(self.auth.auth_url.endswith("/auth/tokens"))
19+
self.assertTrue('Content-Type' in self.auth.headers)
20+
21+
async def test_get_token(self):
22+
body = {
23+
"token": {
24+
"catalog": {
25+
"endpoints": [
26+
{"name": "mock_endpoint", "endpoints": [{"url": "mock_url", "interface": "public"}]}
27+
]
28+
},
29+
"expires_at": "1970-01-01T01:00:00.000000Z"
30+
}
31+
}
32+
headers = {
33+
"Vary": "X-Auth-Token",
34+
"x-openstack-request-id": "1234",
35+
"Content-Type": "application/json",
36+
"X-Subject-Token": "gAAAAABao"
37+
38+
}
39+
with aioresponses() as req:
40+
req.post('auth_url/auth/tokens', payload=body, headers=headers)
41+
42+
res = await self.auth.get_token()
43+
44+
self.assertEqual(res, (headers["X-Subject-Token"], 0, body["token"]["catalog"]))
45+
46+
def test_get_endpoint_url_existing_endpoint(self):
47+
self.auth.endpoints = [
48+
{"name": "mock_endpoint", "endpoints": [{"url": "mock_url", "interface": "public"}]}
49+
]
50+
51+
endpoint_url = self.auth.get_endpoint_url("mock_endpoint")
52+
53+
self.assertEqual(endpoint_url, "mock_url")
54+
55+
def test_get_endpoint_url_bad_endpoint_name(self):
56+
self.auth.endpoints = [
57+
{"name": "mock_endpoint", "endpoints": [{"url": "mock_url", "interface": "public"}]}
58+
]
59+
with self.assertRaises(ValueError):
60+
self.auth.get_endpoint_url("none_existing_endpoint")
61+
62+
def test_get_endpoint_url_bad_interface(self):
63+
self.auth.endpoints = [
64+
{"name": "mock_endpoint", "endpoints": [{"url": "mock_url", "interface": "public"}]}
65+
]
66+
with self.assertRaises(ValueError):
67+
self.auth.get_endpoint_url("mock_endpoint", prefered_interface="not_existing_interface")
68+
69+
async def test_authenticate_first_time(self):
70+
71+
mock_get_token_results = [
72+
futurized(('mock-token1', 1000, 'whatever')),
73+
futurized(('mock-token2', 1000, 'whatever')),
74+
]
75+
76+
# time is gonna be called 2 times becouse of Pythons lazy evaluation
77+
mock_time_results = [
78+
900,
79+
1100
80+
]
81+
82+
patch('asyncopenstackclient.auth.AuthPassword.get_token', side_effect=mock_get_token_results).start()
83+
patch('asyncopenstackclient.auth.time', side_effect=mock_time_results).start()
84+
85+
# first time token should be None and get_token shall be called
86+
await self.auth.authenticate()
87+
self.assertEqual(self.auth.token, 'mock-token1')
88+
89+
# second time, token is not None and current time is before token expiration, no change
90+
await self.auth.authenticate()
91+
self.assertEqual(self.auth.token, 'mock-token1')
92+
93+
# third time, token expires and should be renewed
94+
await self.auth.authenticate()
95+
self.assertEqual(self.auth.token, 'mock-token2')

tests/test_client.py

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
from unittest.mock import patch, Mock, call, ANY
2+
from aiounittest import AsyncTestCase, futurized
3+
from asyncopenstackclient import Client
4+
5+
6+
class TestClient(AsyncTestCase):
7+
8+
def setUp(self):
9+
pass
10+
11+
def tearDown(self):
12+
patch.stopall()
13+
14+
def test_create_object(self):
15+
api_name = 'mock_api'
16+
resources = 'mock_resource'
17+
api_version = 'mock_api_version'
18+
session = 'mock_session'
19+
client = Client(api_name, resources, api_version=api_version, session=session)
20+
21+
self.assertEqual(client.api_name, api_name)
22+
self.assertEqual(client.api_version, api_version)
23+
self.assertEqual(client.resources, resources)
24+
self.assertEqual(client.session, session)
25+
26+
def test_create_object_bad_session(self):
27+
with self.assertRaises(AttributeError):
28+
Client('mock_name', 'mock_version', session=None)
29+
30+
async def test_get_credentials(self):
31+
session = Mock()
32+
session.authenticate.return_value = futurized(None)
33+
session.get_endpoint_url.return_value = futurized("mock_url")
34+
35+
client = Client("mock_name", "mock_version", session=session)
36+
await client.get_credentials()
37+
38+
self.assertEqual(client.api_url, "mock_url")
39+
40+
async def test_init_api(self):
41+
session = Mock()
42+
session.authenticate.return_value = futurized(None)
43+
session.get_endpoint_url.return_value = futurized("mock_url")
44+
session.token = 'mock_token'
45+
mock_api = Mock()
46+
47+
patch('asyncopenstackclient.client.API', new_callable=mock_api).start()
48+
49+
client = Client("mock_name", ['mock', 'resource', 'list'], session=session)
50+
await client.init_api()
51+
52+
print(mock_api.mock_calls)
53+
mock_api().assert_called_once_with(
54+
api_root_url="mock_url",
55+
headers={"X-Auth-Token": 'mock_token'},
56+
json_encode_body=True
57+
)
58+
59+
mock_api()().add_resource.assert_has_calls([
60+
call(resource_name='mock', resource_class=ANY),
61+
call(resource_name='resource', resource_class=ANY),
62+
call(resource_name='list', resource_class=ANY),
63+
])

0 commit comments

Comments
 (0)