diff --git a/Makefile b/Makefile index 3640cf78..2ce2933a 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,10 @@ make-p: # Launch all P targets in parallel and exit as soon as one exits. set -m; (for p in $(P); do ($(MAKE) $$p || kill 0)& done; wait) +clean-install: clean + rm -fr $(NODE_MODULES) + rm -fr $(VENV) + init-db: $(FLASK) drop_db $(FLASK) db upgrade --directory $(MIGRATIONS) diff --git a/mpwo_api/mpwo_api/__init__.py b/mpwo_api/mpwo_api/__init__.py index 64b046ec..fa94206a 100644 --- a/mpwo_api/mpwo_api/__init__.py +++ b/mpwo_api/mpwo_api/__init__.py @@ -5,49 +5,53 @@ from flask_bcrypt import Bcrypt from flask_migrate import Migrate from flask_sqlalchemy import SQLAlchemy - db = SQLAlchemy() bcrypt = Bcrypt() migrate = Migrate() appLog = logging.getLogger('mpwo_api') -# instantiate the app -app = Flask(__name__) -# set config -with app.app_context(): - app.config.from_object('mpwo_api.config.DevelopmentConfig') +def create_app(): + # instantiate the app + app = Flask(__name__) -# set up extensions -db.init_app(app) -bcrypt.init_app(app) -migrate.init_app(app, db) + # set config + with app.app_context(): + app.config.from_object('mpwo_api.config.DevelopmentConfig') -from .users.auth import auth_blueprint # noqa -from .users.users import users_blueprint # noqa -from .activities.activities import activities_blueprint # noqa + # set up extensions + db.init_app(app) + bcrypt.init_app(app) + migrate.init_app(app, db) -app.register_blueprint(users_blueprint, url_prefix='/api') -app.register_blueprint(auth_blueprint, url_prefix='/api') -app.register_blueprint(activities_blueprint, url_prefix='/api') + from .users.auth import auth_blueprint # noqa + from .users.users import users_blueprint # noqa + from .activities.activities import activities_blueprint # noqa -if app.debug: - logging.getLogger('sqlalchemy').setLevel(logging.WARNING) - logging.getLogger('sqlalchemy' - ).handlers = logging.getLogger('werkzeug').handlers - logging.getLogger('sqlalchemy.orm').setLevel(logging.WARNING) - logging.getLogger('flake8').propagate = False - appLog.setLevel(logging.DEBUG) + app.register_blueprint(users_blueprint, url_prefix='/api') + app.register_blueprint(auth_blueprint, url_prefix='/api') + app.register_blueprint(activities_blueprint, url_prefix='/api') -if app.debug: - # Enable CORS - @app.after_request - def after_request(response): - response.headers.add('Access-Control-Allow-Origin', '*') - response.headers.add( - 'Access-Control-Allow-Headers', 'Content-Type,Authorization' - ) - response.headers.add( - 'Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,PATCH,OPTIONS' - ) - return response + if app.debug: + logging.getLogger('sqlalchemy').setLevel(logging.WARNING) + logging.getLogger('sqlalchemy' + ).handlers = logging.getLogger('werkzeug').handlers + logging.getLogger('sqlalchemy.orm').setLevel(logging.WARNING) + logging.getLogger('flake8').propagate = False + appLog.setLevel(logging.DEBUG) + + if app.debug: + # Enable CORS + @app.after_request + def after_request(response): + response.headers.add('Access-Control-Allow-Origin', '*') + response.headers.add( + 'Access-Control-Allow-Headers', 'Content-Type,Authorization' + ) + response.headers.add( + 'Access-Control-Allow-Methods', + 'GET,PUT,POST,DELETE,PATCH,OPTIONS' + ) + return response + + return app diff --git a/mpwo_api/mpwo_api/config.py b/mpwo_api/mpwo_api/config.py index acd73157..d31803c9 100644 --- a/mpwo_api/mpwo_api/config.py +++ b/mpwo_api/mpwo_api/config.py @@ -31,6 +31,7 @@ class DevelopmentConfig(BaseConfig): class TestingConfig(BaseConfig): """Development configuration""" DEBUG = True + TESTING = True SQLALCHEMY_DATABASE_URI = \ os.environ.get('DATABASE_TEST_URL') SECRET_KEY = 'test key' diff --git a/mpwo_api/mpwo_api/tests/base.py b/mpwo_api/mpwo_api/tests/base.py deleted file mode 100644 index 23bb6e20..00000000 --- a/mpwo_api/mpwo_api/tests/base.py +++ /dev/null @@ -1,16 +0,0 @@ -from flask_testing import TestCase -from mpwo_api import app, db - - -class BaseTestCase(TestCase): - def create_app(self): - app.config.from_object('mpwo_api.config.TestingConfig') - return app - - def setUp(self): - db.create_all() - db.session.commit() - - def tearDown(self): - db.session.remove() - db.drop_all() diff --git a/mpwo_api/mpwo_api/tests/conftest.py b/mpwo_api/mpwo_api/tests/conftest.py new file mode 100644 index 00000000..dff9af00 --- /dev/null +++ b/mpwo_api/mpwo_api/tests/conftest.py @@ -0,0 +1,14 @@ +import pytest +from mpwo_api import create_app, db + + +@pytest.fixture +def app(): + app = create_app() + app.config.from_object('mpwo_api.config.TestingConfig') + with app.app_context(): + db.create_all() + yield app + db.session.remove() + db.drop_all() + return app diff --git a/mpwo_api/mpwo_api/tests/test_activities.py b/mpwo_api/mpwo_api/tests/test_activities.py index 33170f0a..a99876eb 100644 --- a/mpwo_api/mpwo_api/tests/test_activities.py +++ b/mpwo_api/mpwo_api/tests/test_activities.py @@ -1,94 +1,87 @@ import datetime import json -from mpwo_api.tests.base import BaseTestCase from mpwo_api.tests.utils import add_activity, add_sport, add_user -class TestActivitiesService(BaseTestCase): - """Tests for Activities.""" +def test_get_all_sports(app): + add_user('test', 'test@test.com', '12345678') + add_sport('cycling') + add_sport('running') - def test_get_all_sports(self): - add_user('test', 'test@test.com', '12345678') - add_sport('cycling') - add_sport('running') + client = app.test_client() + resp_login = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + response = client.get( + '/api/sports', + headers=dict( + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + data = json.loads(response.data.decode()) - with self.client: - resp_login = self.client.post( - '/api/auth/login', - data=json.dumps(dict( - email='test@test.com', - password='12345678' - )), - content_type='application/json' - ) - response = self.client.get( - '/api/sports', - headers=dict( - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - data = json.loads(response.data.decode()) + assert response.status_code == 200 + assert 'success' in data['status'] - self.assertEqual(response.status_code, 200) - self.assertIn('success', data['status']) + assert len(data['data']['sports']) == 2 + assert 'cycling' in data['data']['sports'][0]['label'] + assert 'running' in data['data']['sports'][1]['label'] - self.assertEqual(len(data['data']['sports']), 2) - self.assertIn('cycling', data['data']['sports'][0]['label']) - self.assertIn('running', data['data']['sports'][1]['label']) - def test_get_all_activities(self): - add_user('test', 'test@test.com', '12345678') - add_user('toto', 'toto@toto.com', '12345678') - add_sport('cycling') - add_sport('running') - add_activity( - 1, - 2, - datetime.datetime.strptime('01/01/2018', '%d/%m/%Y'), - datetime.timedelta(seconds=1024)) - add_activity( - 2, - 1, - datetime.datetime.strptime('23/01/2018', '%d/%m/%Y'), - datetime.timedelta(seconds=3600)) +def test_get_all_activities(app): + add_user('test', 'test@test.com', '12345678') + add_user('toto', 'toto@toto.com', '12345678') + add_sport('cycling') + add_sport('running') + add_activity( + 1, + 2, + datetime.datetime.strptime('01/01/2018', '%d/%m/%Y'), + datetime.timedelta(seconds=1024)) + add_activity( + 2, + 1, + datetime.datetime.strptime('23/01/2018', '%d/%m/%Y'), + datetime.timedelta(seconds=3600)) - with self.client: - resp_login = self.client.post( - '/api/auth/login', - data=json.dumps(dict( - email='test@test.com', - password='12345678' - )), - content_type='application/json' - ) - response = self.client.get( - '/api/activities', - headers=dict( - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - data = json.loads(response.data.decode()) + client = app.test_client() + resp_login = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + response = client.get( + '/api/activities', + headers=dict( + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + data = json.loads(response.data.decode()) - self.assertEqual(response.status_code, 200) - self.assertIn('success', data['status']) - self.assertEqual(len(data['data']['activities']), 2) - self.assertTrue('creation_date' in data['data']['activities'][0]) - self.assertTrue('creation_date' in data['data']['activities'][1]) - self.assertEqual('Tue, 23 Jan 2018 00:00:00 GMT', - data['data']['activities'][0][ - 'activity_date']) - self.assertEqual('Mon, 01 Jan 2018 00:00:00 GMT', - data['data']['activities'][1][ - 'activity_date']) - self.assertTrue('creation_date' in data['data']['activities'][1]) - self.assertEqual(2, data['data']['activities'][0]['user_id']) - self.assertEqual(1, data['data']['activities'][1]['user_id']) - self.assertEqual(1, data['data']['activities'][0]['sport_id']) - self.assertEqual(2, data['data']['activities'][1]['sport_id']) - self.assertEqual(3600, data['data']['activities'][0]['duration']) - self.assertEqual(1024, data['data']['activities'][1]['duration']) + assert response.status_code == 200 + assert 'success' in data['status'] + assert len(data['data']['activities']) == 2 + assert 'creation_date' in data['data']['activities'][0] + assert 'creation_date' in data['data']['activities'][1] + assert 'Tue, 23 Jan 2018 00:00:00 GMT' == data['data']['activities'][0]['activity_date'] # noqa + assert 'Mon, 01 Jan 2018 00:00:00 GMT' == data['data']['activities'][1]['activity_date'] # noqa + assert 'creation_date' in data['data']['activities'][1] + assert 2 == data['data']['activities'][0]['user_id'] + assert 1 == data['data']['activities'][1]['user_id'] + assert 1 == data['data']['activities'][0]['sport_id'] + assert 2 == data['data']['activities'][1]['sport_id'] + assert 3600 == data['data']['activities'][0]['duration'] + assert 1024 == data['data']['activities'][1]['duration'] diff --git a/mpwo_api/mpwo_api/tests/test_auth.py b/mpwo_api/mpwo_api/tests/test_auth.py index b188b1bf..79f11e60 100644 --- a/mpwo_api/mpwo_api/tests/test_auth.py +++ b/mpwo_api/mpwo_api/tests/test_auth.py @@ -2,515 +2,527 @@ import json import time from io import BytesIO -from mpwo_api.tests.base import BaseTestCase from mpwo_api.tests.utils import add_user, add_user_full -class TestAuthBlueprint(BaseTestCase): +def test_user_registration(app): + client = app.test_client() + response = client.post( + '/api/auth/register', + data=json.dumps(dict( + username='justatest', + email='test@test.com', + password='12345678', + password_conf='12345678' + )), + content_type='application/json' + ) + data = json.loads(response.data.decode()) + assert data['status'] == 'success' + assert data['message'] == 'Successfully registered.' + assert data['auth_token'] + assert response.content_type == 'application/json' + assert response.status_code == 201 - def test_user_registration(self): - with self.client: - response = self.client.post( - '/api/auth/register', - data=json.dumps(dict( - username='justatest', - email='test@test.com', - password='12345678', - password_conf='12345678' - )), - content_type='application/json' - ) - data = json.loads(response.data.decode()) - self.assertTrue(data['status'] == 'success') - self.assertTrue(data['message'] == 'Successfully registered.') - self.assertTrue(data['auth_token']) - self.assertTrue(response.content_type == 'application/json') - self.assertEqual(response.status_code, 201) - def test_user_registration_user_already_exists(self): - add_user('test', 'test@test.com', '12345678') - with self.client: - response = self.client.post( - '/api/auth/register', - data=json.dumps(dict( - username='test', - email='test@test.com', - password='12345678', - password_conf='12345678' - )), - content_type='application/json' - ) - data = json.loads(response.data.decode()) - self.assertTrue(data['status'] == 'error') - self.assertTrue( - data['message'] == 'Sorry. That user already exists.') - self.assertTrue(response.content_type == 'application/json') - self.assertEqual(response.status_code, 400) +def test_user_registration_user_already_exists(app): + add_user('test', 'test@test.com', '12345678') + client = app.test_client() + response = client.post( + '/api/auth/register', + data=json.dumps(dict( + username='test', + email='test@test.com', + password='12345678', + password_conf='12345678' + )), + content_type='application/json' + ) + data = json.loads(response.data.decode()) + assert data['status'] == 'error' + assert data['message'] == 'Sorry. That user already exists.' + assert response.content_type == 'application/json' + assert response.status_code == 400 - def test_user_registration_invalid_short_username(self): - with self.client: - response = self.client.post( - '/api/auth/register', - data=json.dumps(dict( - username='t', - email='test@test.com', - password='12345678', - password_conf='12345678' - )), - content_type='application/json' - ) - data = json.loads(response.data.decode()) - self.assertTrue(data['status'] == 'error') - self.assertTrue( - data['message'] == "Errors: Username: 3 to 12 characters required.\n") # noqa - self.assertTrue(response.content_type == 'application/json') - self.assertEqual(response.status_code, 400) - def test_user_registration_invalid_long_username(self): - with self.client: - response = self.client.post( - '/api/auth/register', - data=json.dumps(dict( - username='testestestestestest', - email='test@test.com', - password='12345678', - password_conf='12345678' - )), - content_type='application/json' - ) - data = json.loads(response.data.decode()) - self.assertTrue(data['status'] == 'error') - self.assertTrue( - data['message'] == "Errors: Username: 3 to 12 characters required.\n") # noqa - self.assertTrue(response.content_type == 'application/json') - self.assertEqual(response.status_code, 400) +def test_user_registration_invalid_short_username(app): + client = app.test_client() + response = client.post( + '/api/auth/register', + data=json.dumps(dict( + username='t', + email='test@test.com', + password='12345678', + password_conf='12345678' + )), + content_type='application/json' + ) + data = json.loads(response.data.decode()) + assert data['status'] == 'error' + assert data['message'] == "Errors: Username: 3 to 12 characters required.\n" # noqa + assert response.content_type == 'application/json' + assert response.status_code == 400 - def test_user_registration_invalid_email(self): - with self.client: - response = self.client.post( - '/api/auth/register', - data=json.dumps(dict( - username='test', - email='test@test', - password='12345678', - password_conf='12345678' - )), - content_type='application/json' - ) - data = json.loads(response.data.decode()) - self.assertTrue(data['status'] == 'error') - self.assertTrue( - data['message'] == "Errors: Valid email must be provided.\n") # noqa - self.assertTrue(response.content_type == 'application/json') - self.assertEqual(response.status_code, 400) - def test_user_registration_invalid_short_password(self): - with self.client: - response = self.client.post( - '/api/auth/register', - data=json.dumps(dict( - username='test', - email='test@test.com', - password='1234567', - password_conf='1234567' - )), - content_type='application/json' - ) - data = json.loads(response.data.decode()) - self.assertTrue(data['status'] == 'error') - self.assertTrue( - data['message'] == "Errors: Password: 8 characters required.\n") # noqa - self.assertTrue(response.content_type == 'application/json') - self.assertEqual(response.status_code, 400) +def test_user_registration_invalid_long_username(app): + client = app.test_client() + response = client.post( + '/api/auth/register', + data=json.dumps(dict( + username='testestestestestest', + email='test@test.com', + password='12345678', + password_conf='12345678' + )), + content_type='application/json' + ) + data = json.loads(response.data.decode()) + assert data['status'] == 'error' + assert data['message'] == "Errors: Username: 3 to 12 characters required.\n" # noqa + assert response.content_type == 'application/json' + assert response.status_code == 400 - def test_user_registration_mismatched_password(self): - with self.client: - response = self.client.post( - '/api/auth/register', - data=json.dumps(dict( - username='test', - email='test@test.com', - password='12345678', - password_conf='87654321' - )), - content_type='application/json' - ) - data = json.loads(response.data.decode()) - self.assertTrue(data['status'] == 'error') - self.assertTrue( - data['message'] == "Errors: Password and password confirmation don\'t match.\n") # noqa - self.assertTrue(response.content_type == 'application/json') - self.assertEqual(response.status_code, 400) - def test_user_registration_invalid_json(self): - with self.client: - response = self.client.post( - '/api/auth/register', - data=json.dumps(dict()), - content_type='application/json' - ) - data = json.loads(response.data.decode()) - self.assertEqual(response.status_code, 400) - self.assertIn('Invalid payload.', data['message']) - self.assertIn('error', data['status']) +def test_user_registration_invalid_email(app): + client = app.test_client() + response = client.post( + '/api/auth/register', + data=json.dumps(dict( + username='test', + email='test@test', + password='12345678', + password_conf='12345678' + )), + content_type='application/json' + ) + data = json.loads(response.data.decode()) + assert data['status'] == 'error' + assert data['message'] == "Errors: Valid email must be provided.\n" # noqa + assert response.content_type == 'application/json' + assert response.status_code == 400 - def test_user_registration_invalid_json_keys_no_username(self): - with self.client: - response = self.client.post( - '/api/auth/register', - data=json.dumps(dict( - email='test@test.com', - password='12345678', - password_conf='12345678')), - content_type='application/json', - ) - data = json.loads(response.data.decode()) - self.assertEqual(response.status_code, 400) - self.assertIn('Invalid payload.', data['message']) - self.assertIn('error', data['status']) - def test_user_registration_invalid_json_keys_no_email(self): - with self.client: - response = self.client.post( - '/api/auth/register', - data=json.dumps(dict( - username='test', - password='12345678', - password_conf='12345678')), - content_type='application/json', - ) - data = json.loads(response.data.decode()) - self.assertEqual(response.status_code, 400) - self.assertIn('Invalid payload.', data['message']) - self.assertIn('error', data['status']) +def test_user_registration_invalid_short_password(app): + client = app.test_client() + response = client.post( + '/api/auth/register', + data=json.dumps(dict( + username='test', + email='test@test.com', + password='1234567', + password_conf='1234567' + )), + content_type='application/json' + ) + data = json.loads(response.data.decode()) + assert data['status'] == 'error' + assert data['message'] == "Errors: Password: 8 characters required.\n" # noqa + assert response.content_type == 'application/json' + assert response.status_code == 400 - def test_user_registration_invalid_json_keys_no_password(self): - with self.client: - response = self.client.post( - '/api/auth/register', - data=json.dumps(dict( - username='test', - email='test@test.com', - password_conf='12345678')), - content_type='application/json', - ) - data = json.loads(response.data.decode()) - self.assertEqual(response.status_code, 400) - self.assertIn('Invalid payload.', data['message']) - self.assertIn('error', data['status']) - def test_user_registration_invalid_json_keys_no_password_conf(self): - with self.client: - response = self.client.post( - '/api/auth/register', - data=json.dumps(dict( - username='test', - email='test@test.com', - password='12345678')), - content_type='application/json', - ) - data = json.loads(response.data.decode()) - self.assertEqual(response.status_code, 400) - self.assertIn('Invalid payload.', data['message']) - self.assertIn('error', data['status']) +def test_user_registration_mismatched_password(app): + client = app.test_client() + response = client.post( + '/api/auth/register', + data=json.dumps(dict( + username='test', + email='test@test.com', + password='12345678', + password_conf='87654321' + )), + content_type='application/json' + ) + data = json.loads(response.data.decode()) + assert data['status'] == 'error' + assert data['message'] == "Errors: Password and password confirmation don\'t match.\n" # noqa + assert response.content_type == 'application/json' + assert response.status_code == 400 - def test_registered_user_login(self): - with self.client: - add_user('test', 'test@test.com', '12345678') - response = self.client.post( - '/api/auth/login', - data=json.dumps(dict( - email='test@test.com', - password='12345678' - )), - content_type='application/json' - ) - data = json.loads(response.data.decode()) - self.assertTrue(data['status'] == 'success') - self.assertTrue(data['message'] == 'Successfully logged in.') - self.assertTrue(data['auth_token']) - self.assertTrue(response.content_type == 'application/json') - self.assertEqual(response.status_code, 200) - def test_no_registered_user_login(self): - with self.client: - response = self.client.post( - '/api/auth/login', - data=json.dumps(dict( - email='test@test.com', - password='12345678' - )), - content_type='application/json' - ) - data = json.loads(response.data.decode()) - self.assertTrue(data['status'] == 'error') - self.assertTrue(data['message'] == 'Invalid credentials.') - self.assertTrue(response.content_type == 'application/json') - self.assertEqual(response.status_code, 404) +def test_user_registration_invalid_json(app): + client = app.test_client() + response = client.post( + '/api/auth/register', + data=json.dumps(dict()), + content_type='application/json' + ) + data = json.loads(response.data.decode()) + assert response.status_code, 400 + assert 'Invalid payload.', data['message'] + assert 'error', data['status'] - def test_registered_user_login_invalid_password(self): - add_user('test', 'test@test.com', '12345678') - with self.client: - response = self.client.post( - '/api/auth/login', - data=json.dumps(dict( - email='test@test.com', - password='123456789' - )), - content_type='application/json' - ) - data = json.loads(response.data.decode()) - self.assertTrue(data['status'] == 'error') - self.assertTrue(data['message'] == 'Invalid credentials.') - self.assertTrue(response.content_type == 'application/json') - self.assertEqual(response.status_code, 404) - def test_valid_logout(self): - add_user('test', 'test@test.com', '12345678') - with self.client: - # user login - resp_login = self.client.post( - '/api/auth/login', - data=json.dumps(dict( - email='test@test.com', - password='12345678' - )), - content_type='application/json' - ) - # valid token logout - response = self.client.get( - '/api/auth/logout', - headers=dict( - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - data = json.loads(response.data.decode()) - self.assertTrue(data['status'] == 'success') - self.assertTrue(data['message'] == 'Successfully logged out.') - self.assertEqual(response.status_code, 200) +def test_user_registration_invalid_json_keys_no_username(app): + client = app.test_client() + response = client.post( + '/api/auth/register', + data=json.dumps(dict( + email='test@test.com', + password='12345678', + password_conf='12345678')), + content_type='application/json', + ) + data = json.loads(response.data.decode()) + assert response.status_code == 400 + assert 'Invalid payload.' in data['message'] + assert 'error' in data['status'] - def test_invalid_logout_expired_token(self): - add_user('test', 'test@test.com', '12345678') - with self.client: - resp_login = self.client.post( - '/api/auth/login', - data=json.dumps(dict( - email='test@test.com', - password='12345678' - )), - content_type='application/json' - ) - # invalid token logout - time.sleep(4) - response = self.client.get( - '/api/auth/logout', - headers=dict( - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - data = json.loads(response.data.decode()) - self.assertTrue(data['status'] == 'error') - self.assertTrue( - data['message'] == 'Signature expired. Please log in again.') - self.assertEqual(response.status_code, 401) - def test_invalid_logout(self): - with self.client: - response = self.client.get( - '/api/auth/logout', - headers=dict(Authorization='Bearer invalid')) - data = json.loads(response.data.decode()) - self.assertTrue(data['status'] == 'error') - self.assertTrue( - data['message'] == 'Invalid token. Please log in again.') - self.assertEqual(response.status_code, 401) +def test_user_registration_invalid_json_keys_no_email(app): + client = app.test_client() + response = client.post( + '/api/auth/register', + data=json.dumps(dict( + username='test', + password='12345678', + password_conf='12345678')), + content_type='application/json', + ) + data = json.loads(response.data.decode()) + assert response.status_code == 400 + assert 'Invalid payload.' in data['message'] + assert 'error' in data['status'] - def test_user_profile_minimal(self): - add_user('test', 'test@test.com', '12345678') - with self.client: - resp_login = self.client.post( - '/api/auth/login', - data=json.dumps(dict( - email='test@test.com', - password='12345678' - )), - content_type='application/json' - ) - response = self.client.get( - '/api/auth/profile', - headers=dict( - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - data = json.loads(response.data.decode()) - self.assertTrue(data['status'] == 'success') - self.assertTrue(data['data'] is not None) - self.assertTrue(data['data']['username'] == 'test') - self.assertTrue(data['data']['email'] == 'test@test.com') - self.assertTrue(data['data']['created_at']) - self.assertFalse(data['data']['admin']) - self.assertEqual(response.status_code, 200) - def test_user_profile_full(self): - add_user_full('test', 'test@test.com', '12345678') - with self.client: - resp_login = self.client.post( - '/api/auth/login', - data=json.dumps(dict( - email='test@test.com', - password='12345678' - )), - content_type='application/json' - ) - response = self.client.get( - '/api/auth/profile', - headers=dict( - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - data = json.loads(response.data.decode()) - self.assertTrue(data['status'] == 'success') - self.assertTrue(data['data'] is not None) - self.assertTrue(data['data']['username'] == 'test') - self.assertTrue(data['data']['email'] == 'test@test.com') - self.assertTrue(data['data']['created_at']) - self.assertFalse(data['data']['admin']) - self.assertTrue(data['data']['first_name'] == 'John') - self.assertTrue(data['data']['last_name'] == 'Doe') - self.assertTrue(data['data']['birth_date']) - self.assertTrue(data['data']['bio'] == 'just a random guy') - self.assertTrue(data['data']['location'] == 'somewhere') - self.assertEqual(response.status_code, 200) +def test_user_registration_invalid_json_keys_no_password(app): + client = app.test_client() + response = client.post( + '/api/auth/register', + data=json.dumps(dict( + username='test', + email='test@test.com', + password_conf='12345678')), + content_type='application/json', + ) + data = json.loads(response.data.decode()) + assert response.status_code == 400 + assert 'Invalid payload.', data['message'] + assert 'error', data['status'] - def test_invalid_profile(self): - with self.client: - response = self.client.get( - '/api/auth/profile', - headers=dict(Authorization='Bearer invalid')) - data = json.loads(response.data.decode()) - self.assertTrue(data['status'] == 'error') - self.assertTrue( - data['message'] == 'Invalid token. Please log in again.') - self.assertEqual(response.status_code, 401) - def test_user_profile_valid_update(self): - add_user('test', 'test@test.com', '12345678') - with self.client: - resp_login = self.client.post( - '/api/auth/login', - data=json.dumps(dict( - email='test@test.com', - password='12345678' - )), - content_type='application/json' - ) - response = self.client.post( - '/api/auth/profile/edit', - content_type='application/json', - data=json.dumps(dict( - first_name='John', - last_name='Doe', - location='Somewhere', - bio='just a random guy', - birth_date='01/01/1980' - )), - headers=dict( - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - data = json.loads(response.data.decode()) - self.assertTrue(data['status'] == 'success') - self.assertTrue(data['message'] == 'User profile updated.') - self.assertEqual(response.status_code, 200) +def test_user_registration_invalid_json_keys_no_password_conf(app): + client = app.test_client() + response = client.post( + '/api/auth/register', + data=json.dumps(dict( + username='test', + email='test@test.com', + password='12345678')), + content_type='application/json', + ) + data = json.loads(response.data.decode()) + assert response.status_code == 400 + assert 'Invalid payload.' in data['message'] + assert 'error' in data['status'] - def test_user_profile_valid_update_with_one_field(self): - add_user('test', 'test@test.com', '12345678') - with self.client: - resp_login = self.client.post( - '/api/auth/login', - data=json.dumps(dict( - email='test@test.com', - password='12345678' - )), - content_type='application/json' - ) - response = self.client.post( - '/api/auth/profile/edit', - content_type='application/json', - data=json.dumps(dict( - first_name='John' - )), - headers=dict( - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - data = json.loads(response.data.decode()) - self.assertTrue(data['status'] == 'success') - self.assertTrue(data['message'] == 'User profile updated.') - self.assertEqual(response.status_code, 200) - def test_user_profile_update_invalid_json(self): - add_user('test', 'test@test.com', '12345678') - with self.client: - resp_login = self.client.post( - '/api/auth/login', - data=json.dumps(dict( - email='test@test.com', - password='12345678' - )), - content_type='application/json' - ) - response = self.client.post( - '/api/auth/profile/edit', - content_type='application/json', - data=json.dumps(dict()), - headers=dict( - Authorization='Bearer ' + json.loads( - resp_login.data.decode() - )['auth_token'] - ) - ) - data = json.loads(response.data.decode()) - self.assertEqual(response.status_code, 400) - self.assertIn('Invalid payload.', data['message']) - self.assertIn('error', data['status']) +def test_registered_user_login(app): + client = app.test_client() + add_user('test', 'test@test.com', '12345678') + response = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + data = json.loads(response.data.decode()) + assert data['status'] == 'success' + assert data['message'] == 'Successfully logged in.' + assert data['auth_token'] + assert response.content_type == 'application/json' + assert response.status_code == 200 - def test_update_user_picture(self): - add_user('test', 'test@test.com', '12345678') - with self.client: - resp_login = self.client.post( - '/api/auth/login', - data=json.dumps(dict( - email='test@test.com', - password='12345678' - )), - content_type='application/json' - ) - response = self.client.post( - '/api/auth/picture', - data=dict( - file=(BytesIO(b'avatar'), 'avatar.png') - ), - headers=dict( - content_type='multipart/form-data', - authorization='Bearer ' + - json.loads(resp_login.data.decode())['auth_token'] - ) - ) - data = json.loads(response.data.decode()) - self.assertTrue(data['status'] == 'success') - self.assertTrue(data['message'] == 'User picture updated.') - self.assertEqual(response.status_code, 200) +def test_no_registered_user_login(app): + client = app.test_client() + response = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + data = json.loads(response.data.decode()) + assert data['status'] == 'error' + assert data['message'] == 'Invalid credentials.' + assert response.content_type == 'application/json' + assert response.status_code == 404 + + +def test_registered_user_login_invalid_password(app): + add_user('test', 'test@test.com', '12345678') + client = app.test_client() + response = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='123456789' + )), + content_type='application/json' + ) + data = json.loads(response.data.decode()) + assert data['status'] == 'error' + assert data['message'] == 'Invalid credentials.' + assert response.content_type == 'application/json' + assert response.status_code == 404 + + +def test_valid_logout(app): + add_user('test', 'test@test.com', '12345678') + client = app.test_client() + # user login + resp_login = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + # valid token logout + response = client.get( + '/api/auth/logout', + headers=dict( + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + data = json.loads(response.data.decode()) + assert data['status'] == 'success' + assert data['message'] == 'Successfully logged out.' + assert response.status_code == 200 + + +def test_invalid_logout_expired_token(app): + add_user('test', 'test@test.com', '12345678') + client = app.test_client() + resp_login = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + # invalid token logout + time.sleep(4) + response = client.get( + '/api/auth/logout', + headers=dict( + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + data = json.loads(response.data.decode()) + assert data['status'] == 'error' + assert data['message'] == 'Signature expired. Please log in again.' + assert response.status_code == 401 + + +def test_invalid_logout(app): + client = app.test_client() + response = client.get( + '/api/auth/logout', + headers=dict(Authorization='Bearer invalid')) + data = json.loads(response.data.decode()) + assert data['status'] == 'error' + assert data['message'] == 'Invalid token. Please log in again.' + assert response.status_code == 401 + + +def test_user_profile_minimal(app): + add_user('test', 'test@test.com', '12345678') + client = app.test_client() + resp_login = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + response = client.get( + '/api/auth/profile', + headers=dict( + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + data = json.loads(response.data.decode()) + assert data['status'] == 'success' + assert data['data'] is not None + assert data['data']['username'] == 'test' + assert data['data']['email'] == 'test@test.com' + assert data['data']['created_at'] + assert not data['data']['admin'] + assert response.status_code == 200 + + +def test_user_profile_full(app): + add_user_full('test', 'test@test.com', '12345678') + client = app.test_client() + resp_login = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + response = client.get( + '/api/auth/profile', + headers=dict( + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + data = json.loads(response.data.decode()) + assert data['status'] == 'success' + assert data['data'] is not None + assert data['data']['username'] == 'test' + assert data['data']['email'] == 'test@test.com' + assert data['data']['created_at'] + assert not data['data']['admin'] + assert data['data']['first_name'] == 'John' + assert data['data']['last_name'] == 'Doe' + assert data['data']['birth_date'] + assert data['data']['bio'] == 'just a random guy' + assert data['data']['location'] == 'somewhere' + assert response.status_code == 200 + + +def test_invalid_profile(app): + client = app.test_client() + response = client.get( + '/api/auth/profile', + headers=dict(Authorization='Bearer invalid')) + data = json.loads(response.data.decode()) + assert data['status'] == 'error' + assert data['message'] == 'Invalid token. Please log in again.' + assert response.status_code == 401 + + +def test_user_profile_valid_update(app): + add_user('test', 'test@test.com', '12345678') + client = app.test_client() + resp_login = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + response = client.post( + '/api/auth/profile/edit', + content_type='application/json', + data=json.dumps(dict( + first_name='John', + last_name='Doe', + location='Somewhere', + bio='just a random guy', + birth_date='01/01/1980' + )), + headers=dict( + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + data = json.loads(response.data.decode()) + assert data['status'] == 'success' + assert data['message'] == 'User profile updated.' + assert response.status_code == 200 + + +def test_user_profile_valid_update_with_one_field(app): + add_user('test', 'test@test.com', '12345678') + client = app.test_client() + resp_login = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + response = client.post( + '/api/auth/profile/edit', + content_type='application/json', + data=json.dumps(dict( + first_name='John' + )), + headers=dict( + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + data = json.loads(response.data.decode()) + assert data['status'] == 'success' + assert data['message'] == 'User profile updated.' + assert response.status_code == 200 + + +def test_user_profile_update_invalid_json(app): + add_user('test', 'test@test.com', '12345678') + client = app.test_client() + resp_login = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + response = client.post( + '/api/auth/profile/edit', + content_type='application/json', + data=json.dumps(dict()), + headers=dict( + Authorization='Bearer ' + json.loads( + resp_login.data.decode() + )['auth_token'] + ) + ) + data = json.loads(response.data.decode()) + assert response.status_code == 400 + assert 'Invalid payload.' in data['message'] + assert 'error' in data['status'] + + +def test_update_user_picture(app): + add_user('test', 'test@test.com', '12345678') + + client = app.test_client() + resp_login = client.post( + '/api/auth/login', + data=json.dumps(dict( + email='test@test.com', + password='12345678' + )), + content_type='application/json' + ) + response = client.post( + '/api/auth/picture', + data=dict( + file=(BytesIO(b'avatar'), 'avatar.png') + ), + headers=dict( + content_type='multipart/form-data', + authorization='Bearer ' + + json.loads(resp_login.data.decode())['auth_token'] + ) + ) + data = json.loads(response.data.decode()) + assert data['status'] == 'success' + assert data['message'] == 'User picture updated.' + assert response.status_code == 200 diff --git a/mpwo_api/mpwo_api/tests/test_config.py b/mpwo_api/mpwo_api/tests/test_config.py new file mode 100644 index 00000000..b747a84b --- /dev/null +++ b/mpwo_api/mpwo_api/tests/test_config.py @@ -0,0 +1,18 @@ +import os + + +def test_development_config(app): + app.config.from_object('mpwo_api.config.DevelopmentConfig') + assert app.config['DEBUG'] + assert not app.config['TESTING'] + assert app.config['SQLALCHEMY_DATABASE_URI'] == os.environ.get( + 'DATABASE_URL') + + +def test_testing_config(app): + app.config.from_object('mpwo_api.config.TestingConfig') + assert app.config['DEBUG'] + assert app.config['TESTING'] + assert not app.config['PRESERVE_CONTEXT_ON_EXCEPTION'] + assert app.config['SQLALCHEMY_DATABASE_URI'] == os.environ.get( + 'DATABASE_TEST_URL') diff --git a/mpwo_api/mpwo_api/tests/test_users.py b/mpwo_api/mpwo_api/tests/test_users.py index a77440b4..5caacbca 100644 --- a/mpwo_api/mpwo_api/tests/test_users.py +++ b/mpwo_api/mpwo_api/tests/test_users.py @@ -1,83 +1,87 @@ import json -from mpwo_api.tests.base import BaseTestCase from mpwo_api.tests.utils import add_user from mpwo_api.users.models import User -class TestUserService(BaseTestCase): - """Tests for the Users Service.""" +def test_ping(app): + """ => Ensure the /ping route behaves correctly.""" + client = app.test_client() + response = client.get('/api/ping') + data = json.loads(response.data.decode()) + assert response.status_code == 200 + assert 'pong' in data['message'] + assert 'success' in data['status'] - def test_users(self): - """ => Ensure the /ping route behaves correctly.""" - response = self.client.get('/api/ping') - data = json.loads(response.data.decode()) - self.assertEqual(response.status_code, 200) - self.assertIn('pong!', data['message']) - self.assertIn('success', data['status']) - def test_single_user(self): - """=> Get single user details""" - user = add_user('test', 'test@test.com', 'test') +def test_single_user(app): + """=> Get single user details""" + user = add_user('test', 'test@test.com', 'test') + client = app.test_client() - with self.client: - response = self.client.get(f'/api/users/{user.id}') - data = json.loads(response.data.decode()) + response = client.get(f'/api/users/{user.id}') + data = json.loads(response.data.decode()) - self.assertEqual(response.status_code, 200) - self.assertIn('success', data['status']) + assert response.status_code == 200 + assert 'success' in data['status'] - self.assertTrue('created_at' in data['data']) - self.assertIn('test', data['data']['username']) - self.assertIn('test@test.com', data['data']['email']) + assert 'created_at' in data['data'] + assert 'test' in data['data']['username'] + assert 'test@test.com' in data['data']['email'] - def test_single_user_no_id(self): - """=> Ensure error is thrown if an id is not provided.""" - with self.client: - response = self.client.get(f'/api/users/blah') - data = json.loads(response.data.decode()) - self.assertEqual(response.status_code, 404) - self.assertIn('fail', data['status']) - self.assertIn('User does not exist', data['message']) +def test_single_user_no_id(app): + """=> Ensure error is thrown if an id is not provided.""" + client = app.test_client() + response = client.get(f'/api/users/blah') + data = json.loads(response.data.decode()) - def test_single_user_wrong_id(self): - """=> Ensure error is thrown if the id does not exist.""" - with self.client: - response = self.client.get(f'/api/users/99999999999') - data = json.loads(response.data.decode()) + assert response.status_code == 404 + assert 'fail' in data['status'] + assert 'User does not exist' in data['message'] - self.assertEqual(response.status_code, 404) - self.assertIn('fail', data['status']) - self.assertIn('User does not exist', data['message']) - def test_users_list(self): - """=> Ensure get single user behaves correctly.""" - add_user('test', 'test@test.com', 'test') - add_user('toto', 'toto@toto.com', 'toto') - with self.client: - response = self.client.get('/api/users') - data = json.loads(response.data.decode()) +def test_single_user_wrong_id(app): + """=> Ensure error is thrown if the id does not exist.""" + client = app.test_client() + response = client.get(f'/api/users/99999999999') + data = json.loads(response.data.decode()) - self.assertEqual(response.status_code, 200) - self.assertIn('success', data['status']) + assert response.status_code == 404 + assert 'fail' in data['status'] + assert 'User does not exist' in data['message'] - self.assertEqual(len(data['data']['users']), 2) - self.assertTrue('created_at' in data['data']['users'][0]) - self.assertTrue('created_at' in data['data']['users'][1]) - self.assertIn('test', data['data']['users'][0]['username']) - self.assertIn('toto', data['data']['users'][1]['username']) - self.assertIn('test@test.com', data['data']['users'][0]['email']) - self.assertIn('toto@toto.com', data['data']['users'][1]['email']) - def test_encode_auth_token(self): - """=> Ensure correct auth token generation""" - user = add_user('test', 'test@test.com', 'test') - auth_token = user.encode_auth_token(user.id) - self.assertTrue(isinstance(auth_token, bytes)) +def test_users_list(app): + """=> Ensure get single user behaves correctly.""" + add_user('test', 'test@test.com', 'test') + add_user('toto', 'toto@toto.com', 'toto') - def test_decode_auth_token(self): - user = add_user('test', 'test@test.com', 'test') - auth_token = user.encode_auth_token(user.id) - self.assertTrue(isinstance(auth_token, bytes)) - self.assertTrue(User.decode_auth_token(auth_token), user.id) + client = app.test_client() + response = client.get('/api/users') + data = json.loads(response.data.decode()) + + assert response.status_code == 200 + assert 'success' in data['status'] + + assert len(data['data']['users']) == 2 + assert 'created_at' in data['data']['users'][0] + assert 'created_at' in data['data']['users'][1] + assert 'test' in data['data']['users'][0]['username'] + assert 'toto' in data['data']['users'][1]['username'] + assert 'test@test.com' in data['data']['users'][0]['email'] + assert 'toto@toto.com' in data['data']['users'][1]['email'] + + +def test_encode_auth_token(app): + """=> Ensure correct auth token generation""" + user = add_user('test', 'test@test.com', 'test') + auth_token = user.encode_auth_token(user.id) + assert isinstance(auth_token, bytes) + + +def test_decode_auth_token(app): + user = add_user('test', 'test@test.com', 'test') + auth_token = user.encode_auth_token(user.id) + assert isinstance(auth_token, bytes) + assert User.decode_auth_token(auth_token) == user.id diff --git a/mpwo_api/server.py b/mpwo_api/server.py index 3afcb393..045ba3af 100644 --- a/mpwo_api/server.py +++ b/mpwo_api/server.py @@ -1,9 +1,9 @@ -import unittest - -from mpwo_api import app, db +from mpwo_api import create_app, db from mpwo_api.activities.models import Sport from mpwo_api.users.models import User +app = create_app() + @app.cli.command() def drop_db(): @@ -31,21 +31,5 @@ def init_data(): print('Initial data stored in database.') -def run_test(test_path='mpwo_api/tests'): - """Runs the tests without code coverage.""" - tests = unittest.TestLoader().discover( - test_path, pattern='test*.py') - result = unittest.TextTestRunner(verbosity=2).run(tests) - if result.wasSuccessful(): - return 0 - return 1 - - -@app.cli.command() -def test(): - """Runs the tests without code coverage.""" - run_test() - - if __name__ == '__main__': app.run()