From eb62162f4b5d55b6a81aee7d8c2be67d3762ba62 Mon Sep 17 00:00:00 2001 From: kelly Date: Wed, 24 Mar 2021 17:37:46 -0500 Subject: [PATCH 01/17] refactor: TT-185 create SQLBuilder, TimeEntryQueryBuilder and find_all_v2 method in time entries repository --- .../time_entries/time_entries_model_test.py | 55 +++++++++++ .../time_entries_query_builder.py | 22 +++++ .../time_entries/time_entries_repository.py | 37 +++++++ utils/query_builder.py | 96 +++++++++++++++++++ 4 files changed, 210 insertions(+) create mode 100644 time_tracker_api/time_entries/time_entries_query_builder.py create mode 100644 utils/query_builder.py 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 e551b6d0..5f4c9df1 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 @@ -8,6 +8,7 @@ ) from time_tracker_api.time_entries.time_entries_repository import ( TimeEntryCosmosDBRepository, + TimeEntryCosmosDBModel, ) @@ -266,3 +267,57 @@ def test_updated_item_without_deleted_key_should_call_validate_data( time_entry_repository.on_update({}, event_context) on_update_mock.assert_called_once() time_entry_repository.validate_data.assert_called_once() + + +@pytest.mark.parametrize( + 'owner_list,expected_result', + [ + ([], ""), + (None, ""), + (["id"], "AND c.owner_id IN ('id')"), + (["id1", "id2"], "AND c.owner_id IN ('id1', 'id2')"), + ], +) +def test_create_owner_condition( + owner_list, + expected_result, + event_context: EventContext, + time_entry_repository: TimeEntryCosmosDBRepository, +): + result = time_entry_repository.create_owner_condition(owner_list) + assert result == expected_result + + +def test_find_all_v2( + 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.find_all_v2( + event_context, + ['owner_id'], + { + 'start_date': "2021-03-22T10:00:00.000Z", + 'end_date': "2021-03-22T11:00:00.000Z", + }, + ) + + print(result) + assert len(result) == 1 + time_entry = result[0] + print(time_entry.__dict__) + assert isinstance(time_entry, TimeEntryCosmosDBModel) + assert time_entry.__dict__ == expected_item diff --git a/time_tracker_api/time_entries/time_entries_query_builder.py b/time_tracker_api/time_entries/time_entries_query_builder.py new file mode 100644 index 00000000..bdeae63f --- /dev/null +++ b/time_tracker_api/time_entries/time_entries_query_builder.py @@ -0,0 +1,22 @@ +from utils.query_builder import CosmosDBQueryBuilder + + +class TimeEntryQueryBuilder(CosmosDBQueryBuilder): + def __init__(self): + super().__init__() + + def add_sql_date_range_condition(self, dates: tuple = None): + if dates and len(dates) == 2: + start_date, end_date = dates + condition = """ + ((c.start_date BETWEEN @start_date AND @end_date) OR + (c.end_date BETWEEN @start_date AND @end_date)) + """ + self.where_conditions.append(condition) + self.parameters.extend( + [ + {'name': '@start_date', 'value': start_date}, + {'name': '@end_date', 'value': end_date}, + ] + ) + return self diff --git a/time_tracker_api/time_entries/time_entries_repository.py b/time_tracker_api/time_entries/time_entries_repository.py index b86a6ae3..60c245e5 100644 --- a/time_tracker_api/time_entries/time_entries_repository.py +++ b/time_tracker_api/time_entries/time_entries_repository.py @@ -26,6 +26,9 @@ from commons.data_access_layer.database import EventContext from typing import List, Callable from time_tracker_api.projects import projects_model +from time_tracker_api.time_entries.time_entries_query_builder import ( + TimeEntryQueryBuilder, +) class TimeEntryCosmosDBRepository(CosmosDBRepository): @@ -196,6 +199,40 @@ def find_all( abort(HTTPStatus.NOT_FOUND, "Time entry not found") return time_entries + def find_all_v2( + self, + event_context: EventContext, + owner_id_list: List[str], + date_range: tuple = None, + fields: dict = None, + limit: int = None, + offset: int = 0, + visible_only=True, + mapper: Callable = None, + ): + limit = self.get_page_size_or(limit) + partition_key_value = self.find_partition_key_value(event_context) + query_builder = ( + TimeEntryQueryBuilder() + .add_sql_date_range_condition(date_range) + .add_sql_in_condition(owner_id_list) + .add_sql_where_equal_condition(fields) + .add_sql_limit_condition(limit) + .add_sql_offset_condition(offset) + .build() + ) + + query_str = query_builder.get_query() + params = query_builder.get_parameters() + + result = self.container.query_items( + query=query_str, + parameters=params, + partition_key=partition_key_value, + ) + function_mapper = self.get_mapper_or_dict(mapper) + return list(map(function_mapper, result)) + def on_create(self, new_item_data: dict, event_context: EventContext): CosmosDBRepository.on_create(self, new_item_data, event_context) diff --git a/utils/query_builder.py b/utils/query_builder.py new file mode 100644 index 00000000..b24f5719 --- /dev/null +++ b/utils/query_builder.py @@ -0,0 +1,96 @@ +from typing import List +from utils.repository import convert_list_to_tuple_string + + +class CosmosDBQueryBuilder: + query: str + + def __init__(self): + super().__init__() + self.query = "" + self.parameters = [] + self.select_conditions = [] + self.where_conditions = [] + self.limit = None + self.offset = None + + def add_select_conditions(self, column: List[str] = None): + column = column if column else ["*"] + self.select_conditions.extend(column) + return self + + def add_sql_in_condition( + self, attribute: str = None, ids_list: List[str] = None + ): + if ids_list and attribute and len(ids_list) > 0: + ids_values = convert_list_to_tuple_string(ids_list) + self.where_conditions.append(f"c.{attribute} IN {ids_values}") + return self + + def add_sql_where_equal_condition(self, data: dict = None): + if data: + for k, v in data.items(): + condition = f"c.{k} = @{k}" + self.where_conditions.append(condition) + self.parameters.append({'name': f'@{k}', 'value': v}) + return self + + def add_sql_visibility_condition(self, visible_only: bool): + if visible_only: + self.where_conditions.append('NOT IS_DEFINED(c.deleted)') + return self + + def add_sql_limit_condition(self, limit): + if limit and isinstance(limit, int): + self.limit = limit + return self + + def add_sql_offset_condition(self, offset): + if offset and isinstance(offset, int): + self.offset = offset + return self + + def build_select(self): + if len(self.select_conditions) < 1: + self.select_conditions.append("*") + return ",".join(self.select_conditions) + + def build_where(self): + if len(self.where_conditions) > 0: + return "WHERE " + " AND ".join(self.where_conditions) + else: + return "" + + def build_offset(self): + if self.offset: + self.parameters.append({'name': '@offset', 'value': self.offset}) + return "OFFSET @offset" + else: + return "" + + def build_limit(self): + if self.limit: + self.parameters.append({'name': '@limit', 'value': self.limit}) + return "LIMIT @limit" + else: + return "" + + def build(self): + self.query = """ + SELECT {select_conditions} FROM c + {where_conditions} + {offset_condition} + {limit_condition} + """.format( + select_conditions=self.build_select(), + where_conditions=self.build_where(), + offset_condition=self.build_offset(), + limit_condition=self.build_limit(), + ) + return self + + def get_query(self): + return self.query + + def get_parameters(self): + return self.parameters From 031b883351206d90e4a3f10bece8ac2de00e438b Mon Sep 17 00:00:00 2001 From: kelly Date: Thu, 25 Mar 2021 14:50:00 -0500 Subject: [PATCH 02/17] test: TT-185 query_builder tests --- tests/utils/query_builder_test.py | 180 ++++++++++++++++++ .../time_entries_query_builder.py | 2 +- utils/query_builder.py | 16 +- 3 files changed, 189 insertions(+), 9 deletions(-) create mode 100644 tests/utils/query_builder_test.py diff --git a/tests/utils/query_builder_test.py b/tests/utils/query_builder_test.py new file mode 100644 index 00000000..55b6171e --- /dev/null +++ b/tests/utils/query_builder_test.py @@ -0,0 +1,180 @@ +from unittest.mock import patch +from utils.query_builder import CosmosDBQueryBuilder +import pytest + + +@pytest.mark.parametrize( + "condition_list, expected_select_condition", + [ + (None, ["*"]), + ([], ["*"]), + (["*"], ["*"]), + (["c.id"], ["c.id"]), + (["c.id", "c.name"], ["c.id", "c.name"]), + ], +) +def test_add_select_conditions_should_update_select_list( + condition_list, expected_select_condition +): + query_builder = CosmosDBQueryBuilder().add_select_conditions( + condition_list + ) + + assert len(query_builder.select_conditions) == len( + expected_select_condition + ) + assert query_builder.select_conditions == expected_select_condition + + +@pytest.mark.parametrize( + "attribute,ids_list,expected_where_condition_list", + [ + ("id", [], []), + (None, None, []), + ("id", None, []), + (None, ["id"], []), + ("id", ["id"], ["c.id IN ('id')"]), + ("id", ["id1", "id2"], ["c.id IN ('id1', 'id2')"]), + ], +) +def test_add_sql_in_condition_should_update_where_list( + attribute, + ids_list, + expected_where_condition_list, +): + query_builder = CosmosDBQueryBuilder().add_sql_in_condition( + attribute, ids_list + ) + + assert len(query_builder.where_conditions) == len( + expected_where_condition_list + ) + assert query_builder.where_conditions == expected_where_condition_list + + +@pytest.mark.parametrize( + "data,expected_where_list,expected_params", + [ + ({}, [], []), + ({'id': 1}, ["c.id = @id"], [{'name': "@id", 'value': 1}]), + ( + {'id': 1, 'name': 'test'}, + ["c.id = @id", "c.name = @name"], + [{'name': "@id", 'value': 1}, {'name': "@name", 'value': 'test'}], + ), + ], +) +def test_add_sql_where_equal_condition_should_update_where_params_list( + data, + expected_where_list, + expected_params, +): + query_builder = CosmosDBQueryBuilder().add_sql_where_equal_condition(data) + + assert len(query_builder.where_conditions) == len(data) + assert query_builder.where_conditions == expected_where_list + + assert len(query_builder.parameters) == len(expected_params) + assert query_builder.parameters == expected_params + + +def test_add_sql_where_equal_condition_with_None_should_not_update_lists(): + query_builder = CosmosDBQueryBuilder().add_sql_where_equal_condition(None) + + assert len(query_builder.where_conditions) == 0 + assert query_builder.where_conditions == [] + assert len(query_builder.parameters) == 0 + assert query_builder.parameters == [] + + +@pytest.mark.parametrize( + "visibility_bool,expected_where_list", + [(True, ['NOT IS_DEFINED(c.deleted)']), (False, [])], +) +def test_add_sql_visibility_condition( + visibility_bool, + expected_where_list, +): + query_builder = CosmosDBQueryBuilder().add_sql_visibility_condition( + visibility_bool + ) + + assert len(query_builder.where_conditions) == len(expected_where_list) + assert query_builder.where_conditions == expected_where_list + + +@pytest.mark.parametrize( + "limit_value,expected_limit", + [ + (1, 1), + (10, 10), + (None, None), + ("hola", None), + (["lista"], None), + ], +) +def test_add_sql_limit_condition(limit_value, expected_limit): + query_builder = CosmosDBQueryBuilder().add_sql_limit_condition(limit_value) + + assert query_builder.limit == expected_limit + + +@pytest.mark.parametrize( + "offset_value,expected_offset", + [ + (1, 1), + (10, 10), + (None, None), + ("hola", None), + (["lista"], None), + ], +) +def test_add_sql_offset_condition( + offset_value, + expected_offset, +): + query_builder = CosmosDBQueryBuilder().add_sql_offset_condition( + offset_value + ) + + assert query_builder.offset == expected_offset + + +@pytest.mark.parametrize( + "select_conditions,expected_condition", + [ + ([], "*"), + (["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, +): + query_builder = CosmosDBQueryBuilder().add_select_conditions( + select_conditions + ) + + result = query_builder._CosmosDBQueryBuilder__build_select() + assert result == expected_condition + + +@pytest.mark.parametrize( + "where_dict,expected_condition", + [ + ({"id": 1}, "WHERE c.id = @id"), + ({"id": 1, "name": "test"}, "WHERE c.id = @id AND c.name = @name"), + ], +) +def test__build_where_should_return_concatenate_conditions( + where_dict, + expected_condition, +): + query_builder = CosmosDBQueryBuilder().add_sql_where_equal_condition( + where_dict + ) + + result = query_builder._CosmosDBQueryBuilder__build_where() + + assert result == expected_condition diff --git a/time_tracker_api/time_entries/time_entries_query_builder.py b/time_tracker_api/time_entries/time_entries_query_builder.py index bdeae63f..58127524 100644 --- a/time_tracker_api/time_entries/time_entries_query_builder.py +++ b/time_tracker_api/time_entries/time_entries_query_builder.py @@ -3,7 +3,7 @@ class TimeEntryQueryBuilder(CosmosDBQueryBuilder): def __init__(self): - super().__init__() + super(TimeEntryQueryBuilder, self).__init__() def add_sql_date_range_condition(self, dates: tuple = None): if dates and len(dates) == 2: diff --git a/utils/query_builder.py b/utils/query_builder.py index b24f5719..229638de 100644 --- a/utils/query_builder.py +++ b/utils/query_builder.py @@ -50,25 +50,25 @@ def add_sql_offset_condition(self, offset): self.offset = offset return self - def build_select(self): + def __build_select(self): if len(self.select_conditions) < 1: self.select_conditions.append("*") return ",".join(self.select_conditions) - def build_where(self): + def __build_where(self): if len(self.where_conditions) > 0: return "WHERE " + " AND ".join(self.where_conditions) else: return "" - def build_offset(self): + def __build_offset(self): if self.offset: self.parameters.append({'name': '@offset', 'value': self.offset}) return "OFFSET @offset" else: return "" - def build_limit(self): + def __build_limit(self): if self.limit: self.parameters.append({'name': '@limit', 'value': self.limit}) return "LIMIT @limit" @@ -82,10 +82,10 @@ def build(self): {offset_condition} {limit_condition} """.format( - select_conditions=self.build_select(), - where_conditions=self.build_where(), - offset_condition=self.build_offset(), - limit_condition=self.build_limit(), + select_conditions=self.__build_select(), + where_conditions=self.__build_where(), + offset_condition=self.__build_offset(), + limit_condition=self.__build_limit(), ) return self From 262ace0860cdafaccd473d2c12f6cf956b623186 Mon Sep 17 00:00:00 2001 From: kelly Date: Fri, 26 Mar 2021 10:50:37 -0500 Subject: [PATCH 03/17] test: TT-185 add test methods for TimeEntryQueryBuilder and new function in repository --- .../time_entries/time_entries_model_test.py | 29 ++----- .../time_entries_query_builder_test.py | 81 +++++++++++++++++ tests/utils/query_builder_test.py | 86 +++++++++++++++++++ tests/utils/repository_test.py | 18 +++- utils/repository.py | 7 ++ 5 files changed, 200 insertions(+), 21 deletions(-) create mode 100644 tests/time_tracker_api/time_entries/time_entries_query_builder_test.py 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 5f4c9df1..ff00a87b 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 @@ -269,26 +269,15 @@ def test_updated_item_without_deleted_key_should_call_validate_data( time_entry_repository.validate_data.assert_called_once() -@pytest.mark.parametrize( - 'owner_list,expected_result', - [ - ([], ""), - (None, ""), - (["id"], "AND c.owner_id IN ('id')"), - (["id1", "id2"], "AND c.owner_id IN ('id1', 'id2')"), - ], +@patch( + 'time_tracker_api.time_entries.time_entries_repository.TimeEntryCosmosDBRepository.find_partition_key_value' +) +@patch( + 'time_tracker_api.time_entries.time_entries_repository.TimeEntryCosmosDBRepository.get_page_size_or' ) -def test_create_owner_condition( - owner_list, - expected_result, - event_context: EventContext, - time_entry_repository: TimeEntryCosmosDBRepository, -): - result = time_entry_repository.create_owner_condition(owner_list) - assert result == expected_result - - def test_find_all_v2( + get_page_size_or_mock, + find_partition_key_value_mock, event_context: EventContext, time_entry_repository: TimeEntryCosmosDBRepository, ): @@ -315,9 +304,9 @@ def test_find_all_v2( }, ) - print(result) + find_partition_key_value_mock.assert_called_once() + get_page_size_or_mock.assert_called_once() assert len(result) == 1 time_entry = result[0] - print(time_entry.__dict__) assert isinstance(time_entry, TimeEntryCosmosDBModel) assert time_entry.__dict__ == expected_item diff --git a/tests/time_tracker_api/time_entries/time_entries_query_builder_test.py b/tests/time_tracker_api/time_entries/time_entries_query_builder_test.py new file mode 100644 index 00000000..59c3d2f2 --- /dev/null +++ b/tests/time_tracker_api/time_entries/time_entries_query_builder_test.py @@ -0,0 +1,81 @@ +import pytest +from unittest.mock import patch +from utils.query_builder import CosmosDBQueryBuilder +from time_tracker_api.time_entries.time_entries_query_builder import ( + TimeEntryQueryBuilder, +) +from utils.repository import get_string_without_empty_spaces + + +def test_TimeEntryQueryBuilder_is_subclass_CosmosDBQueryBuilder(): + query_builder = CosmosDBQueryBuilder() + time_entries_query_builder = TimeEntryQueryBuilder() + + assert issubclass(TimeEntryQueryBuilder, CosmosDBQueryBuilder) == True + + +@pytest.mark.parametrize( + "start_date,end_date,expected_params", + [ + ( + "2021-03-19T05:07:00.000Z", + "2021-03-25T10:00:00.000Z", + [ + {"name": "@start_date", "value": "2021-03-19T05:07:00.000Z"}, + {"name": "@end_date", "value": "2021-03-25T10:00:00.000Z"}, + ], + ), + ( + "2021-02-10T05:08:00.000Z", + "2021-03-20T11:00:00.000Z", + [ + {"name": "@start_date", "value": "2021-02-10T05:08:00.000Z"}, + {"name": "@end_date", "value": "2021-03-20T11:00:00.000Z"}, + ], + ), + ( + "2021-01-02T05:09:00.000Z", + "2021-01-26T12:00:00.000Z", + [ + {"name": "@start_date", "value": "2021-01-02T05:09:00.000Z"}, + {"name": "@end_date", "value": "2021-01-26T12:00:00.000Z"}, + ], + ), + ], +) +def test_add_sql_date_range_condition_should_update_where_list( + start_date, end_date, expected_params +): + time_entry_query_builder = ( + TimeEntryQueryBuilder().add_sql_date_range_condition( + (start_date, end_date) + ) + ) + + assert len(time_entry_query_builder.where_conditions) == 1 + assert len(time_entry_query_builder.parameters) == len(expected_params) + assert time_entry_query_builder.get_parameters() == expected_params + + +def test_build_with_add_sql_date_range_condition(): + time_entry_query_builder = ( + TimeEntryQueryBuilder() + .add_sql_date_range_condition( + ("2021-03-19T05:00:00.000Z", "2021-03-20T10:00:00.000Z") + ) + .build() + ) + + expected_query = """ + SELECT * FROM c + WHERE ((c.start_date BETWEEN @start_date AND @end_date) OR + (c.end_date BETWEEN @start_date AND @end_date)) + """ + query = time_entry_query_builder.get_query() + + assert get_string_without_empty_spaces( + query + ) == get_string_without_empty_spaces(expected_query) + + assert len(time_entry_query_builder.where_conditions) == 1 + assert len(time_entry_query_builder.get_parameters()) == 2 diff --git a/tests/utils/query_builder_test.py b/tests/utils/query_builder_test.py index 55b6171e..aa861667 100644 --- a/tests/utils/query_builder_test.py +++ b/tests/utils/query_builder_test.py @@ -1,5 +1,6 @@ from unittest.mock import patch from utils.query_builder import CosmosDBQueryBuilder +from utils.repository import get_string_without_empty_spaces import pytest @@ -178,3 +179,88 @@ def test__build_where_should_return_concatenate_conditions( result = query_builder._CosmosDBQueryBuilder__build_where() assert result == expected_condition + + +@pytest.mark.parametrize( + "offset,expected_condition,expected_params", + [(1, "OFFSET @offset", [{'name': '@offset', 'value': 1}]), (None, "", [])], +) +def test__build_offset( + offset, + expected_condition, + expected_params, +): + query_builder = CosmosDBQueryBuilder().add_sql_offset_condition(offset) + + result = query_builder._CosmosDBQueryBuilder__build_offset() + assert result == expected_condition + assert len(query_builder.parameters) == len(expected_params) + assert query_builder.get_parameters() == expected_params + + +@pytest.mark.parametrize( + "limit,expected_condition,expected_params", + [(1, "LIMIT @limit", [{'name': '@limit', 'value': 1}]), (None, "", [])], +) +def test__build_limit( + limit, + expected_condition, + expected_params, +): + query_builder = CosmosDBQueryBuilder().add_sql_limit_condition(limit) + + result = query_builder._CosmosDBQueryBuilder__build_limit() + assert result == expected_condition + assert len(query_builder.parameters) == len(expected_params) + assert query_builder.get_parameters() == expected_params + + +def test_build_with_all_calls_return_query_with_all_conditions(): + query_builder = ( + CosmosDBQueryBuilder() + .add_select_conditions(["c.description"]) + .add_sql_in_condition("id", ["id1", "id2"]) + .add_sql_where_equal_condition({'name': 'test'}) + .add_sql_offset_condition(2) + .add_sql_limit_condition(10) + .add_sql_visibility_condition(True) + .build() + ) + query = query_builder.get_query() + expected_query = """ + SELECT c.description FROM c + WHERE c.id IN ('id1', 'id2') AND c.name = @name AND NOT IS_DEFINED(c.deleted) + OFFSET @offset + LIMIT @limit + """ + + assert get_string_without_empty_spaces( + query + ) == get_string_without_empty_spaces(expected_query) + + assert len(query_builder.get_parameters()) > 0 + assert len(query_builder.where_conditions) > 0 + assert len(query_builder.select_conditions) > 0 + + +def test_build_with_empty_and_None_attributes_return_query_select_all(): + + query_builder = ( + CosmosDBQueryBuilder() + .add_select_conditions() + .add_sql_in_condition() + .add_sql_where_equal_condition() + .add_sql_limit_condition(None) + .add_sql_offset_condition(None) + .build() + ) + + query = query_builder.get_query() + + expected_query = """SELECT * FROM c""" + query = get_string_without_empty_spaces(query) + expected_query = get_string_without_empty_spaces(expected_query) + + assert query == expected_query + assert len(query_builder.get_parameters()) == 0 + assert len(query_builder.where_conditions) == 0 diff --git a/tests/utils/repository_test.py b/tests/utils/repository_test.py index 16b0b440..869d9e48 100644 --- a/tests/utils/repository_test.py +++ b/tests/utils/repository_test.py @@ -1,5 +1,8 @@ from unittest.mock import patch -from utils.repository import convert_list_to_tuple_string +from utils.repository import ( + convert_list_to_tuple_string, + get_string_without_empty_spaces, +) import pytest @@ -34,3 +37,16 @@ def test_convert_list_to_tuple_string_should_success( result = convert_list_to_tuple_string(ids_list) assert expected_result == result + + +@pytest.mark.parametrize( + "string,expected_string", + [ + (" text with \t tab", "text with tab"), + (" text with \n new line", "text with new line"), + (""" SELECT * from c """, "SELECT * from c"), + ], +) +def test_get_string_without_empty_spaces(string, expected_string): + string = get_string_without_empty_spaces(string) + assert string == expected_string diff --git a/utils/repository.py b/utils/repository.py index 4c0ed5fd..11b5c31a 100644 --- a/utils/repository.py +++ b/utils/repository.py @@ -7,3 +7,10 @@ def convert_list_to_tuple_string(ids_list): else str(tuple(ids_list)) ) return result + + +def get_string_without_empty_spaces(string: str): + from re import sub + + string = string.replace("\n", "") + return sub("[\s]+", ' ', string).rstrip().lstrip() From 7914fdfd5bba51f79237c47dfac4c8d044f53a63 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 Mar 2021 11:09:57 -0500 Subject: [PATCH 04/17] build: TT-199 build(deps): bump jinja2 in /requirements/time_tracker_api (#268) Bumps [jinja2](https://github.com/pallets/jinja) from 2.11.1 to 2.11.3. - [Release notes](https://github.com/pallets/jinja/releases) - [Changelog](https://github.com/pallets/jinja/blob/master/CHANGES.rst) - [Commits](https://github.com/pallets/jinja/compare/2.11.1...2.11.3) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sandro Castillo --- requirements/time_tracker_api/prod.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements/time_tracker_api/prod.txt b/requirements/time_tracker_api/prod.txt index 86b5c632..c6d43532 100644 --- a/requirements/time_tracker_api/prod.txt +++ b/requirements/time_tracker_api/prod.txt @@ -12,7 +12,7 @@ Flask==1.1.1 flake8==3.7.9 WSGIserver==1.3 Werkzeug==0.16.1 -Jinja2==2.11.1 +Jinja2==2.11.3 #WSGI server gunicorn==20.0.4 From 2b57549eaac65c98de83f51ba9915524fc9e87fa Mon Sep 17 00:00:00 2001 From: kelly Date: Wed, 24 Mar 2021 17:37:46 -0500 Subject: [PATCH 05/17] refactor: TT-185 create SQLBuilder, TimeEntryQueryBuilder and find_all_v2 method in time entries repository --- .../time_entries/time_entries_model_test.py | 55 +++++++++++ .../time_entries_query_builder.py | 22 +++++ .../time_entries/time_entries_repository.py | 37 +++++++ utils/query_builder.py | 96 +++++++++++++++++++ 4 files changed, 210 insertions(+) create mode 100644 time_tracker_api/time_entries/time_entries_query_builder.py create mode 100644 utils/query_builder.py 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 e551b6d0..5f4c9df1 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 @@ -8,6 +8,7 @@ ) from time_tracker_api.time_entries.time_entries_repository import ( TimeEntryCosmosDBRepository, + TimeEntryCosmosDBModel, ) @@ -266,3 +267,57 @@ def test_updated_item_without_deleted_key_should_call_validate_data( time_entry_repository.on_update({}, event_context) on_update_mock.assert_called_once() time_entry_repository.validate_data.assert_called_once() + + +@pytest.mark.parametrize( + 'owner_list,expected_result', + [ + ([], ""), + (None, ""), + (["id"], "AND c.owner_id IN ('id')"), + (["id1", "id2"], "AND c.owner_id IN ('id1', 'id2')"), + ], +) +def test_create_owner_condition( + owner_list, + expected_result, + event_context: EventContext, + time_entry_repository: TimeEntryCosmosDBRepository, +): + result = time_entry_repository.create_owner_condition(owner_list) + assert result == expected_result + + +def test_find_all_v2( + 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.find_all_v2( + event_context, + ['owner_id'], + { + 'start_date': "2021-03-22T10:00:00.000Z", + 'end_date': "2021-03-22T11:00:00.000Z", + }, + ) + + print(result) + assert len(result) == 1 + time_entry = result[0] + print(time_entry.__dict__) + assert isinstance(time_entry, TimeEntryCosmosDBModel) + assert time_entry.__dict__ == expected_item diff --git a/time_tracker_api/time_entries/time_entries_query_builder.py b/time_tracker_api/time_entries/time_entries_query_builder.py new file mode 100644 index 00000000..bdeae63f --- /dev/null +++ b/time_tracker_api/time_entries/time_entries_query_builder.py @@ -0,0 +1,22 @@ +from utils.query_builder import CosmosDBQueryBuilder + + +class TimeEntryQueryBuilder(CosmosDBQueryBuilder): + def __init__(self): + super().__init__() + + def add_sql_date_range_condition(self, dates: tuple = None): + if dates and len(dates) == 2: + start_date, end_date = dates + condition = """ + ((c.start_date BETWEEN @start_date AND @end_date) OR + (c.end_date BETWEEN @start_date AND @end_date)) + """ + self.where_conditions.append(condition) + self.parameters.extend( + [ + {'name': '@start_date', 'value': start_date}, + {'name': '@end_date', 'value': end_date}, + ] + ) + return self diff --git a/time_tracker_api/time_entries/time_entries_repository.py b/time_tracker_api/time_entries/time_entries_repository.py index b86a6ae3..60c245e5 100644 --- a/time_tracker_api/time_entries/time_entries_repository.py +++ b/time_tracker_api/time_entries/time_entries_repository.py @@ -26,6 +26,9 @@ from commons.data_access_layer.database import EventContext from typing import List, Callable from time_tracker_api.projects import projects_model +from time_tracker_api.time_entries.time_entries_query_builder import ( + TimeEntryQueryBuilder, +) class TimeEntryCosmosDBRepository(CosmosDBRepository): @@ -196,6 +199,40 @@ def find_all( abort(HTTPStatus.NOT_FOUND, "Time entry not found") return time_entries + def find_all_v2( + self, + event_context: EventContext, + owner_id_list: List[str], + date_range: tuple = None, + fields: dict = None, + limit: int = None, + offset: int = 0, + visible_only=True, + mapper: Callable = None, + ): + limit = self.get_page_size_or(limit) + partition_key_value = self.find_partition_key_value(event_context) + query_builder = ( + TimeEntryQueryBuilder() + .add_sql_date_range_condition(date_range) + .add_sql_in_condition(owner_id_list) + .add_sql_where_equal_condition(fields) + .add_sql_limit_condition(limit) + .add_sql_offset_condition(offset) + .build() + ) + + query_str = query_builder.get_query() + params = query_builder.get_parameters() + + result = self.container.query_items( + query=query_str, + parameters=params, + partition_key=partition_key_value, + ) + function_mapper = self.get_mapper_or_dict(mapper) + return list(map(function_mapper, result)) + def on_create(self, new_item_data: dict, event_context: EventContext): CosmosDBRepository.on_create(self, new_item_data, event_context) diff --git a/utils/query_builder.py b/utils/query_builder.py new file mode 100644 index 00000000..b24f5719 --- /dev/null +++ b/utils/query_builder.py @@ -0,0 +1,96 @@ +from typing import List +from utils.repository import convert_list_to_tuple_string + + +class CosmosDBQueryBuilder: + query: str + + def __init__(self): + super().__init__() + self.query = "" + self.parameters = [] + self.select_conditions = [] + self.where_conditions = [] + self.limit = None + self.offset = None + + def add_select_conditions(self, column: List[str] = None): + column = column if column else ["*"] + self.select_conditions.extend(column) + return self + + def add_sql_in_condition( + self, attribute: str = None, ids_list: List[str] = None + ): + if ids_list and attribute and len(ids_list) > 0: + ids_values = convert_list_to_tuple_string(ids_list) + self.where_conditions.append(f"c.{attribute} IN {ids_values}") + return self + + def add_sql_where_equal_condition(self, data: dict = None): + if data: + for k, v in data.items(): + condition = f"c.{k} = @{k}" + self.where_conditions.append(condition) + self.parameters.append({'name': f'@{k}', 'value': v}) + return self + + def add_sql_visibility_condition(self, visible_only: bool): + if visible_only: + self.where_conditions.append('NOT IS_DEFINED(c.deleted)') + return self + + def add_sql_limit_condition(self, limit): + if limit and isinstance(limit, int): + self.limit = limit + return self + + def add_sql_offset_condition(self, offset): + if offset and isinstance(offset, int): + self.offset = offset + return self + + def build_select(self): + if len(self.select_conditions) < 1: + self.select_conditions.append("*") + return ",".join(self.select_conditions) + + def build_where(self): + if len(self.where_conditions) > 0: + return "WHERE " + " AND ".join(self.where_conditions) + else: + return "" + + def build_offset(self): + if self.offset: + self.parameters.append({'name': '@offset', 'value': self.offset}) + return "OFFSET @offset" + else: + return "" + + def build_limit(self): + if self.limit: + self.parameters.append({'name': '@limit', 'value': self.limit}) + return "LIMIT @limit" + else: + return "" + + def build(self): + self.query = """ + SELECT {select_conditions} FROM c + {where_conditions} + {offset_condition} + {limit_condition} + """.format( + select_conditions=self.build_select(), + where_conditions=self.build_where(), + offset_condition=self.build_offset(), + limit_condition=self.build_limit(), + ) + return self + + def get_query(self): + return self.query + + def get_parameters(self): + return self.parameters From 760666337df468adbc76c663f0efc9ad477bfb46 Mon Sep 17 00:00:00 2001 From: kelly Date: Thu, 25 Mar 2021 14:50:00 -0500 Subject: [PATCH 06/17] test: TT-185 query_builder tests --- tests/utils/query_builder_test.py | 180 ++++++++++++++++++ .../time_entries_query_builder.py | 2 +- utils/query_builder.py | 16 +- 3 files changed, 189 insertions(+), 9 deletions(-) create mode 100644 tests/utils/query_builder_test.py diff --git a/tests/utils/query_builder_test.py b/tests/utils/query_builder_test.py new file mode 100644 index 00000000..55b6171e --- /dev/null +++ b/tests/utils/query_builder_test.py @@ -0,0 +1,180 @@ +from unittest.mock import patch +from utils.query_builder import CosmosDBQueryBuilder +import pytest + + +@pytest.mark.parametrize( + "condition_list, expected_select_condition", + [ + (None, ["*"]), + ([], ["*"]), + (["*"], ["*"]), + (["c.id"], ["c.id"]), + (["c.id", "c.name"], ["c.id", "c.name"]), + ], +) +def test_add_select_conditions_should_update_select_list( + condition_list, expected_select_condition +): + query_builder = CosmosDBQueryBuilder().add_select_conditions( + condition_list + ) + + assert len(query_builder.select_conditions) == len( + expected_select_condition + ) + assert query_builder.select_conditions == expected_select_condition + + +@pytest.mark.parametrize( + "attribute,ids_list,expected_where_condition_list", + [ + ("id", [], []), + (None, None, []), + ("id", None, []), + (None, ["id"], []), + ("id", ["id"], ["c.id IN ('id')"]), + ("id", ["id1", "id2"], ["c.id IN ('id1', 'id2')"]), + ], +) +def test_add_sql_in_condition_should_update_where_list( + attribute, + ids_list, + expected_where_condition_list, +): + query_builder = CosmosDBQueryBuilder().add_sql_in_condition( + attribute, ids_list + ) + + assert len(query_builder.where_conditions) == len( + expected_where_condition_list + ) + assert query_builder.where_conditions == expected_where_condition_list + + +@pytest.mark.parametrize( + "data,expected_where_list,expected_params", + [ + ({}, [], []), + ({'id': 1}, ["c.id = @id"], [{'name': "@id", 'value': 1}]), + ( + {'id': 1, 'name': 'test'}, + ["c.id = @id", "c.name = @name"], + [{'name': "@id", 'value': 1}, {'name': "@name", 'value': 'test'}], + ), + ], +) +def test_add_sql_where_equal_condition_should_update_where_params_list( + data, + expected_where_list, + expected_params, +): + query_builder = CosmosDBQueryBuilder().add_sql_where_equal_condition(data) + + assert len(query_builder.where_conditions) == len(data) + assert query_builder.where_conditions == expected_where_list + + assert len(query_builder.parameters) == len(expected_params) + assert query_builder.parameters == expected_params + + +def test_add_sql_where_equal_condition_with_None_should_not_update_lists(): + query_builder = CosmosDBQueryBuilder().add_sql_where_equal_condition(None) + + assert len(query_builder.where_conditions) == 0 + assert query_builder.where_conditions == [] + assert len(query_builder.parameters) == 0 + assert query_builder.parameters == [] + + +@pytest.mark.parametrize( + "visibility_bool,expected_where_list", + [(True, ['NOT IS_DEFINED(c.deleted)']), (False, [])], +) +def test_add_sql_visibility_condition( + visibility_bool, + expected_where_list, +): + query_builder = CosmosDBQueryBuilder().add_sql_visibility_condition( + visibility_bool + ) + + assert len(query_builder.where_conditions) == len(expected_where_list) + assert query_builder.where_conditions == expected_where_list + + +@pytest.mark.parametrize( + "limit_value,expected_limit", + [ + (1, 1), + (10, 10), + (None, None), + ("hola", None), + (["lista"], None), + ], +) +def test_add_sql_limit_condition(limit_value, expected_limit): + query_builder = CosmosDBQueryBuilder().add_sql_limit_condition(limit_value) + + assert query_builder.limit == expected_limit + + +@pytest.mark.parametrize( + "offset_value,expected_offset", + [ + (1, 1), + (10, 10), + (None, None), + ("hola", None), + (["lista"], None), + ], +) +def test_add_sql_offset_condition( + offset_value, + expected_offset, +): + query_builder = CosmosDBQueryBuilder().add_sql_offset_condition( + offset_value + ) + + assert query_builder.offset == expected_offset + + +@pytest.mark.parametrize( + "select_conditions,expected_condition", + [ + ([], "*"), + (["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, +): + query_builder = CosmosDBQueryBuilder().add_select_conditions( + select_conditions + ) + + result = query_builder._CosmosDBQueryBuilder__build_select() + assert result == expected_condition + + +@pytest.mark.parametrize( + "where_dict,expected_condition", + [ + ({"id": 1}, "WHERE c.id = @id"), + ({"id": 1, "name": "test"}, "WHERE c.id = @id AND c.name = @name"), + ], +) +def test__build_where_should_return_concatenate_conditions( + where_dict, + expected_condition, +): + query_builder = CosmosDBQueryBuilder().add_sql_where_equal_condition( + where_dict + ) + + result = query_builder._CosmosDBQueryBuilder__build_where() + + assert result == expected_condition diff --git a/time_tracker_api/time_entries/time_entries_query_builder.py b/time_tracker_api/time_entries/time_entries_query_builder.py index bdeae63f..58127524 100644 --- a/time_tracker_api/time_entries/time_entries_query_builder.py +++ b/time_tracker_api/time_entries/time_entries_query_builder.py @@ -3,7 +3,7 @@ class TimeEntryQueryBuilder(CosmosDBQueryBuilder): def __init__(self): - super().__init__() + super(TimeEntryQueryBuilder, self).__init__() def add_sql_date_range_condition(self, dates: tuple = None): if dates and len(dates) == 2: diff --git a/utils/query_builder.py b/utils/query_builder.py index b24f5719..229638de 100644 --- a/utils/query_builder.py +++ b/utils/query_builder.py @@ -50,25 +50,25 @@ def add_sql_offset_condition(self, offset): self.offset = offset return self - def build_select(self): + def __build_select(self): if len(self.select_conditions) < 1: self.select_conditions.append("*") return ",".join(self.select_conditions) - def build_where(self): + def __build_where(self): if len(self.where_conditions) > 0: return "WHERE " + " AND ".join(self.where_conditions) else: return "" - def build_offset(self): + def __build_offset(self): if self.offset: self.parameters.append({'name': '@offset', 'value': self.offset}) return "OFFSET @offset" else: return "" - def build_limit(self): + def __build_limit(self): if self.limit: self.parameters.append({'name': '@limit', 'value': self.limit}) return "LIMIT @limit" @@ -82,10 +82,10 @@ def build(self): {offset_condition} {limit_condition} """.format( - select_conditions=self.build_select(), - where_conditions=self.build_where(), - offset_condition=self.build_offset(), - limit_condition=self.build_limit(), + select_conditions=self.__build_select(), + where_conditions=self.__build_where(), + offset_condition=self.__build_offset(), + limit_condition=self.__build_limit(), ) return self From f1fc8a6d9de36bb457ecfc144dec42d343e3c9cf Mon Sep 17 00:00:00 2001 From: kelly Date: Fri, 26 Mar 2021 10:50:37 -0500 Subject: [PATCH 07/17] test: TT-185 add test methods for TimeEntryQueryBuilder and new function in repository --- .../time_entries/time_entries_model_test.py | 29 ++----- .../time_entries_query_builder_test.py | 81 +++++++++++++++++ tests/utils/query_builder_test.py | 86 +++++++++++++++++++ tests/utils/repository_test.py | 22 ++++- utils/repository.py | 10 ++- 5 files changed, 204 insertions(+), 24 deletions(-) create mode 100644 tests/time_tracker_api/time_entries/time_entries_query_builder_test.py 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 5f4c9df1..ff00a87b 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 @@ -269,26 +269,15 @@ def test_updated_item_without_deleted_key_should_call_validate_data( time_entry_repository.validate_data.assert_called_once() -@pytest.mark.parametrize( - 'owner_list,expected_result', - [ - ([], ""), - (None, ""), - (["id"], "AND c.owner_id IN ('id')"), - (["id1", "id2"], "AND c.owner_id IN ('id1', 'id2')"), - ], +@patch( + 'time_tracker_api.time_entries.time_entries_repository.TimeEntryCosmosDBRepository.find_partition_key_value' +) +@patch( + 'time_tracker_api.time_entries.time_entries_repository.TimeEntryCosmosDBRepository.get_page_size_or' ) -def test_create_owner_condition( - owner_list, - expected_result, - event_context: EventContext, - time_entry_repository: TimeEntryCosmosDBRepository, -): - result = time_entry_repository.create_owner_condition(owner_list) - assert result == expected_result - - def test_find_all_v2( + get_page_size_or_mock, + find_partition_key_value_mock, event_context: EventContext, time_entry_repository: TimeEntryCosmosDBRepository, ): @@ -315,9 +304,9 @@ def test_find_all_v2( }, ) - print(result) + find_partition_key_value_mock.assert_called_once() + get_page_size_or_mock.assert_called_once() assert len(result) == 1 time_entry = result[0] - print(time_entry.__dict__) assert isinstance(time_entry, TimeEntryCosmosDBModel) assert time_entry.__dict__ == expected_item diff --git a/tests/time_tracker_api/time_entries/time_entries_query_builder_test.py b/tests/time_tracker_api/time_entries/time_entries_query_builder_test.py new file mode 100644 index 00000000..59c3d2f2 --- /dev/null +++ b/tests/time_tracker_api/time_entries/time_entries_query_builder_test.py @@ -0,0 +1,81 @@ +import pytest +from unittest.mock import patch +from utils.query_builder import CosmosDBQueryBuilder +from time_tracker_api.time_entries.time_entries_query_builder import ( + TimeEntryQueryBuilder, +) +from utils.repository import get_string_without_empty_spaces + + +def test_TimeEntryQueryBuilder_is_subclass_CosmosDBQueryBuilder(): + query_builder = CosmosDBQueryBuilder() + time_entries_query_builder = TimeEntryQueryBuilder() + + assert issubclass(TimeEntryQueryBuilder, CosmosDBQueryBuilder) == True + + +@pytest.mark.parametrize( + "start_date,end_date,expected_params", + [ + ( + "2021-03-19T05:07:00.000Z", + "2021-03-25T10:00:00.000Z", + [ + {"name": "@start_date", "value": "2021-03-19T05:07:00.000Z"}, + {"name": "@end_date", "value": "2021-03-25T10:00:00.000Z"}, + ], + ), + ( + "2021-02-10T05:08:00.000Z", + "2021-03-20T11:00:00.000Z", + [ + {"name": "@start_date", "value": "2021-02-10T05:08:00.000Z"}, + {"name": "@end_date", "value": "2021-03-20T11:00:00.000Z"}, + ], + ), + ( + "2021-01-02T05:09:00.000Z", + "2021-01-26T12:00:00.000Z", + [ + {"name": "@start_date", "value": "2021-01-02T05:09:00.000Z"}, + {"name": "@end_date", "value": "2021-01-26T12:00:00.000Z"}, + ], + ), + ], +) +def test_add_sql_date_range_condition_should_update_where_list( + start_date, end_date, expected_params +): + time_entry_query_builder = ( + TimeEntryQueryBuilder().add_sql_date_range_condition( + (start_date, end_date) + ) + ) + + assert len(time_entry_query_builder.where_conditions) == 1 + assert len(time_entry_query_builder.parameters) == len(expected_params) + assert time_entry_query_builder.get_parameters() == expected_params + + +def test_build_with_add_sql_date_range_condition(): + time_entry_query_builder = ( + TimeEntryQueryBuilder() + .add_sql_date_range_condition( + ("2021-03-19T05:00:00.000Z", "2021-03-20T10:00:00.000Z") + ) + .build() + ) + + expected_query = """ + SELECT * FROM c + WHERE ((c.start_date BETWEEN @start_date AND @end_date) OR + (c.end_date BETWEEN @start_date AND @end_date)) + """ + query = time_entry_query_builder.get_query() + + assert get_string_without_empty_spaces( + query + ) == get_string_without_empty_spaces(expected_query) + + assert len(time_entry_query_builder.where_conditions) == 1 + assert len(time_entry_query_builder.get_parameters()) == 2 diff --git a/tests/utils/query_builder_test.py b/tests/utils/query_builder_test.py index 55b6171e..aa861667 100644 --- a/tests/utils/query_builder_test.py +++ b/tests/utils/query_builder_test.py @@ -1,5 +1,6 @@ from unittest.mock import patch from utils.query_builder import CosmosDBQueryBuilder +from utils.repository import get_string_without_empty_spaces import pytest @@ -178,3 +179,88 @@ def test__build_where_should_return_concatenate_conditions( result = query_builder._CosmosDBQueryBuilder__build_where() assert result == expected_condition + + +@pytest.mark.parametrize( + "offset,expected_condition,expected_params", + [(1, "OFFSET @offset", [{'name': '@offset', 'value': 1}]), (None, "", [])], +) +def test__build_offset( + offset, + expected_condition, + expected_params, +): + query_builder = CosmosDBQueryBuilder().add_sql_offset_condition(offset) + + result = query_builder._CosmosDBQueryBuilder__build_offset() + assert result == expected_condition + assert len(query_builder.parameters) == len(expected_params) + assert query_builder.get_parameters() == expected_params + + +@pytest.mark.parametrize( + "limit,expected_condition,expected_params", + [(1, "LIMIT @limit", [{'name': '@limit', 'value': 1}]), (None, "", [])], +) +def test__build_limit( + limit, + expected_condition, + expected_params, +): + query_builder = CosmosDBQueryBuilder().add_sql_limit_condition(limit) + + result = query_builder._CosmosDBQueryBuilder__build_limit() + assert result == expected_condition + assert len(query_builder.parameters) == len(expected_params) + assert query_builder.get_parameters() == expected_params + + +def test_build_with_all_calls_return_query_with_all_conditions(): + query_builder = ( + CosmosDBQueryBuilder() + .add_select_conditions(["c.description"]) + .add_sql_in_condition("id", ["id1", "id2"]) + .add_sql_where_equal_condition({'name': 'test'}) + .add_sql_offset_condition(2) + .add_sql_limit_condition(10) + .add_sql_visibility_condition(True) + .build() + ) + query = query_builder.get_query() + expected_query = """ + SELECT c.description FROM c + WHERE c.id IN ('id1', 'id2') AND c.name = @name AND NOT IS_DEFINED(c.deleted) + OFFSET @offset + LIMIT @limit + """ + + assert get_string_without_empty_spaces( + query + ) == get_string_without_empty_spaces(expected_query) + + assert len(query_builder.get_parameters()) > 0 + assert len(query_builder.where_conditions) > 0 + assert len(query_builder.select_conditions) > 0 + + +def test_build_with_empty_and_None_attributes_return_query_select_all(): + + query_builder = ( + CosmosDBQueryBuilder() + .add_select_conditions() + .add_sql_in_condition() + .add_sql_where_equal_condition() + .add_sql_limit_condition(None) + .add_sql_offset_condition(None) + .build() + ) + + query = query_builder.get_query() + + expected_query = """SELECT * FROM c""" + query = get_string_without_empty_spaces(query) + expected_query = get_string_without_empty_spaces(expected_query) + + assert query == expected_query + assert len(query_builder.get_parameters()) == 0 + assert len(query_builder.where_conditions) == 0 diff --git a/tests/utils/repository_test.py b/tests/utils/repository_test.py index 58959fc8..2fbe252e 100644 --- a/tests/utils/repository_test.py +++ b/tests/utils/repository_test.py @@ -1,5 +1,6 @@ from unittest.mock import patch from utils.repository import convert_list_to_tuple_string, create_sql_in_condition +from utils.repository import get_string_without_empty_spaces import pytest @@ -41,10 +42,12 @@ def test_convert_list_to_tuple_string_should_success( [ ("customer_id", ["id1"], "c.customer_id IN ('id1')"), ("customer_id", ["id1", "id2"], "c.customer_id IN ('id1', 'id2')"), - ("customer_id", ["id1", "id2", "id3", "id4"], "c.customer_id IN ('id1', 'id2', 'id3', 'id4')"), + ("customer_id", ["id1", "id2", "id3", "id4"], + "c.customer_id IN ('id1', 'id2', 'id3', 'id4')"), ("id", ["id1"], "c.id IN ('id1')"), ("id", ["id1", "id4"], "c.id IN ('id1', 'id4')"), - ("id", ["id1", "id2", "id3", "id4"], "c.id IN ('id1', 'id2', 'id3', 'id4')"), + ("id", ["id1", "id2", "id3", "id4"], + "c.id IN ('id1', 'id2', 'id3', 'id4')"), ], ) def test_create_sql_in_condition( @@ -53,4 +56,17 @@ def test_create_sql_in_condition( expected_result, ): result = create_sql_in_condition(field, values) - assert expected_result == result \ No newline at end of file + assert expected_result == result + + +@pytest.mark.parametrize( + "string,expected_string", + [ + (" text with \t tab", "text with tab"), + (" text with \n new line", "text with new line"), + (""" SELECT * from c """, "SELECT * from c"), + ], +) +def test_get_string_without_empty_spaces(string, expected_string): + string = get_string_without_empty_spaces(string) + assert string == expected_string diff --git a/utils/repository.py b/utils/repository.py index 0a10abed..dda0371b 100644 --- a/utils/repository.py +++ b/utils/repository.py @@ -8,7 +8,15 @@ def convert_list_to_tuple_string(ids_list): ) return result + def create_sql_in_condition(field, values): tuple_string = convert_list_to_tuple_string(values) - return "c.{field} IN {list}".format(field=field, list=tuple_string) \ No newline at end of file + return "c.{field} IN {list}".format(field=field, list=tuple_string) + + +def get_string_without_empty_spaces(string: str): + from re import sub + + string = string.replace("\n", "") + return sub("[\s]+", ' ', string).rstrip().lstrip() From be72ff8a08a3713cd74c4d2ac1e83693daaf1eb9 Mon Sep 17 00:00:00 2001 From: kelly Date: Fri, 26 Mar 2021 12:19:38 -0500 Subject: [PATCH 08/17] refactor: TT-185 rename get_string_without_empty_spaces to remove_white_spaces --- .../time_entries_query_builder_test.py | 6 ++--- tests/utils/query_builder_test.py | 10 +++----- tests/utils/repository_test.py | 25 +++++++++++++------ utils/repository.py | 2 +- 4 files changed, 24 insertions(+), 19 deletions(-) diff --git a/tests/time_tracker_api/time_entries/time_entries_query_builder_test.py b/tests/time_tracker_api/time_entries/time_entries_query_builder_test.py index 59c3d2f2..4b92b387 100644 --- a/tests/time_tracker_api/time_entries/time_entries_query_builder_test.py +++ b/tests/time_tracker_api/time_entries/time_entries_query_builder_test.py @@ -4,7 +4,7 @@ from time_tracker_api.time_entries.time_entries_query_builder import ( TimeEntryQueryBuilder, ) -from utils.repository import get_string_without_empty_spaces +from utils.repository import remove_white_spaces def test_TimeEntryQueryBuilder_is_subclass_CosmosDBQueryBuilder(): @@ -73,9 +73,7 @@ def test_build_with_add_sql_date_range_condition(): """ query = time_entry_query_builder.get_query() - assert get_string_without_empty_spaces( - query - ) == get_string_without_empty_spaces(expected_query) + assert remove_white_spaces(query) == remove_white_spaces(expected_query) assert len(time_entry_query_builder.where_conditions) == 1 assert len(time_entry_query_builder.get_parameters()) == 2 diff --git a/tests/utils/query_builder_test.py b/tests/utils/query_builder_test.py index aa861667..4fb505b1 100644 --- a/tests/utils/query_builder_test.py +++ b/tests/utils/query_builder_test.py @@ -1,6 +1,6 @@ from unittest.mock import patch from utils.query_builder import CosmosDBQueryBuilder -from utils.repository import get_string_without_empty_spaces +from utils.repository import remove_white_spaces import pytest @@ -234,9 +234,7 @@ def test_build_with_all_calls_return_query_with_all_conditions(): LIMIT @limit """ - assert get_string_without_empty_spaces( - query - ) == get_string_without_empty_spaces(expected_query) + assert remove_white_spaces(query) == remove_white_spaces(expected_query) assert len(query_builder.get_parameters()) > 0 assert len(query_builder.where_conditions) > 0 @@ -258,8 +256,8 @@ def test_build_with_empty_and_None_attributes_return_query_select_all(): query = query_builder.get_query() expected_query = """SELECT * FROM c""" - query = get_string_without_empty_spaces(query) - expected_query = get_string_without_empty_spaces(expected_query) + query = remove_white_spaces(query) + expected_query = remove_white_spaces(expected_query) assert query == expected_query assert len(query_builder.get_parameters()) == 0 diff --git a/tests/utils/repository_test.py b/tests/utils/repository_test.py index 2fbe252e..5721f160 100644 --- a/tests/utils/repository_test.py +++ b/tests/utils/repository_test.py @@ -1,6 +1,9 @@ from unittest.mock import patch -from utils.repository import convert_list_to_tuple_string, create_sql_in_condition -from utils.repository import get_string_without_empty_spaces +from utils.repository import ( + convert_list_to_tuple_string, + create_sql_in_condition, + remove_white_spaces, +) import pytest @@ -42,12 +45,18 @@ def test_convert_list_to_tuple_string_should_success( [ ("customer_id", ["id1"], "c.customer_id IN ('id1')"), ("customer_id", ["id1", "id2"], "c.customer_id IN ('id1', 'id2')"), - ("customer_id", ["id1", "id2", "id3", "id4"], - "c.customer_id IN ('id1', 'id2', 'id3', 'id4')"), + ( + "customer_id", + ["id1", "id2", "id3", "id4"], + "c.customer_id IN ('id1', 'id2', 'id3', 'id4')", + ), ("id", ["id1"], "c.id IN ('id1')"), ("id", ["id1", "id4"], "c.id IN ('id1', 'id4')"), - ("id", ["id1", "id2", "id3", "id4"], - "c.id IN ('id1', 'id2', 'id3', 'id4')"), + ( + "id", + ["id1", "id2", "id3", "id4"], + "c.id IN ('id1', 'id2', 'id3', 'id4')", + ), ], ) def test_create_sql_in_condition( @@ -67,6 +76,6 @@ def test_create_sql_in_condition( (""" SELECT * from c """, "SELECT * from c"), ], ) -def test_get_string_without_empty_spaces(string, expected_string): - string = get_string_without_empty_spaces(string) +def test_remove_white_spaces(string, expected_string): + string = remove_white_spaces(string) assert string == expected_string diff --git a/utils/repository.py b/utils/repository.py index dda0371b..4e0a5b20 100644 --- a/utils/repository.py +++ b/utils/repository.py @@ -15,7 +15,7 @@ def create_sql_in_condition(field, values): return "c.{field} IN {list}".format(field=field, list=tuple_string) -def get_string_without_empty_spaces(string: str): +def remove_white_spaces(string: str): from re import sub string = string.replace("\n", "") From 3401b398dcc6a448543fdf90855c422faa7abf13 Mon Sep 17 00:00:00 2001 From: kelly Date: Fri, 26 Mar 2021 12:21:17 -0500 Subject: [PATCH 09/17] refactor: TT-185 add time_entries_id in condition --- time_tracker_api/time_entries/time_entries_repository.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/time_tracker_api/time_entries/time_entries_repository.py b/time_tracker_api/time_entries/time_entries_repository.py index 60c245e5..dc9f7f0e 100644 --- a/time_tracker_api/time_entries/time_entries_repository.py +++ b/time_tracker_api/time_entries/time_entries_repository.py @@ -202,6 +202,7 @@ def find_all( def find_all_v2( self, event_context: EventContext, + time_entries_id: List[str], owner_id_list: List[str], date_range: tuple = None, fields: dict = None, @@ -215,6 +216,7 @@ def find_all_v2( query_builder = ( TimeEntryQueryBuilder() .add_sql_date_range_condition(date_range) + .add_sql_in_condition(time_entries_id) .add_sql_in_condition(owner_id_list) .add_sql_where_equal_condition(fields) .add_sql_limit_condition(limit) From 5358c406d183f6714fb091c7e01cd1d32dde5056 Mon Sep 17 00:00:00 2001 From: kelly Date: Fri, 26 Mar 2021 12:36:36 -0500 Subject: [PATCH 10/17] refactor: TT-185 delete isintance validation --- tests/utils/query_builder_test.py | 4 ---- utils/query_builder.py | 4 ++-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/utils/query_builder_test.py b/tests/utils/query_builder_test.py index ca4731e2..9c04c7b2 100644 --- a/tests/utils/query_builder_test.py +++ b/tests/utils/query_builder_test.py @@ -110,8 +110,6 @@ def test_add_sql_visibility_condition( (1, 1), (10, 10), (None, None), - ("hola", None), - (["lista"], None), ], ) def test_add_sql_limit_condition(limit_value, expected_limit): @@ -126,8 +124,6 @@ def test_add_sql_limit_condition(limit_value, expected_limit): (1, 1), (10, 10), (None, None), - ("hola", None), - (["lista"], None), ], ) def test_add_sql_offset_condition( diff --git a/utils/query_builder.py b/utils/query_builder.py index 229638de..b57024c5 100644 --- a/utils/query_builder.py +++ b/utils/query_builder.py @@ -41,12 +41,12 @@ def add_sql_visibility_condition(self, visible_only: bool): return self def add_sql_limit_condition(self, limit): - if limit and isinstance(limit, int): + if limit: self.limit = limit return self def add_sql_offset_condition(self, offset): - if offset and isinstance(offset, int): + if offset: self.offset = offset return self From a403edbf3d8ea99d061fd8e1a888ffce5d64785e Mon Sep 17 00:00:00 2001 From: roberto Date: Fri, 26 Mar 2021 12:57:32 -0500 Subject: [PATCH 11/17] refactor: TT-185 improve function remove_white_spaces --- utils/repository.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/utils/repository.py b/utils/repository.py index 4e0a5b20..48db2cf0 100644 --- a/utils/repository.py +++ b/utils/repository.py @@ -18,5 +18,4 @@ def create_sql_in_condition(field, values): def remove_white_spaces(string: str): from re import sub - string = string.replace("\n", "") - return sub("[\s]+", ' ', string).rstrip().lstrip() + return sub('[\s]+', ' ', string).strip() From d538899452fadaaaad90c776d7d266ea417e11ae Mon Sep 17 00:00:00 2001 From: roberto Date: Fri, 26 Mar 2021 13:06:43 -0500 Subject: [PATCH 12/17] refactor: TT-185 change column to columns --- utils/query_builder.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/utils/query_builder.py b/utils/query_builder.py index b57024c5..9ff4248b 100644 --- a/utils/query_builder.py +++ b/utils/query_builder.py @@ -14,9 +14,9 @@ def __init__(self): self.limit = None self.offset = None - def add_select_conditions(self, column: List[str] = None): - column = column if column else ["*"] - self.select_conditions.extend(column) + def add_select_conditions(self, columns: List[str] = None): + columns = columns if columns else ["*"] + self.select_conditions.extend(columns) return self def add_sql_in_condition( From 06cbca78b8ae5a42307a79a1a88e12c996167e74 Mon Sep 17 00:00:00 2001 From: roberto Date: Fri, 26 Mar 2021 13:11:50 -0500 Subject: [PATCH 13/17] refactor: TT-185 add more scenarios to test_add_sql_in_condition_should_update_where_list --- tests/utils/query_builder_test.py | 50 ++++++++----------------------- 1 file changed, 13 insertions(+), 37 deletions(-) diff --git a/tests/utils/query_builder_test.py b/tests/utils/query_builder_test.py index 9c04c7b2..6ed6440b 100644 --- a/tests/utils/query_builder_test.py +++ b/tests/utils/query_builder_test.py @@ -36,12 +36,12 @@ def test_add_select_conditions_should_update_select_list( (None, ["id"], []), ("id", ["id"], ["c.id IN ('id')"]), ("id", ["id1", "id2"], ["c.id IN ('id1', 'id2')"]), + ("owner_id", ["id1", "id2"], ["c.owner_id IN ('id1', 'id2')"]), + ("customer_id", ["id1", "id2"], ["c.customer_id IN ('id1', 'id2')"]), ], ) 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 @@ -66,9 +66,7 @@ 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) @@ -93,8 +91,7 @@ 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 @@ -105,12 +102,7 @@ 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) @@ -119,16 +111,10 @@ 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 @@ -139,15 +125,10 @@ 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 @@ -165,8 +146,7 @@ def test__build_select_return_fields_in_select_list( ], ) def test__build_where_should_return_concatenate_conditions( - where_dict, - expected_condition, + where_dict, expected_condition, ): query_builder = CosmosDBQueryBuilder().add_sql_where_equal_condition( where_dict @@ -182,9 +162,7 @@ def test__build_where_should_return_concatenate_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) @@ -199,9 +177,7 @@ 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) From a1331fcfc507594f38d184d3e0bd09317c5936db Mon Sep 17 00:00:00 2001 From: roberto Date: Fri, 26 Mar 2021 13:25:20 -0500 Subject: [PATCH 14/17] refactor: TT-185 add more scenarios to test__build_where_should_return_concatenated_conditions --- tests/utils/query_builder_test.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/utils/query_builder_test.py b/tests/utils/query_builder_test.py index 6ed6440b..bbe2e2ce 100644 --- a/tests/utils/query_builder_test.py +++ b/tests/utils/query_builder_test.py @@ -139,17 +139,19 @@ def test__build_select_return_fields_in_select_list( @pytest.mark.parametrize( - "where_dict,expected_condition", + "fields,expected_condition", [ + (None, ""), + ({}, ""), ({"id": 1}, "WHERE c.id = @id"), ({"id": 1, "name": "test"}, "WHERE c.id = @id AND c.name = @name"), ], ) -def test__build_where_should_return_concatenate_conditions( - where_dict, expected_condition, +def test__build_where_should_return_concatenated_conditions( + fields, expected_condition, ): query_builder = CosmosDBQueryBuilder().add_sql_where_equal_condition( - where_dict + fields ) result = query_builder._CosmosDBQueryBuilder__build_where() From c44f20ed4ec1037e7b5fb5751e4aa260d4af4222 Mon Sep 17 00:00:00 2001 From: roberto Date: Fri, 26 Mar 2021 13:38:38 -0500 Subject: [PATCH 15/17] refactor: TT-185 improve test_TimeEntryQueryBuilder_is_subclass_CosmosDBQueryBuilder --- .../time_entries/time_entries_query_builder_test.py | 11 +++++------ .../time_entries/time_entries_query_builder.py | 6 +++--- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/tests/time_tracker_api/time_entries/time_entries_query_builder_test.py b/tests/time_tracker_api/time_entries/time_entries_query_builder_test.py index a4cab177..be02c3ef 100644 --- a/tests/time_tracker_api/time_entries/time_entries_query_builder_test.py +++ b/tests/time_tracker_api/time_entries/time_entries_query_builder_test.py @@ -1,5 +1,4 @@ import pytest -from unittest.mock import patch from utils.query_builder import CosmosDBQueryBuilder from time_tracker_api.time_entries.time_entries_query_builder import ( TimeEntryQueryBuilder, @@ -11,7 +10,9 @@ def test_TimeEntryQueryBuilder_is_subclass_CosmosDBQueryBuilder(): query_builder = CosmosDBQueryBuilder() time_entries_query_builder = TimeEntryQueryBuilder() - assert issubclass(TimeEntryQueryBuilder, CosmosDBQueryBuilder) == True + assert issubclass( + time_entries_query_builder.__class__, query_builder.__class__ + ) @pytest.mark.parametrize( @@ -46,10 +47,8 @@ def test_TimeEntryQueryBuilder_is_subclass_CosmosDBQueryBuilder(): def test_add_sql_date_range_condition_should_update_where_list( start_date, end_date, expected_params ): - time_entry_query_builder = ( - TimeEntryQueryBuilder().add_sql_date_range_condition( - (start_date, end_date) - ) + time_entry_query_builder = TimeEntryQueryBuilder().add_sql_date_range_condition( + (start_date, end_date) ) assert len(time_entry_query_builder.where_conditions) == 1 diff --git a/time_tracker_api/time_entries/time_entries_query_builder.py b/time_tracker_api/time_entries/time_entries_query_builder.py index 58127524..40be129e 100644 --- a/time_tracker_api/time_entries/time_entries_query_builder.py +++ b/time_tracker_api/time_entries/time_entries_query_builder.py @@ -5,9 +5,9 @@ class TimeEntryQueryBuilder(CosmosDBQueryBuilder): def __init__(self): super(TimeEntryQueryBuilder, self).__init__() - def add_sql_date_range_condition(self, dates: tuple = None): - if dates and len(dates) == 2: - start_date, end_date = dates + def add_sql_date_range_condition(self, date_range: tuple = None): + if date_range and len(date_range) == 2: + start_date, end_date = date_range condition = """ ((c.start_date BETWEEN @start_date AND @end_date) OR (c.end_date BETWEEN @start_date AND @end_date)) From f17d841f13f552a5ec81701dfb1800cb1851916b Mon Sep 17 00:00:00 2001 From: roberto Date: Fri, 26 Mar 2021 14:08:09 -0500 Subject: [PATCH 16/17] refactor: TT-185 rename args in TimeEntriesRepository --- .../time_entries/time_entries_repository.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/time_tracker_api/time_entries/time_entries_repository.py b/time_tracker_api/time_entries/time_entries_repository.py index dc9f7f0e..019a79a8 100644 --- a/time_tracker_api/time_entries/time_entries_repository.py +++ b/time_tracker_api/time_entries/time_entries_repository.py @@ -8,9 +8,7 @@ TimeEntryCosmosDBModel, ) -from utils.time import ( - current_datetime_str, -) +from utils.time import current_datetime_str from utils.extend_model import ( add_project_info_to_time_entries, @@ -202,8 +200,8 @@ def find_all( def find_all_v2( self, event_context: EventContext, - time_entries_id: List[str], - owner_id_list: List[str], + time_entry_ids: List[str], + owner_ids: List[str], date_range: tuple = None, fields: dict = None, limit: int = None, @@ -216,8 +214,8 @@ def find_all_v2( query_builder = ( TimeEntryQueryBuilder() .add_sql_date_range_condition(date_range) - .add_sql_in_condition(time_entries_id) - .add_sql_in_condition(owner_id_list) + .add_sql_in_condition(time_entry_ids) + .add_sql_in_condition(owner_ids) .add_sql_where_equal_condition(fields) .add_sql_limit_condition(limit) .add_sql_offset_condition(offset) From be6e9b17bc89fb33e78172ce47e342eb2397e45f Mon Sep 17 00:00:00 2001 From: kelly Date: Fri, 26 Mar 2021 16:01:52 -0500 Subject: [PATCH 17/17] refactor: TT-185 change the scenarios in test_add_sql_date_range_condition_should_update_where_list --- .../time_entries_query_builder_test.py | 44 ++++--------------- 1 file changed, 9 insertions(+), 35 deletions(-) diff --git a/tests/time_tracker_api/time_entries/time_entries_query_builder_test.py b/tests/time_tracker_api/time_entries/time_entries_query_builder_test.py index be02c3ef..88e7abb8 100644 --- a/tests/time_tracker_api/time_entries/time_entries_query_builder_test.py +++ b/tests/time_tracker_api/time_entries/time_entries_query_builder_test.py @@ -15,42 +15,16 @@ def test_TimeEntryQueryBuilder_is_subclass_CosmosDBQueryBuilder(): ) -@pytest.mark.parametrize( - "start_date,end_date,expected_params", - [ - ( - "2021-03-19T05:07:00.000Z", - "2021-03-25T10:00:00.000Z", - [ - {"name": "@start_date", "value": "2021-03-19T05:07:00.000Z"}, - {"name": "@end_date", "value": "2021-03-25T10:00:00.000Z"}, - ], - ), - ( - "2021-02-10T05:08:00.000Z", - "2021-03-20T11:00:00.000Z", - [ - {"name": "@start_date", "value": "2021-02-10T05:08:00.000Z"}, - {"name": "@end_date", "value": "2021-03-20T11:00:00.000Z"}, - ], - ), - ( - "2021-01-02T05:09:00.000Z", - "2021-01-26T12:00:00.000Z", - [ - {"name": "@start_date", "value": "2021-01-02T05:09:00.000Z"}, - {"name": "@end_date", "value": "2021-01-26T12:00:00.000Z"}, - ], - ), - ], -) -def test_add_sql_date_range_condition_should_update_where_list( - start_date, end_date, expected_params -): - time_entry_query_builder = TimeEntryQueryBuilder().add_sql_date_range_condition( - (start_date, end_date) +def test_add_sql_date_range_condition_should_update_where_list(): + time_entry_query_builder = ( + TimeEntryQueryBuilder().add_sql_date_range_condition( + ("2021-03-19T05:07:00.000Z", "2021-03-25T10:00:00.000Z") + ) ) - + expected_params = [ + {"name": "@start_date", "value": "2021-03-19T05:07:00.000Z"}, + {"name": "@end_date", "value": "2021-03-25T10:00:00.000Z"}, + ] assert len(time_entry_query_builder.where_conditions) == 1 assert len(time_entry_query_builder.parameters) == len(expected_params) assert time_entry_query_builder.get_parameters() == expected_params