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
Next Next commit
fix: Close #94 Fix tests to support authentication
  • Loading branch information
EliuX committed Apr 22, 2020
commit 7efc921ce662c4d7b2d264e2837c5acb5fe5690c
1 change: 0 additions & 1 deletion commons/data_access_layer/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ def delete(self, id):


def init_app(app: Flask) -> None:
init_sql(app) # TODO Delete after the migration to Cosmos DB has finished.
init_cosmos_db(app)


Expand Down
12 changes: 0 additions & 12 deletions commons/data_access_layer/sql.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@
from flask_sqlalchemy import SQLAlchemy

from commons.data_access_layer.database import CRUDDao, ID_MAX_LENGTH
from time_tracker_api.security import current_user_id

db: SQLAlchemy = None
AuditedSQLModel = None


def handle_commit_issues(f):
Expand All @@ -25,16 +23,6 @@ def init_app(app: Flask) -> None:
global db
db = SQLAlchemy(app)

global AuditedSQLModel

class AuditedSQLModelClass():
created_at = db.Column(db.DateTime, server_default=db.func.now())
updated_at = db.Column(db.DateTime, onupdate=datetime.utcnow)
created_by = db.Column(db.String(ID_MAX_LENGTH), default=current_user_id)
updated_by = db.Column(db.String(ID_MAX_LENGTH), onupdate=current_user_id)

AuditedSQLModel = AuditedSQLModelClass


class SQLRepository():
def __init__(self, model_type: type):
Expand Down
7 changes: 0 additions & 7 deletions tests/commons/data_access_layer/sql_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ def test_create(sql_repository):

assert result is not None
assert result.id is not None
assert result.created_at is not None
assert result.created_by is not None
assert result.updated_at is None
assert result.updated_by is None

existing_elements_registry.append(result)

Expand All @@ -43,9 +39,6 @@ def test_update(sql_repository):
assert updated_element.id == existing_element.id
assert updated_element.name == "Jon Snow"
assert updated_element.age == 34
assert updated_element.updated_at is not None
assert updated_element.updated_at > updated_element.created_at
assert updated_element.updated_by is not None


def test_find_all(sql_repository):
Expand Down
47 changes: 10 additions & 37 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,44 +7,14 @@
from flask.testing import FlaskClient

from commons.data_access_layer.cosmos_db import CosmosDBRepository, datetime_str, current_datetime
from commons.data_access_layer.database import init_sql
from time_tracker_api import create_app
from time_tracker_api.security import get_or_generate_dev_secret_key
from time_tracker_api.time_entries.time_entries_model import TimeEntryCosmosDBRepository

fake = Faker()
Faker.seed()

TEST_USER = {
"name": "[email protected]",
"password": "secret"
}


class User:
def __init__(self, username, password):
self.username = username
self.password = password


class AuthActions:
"""Auth actions container in tests"""

def __init__(self, app, client):
self._app = app
self._client = client

# def login(self, username=TEST_USER["name"],
# password=TEST_USER["password"]):
# login_url = url_for("security.login", self._app)
# return open_with_basic_auth(self._client,
# login_url,
# username,
# password)
#
# def logout(self):
# return self._client.get(url_for("security.logout", self._app),
# follow_redirects=True)


@pytest.fixture(scope='session')
def app() -> Flask:
Expand All @@ -58,9 +28,12 @@ def client(app: Flask) -> FlaskClient:


@pytest.fixture(scope="module")
def sql_model_class():
from commons.data_access_layer.sql import db, AuditedSQLModel
class PersonSQLModel(db.Model, AuditedSQLModel):
def sql_model_class(app: Flask):
with app.app_context():
init_sql(app)

from commons.data_access_layer.sql import db
class PersonSQLModel(db.Model):
__tablename__ = 'test'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80), unique=False, nullable=False)
Expand Down Expand Up @@ -186,11 +159,11 @@ def running_time_entry(time_entry_repository: TimeEntryCosmosDBRepository,


@pytest.fixture(scope="session")
def valid_jwt(app: Flask) -> str:
def valid_jwt(app: Flask, tenant_id: str, owner_id: str) -> str:
expiration_time = datetime.utcnow() + timedelta(seconds=3600)
return jwt.encode({
"iss": "https://securityioet.b2clogin.com/%s/v2.0/" % fake.uuid4(),
"oid": fake.uuid4(),
"iss": "https://securityioet.b2clogin.com/%s/v2.0/" % tenant_id,
"oid": owner_id,
'exp': expiration_time
}, key=get_or_generate_dev_secret_key()).decode("UTF-8")

Expand Down
99 changes: 69 additions & 30 deletions tests/time_tracker_api/activities/activities_namespace_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
from flask_restplus._http import HTTPStatus
from pytest_mock import MockFixture

from time_tracker_api.security import current_user_tenant_id

fake = Faker()

valid_activity_data = {
Expand Down Expand Up @@ -36,34 +34,45 @@ def test_create_activity_should_succeed_with_valid_request(client: FlaskClient,
repository_create_mock.assert_called_once()


def test_create_activity_should_reject_bad_request(client: FlaskClient, mocker: MockFixture):
def test_create_activity_should_reject_bad_request(client: FlaskClient,
mocker: MockFixture,
valid_header: dict):
from time_tracker_api.activities.activities_namespace import activity_dao
repository_create_mock = mocker.patch.object(activity_dao.repository,
'create',
return_value=fake_activity)

response = client.post("/activities", json=None, follow_redirects=True)
response = client.post("/activities",
headers=valid_header,
json=None,
follow_redirects=True)

assert HTTPStatus.BAD_REQUEST == response.status_code
repository_create_mock.assert_not_called()


def test_list_all_activities(client: FlaskClient, mocker: MockFixture):
def test_list_all_activities(client: FlaskClient,
mocker: MockFixture,
tenant_id: str,
valid_header: dict):
from time_tracker_api.activities.activities_namespace import activity_dao
repository_find_all_mock = mocker.patch.object(activity_dao.repository,
'find_all',
return_value=[])

response = client.get("/activities", follow_redirects=True)
response = client.get("/activities",
headers=valid_header,
follow_redirects=True)

assert HTTPStatus.OK == response.status_code
json_data = json.loads(response.data)
assert [] == json_data
repository_find_all_mock.assert_called_once()
repository_find_all_mock.assert_called_once_with(partition_key_value=tenant_id)


def test_get_activity_should_succeed_with_valid_id(client: FlaskClient,
mocker: MockFixture,
tenant_id: str,
valid_header: dict):
from time_tracker_api.activities.activities_namespace import activity_dao

Expand All @@ -79,12 +88,12 @@ def test_get_activity_should_succeed_with_valid_id(client: FlaskClient,

assert HTTPStatus.OK == response.status_code
fake_activity == json.loads(response.data)
repository_find_mock.assert_called_once_with(str(valid_id),
partition_key_value=current_user_tenant_id())
repository_find_mock.assert_called_once_with(str(valid_id), partition_key_value=tenant_id)


def test_get_activity_should_return_not_found_with_invalid_id(client: FlaskClient,
mocker: MockFixture,
tenant_id: str,
valid_header: dict):
from time_tracker_api.activities.activities_namespace import activity_dao
from werkzeug.exceptions import NotFound
Expand All @@ -101,10 +110,11 @@ def test_get_activity_should_return_not_found_with_invalid_id(client: FlaskClien

assert HTTPStatus.NOT_FOUND == response.status_code
repository_find_mock.assert_called_once_with(str(invalid_id),
partition_key_value=current_user_tenant_id())
partition_key_value=tenant_id)


def test_get_activity_should_return_422_for_invalid_id_format(client: FlaskClient, mocker: MockFixture):
def test_get_activity_should_return_422_for_invalid_id_format(client: FlaskClient,
mocker: MockFixture):
from time_tracker_api.activities.activities_namespace import activity_dao
from werkzeug.exceptions import UnprocessableEntity

Expand All @@ -117,41 +127,54 @@ def test_get_activity_should_return_422_for_invalid_id_format(client: FlaskClien
response = client.get("/activities/%s" % invalid_id, follow_redirects=True)

assert HTTPStatus.UNPROCESSABLE_ENTITY == response.status_code
repository_find_mock.assert_called_once_with(str(invalid_id),
partition_key_value=current_user_tenant_id())
repository_find_mock.assert_not_called()


def test_update_activity_should_succeed_with_valid_data(client: FlaskClient, mocker: MockFixture):
def test_update_activity_should_succeed_with_valid_data(client: FlaskClient,
tenant_id: str,
mocker: MockFixture,
valid_header: dict):
from time_tracker_api.activities.activities_namespace import activity_dao

repository_update_mock = mocker.patch.object(activity_dao.repository,
'partial_update',
return_value=fake_activity)

valid_id = fake.random_int(1, 9999)
response = client.put("/activities/%s" % valid_id, json=valid_activity_data, follow_redirects=True)
valid_id = fake.uuid4()
response = client.put("/activities/%s" % valid_id,
headers=valid_header,
json=valid_activity_data,
follow_redirects=True)

assert HTTPStatus.OK == response.status_code
fake_activity == json.loads(response.data)
repository_update_mock.assert_called_once_with(str(valid_id),
changes=valid_activity_data,
partition_key_value=current_user_tenant_id())
partition_key_value=tenant_id)


def test_update_activity_should_reject_bad_request(client: FlaskClient, mocker: MockFixture):
def test_update_activity_should_reject_bad_request(client: FlaskClient,
mocker: MockFixture,
valid_header: dict):
from time_tracker_api.activities.activities_namespace import activity_dao
repository_update_mock = mocker.patch.object(activity_dao.repository,
'partial_update',
return_value=fake_activity)

valid_id = fake.random_int(1, 9999)
response = client.put("/activities/%s" % valid_id, json=None, follow_redirects=True)
response = client.put("/activities/%s" % valid_id,
headers=valid_header,
json=None,
follow_redirects=True)

assert HTTPStatus.BAD_REQUEST == response.status_code
repository_update_mock.assert_not_called()


def test_update_activity_should_return_not_found_with_invalid_id(client: FlaskClient, mocker: MockFixture):
def test_update_activity_should_return_not_found_with_invalid_id(client: FlaskClient,
tenant_id: str,
mocker: MockFixture,
valid_header: dict):
from time_tracker_api.activities.activities_namespace import activity_dao
from werkzeug.exceptions import NotFound

Expand All @@ -162,16 +185,20 @@ def test_update_activity_should_return_not_found_with_invalid_id(client: FlaskCl
side_effect=NotFound)

response = client.put("/activities/%s" % invalid_id,
headers=valid_header,
json=valid_activity_data,
follow_redirects=True)

assert HTTPStatus.NOT_FOUND == response.status_code
repository_update_mock.assert_called_once_with(str(invalid_id),
changes=valid_activity_data,
partition_key_value=current_user_tenant_id())
partition_key_value=tenant_id)


def test_delete_activity_should_succeed_with_valid_id(client: FlaskClient, mocker: MockFixture):
def test_delete_activity_should_succeed_with_valid_id(client: FlaskClient,
mocker: MockFixture,
tenant_id: str,
valid_header: dict):
from time_tracker_api.activities.activities_namespace import activity_dao

valid_id = fake.random_int(1, 9999)
Expand All @@ -180,15 +207,20 @@ def test_delete_activity_should_succeed_with_valid_id(client: FlaskClient, mocke
'delete',
return_value=None)

response = client.delete("/activities/%s" % valid_id, follow_redirects=True)
response = client.delete("/activities/%s" % valid_id,
headers=valid_header,
follow_redirects=True)

assert HTTPStatus.NO_CONTENT == response.status_code
assert b'' == response.data
repository_remove_mock.assert_called_once_with(str(valid_id),
partition_key_value=current_user_tenant_id())
partition_key_value=tenant_id)


def test_delete_activity_should_return_not_found_with_invalid_id(client: FlaskClient, mocker: MockFixture):
def test_delete_activity_should_return_not_found_with_invalid_id(client: FlaskClient,
mocker: MockFixture,
tenant_id: str,
valid_header: dict):
from time_tracker_api.activities.activities_namespace import activity_dao
from werkzeug.exceptions import NotFound

Expand All @@ -198,14 +230,19 @@ def test_delete_activity_should_return_not_found_with_invalid_id(client: FlaskCl
'delete',
side_effect=NotFound)

response = client.delete("/activities/%s" % invalid_id, follow_redirects=True)
response = client.delete("/activities/%s" % invalid_id,
headers=valid_header,
follow_redirects=True)

assert HTTPStatus.NOT_FOUND == response.status_code
repository_remove_mock.assert_called_once_with(str(invalid_id),
partition_key_value=current_user_tenant_id())
partition_key_value=tenant_id)


def test_delete_activity_should_return_422_for_invalid_id_format(client: FlaskClient, mocker: MockFixture):
def test_delete_activity_should_return_422_for_invalid_id_format(client: FlaskClient,
mocker: MockFixture,
tenant_id: str,
valid_header: dict):
from time_tracker_api.activities.activities_namespace import activity_dao
from werkzeug.exceptions import UnprocessableEntity

Expand All @@ -215,8 +252,10 @@ def test_delete_activity_should_return_422_for_invalid_id_format(client: FlaskCl
'delete',
side_effect=UnprocessableEntity)

response = client.delete("/activities/%s" % invalid_id, follow_redirects=True)
response = client.delete("/activities/%s" % invalid_id,
headers=valid_header,
follow_redirects=True)

assert HTTPStatus.UNPROCESSABLE_ENTITY == response.status_code
repository_remove_mock.assert_called_once_with(str(invalid_id),
partition_key_value=current_user_tenant_id())
partition_key_value=tenant_id)
Loading