Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
12 changes: 11 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import copy
from datetime import datetime, timedelta
from http import HTTPStatus

import jwt
import pytest
Expand Down Expand Up @@ -217,13 +218,22 @@ def time_entries_dao():
return time_entries_dao


@pytest.fixture(scope="module")
@pytest.fixture
def running_time_entry(
time_entry_repository: TimeEntryCosmosDBRepository,
owner_id: str,
tenant_id: str,
event_context: EventContext,
mocker,
):
mocker.patch(
'time_tracker_api.time_entries.time_entries_repository.are_related_entry_entities_valid',
return_value={
"is_valid": True,
"status_code": HTTPStatus.OK,
"message": "Related entry entities valid",
},
)
current_time_entry_repository = copy.copy(time_entry_repository)
created_time_entry = current_time_entry_repository.create(
{
Expand Down
28 changes: 22 additions & 6 deletions tests/time_tracker_api/time_entries/time_entries_model_test.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
from http import HTTPStatus
from unittest.mock import Mock, patch
import pytest
from faker import Faker

from commons.data_access_layer.cosmos_db import CustomError
from commons.data_access_layer.database import EventContext
from time_tracker_api.time_entries.time_entries_model import (
TimeEntryCosmosDBModel,
)
from time_tracker_api.time_entries.time_entries_repository import (
TimeEntryCosmosDBRepository,
TimeEntryCosmosDBModel,
Expand All @@ -17,6 +16,7 @@ def create_time_entry(
end_date: str,
owner_id: str,
tenant_id: str,
mocker,
event_context: EventContext,
time_entry_repository: TimeEntryCosmosDBRepository,
) -> TimeEntryCosmosDBModel:
Expand All @@ -30,6 +30,15 @@ def create_time_entry(
"tenant_id": tenant_id,
}

mocker.patch(
'time_tracker_api.time_entries.time_entries_repository.are_related_entry_entities_valid',
return_value={
"is_valid": True,
"status_code": HTTPStatus.OK,
"message": "Related entry entities valid",
},
)

created_item = time_entry_repository.create(
data, event_context, mapper=TimeEntryCosmosDBModel
)
Expand Down Expand Up @@ -78,6 +87,7 @@ def test_find_interception_with_date_range_should_find(
end_date_: str,
owner_id: str,
tenant_id: str,
mocker,
time_entry_repository: TimeEntryCosmosDBRepository,
event_context: EventContext,
):
Expand All @@ -86,6 +96,7 @@ def test_find_interception_with_date_range_should_find(
end_date,
owner_id,
tenant_id,
mocker,
event_context,
time_entry_repository,
)
Expand Down Expand Up @@ -142,12 +153,14 @@ def test_find_interception_with_date_range_should_not_find(
tenant_id: str,
time_entry_repository: TimeEntryCosmosDBRepository,
event_context: EventContext,
mocker,
):
existing_item = create_time_entry(
start_date,
end_date,
owner_id,
tenant_id,
mocker,
event_context,
time_entry_repository,
)
Expand All @@ -171,14 +184,17 @@ def test_find_interception_should_ignore_id_of_existing_item(
tenant_id: str,
time_entry_repository: TimeEntryCosmosDBRepository,
event_context: EventContext,
mocker,
):
start_date = "2020-10-01T05:00:00.000Z"
end_date = "2020-10-01T10:00:00.000Z"

existing_item = create_time_entry(
start_date,
end_date,
owner_id,
tenant_id,
mocker,
event_context,
time_entry_repository,
)
Expand Down Expand Up @@ -229,10 +245,10 @@ def test_find_running_should_not_find_any_item(
owner_id: str,
time_entry_repository: TimeEntryCosmosDBRepository,
):
try:
with pytest.raises(CustomError) as custom_error:
time_entry_repository.find_running(tenant_id, owner_id)
except Exception as e:
assert type(e) is StopIteration

assert custom_error.value.code == HTTPStatus.NO_CONTENT


@patch(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,15 @@ def test_create_with_valid_uuid_format_should_return_created(
valid_uuid: str,
time_entries_dao,
):
mocker.patch(
'time_tracker_api.time_entries.time_entries_repository.are_related_entry_entities_valid',
return_value={
"is_valid": True,
"status_code": HTTPStatus.OK,
"message": "Related entry entities valid",
},
)

repository_container_create_item_mock = mocker.patch.object(
time_entries_dao.repository.container,
'create_item',
Expand Down
113 changes: 113 additions & 0 deletions tests/utils/validate_entries_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
from http import HTTPStatus

from azure.cosmos.exceptions import CosmosResourceNotFoundError
from faker import Faker

from time_tracker_api.activities import activities_model
from time_tracker_api.activities.activities_model import ActivityCosmosDBDao
from time_tracker_api.projects.projects_model import ProjectCosmosDBDao
from utils.validate_entries import (
are_related_entry_entities_valid,
exists_related_entity,
)

fake = Faker()


def test_validate_related_entry_entities_must_failed_if_project_id_is_empty():
are_entities_valid = are_related_entry_entities_valid(
project_id=None, activity_id=fake.uuid4()
)

assert are_entities_valid.get('is_valid') is False
assert are_entities_valid.get('status_code') == HTTPStatus.BAD_REQUEST
assert are_entities_valid.get('message') == "Project id can not be empty"


def test_validate_related_entry_entities_must_failed_if_activity_id_is_empty():
are_entities_valid = are_related_entry_entities_valid(
project_id=fake.uuid4(), activity_id=None
)

assert are_entities_valid.get('is_valid') is False
assert are_entities_valid.get('status_code') == HTTPStatus.BAD_REQUEST
assert are_entities_valid.get('message') == "Activity id can not be empty"


def test_validate_related_entry_entities_must_pass_if_the_data_is_valid(
mocker,
):
mocker.patch.object(ProjectCosmosDBDao, 'get')
mocker.patch.object(ActivityCosmosDBDao, 'get')

are_entities_valid = are_related_entry_entities_valid(
project_id=fake.uuid4(), activity_id=fake.uuid4()
)

assert are_entities_valid.get('is_valid') is True
assert are_entities_valid.get('status_code') == HTTPStatus.OK
assert are_entities_valid.get('message') == 'Related entry entities valid'


def test_validate_related_entry_entities_must_fail_if_the_project_id_does_not_exists(
mocker,
):
mocker.patch(
'utils.validate_entries.exists_related_entity', return_value=False
)

are_entities_valid = are_related_entry_entities_valid(
project_id=fake.uuid4(), activity_id=fake.uuid4()
)

assert are_entities_valid.get('is_valid') is False
assert are_entities_valid.get('status_code') == HTTPStatus.BAD_REQUEST
assert (
are_entities_valid.get('message') == 'Related Project does not exists'
)


def test_validate_related_entry_entities_must_fail_if_the_activity_id_does_not_exists(
mocker,
):
mocker.patch.object(ProjectCosmosDBDao, 'get')

mocker.patch.object(
ActivityCosmosDBDao, 'get', side_effect=CosmosResourceNotFoundError
)

are_entities_valid = are_related_entry_entities_valid(
project_id=fake.uuid4(), activity_id=fake.uuid4()
)

assert are_entities_valid.get('is_valid') is False
assert are_entities_valid.get('status_code') == HTTPStatus.BAD_REQUEST
assert (
are_entities_valid.get('message') == 'Related Activity does not exists'
)


def test_exists_related_entity_should_return_true_if_entity_exists(mocker):
mocker.patch.object(ActivityCosmosDBDao, 'get')
activity_dao = activities_model.create_dao()

exists_entity = exists_related_entity(
related_id=fake.uuid4(), dao=activity_dao
)

assert exists_entity is True


def test_exists_related_entity_should_return_false_if_entity_does_not_exists(
mocker,
):
mocker.patch.object(
ActivityCosmosDBDao, 'get', side_effect=CosmosResourceNotFoundError
)
activity_dao = activities_model.create_dao()

exists_entity = exists_related_entity(
related_id=fake.uuid4(), dao=activity_dao
)

assert exists_entity is False
2 changes: 1 addition & 1 deletion time_tracker_api/time_entries/time_entries_namespace.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
),
'activity_id': UUID(
title='Activity',
required=False,
required=True,
description='The id of the selected activity',
example=faker.uuid4(),
),
Expand Down
16 changes: 16 additions & 0 deletions time_tracker_api/time_entries/time_entries_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
)
from utils.query_builder import CosmosDBQueryBuilder, Order
from utils.time import str_to_datetime
from utils.validate_entries import are_related_entry_entities_valid


class TimeEntryCosmosDBRepository(CosmosDBRepository):
Expand Down Expand Up @@ -303,6 +304,21 @@ def find_running(
def validate_data(self, data, event_context: EventContext):
start_date = data.get('start_date')

related_project_id = data.get('project_id')
related_activity_id = data.get('activity_id')

are_related_entities_valid = are_related_entry_entities_valid(
project_id=related_project_id, activity_id=related_activity_id
)

if not are_related_entities_valid.get('is_valid'):
status_code = are_related_entities_valid.get('status_code')
message = are_related_entities_valid.get('message')
raise CustomError(
status_code,
description=message,
)

if data.get('end_date') is not None:
if data['end_date'] <= start_date:
raise CustomError(
Expand Down
58 changes: 58 additions & 0 deletions utils/validate_entries.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from http import HTTPStatus

from azure.cosmos.exceptions import CosmosResourceNotFoundError

from time_tracker_api.projects import projects_model
from time_tracker_api.activities import activities_model


def are_related_entry_entities_valid(project_id: str, activity_id: str):
if not project_id:
return {
"is_valid": False,
"status_code": HTTPStatus.BAD_REQUEST,
"message": "Project id can not be empty",
}

if not activity_id:
return {
"is_valid": False,
"status_code": HTTPStatus.BAD_REQUEST,
"message": "Activity id can not be empty",
}

exists_project = exists_related_entity(
project_id, projects_model.create_dao()
)

if not exists_project:
return {
"is_valid": False,
"status_code": HTTPStatus.BAD_REQUEST,
"message": "Related Project does not exists",
}

exists_activity = exists_related_entity(
activity_id, activities_model.create_dao()
)

if not exists_activity:
return {
"is_valid": False,
"status_code": HTTPStatus.BAD_REQUEST,
"message": "Related Activity does not exists",
}

return {
"is_valid": True,
"status_code": HTTPStatus.OK,
"message": "Related entry entities valid",
}


def exists_related_entity(related_id: str, dao):
try:
dao.get(related_id)
return True
except CosmosResourceNotFoundError:
return False