-
Notifications
You must be signed in to change notification settings - Fork 0
Persist and test API namespace for projects #40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,16 +1,201 @@ | ||
| from faker import Faker | ||
| from flask import json | ||
| from flask.testing import FlaskClient | ||
| from pytest_mock import MockFixture | ||
|
|
||
| from time_tracker_api.projects.projects_model import PROJECT_TYPE | ||
|
|
||
| def test_list_all_elements(client: FlaskClient, mocker: MockFixture): | ||
| fake = Faker() | ||
|
|
||
| valid_project_data = { | ||
| "name": fake.company(), | ||
| "description": fake.paragraph(), | ||
| "type": fake.word(PROJECT_TYPE.valid_type_values()), | ||
| } | ||
| fake_project = ({ | ||
| "id": fake.random_int(1, 9999) | ||
| }).update(valid_project_data) | ||
|
|
||
|
|
||
| def test_create_project_should_succeed_with_valid_request(client: FlaskClient, mocker: MockFixture): | ||
| from time_tracker_api.projects.projects_namespace import project_dao | ||
| repository_create_mock = mocker.patch.object(project_dao.repository, | ||
| 'create', | ||
| return_value=fake_project) | ||
|
|
||
| response = client.post("/projects", json=valid_project_data, follow_redirects=True) | ||
|
|
||
| assert 201 == response.status_code | ||
| repository_create_mock.assert_called_once_with(valid_project_data) | ||
|
|
||
|
|
||
| def test_create_project_should_reject_bad_request(client: FlaskClient, mocker: MockFixture): | ||
| from time_tracker_api.projects.projects_namespace import project_dao | ||
| repository_find_all_mock = mocker.patch.object(project_dao.repository, 'find_all', return_value=[]) | ||
| invalid_project_data = valid_project_data.copy().update({ | ||
| "type": 'anything', | ||
| }) | ||
| repository_create_mock = mocker.patch.object(project_dao.repository, | ||
| 'create', | ||
| return_value=fake_project) | ||
|
|
||
| response = client.post("/projects", json=invalid_project_data, follow_redirects=True) | ||
|
|
||
| assert 400 == response.status_code | ||
| repository_create_mock.assert_not_called() | ||
|
|
||
|
|
||
| def test_list_all_projects(client: FlaskClient, mocker: MockFixture): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The description in the comments says list. As long as it makes sense and it is not ambiguous we are Ok. |
||
| from time_tracker_api.projects.projects_namespace import project_dao | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Notice, you are calling There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, if I do it before the project_dao would be broken because it has to be constructed after the app instance is ready. This is a necessary evil. |
||
| repository_find_all_mock = mocker.patch.object(project_dao.repository, | ||
| 'find_all', | ||
| return_value=[]) | ||
|
|
||
| response = client.get("/projects", follow_redirects=True) | ||
|
|
||
| assert 200 == response.status_code | ||
|
|
||
| json_data = json.loads(response.data) | ||
| assert [] == json_data | ||
| repository_find_all_mock.assert_called_once() | ||
|
|
||
|
|
||
| def test_get_project_should_succeed_with_valid_id(client: FlaskClient, mocker: MockFixture): | ||
| from time_tracker_api.projects.projects_namespace import project_dao | ||
|
|
||
| valid_id = fake.random_int(1, 9999) | ||
|
|
||
| repository_find_mock = mocker.patch.object(project_dao.repository, | ||
| 'find', | ||
| return_value=fake_project) | ||
|
|
||
| response = client.get("/projects/%s" % valid_id, follow_redirects=True) | ||
|
|
||
| assert 200 == response.status_code | ||
| fake_project == json.loads(response.data) | ||
| repository_find_mock.assert_called_once_with(str(valid_id)) | ||
|
|
||
|
|
||
| def test_get_project_should_return_not_found_with_invalid_id(client: FlaskClient, mocker: MockFixture): | ||
| from time_tracker_api.projects.projects_namespace import project_dao | ||
| from werkzeug.exceptions import NotFound | ||
|
|
||
| invalid_id = fake.random_int(1, 9999) | ||
|
|
||
| repository_find_mock = mocker.patch.object(project_dao.repository, | ||
| 'find', | ||
| side_effect=NotFound) | ||
|
|
||
| response = client.get("/projects/%s" % invalid_id, follow_redirects=True) | ||
|
|
||
| assert 404 == response.status_code | ||
| repository_find_mock.assert_called_once_with(str(invalid_id)) | ||
|
|
||
|
|
||
| def test_get_project_should_return_422_for_invalid_id_format(client: FlaskClient, mocker: MockFixture): | ||
| from time_tracker_api.projects.projects_namespace import project_dao | ||
| from werkzeug.exceptions import UnprocessableEntity | ||
|
|
||
| invalid_id = fake.company() | ||
|
|
||
| repository_find_mock = mocker.patch.object(project_dao.repository, | ||
| 'find', | ||
| side_effect=UnprocessableEntity) | ||
|
|
||
| response = client.get("/projects/%s" % invalid_id, follow_redirects=True) | ||
|
|
||
| assert 422 == response.status_code | ||
| repository_find_mock.assert_called_once_with(str(invalid_id)) | ||
|
|
||
|
|
||
| def update_project_should_succeed_with_valid_data(client: FlaskClient, mocker: MockFixture): | ||
|
||
| from time_tracker_api.projects.projects_namespace import project_dao | ||
|
|
||
| repository_update_mock = mocker.patch.object(project_dao.repository, | ||
| 'update', | ||
| return_value=fake_project) | ||
|
|
||
| valid_id = fake.random_int(1, 9999) | ||
| response = client.put("/projects/%s" % valid_id, json=valid_project_data, follow_redirects=True) | ||
|
|
||
| assert 200 == response.status_code | ||
| fake_project == json.loads(response.data) | ||
| repository_update_mock.assert_called_once_with(valid_id, valid_project_data) | ||
|
|
||
|
|
||
| def test_update_project_should_reject_bad_request(client: FlaskClient, mocker: MockFixture): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can use the name There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bad request is better |
||
| from time_tracker_api.projects.projects_namespace import project_dao | ||
| invalid_project_data = valid_project_data.copy().update({ | ||
| "type": 'anything', | ||
| }) | ||
| repository_update_mock = mocker.patch.object(project_dao.repository, | ||
| 'update', | ||
| return_value=fake_project) | ||
|
|
||
| valid_id = fake.random_int(1, 9999) | ||
| response = client.put("/projects/%s" % valid_id, json=invalid_project_data, follow_redirects=True) | ||
|
|
||
| assert 400 == response.status_code | ||
| repository_update_mock.assert_not_called() | ||
|
|
||
|
|
||
| def test_update_project_should_return_not_found_with_invalid_id(client: FlaskClient, mocker: MockFixture): | ||
| from time_tracker_api.projects.projects_namespace import project_dao | ||
| from werkzeug.exceptions import NotFound | ||
|
|
||
| invalid_id = fake.random_int(1, 9999) | ||
|
|
||
| repository_update_mock = mocker.patch.object(project_dao.repository, | ||
| 'update', | ||
| side_effect=NotFound) | ||
|
|
||
| response = client.put("/projects/%s" % invalid_id, json=valid_project_data, follow_redirects=True) | ||
|
|
||
| assert 404 == response.status_code | ||
| repository_update_mock.assert_called_once_with(str(invalid_id), valid_project_data) | ||
|
|
||
|
|
||
| def test_delete_project_should_succeed_with_valid_id(client: FlaskClient, mocker: MockFixture): | ||
| from time_tracker_api.projects.projects_namespace import project_dao | ||
|
|
||
| valid_id = fake.random_int(1, 9999) | ||
|
|
||
| repository_remove_mock = mocker.patch.object(project_dao.repository, | ||
| 'remove', | ||
| return_value=None) | ||
|
|
||
| response = client.delete("/projects/%s" % valid_id, follow_redirects=True) | ||
|
|
||
| assert 204 == response.status_code | ||
| assert b'' == response.data | ||
| repository_remove_mock.assert_called_once_with(str(valid_id)) | ||
|
|
||
|
|
||
| def test_delete_project_should_return_not_found_with_invalid_id(client: FlaskClient, mocker: MockFixture): | ||
| from time_tracker_api.projects.projects_namespace import project_dao | ||
| from werkzeug.exceptions import NotFound | ||
|
|
||
| invalid_id = fake.random_int(1, 9999) | ||
|
|
||
| repository_remove_mock = mocker.patch.object(project_dao.repository, | ||
| 'remove', | ||
| side_effect=NotFound) | ||
|
|
||
| response = client.delete("/projects/%s" % invalid_id, follow_redirects=True) | ||
|
|
||
| assert 404 == response.status_code | ||
| repository_remove_mock.assert_called_once_with(str(invalid_id)) | ||
|
|
||
|
|
||
| def test_delete_project_should_return_422_for_invalid_id_format(client: FlaskClient, mocker: MockFixture): | ||
| from time_tracker_api.projects.projects_namespace import project_dao | ||
| from werkzeug.exceptions import UnprocessableEntity | ||
|
|
||
| invalid_id = fake.company() | ||
|
|
||
| repository_remove_mock = mocker.patch.object(project_dao.repository, | ||
| 'remove', | ||
| side_effect=UnprocessableEntity) | ||
|
|
||
| response = client.delete("/projects/%s" % invalid_id, follow_redirects=True) | ||
|
|
||
| assert 422 == response.status_code | ||
| repository_remove_mock.assert_called_once_with(str(invalid_id)) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| import logging | ||
| import os | ||
|
|
||
| from flask import Flask | ||
|
|
@@ -34,16 +35,18 @@ def init_app_config(app: Flask, config_path: str, config_data: dict = None): | |
|
|
||
|
|
||
| def init_app(app: Flask): | ||
| from .database import init_app as init_database | ||
| from time_tracker_api.database import init_app as init_database | ||
| init_database(app) | ||
|
|
||
| from .api import api | ||
| from time_tracker_api.api import api | ||
| api.init_app(app) | ||
|
|
||
| if app.config.get('DEBUG'): | ||
| app.logger.setLevel(logging.INFO) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should not be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMHO |
||
| add_debug_toolbar(app) | ||
|
|
||
|
|
||
|
|
||
| def add_debug_toolbar(app): | ||
| app.config['DEBUG_TB_PANELS'] = ( | ||
| 'flask_debugtoolbar.panels.versions.VersionDebugPanel', | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,7 @@ class Config: | |
| SECRET_KEY = generate_dev_secret_key() | ||
| DATABASE_URI = os.environ.get('DATABASE_URI') | ||
| PROPAGATE_EXCEPTIONS = True | ||
| RESTPLUS_VALIDATE = True | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good. |
||
|
|
||
|
|
||
| class DevelopConfig(Config): | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can use the name
test_create_project_should_fail_with_invalid_requestfor consistency with the previous request.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would rather to leave bad request because that is the verbose name of the HTTP status code
400.