From ce996032bf4cf20c129b84ad293db4c031571c95 Mon Sep 17 00:00:00 2001 From: roberto Date: Tue, 6 Oct 2020 13:23:07 -0500 Subject: [PATCH] fix: allow limits overlap in time entries #217 --- .../time_entries/time_entries_model_test.py | 164 +++++++++++++----- .../time_entries/time_entries_model.py | 28 +-- 2 files changed, 130 insertions(+), 62 deletions(-) diff --git a/tests/time_tracker_api/time_entries/time_entries_model_test.py b/tests/time_tracker_api/time_entries/time_entries_model_test.py index f8a403f1..002e8ccf 100644 --- a/tests/time_tracker_api/time_entries/time_entries_model_test.py +++ b/tests/time_tracker_api/time_entries/time_entries_model_test.py @@ -1,37 +1,27 @@ -from datetime import datetime, timedelta - import pytest from faker import Faker -from utils.time import datetime_str, current_datetime from commons.data_access_layer.database import EventContext from time_tracker_api.time_entries.time_entries_model import ( TimeEntryCosmosDBRepository, TimeEntryCosmosDBModel, - container_definition, ) -fake = Faker() - -now = current_datetime() -yesterday = current_datetime() - timedelta(days=1) -two_days_ago = current_datetime() - timedelta(days=2) - def create_time_entry( - start_date: datetime, - end_date: datetime, + start_date: str, + end_date: str, owner_id: str, tenant_id: str, event_context: EventContext, time_entry_repository: TimeEntryCosmosDBRepository, ) -> TimeEntryCosmosDBModel: data = { - "project_id": fake.uuid4(), - "activity_id": fake.uuid4(), - "description": fake.paragraph(nb_sentences=2), - "start_date": datetime_str(start_date), - "end_date": datetime_str(end_date), + "project_id": Faker().uuid4(), + "activity_id": Faker().uuid4(), + "description": Faker().paragraph(nb_sentences=2), + "start_date": start_date, + "end_date": end_date, "owner_id": owner_id, "tenant_id": tenant_id, } @@ -43,59 +33,146 @@ def create_time_entry( @pytest.mark.parametrize( - 'start_date,end_date', [(two_days_ago, yesterday), (now, None)] + 'start_date,end_date,start_date_,end_date_', + [ + ( + "2020-10-01T05:00:00.000Z", + "2020-10-01T10:00:00.000Z", + "2020-10-01T05:00:00.000Z", + "2020-10-01T10:00:00.000Z", + ), + ( + "2020-10-01T05:00:00.000Z", + "2020-10-01T10:00:00.000Z", + "2020-10-01T07:00:00.000Z", + "2020-10-01T12:00:00.000Z", + ), + ( + "2020-10-01T05:00:00.000Z", + "2020-10-01T10:00:00.000Z", + "2020-10-01T02:00:00.000Z", + "2020-10-01T07:00:00.000Z", + ), + ( + "2020-10-01T05:00:00.000Z", + "2020-10-01T10:00:00.000Z", + "2020-10-01T02:00:00.000Z", + "2020-10-01T12:00:00.000Z", + ), + ], ) def test_find_interception_with_date_range_should_find( - start_date: datetime, - end_date: datetime, + start_date: str, + end_date: str, + start_date_: str, + end_date_: str, owner_id: str, tenant_id: str, time_entry_repository: TimeEntryCosmosDBRepository, + event_context: EventContext, ): - event_ctx = EventContext( - container_definition["id"], - "create", - tenant_id=tenant_id, - user_id=owner_id, - ) - existing_item = create_time_entry( start_date, end_date, owner_id, tenant_id, - event_ctx, + event_context, time_entry_repository, ) try: result = time_entry_repository.find_interception_with_date_range( - datetime_str(yesterday), datetime_str(now), owner_id, tenant_id + start_date_, end_date_, owner_id, tenant_id ) assert result is not None assert len(result) > 0 assert any([existing_item.id == item.id for item in result]) finally: - time_entry_repository.delete_permanently(existing_item.id, event_ctx) + time_entry_repository.delete_permanently( + existing_item.id, event_context + ) -def test_find_interception_should_ignore_id_of_existing_item( +@pytest.mark.parametrize( + 'start_date,end_date,start_date_,end_date_', + [ + ( + "2020-10-01T05:00:00.000Z", + "2020-10-01T10:00:00.000Z", + "2020-10-01T10:00:00.000Z", + "2020-10-01T15:00:00.000Z", + ), + ( + "2020-10-01T05:00:00.000Z", + "2020-10-01T10:00:00.000Z", + "2020-10-01T12:00:00.000Z", + "2020-10-01T15:00:00.000Z", + ), + ( + "2020-10-01T05:00:00.000Z", + "2020-10-01T10:00:00.000Z", + "2020-10-01T02:00:00.000Z", + "2020-10-01T05:00:00.000Z", + ), + ( + "2020-10-01T05:00:00.000Z", + "2020-10-01T10:00:00.000Z", + "2020-10-01T02:00:00.000Z", + "2020-10-01T04:00:00.000Z", + ), + ], +) +def test_find_interception_with_date_range_should_not_find( + start_date: str, + end_date: str, + start_date_: str, + end_date_: str, owner_id: str, tenant_id: str, time_entry_repository: TimeEntryCosmosDBRepository, + event_context: EventContext, ): - event_ctx = EventContext( - container_definition["id"], - "create", - tenant_id=tenant_id, - user_id=owner_id, + existing_item = create_time_entry( + start_date, + end_date, + owner_id, + tenant_id, + event_context, + time_entry_repository, ) - start_date = datetime_str(yesterday) - end_date = datetime_str(now) + + try: + result = time_entry_repository.find_interception_with_date_range( + start_date_, end_date_, owner_id, tenant_id + ) + + assert result == [] + assert len(result) == 0 + assert not any([existing_item.id == item.id for item in result]) + finally: + time_entry_repository.delete_permanently( + existing_item.id, event_context + ) + + +def test_find_interception_should_ignore_id_of_existing_item( + owner_id: str, + tenant_id: str, + time_entry_repository: TimeEntryCosmosDBRepository, + event_context: EventContext, +): + start_date = "2020-10-01T05:00:00.000Z" + end_date = "2020-10-01T10:00:00.000Z" existing_item = create_time_entry( - yesterday, now, owner_id, tenant_id, event_ctx, time_entry_repository + start_date, + end_date, + owner_id, + tenant_id, + event_context, + time_entry_repository, ) + try: colliding_result = time_entry_repository.find_interception_with_date_range( start_date, end_date, owner_id, tenant_id @@ -109,15 +186,16 @@ def test_find_interception_should_ignore_id_of_existing_item( ignore_id=existing_item.id, ) - colliding_result is not None + assert colliding_result is not None assert any([existing_item.id == item.id for item in colliding_result]) - - non_colliding_result is not None + assert non_colliding_result is not None assert not any( [existing_item.id == item.id for item in non_colliding_result] ) finally: - time_entry_repository.delete_permanently(existing_item.id, event_ctx) + time_entry_repository.delete_permanently( + existing_item.id, event_context + ) def test_find_running_should_return_running_time_entry( diff --git a/time_tracker_api/time_entries/time_entries_model.py b/time_tracker_api/time_entries/time_entries_model.py index 3070f839..636225f6 100644 --- a/time_tracker_api/time_entries/time_entries_model.py +++ b/time_tracker_api/time_entries/time_entries_model.py @@ -308,6 +308,8 @@ def find_interception_with_date_range( SELECT * FROM c WHERE ((c.start_date BETWEEN @start_date AND @end_date) OR (c.end_date BETWEEN @start_date AND @end_date)) + AND c.start_date!= @end_date + AND c.end_date!= @start_date {conditions_clause} {ignore_id_condition} {visibility_condition} @@ -455,8 +457,7 @@ def get_all(self, conditions: dict = None, **kwargs) -> list: conditions.update({"owner_id": event_ctx.user_id}) custom_query = self.build_custom_query( - is_admin=event_ctx.is_admin, - conditions=conditions, + is_admin=event_ctx.is_admin, conditions=conditions, ) date_range = self.handle_date_filter_args(args=conditions) limit = conditions.get("limit", None) @@ -476,8 +477,7 @@ def get_all_paginated(self, conditions: dict = None, **kwargs) -> list: event_ctx = self.create_event_context("read-many") get_all_conditions.update({"owner_id": event_ctx.user_id}) custom_query = self.build_custom_query( - is_admin=event_ctx.is_admin, - conditions=get_all_conditions, + is_admin=event_ctx.is_admin, conditions=get_all_conditions, ) date_range = self.handle_date_filter_args(args=get_all_conditions) records_total = self.repository.count( @@ -488,8 +488,7 @@ def get_all_paginated(self, conditions: dict = None, **kwargs) -> list: ) conditions.update({"owner_id": event_ctx.user_id}) custom_query = self.build_custom_query( - is_admin=event_ctx.is_admin, - conditions=conditions, + is_admin=event_ctx.is_admin, conditions=conditions, ) date_range = self.handle_date_filter_args(args=conditions) length = conditions.get("length", None) @@ -533,11 +532,7 @@ def update(self, id, data: dict, description=None): time_entry = self.repository.find(id, event_ctx) self.check_whether_current_user_owns_item(time_entry) - return self.repository.partial_update( - id, - data, - event_ctx, - ) + return self.repository.partial_update(id, data, event_ctx,) def stop(self, id): event_ctx = self.create_event_context("update", "Stop time entry") @@ -547,9 +542,7 @@ def stop(self, id): self.check_time_entry_is_not_stopped(time_entry) return self.repository.partial_update( - id, - {'end_date': current_datetime_str()}, - event_ctx, + id, {'end_date': current_datetime_str()}, event_ctx, ) def restart(self, id): @@ -560,9 +553,7 @@ def restart(self, id): self.check_time_entry_is_not_started(time_entry) return self.repository.partial_update( - id, - {'end_date': None}, - event_ctx, + id, {'end_date': None}, event_ctx, ) def delete(self, id): @@ -570,8 +561,7 @@ def delete(self, id): time_entry = self.repository.find(id, event_ctx) self.check_whether_current_user_owns_item(time_entry) self.repository.delete( - id, - event_ctx, + id, event_ctx, ) def find_running(self):