Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Apply requested changes
  • Loading branch information
EliuX committed Mar 24, 2020
commit 4ce46121dddbf4300e55e367be3b84fc4f34b3dc
5 changes: 5 additions & 0 deletions .env.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Package where the app is located
export FLASK_APP=time_tracker_api

# The database connection URI. Check out the README.md for more details
DATABASE_URI=mssql+pyodbc://<user>:<password>@time-tracker-srv.database.windows.net/<database>?driver\=ODBC Driver 17 for SQL Server
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,6 @@ htmlcov/
.env
timetracker-api-postman-collection.json
swagger.json

# Ignore any SQLite generated database
*.db
25 changes: 22 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,34 @@ a link to the swagger.json with the definition of the api.
## Development

### Test
We are using Pytest](https://docs.pytest.org/en/latest/index.html) for tests. The tests are located in the package
We are using [Pytest](https://docs.pytest.org/en/latest/index.html) for tests. The tests are located in the package
`tests` and use the [conventions for python test discovery](https://docs.pytest.org/en/latest/goodpractices.html#test-discovery).

To run the tests just execute:
#### Integration tests
The [integrations tests](https://en.wikipedia.org/wiki/Integration_testing) verifies that all the components of the app
are working well together. These are the default tests we should run:

```bash
```dotenv
python3 -m pytest -v --ignore=tests/sql_repository_test.py
```

As you may have noticed we are ignoring the tests related with the repository.


#### System tests
In addition to the integration testing we might include tests to the data access layer in order to verify that the
persisted data is being managed the right way, i.e. it actually works. We may classify the execution of all the existing
tests as [system testing](https://en.wikipedia.org/wiki/System_testing):

```dotenv
python3 -m pytest -v
```

The database tests will be done in the table `tests` of the database specified by the variable `DATABASE_URI`. If this
variable is not specified it will automatically connect to `sqlite:///tests.db`. This will do, because we are using SQL
Alchemy to be able connect to any SQL database maintaining the same codebase.


The option `-v` shows which tests failed or succeeded. Have into account that you can also debug each test
(test_* files) with the help of an IDE like PyCharm.

Expand Down
4 changes: 2 additions & 2 deletions run.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
"""
from time_tracker_api import create_app

app = create_app()
print("TimeTracker API server was created")
app = create_app('time_tracker_api.config.ProductionConfig')
print("TimeTracker API server created!")
6 changes: 2 additions & 4 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@

from time_tracker_api import create_app

CONFIGURATIONS = ['AzureSQLDatabaseDevelopTestConfig']


@pytest.fixture(scope='session', params=CONFIGURATIONS)
@pytest.fixture(scope='session')
def app(request: FixtureRequest) -> Flask:
return create_app("time_tracker_api.config.%s" % request.param)
return create_app("time_tracker_api.config.TestConfig")


@pytest.fixture
Expand Down
12 changes: 7 additions & 5 deletions tests/projects/projects_namespace_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ def test_get_project_should_return_422_for_invalid_id_format(client: FlaskClient
repository_find_mock.assert_called_once_with(str(invalid_id))


def update_project_should_succeed_with_valid_data(client: FlaskClient, mocker: MockFixture):
def test_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,
Expand All @@ -118,7 +118,7 @@ def update_project_should_succeed_with_valid_data(client: FlaskClient, mocker: M

assert 200 == response.status_code
fake_project == json.loads(response.data)
repository_update_mock.assert_called_once_with(valid_id, valid_project_data)
repository_update_mock.assert_called_once_with(str(valid_id), valid_project_data)


def test_update_project_should_reject_bad_request(client: FlaskClient, mocker: MockFixture):
Copy link
Contributor

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_update_project_should_fail_with_invalid_request as before.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bad request is better

Expand Down Expand Up @@ -147,7 +147,9 @@ def test_update_project_should_return_not_found_with_invalid_id(client: FlaskCli
'update',
side_effect=NotFound)

response = client.put("/projects/%s" % invalid_id, json=valid_project_data, follow_redirects=True)
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)
Expand Down Expand Up @@ -192,8 +194,8 @@ def test_delete_project_should_return_422_for_invalid_id_format(client: FlaskCli
invalid_id = fake.company()

repository_remove_mock = mocker.patch.object(project_dao.repository,
'remove',
side_effect=UnprocessableEntity)
'remove',
side_effect=UnprocessableEntity)

response = client.delete("/projects/%s" % invalid_id, follow_redirects=True)

Expand Down
1 change: 0 additions & 1 deletion time_tracker_api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ def init_app(app: Flask):
add_debug_toolbar(app)



def add_debug_toolbar(app):
app.config['DEBUG_TB_PANELS'] = (
'flask_debugtoolbar.panels.versions.VersionDebugPanel',
Expand Down
27 changes: 17 additions & 10 deletions time_tracker_api/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,44 @@ class Config:
RESTPLUS_VALIDATE = True
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good.



class DevelopConfig(Config):
class DevelopmentConfig(Config):
DEBUG = True
FLASK_DEBUG = True
FLASK_ENV = "develop"
FLASK_ENV = "development"


class TestConfig(Config):
class SQLConfig(Config):
SQLALCHEMY_DATABASE_URI = Config.DATABASE_URI
SQLALCHEMY_COMMIT_ON_TEARDOWN = True
SQLALCHEMY_TRACK_MODIFICATIONS = False


class TestConfig(SQLConfig):
TESTING = True
FLASK_DEBUG = True
TEST_TABLE = 'tests'
DATABASE_URI = os.environ.get('DATABASE_URI', 'sqlite:///tests.db')
SQLALCHEMY_DATABASE_URI = DATABASE_URI


class SQLConfig(Config):
SQLALCHEMY_DATABASE_URI = Config.DATABASE_URI
SQLALCHEMY_COMMIT_ON_TEARDOWN = True
SQLALCHEMY_TRACK_MODIFICATIONS = False
class ProductionConfig(Config):
FLASK_ENV = 'production'


class AzureConfig(SQLConfig):
pass


class AzureSQLDatabaseDevelopConfig(DevelopConfig, AzureConfig):
class AzureDevelopmentConfig(DevelopmentConfig, AzureConfig):
pass


class AzureSQLDatabaseDevelopTestConfig(TestConfig, AzureSQLDatabaseDevelopConfig):
class AzureProductionConfig(ProductionConfig, AzureConfig):
pass


DefaultConfig = AzureSQLDatabaseDevelopConfig
DefaultConfig = AzureDevelopmentConfig
ProductionConfig = AzureProductionConfig


class CLIConfig(DefaultConfig):
Expand Down
3 changes: 0 additions & 3 deletions time_tracker_api/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
To know more about protocols and subtyping check out PEP-0544
"""
import abc
import enum
from datetime import datetime

from flask import Flask

Expand Down Expand Up @@ -75,4 +73,3 @@ def init_app(app: Flask) -> None:
init_app(app)
global seeder
seeder = SQLSeeder()