Browse Source

Refactor: Improve testing

merge-requests/1/head
Drew Short 5 years ago
parent
commit
8f604f60f9
  1. 7
      server/.gitignore
  2. 2
      server/Dockerfile
  3. 2
      server/Pipfile
  4. 4
      server/Pipfile.lock
  5. 5
      server/corvus/api/authentication_api.py
  6. 9
      server/corvus/api/model.py
  7. 150
      server/tests/api/test_authentication_api.py
  8. 19
      server/tests/api/test_health_api.py
  9. 281
      server/tests/api/test_user_api.py
  10. 6
      server/tests/conftest.py
  11. 38
      server/tests/service/test_authentication_service.py

7
server/.gitignore

@ -0,0 +1,7 @@
.idea/
.mypy_cache
.pytest_cache
.*_credentials
.coverage
*.iml

2
server/Dockerfile

@ -1,4 +1,4 @@
FROM python:3.6-slim-stretch
FROM python:3.7-slim-stretch
MAINTAINER Drew Short <warrick@sothr.com>
ENV CORVUS_APP_DIRECTORY /opt/corvus

2
server/Pipfile

@ -27,4 +27,4 @@ sphinxcontrib-httpdomain = ">=1.7,<1.8"
sphinx-jsondomain = "*"
[requires]
python_version = "3.6"
python_version = "3.7"

4
server/Pipfile.lock

@ -1,11 +1,11 @@
{
"_meta": {
"hash": {
"sha256": "d7520d408998ef148cfa4cd8138e44f093e5604c75938e27dde748c17afa11ee"
"sha256": "79477dccc0014acea82818b6431dccece7c6ae8f6543c3f9ac1480283fc160ec"
},
"pipfile-spec": 6,
"requires": {
"python_version": "3.6"
"python_version": "3.7"
},
"sources": [
{

5
server/corvus/api/authentication_api.py

@ -73,8 +73,9 @@ def get_tokens() -> APIResponse:
"""
page, per_page = get_pagination_params(request.args)
user_token_page = user_token_service.find_by_user(g.user, page, per_page)
if user_token_page is not None:
return APIResponse(APIPage.from_page(user_token_page), 200)
api_page = APIPage.from_page(user_token_page)
if api_page is not None:
return APIResponse(api_page, 200)
return abort(404)

9
server/corvus/api/model.py

@ -66,7 +66,7 @@ class APIPage(BaseAPIMessage):
self.page = page
self.count = len(items)
self.total_count = total_count
self.last_page = last_page
self.last_page = last_page if last_page > 0 else page
self.items = items
def to_dict(self) -> Dict[str, Any]:
@ -79,7 +79,12 @@ class APIPage(BaseAPIMessage):
'items': self.items
}
def is_empty(self) -> bool:
"""Check if the page is empty."""
return self.count == 0 and self.total_count == 0
@staticmethod
def from_page(page: Pagination) -> 'APIPage':
"""Create an APIPage from a Pagination object."""
return APIPage(page.page, page.total, page.pages, page.items)
page = APIPage(page.page, page.total, page.pages, page.items)
return page if not page.is_empty() else None

150
server/tests/api/test_authentication_api.py

@ -1,18 +1,25 @@
from datetime import timedelta
import rfc3339
from flask import json
from flask.testing import FlaskClient
from tests.conftest import AuthActions
def test_login_happy_path(auth: AuthActions):
result = auth.login()
assert result.status_code == 200
assert result.json['token'] is not None and len(result.json['token']) > 0
with auth as result:
assert result.status_code == 200
assert result.json[
'token'] is not None and len(result.json['token']) > 0
def test_bump_happy_path(auth: AuthActions):
auth.login()
result = auth.bump()
assert result.status_code == 200
assert (result.json['lastLoginTime'] is not None
and len(result.json['lastLoginTime']) > 0)
with auth:
result = auth.bump()
assert result.status_code == 200
assert (result.json['lastLoginTime'] is not None
and len(result.json['lastLoginTime']) > 0)
def test_logout_happy_path(auth: AuthActions):
@ -20,3 +27,130 @@ def test_logout_happy_path(auth: AuthActions):
result = auth.logout()
assert result.status_code == 200
assert result.json['success']
def test_get_tokens_no_tokens(auth: AuthActions, client: FlaskClient):
auth_header = auth.get_authorization_header_basic()
result = client.get(
'/auth/token',
headers={
auth_header[0]: auth_header[1]
})
assert 404 == result.status_code
assert result.json is not None
def test_get_tokens(auth: AuthActions, client: FlaskClient):
with auth:
auth_header = auth.get_authorization_header_basic()
result = client.get(
'/auth/token',
headers={
auth_header[0]: auth_header[1]
})
assert 200 == result.status_code
assert result.json is not None
assert result.json['page'] == 1
assert result.json['lastPage'] == 1
assert result.json['count'] == 1
assert result.json['totalCount'] == 1
assert result.json['items'][0]['token'] == auth.token
def test_get_nonexistant_token(auth: AuthActions, client: FlaskClient):
auth_header = auth.get_authorization_header_basic()
result = client.get(
'/auth/token/not-a-token',
headers={
auth_header[0]: auth_header[1]
})
assert 404 == result.status_code
assert result.json is not None
def test_create_get_delete_token(auth: AuthActions, client: FlaskClient):
auth_header = auth.get_authorization_header_basic()
result = client.post(
'/auth/token',
headers={
auth_header[0]: auth_header[1],
'Content-Type': 'application/json'
},
data=json.dumps({
'note': 'test note',
'enabled': False
}))
assert 200 == result.status_code
assert result.json is not None
assert result.json['token'] is not None
assert result.json['note'] == 'test note'
assert not result.json['enabled']
assert not result.json['isValid']
auth_token = result.json['token']
result = client.get(
'/auth/token/%s' % auth_token,
headers={
auth_header[0]: auth_header[1]
})
assert 200 == result.status_code
assert result.json is not None
assert result.json['token'] == auth_token
result = client.delete(
'/auth/token/%s' % auth_token,
headers={
auth_header[0]: auth_header[1]
})
assert 200 == result.status_code
assert result.json is not None
assert 'message' not in result.json
assert result.json['success']
def test_create_get_delete_expired_token(
auth: AuthActions, client: FlaskClient):
auth_header = auth.get_authorization_header_basic()
result = client.post(
'/auth/token',
headers={
auth_header[0]: auth_header[1],
'Content-Type': 'application/json'
},
data=json.dumps({
'note': 'test note',
'expirationTime': rfc3339.format(
rfc3339.datetime.now() - timedelta(days=1))
}))
assert 200 == result.status_code
assert result.json is not None
assert result.json['token'] is not None
assert result.json['note'] == 'test note'
assert not result.json['isValid']
auth_token = result.json['token']
result = client.get(
'/auth/token/%s' % auth_token,
headers={
auth_header[0]: auth_header[1]
})
assert 200 == result.status_code
assert result.json is not None
assert result.json['token'] == auth_token
result = client.delete(
'/auth/token/%s' % auth_token,
headers={
auth_header[0]: auth_header[1]
})
assert 200 == result.status_code
assert result.json is not None
assert 'message' not in result.json
assert result.json['success']
def test_delete_nonexistant_token(auth: AuthActions, client: FlaskClient):
auth_header = auth.get_authorization_header_basic()
result = client.delete(
'/auth/token/not-a-token',
headers={
auth_header[0]: auth_header[1]
})
assert 404 == result.status_code
assert result.json is not None

19
server/tests/api/test_health_api.py

@ -0,0 +1,19 @@
from datetime import datetime
from flask.testing import FlaskClient
from tests.conftest import AuthActions
def test_get_health_happy_path(auth: AuthActions, client: FlaskClient):
with auth:
auth_header = auth.get_authorization_header_token()
result = client.get(
'/health',
headers={
auth_header[0]: auth_header[1]
})
assert 200 == result.status_code
assert result.json is not None
assert result.json['message'] == 'Service is healthy'
assert result.json['success']

281
server/tests/api/test_user_api.py

@ -8,159 +8,172 @@ from tests.conftest import AuthActions
def test_get_users_happy_path(auth: AuthActions, client: FlaskClient):
auth.login()
auth_header = auth.get_authorization_header_token()
result = client.get(
'/user',
headers={
auth_header[0]: auth_header[1]
})
assert 200 == result.status_code
assert result.json is not None
assert result.json['page'] == 1
assert result.json['lastPage'] == 1
assert result.json['count'] == 1
assert result.json['totalCount'] == 1
assert result.json['items'][0]['name'] == auth.username
with auth:
auth_header = auth.get_authorization_header_token()
result = client.get(
'/user',
headers={
auth_header[0]: auth_header[1]
})
assert 200 == result.status_code
assert result.json is not None
assert result.json['page'] == 1
assert result.json['lastPage'] == 1
assert result.json['count'] == 1
assert result.json['totalCount'] == 1
assert result.json['items'][0]['name'] == auth.username
def test_get_users_nonexistent_page(auth: AuthActions, client: FlaskClient):
auth.login()
auth_header = auth.get_authorization_header_token()
result = client.get(
'/user?page=2',
headers={
auth_header[0]: auth_header[1]
})
assert 404 == result.status_code
assert result.json is not None
with auth:
auth_header = auth.get_authorization_header_token()
result = client.get(
'/user?page=2',
headers={
auth_header[0]: auth_header[1]
})
assert 404 == result.status_code
assert result.json is not None
def test_get_users_bad_page_parameters(auth: AuthActions, client: FlaskClient):
with auth:
auth_header = auth.get_authorization_header_token()
result = client.get(
'/user?page=a',
headers={
auth_header[0]: auth_header[1]
})
assert 400 == result.status_code
assert result.json is not None
def test_get_user_happy_path(auth: AuthActions, client: FlaskClient):
auth.login()
auth_header = auth.get_authorization_header_token()
result = client.get(
'/user/{}'.format(client.application.config['test_username']),
headers={
auth_header[0]: auth_header[1]
})
assert 200 == result.status_code
assert result.json is not None
assert result.json['name'] == client.application.config['test_username']
with auth:
auth_header = auth.get_authorization_header_token()
result = client.get(
'/user/{}'.format(client.application.config['test_username']),
headers={
auth_header[0]: auth_header[1]
})
assert 200 == result.status_code
assert result.json is not None
assert result.json['name'] == client.application.config[
'test_username']
def test_patch_user_happy_path(auth: AuthActions, client: FlaskClient):
auth.login()
auth_header = auth.get_authorization_header_token()
last_login_time = rfc3339.format(datetime.now())
user = client.get(
'/user/{}'.format(client.application.config['test_username']),
headers={
auth_header[0]: auth_header[1]
})
patched_user = client.patch(
'/user/{}'.format(client.application.config['test_username']),
data=json.dumps({
'version': user.json['version'],
'lastLoginTime': last_login_time
}),
headers={
auth_header[0]: auth_header[1],
'Content-Type': 'application/json'
})
assert 200 == patched_user.status_code
assert patched_user.json['version'] == user.json['version'] + 1
assert patched_user.json['lastLoginTime'] == last_login_time
with auth:
auth_header = auth.get_authorization_header_token()
last_login_time = rfc3339.format(datetime.now())
user = client.get(
'/user/{}'.format(client.application.config['test_username']),
headers={
auth_header[0]: auth_header[1]
})
patched_user = client.patch(
'/user/{}'.format(client.application.config['test_username']),
data=json.dumps({
'version': user.json['version'],
'lastLoginTime': last_login_time
}),
headers={
auth_header[0]: auth_header[1],
'Content-Type': 'application/json'
})
assert 200 == patched_user.status_code
assert patched_user.json['version'] == user.json['version'] + 1
assert patched_user.json['lastLoginTime'] == last_login_time
def test_register_user_happy_path(auth: AuthActions, client: FlaskClient):
auth.login()
auth_header = auth.get_authorization_header_token()
result = client.post(
'/user',
data=json.dumps({
'name': 'test_registered_user'
}),
headers={
auth_header[0]: auth_header[1],
'Content-Type': 'application/json'
})
assert 200 == result.status_code
assert result.json is not None
assert result.json['name'] == 'test_registered_user'
with auth:
auth_header = auth.get_authorization_header_token()
result = client.post(
'/user',
data=json.dumps({
'name': 'test_registered_user'
}),
headers={
auth_header[0]: auth_header[1],
'Content-Type': 'application/json'
})
assert 200 == result.status_code
assert result.json is not None
assert result.json['name'] == 'test_registered_user'
def test_register_user_invalid_password(
auth: AuthActions, client: FlaskClient):
auth.login()
auth_header = auth.get_authorization_header_token()
result = client.post(
'/user',
data=json.dumps({
'name': 'test_registered_user',
'password': ''
}),
headers={
auth_header[0]: auth_header[1],
'Content-Type': 'application/json'
})
assert 400 == result.status_code
assert result.json is not None
assert 'message' in result.json
with auth:
auth_header = auth.get_authorization_header_token()
result = client.post(
'/user',
data=json.dumps({
'name': 'test_registered_user',
'password': ''
}),
headers={
auth_header[0]: auth_header[1],
'Content-Type': 'application/json'
})
assert 400 == result.status_code
assert result.json is not None
assert 'message' in result.json
def test_register_user_twice_failure(auth: AuthActions, client: FlaskClient):
auth.login()
auth_header = auth.get_authorization_header_token()
result1 = client.post(
'/user',
data=json.dumps({
'name': 'test_registered_user'
}),
headers={
auth_header[0]: auth_header[1],
'Content-Type': 'application/json'
})
result2 = client.post(
'/user',
data=json.dumps({
'name': 'test_registered_user'
}),
headers={
auth_header[0]: auth_header[1],
'Content-Type': 'application/json'
})
assert 200 == result1.status_code
assert result1.json is not None
assert result1.json['name'] == 'test_registered_user'
assert 400 == result2.status_code
assert result2.json is not None
assert result2.json['message'] == 'User name is already taken.'
with auth:
auth_header = auth.get_authorization_header_token()
result1 = client.post(
'/user',
data=json.dumps({
'name': 'test_registered_user'
}),
headers={
auth_header[0]: auth_header[1],
'Content-Type': 'application/json'
})
result2 = client.post(
'/user',
data=json.dumps({
'name': 'test_registered_user'
}),
headers={
auth_header[0]: auth_header[1],
'Content-Type': 'application/json'
})
assert 200 == result1.status_code
assert result1.json is not None
assert result1.json['name'] == 'test_registered_user'
assert 400 == result2.status_code
assert result2.json is not None
assert result2.json['message'] == 'User name is already taken.'
def test_delete_user_happy_path(auth: AuthActions, client: FlaskClient):
auth.login()
auth_header = auth.get_authorization_header_token()
result1 = client.post(
'/user',
data=json.dumps({
'name': 'test_registered_user'
}),
headers={
auth_header[0]: auth_header[1],
'Content-Type': 'application/json'
})
result2 = client.delete(
'/user/'+result1.json['name'],
headers={
auth_header[0]: auth_header[1]
})
assert 200 == result1.status_code
assert result1.json is not None
assert result1.json['name'] == 'test_registered_user'
assert 200 == result2.status_code
assert result2.json is not None
assert 'message' in result2.json
with auth:
auth_header = auth.get_authorization_header_token()
result1 = client.post(
'/user',
data=json.dumps({
'name': 'test_registered_user'
}),
headers={
auth_header[0]: auth_header[1],
'Content-Type': 'application/json'
})
result2 = client.delete(
'/user/'+result1.json['name'],
headers={
auth_header[0]: auth_header[1]
})
assert 200 == result1.status_code
assert result1.json is not None
assert result1.json['name'] == 'test_registered_user'
assert 200 == result2.status_code
assert result2.json is not None
assert 'message' in result2.json

6
server/tests/conftest.py

@ -122,6 +122,12 @@ class AuthActions(object):
.decode('utf8').strip()
return 'X-Auth-Token', '{}'.format(credentials)
def __enter__(self) -> 'AuthActions':
return self.login()
def __exit__(self, type: Any, value: Any, traceback: Any) -> None:
self.logout()
@pytest.fixture
def auth(client: Client) -> AuthActions:

38
server/tests/service/test_authentication_service.py

@ -0,0 +1,38 @@
import pytest
from corvus import errors
from corvus.service import authentication_service
def test_validate_password_strength_good_password():
proposed_good_password = 'AazZ1001'
assert proposed_good_password == authentication_service\
.validate_password_strength(proposed_good_password)
def test_validate_password_strength_too_short():
proposed_good_password = 'AazZ100'
with pytest.raises(errors.ValidationError) as error_info:
authentication_service.validate_password_strength(
proposed_good_password)
def test_validate_password_strength_missing_uppercase():
proposed_good_password = 'aazz1001'
with pytest.raises(errors.ValidationError) as error_info:
authentication_service.validate_password_strength(
proposed_good_password)
def test_validate_password_strength_missing_lowercase():
proposed_good_password = 'AAZZ1001'
with pytest.raises(errors.ValidationError) as error_info:
authentication_service.validate_password_strength(
proposed_good_password)
def test_validate_password_strength_missing_numbers():
proposed_good_password = 'AAZZZZAA'
with pytest.raises(errors.ValidationError) as error_info:
authentication_service.validate_password_strength(
proposed_good_password)
Loading…
Cancel
Save