Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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
55 changes: 48 additions & 7 deletions tests/time_tracker_api/api_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,57 @@ def test_remove_required_constraint():
from flask_restplus import Namespace

ns = Namespace('todos', description='Namespace for testing')
sample_model = ns.model('Todo', {
'id': fields.Integer(readonly=True, description='The task unique identifier'),
'task': fields.String(required=True, description='The task details'),
'done': fields.Boolean(required=False, description='Has it being done or not')
})
sample_model = ns.model(
'Todo',
{
'id': fields.Integer(
readonly=True, description='The task unique identifier'
),
'task': fields.String(
required=True, description='The task details'
),
'done': fields.Boolean(
required=False, description='Has it being done or not'
),
},
)

new_model = remove_required_constraint(sample_model)

assert new_model is not sample_model

for attrib in sample_model:
assert new_model[attrib].required is False, "No attribute should be required"
assert new_model[attrib] is not sample_model[attrib], "No attribute should be required"
assert (
new_model[attrib].required is False
), "No attribute should be required"
assert (
new_model[attrib] is not sample_model[attrib]
), "No attribute should be required"


def test_add_update_last_entry_if_overlap():
from time_tracker_api.api import add_update_last_entry_if_overlap
from flask_restplus import fields
from flask_restplus import Namespace

ns = Namespace('todos', description='Namespace for testing')
sample_model = ns.model(
'Todo',
{
'id': fields.Integer(
readonly=True, description='The task unique identifier'
),
'task': fields.String(
required=True, description='The task details'
),
},
)

new_model = add_update_last_entry_if_overlap(sample_model)

assert new_model is not sample_model

update_last_entry_if_overlap = new_model.get(
'update_last_entry_if_overlap'
)
assert update_last_entry_if_overlap is not None
96 changes: 96 additions & 0 deletions tests/time_tracker_api/time_entries/time_entries_model_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,3 +310,99 @@ def test_find_all_v2(
time_entry = result[0]
assert isinstance(time_entry, TimeEntryCosmosDBModel)
assert time_entry.__dict__ == expected_item


@patch(
'time_tracker_api.time_entries.time_entries_repository.TimeEntryCosmosDBRepository.find_partition_key_value'
)
def test_get_last_entry(
find_partition_key_value_mock,
event_context: EventContext,
time_entry_repository: TimeEntryCosmosDBRepository,
):
expected_item = {
'id': 'id',
'start_date': '2021-03-22T10:00:00.000Z',
'end_date': "2021-03-22T11:00:00.000Z",
'description': 'do some testing',
'tenant_id': 'tenant_id',
'project_id': 'project_id',
'activity_id': 'activity_id',
'technologies': ['python'],
}
query_items_mock = Mock(return_value=[expected_item])
time_entry_repository.container = Mock()
time_entry_repository.container.query_items = query_items_mock

result = time_entry_repository.get_last_entry('id1', event_context)
find_partition_key_value_mock.assert_called_once()

assert len(result) == 1
time_entry = result.pop()
assert isinstance(time_entry, TimeEntryCosmosDBModel)
assert time_entry.__dict__ == expected_item


expected_item = {
'id': 'id',
'owner_id': '1',
'start_date': '2021-03-22T10:00:00.000Z',
'end_date': "2021-03-22T11:00:00.000Z",
'description': 'do some testing',
'tenant_id': 'tenant_id',
'project_id': 'project_id',
'activity_id': 'activity_id',
'technologies': ['python'],

}

running_item = {
'id': 'id',
'owner_id': '1',
'update_last_entry_if_overlap': True,
'start_date': '2021-03-22T10:30:00.000Z',
'end_date': '2021-03-22T11:30:00.000Z',
'description': 'do some testing',
'tenant_id': 'tenant_id',
'project_id': 'project_id',
'activity_id': 'activity_id',
'technologies': ['python'],
}

last_item_update = {
'id': 'id',
'owner_id': '1',
'start_date': '2021-03-22T10:00:00.000Z',
'end_date': "2021-03-22T10:30:00.000Z",
'description': 'do some testing',
'tenant_id': 'tenant_id',
'project_id': 'project_id',
'activity_id': 'activity_id',
'technologies': ['python'],
}

@pytest.mark.parametrize("expected_item, running_item, last_item_update",
[(expected_item, running_item, last_item_update)])
def test_update_last_entry(
event_context: EventContext,
time_entry_repository: TimeEntryCosmosDBRepository,
expected_item,
running_item,
last_item_update
):
query_items_mock = Mock(return_value=[expected_item])
time_entry_repository.container = Mock()
time_entry_repository.container.query_items = query_items_mock

partial_update_mock = Mock(return_value=[last_item_update])
time_entry_repository.partial_update = partial_update_mock

result = time_entry_repository.update_last_entry(running_item, event_context)

partial_update_mock.assert_called_once()
query_items_mock.assert_called_once()
assert result == [last_item_update]




30 changes: 30 additions & 0 deletions tests/time_tracker_api/time_entries/time_entries_namespace_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -881,3 +881,33 @@ def test_paginated_sends_max_count_and_offset_on_call_to_repository(
_, kwargs = time_entries_dao.repository.find_all.call_args
assert 'max_count' in kwargs and kwargs['max_count'] is not None
assert 'offset' in kwargs and kwargs['offset'] is not None


def test_update_time_entry_calls_update_last_entry(
client: FlaskClient,
mocker: MockFixture,
valid_header: dict,
valid_id: str,
time_entries_dao,
):
time_entries_dao.repository.partial_update = Mock(return_value={})
time_entries_dao.repository.find = Mock(return_value={})
time_entries_dao.check_whether_current_user_owns_item = Mock()
time_entries_dao.repository.update_last_entry = Mock(return_value={})

update_time_entry = valid_time_entry_input.copy()
update_time_entry['update_last_entry_if_overlap'] = True

response = client.put(
f'/time-entries/{valid_id}',
headers=valid_header,
json=update_time_entry,
follow_redirects=True,
)

assert HTTPStatus.OK == response.status_code
time_entries_dao.repository.partial_update.assert_called_once()
time_entries_dao.repository.find.assert_called_once()
time_entries_dao.check_whether_current_user_owns_item.assert_called_once()
time_entries_dao.repository.update_last_entry.assert_called_once()

70 changes: 58 additions & 12 deletions tests/utils/query_builder_test.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from unittest.mock import patch
from utils.query_builder import CosmosDBQueryBuilder
from utils.query_builder import CosmosDBQueryBuilder, Order
from utils.repository import remove_white_spaces
import pytest

Expand Down Expand Up @@ -41,7 +41,9 @@ def test_add_select_conditions_should_update_select_list(
],
)
def test_add_sql_in_condition_should_update_where_list(
attribute, ids_list, expected_where_condition_list,
attribute,
ids_list,
expected_where_condition_list,
):
query_builder = CosmosDBQueryBuilder().add_sql_in_condition(
attribute, ids_list
Expand All @@ -66,7 +68,9 @@ def test_add_sql_in_condition_should_update_where_list(
],
)
def test_add_sql_where_equal_condition_should_update_where_params_list(
data, expected_where_list, expected_params,
data,
expected_where_list,
expected_params,
):
query_builder = CosmosDBQueryBuilder().add_sql_where_equal_condition(data)

Expand All @@ -91,7 +95,8 @@ def test_add_sql_where_equal_condition_with_None_should_not_update_lists():
[(True, ['NOT IS_DEFINED(c.deleted)']), (False, [])],
)
def test_add_sql_visibility_condition(
visibility_bool, expected_where_list,
visibility_bool,
expected_where_list,
):
query_builder = CosmosDBQueryBuilder().add_sql_visibility_condition(
visibility_bool
Expand All @@ -102,7 +107,12 @@ def test_add_sql_visibility_condition(


@pytest.mark.parametrize(
"limit_value,expected_limit", [(1, 1), (10, 10), (None, None),],
"limit_value,expected_limit",
[
(1, 1),
(10, 10),
(None, None),
],
)
def test_add_sql_limit_condition(limit_value, expected_limit):
query_builder = CosmosDBQueryBuilder().add_sql_limit_condition(limit_value)
Expand All @@ -111,10 +121,16 @@ def test_add_sql_limit_condition(limit_value, expected_limit):


@pytest.mark.parametrize(
"offset_value,expected_offset", [(1, 1), (10, 10), (None, None),],
"offset_value,expected_offset",
[
(1, 1),
(10, 10),
(None, None),
],
)
def test_add_sql_offset_condition(
offset_value, expected_offset,
offset_value,
expected_offset,
):
query_builder = CosmosDBQueryBuilder().add_sql_offset_condition(
offset_value
Expand All @@ -125,10 +141,15 @@ def test_add_sql_offset_condition(

@pytest.mark.parametrize(
"select_conditions,expected_condition",
[([], "*"), (["c.id"], "c.id"), (["c.id", "c.name"], "c.id,c.name"),],
[
([], "*"),
(["c.id"], "c.id"),
(["c.id", "c.name"], "c.id,c.name"),
],
)
def test__build_select_return_fields_in_select_list(
select_conditions, expected_condition,
select_conditions,
expected_condition,
):
query_builder = CosmosDBQueryBuilder().add_select_conditions(
select_conditions
Expand All @@ -148,7 +169,8 @@ def test__build_select_return_fields_in_select_list(
],
)
def test__build_where_should_return_concatenated_conditions(
fields, expected_condition,
fields,
expected_condition,
):
query_builder = CosmosDBQueryBuilder().add_sql_where_equal_condition(
fields
Expand All @@ -164,7 +186,9 @@ def test__build_where_should_return_concatenated_conditions(
[(1, "OFFSET @offset", [{'name': '@offset', 'value': 1}]), (None, "", [])],
)
def test__build_offset(
offset, expected_condition, expected_params,
offset,
expected_condition,
expected_params,
):
query_builder = CosmosDBQueryBuilder().add_sql_offset_condition(offset)

Expand All @@ -179,7 +203,9 @@ def test__build_offset(
[(1, "LIMIT @limit", [{'name': '@limit', 'value': 1}]), (None, "", [])],
)
def test__build_limit(
limit, expected_condition, expected_params,
limit,
expected_condition,
expected_params,
):
query_builder = CosmosDBQueryBuilder().add_sql_limit_condition(limit)

Expand Down Expand Up @@ -235,3 +261,23 @@ def test_build_with_empty_and_None_attributes_return_query_select_all():
assert query == expected_query
assert len(query_builder.get_parameters()) == 0
assert len(query_builder.where_conditions) == 0


def test_order_by_condition():
query_builder = CosmosDBQueryBuilder().add_sql_order_by_condition(
'start_date', Order.DESC
)

assert len(query_builder.order_by) == 2
assert query_builder.order_by == ('start_date', 'DESC')


def test__build_orderBy():
query_builder = CosmosDBQueryBuilder().add_sql_order_by_condition(
'start_date', Order.DESC
)

orderBy_condition = query_builder._CosmosDBQueryBuilder__build_order_By()
expected_order_string = "ORDER BY c.start_date DESC"

assert expected_order_string == orderBy_condition
13 changes: 13 additions & 0 deletions time_tracker_api/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,19 @@ def remove_required_constraint(model: Model):
return result


def add_update_last_entry_if_overlap(time_entry_model: Model):
time_entry_flag = {
'update_last_entry_if_overlap': fields.Boolean(
title='Update last entry if overlap',
required=False,
description='Flag that indicates if the last time entry is updated',
example=True,
)
}
new_model = time_entry_model.clone('TimeEntryInput', time_entry_flag)
return new_model


def create_attributes_filter(
ns: namespace, model: Model, filter_attrib_names: list
) -> RequestParser:
Expand Down
3 changes: 3 additions & 0 deletions time_tracker_api/time_entries/time_entries_dao.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,9 @@ def create(self, data: dict):
def update(self, id, data: dict, description=None):
event_ctx = self.create_event_context("update", description)

if data.get('update_last_entry_if_overlap'):
self.repository.update_last_entry(data, event_ctx)

time_entry = self.repository.find(id, event_ctx)
self.check_whether_current_user_owns_item(time_entry)

Expand Down
Loading