Skip to content

Commit f386c1e

Browse files
authored
Merge pull request #218 from ioet/217_allow_limits_overlap
fix: allow limits overlap in time entries #217
2 parents 4a7226f + ce99603 commit f386c1e

File tree

2 files changed

+130
-62
lines changed

2 files changed

+130
-62
lines changed

tests/time_tracker_api/time_entries/time_entries_model_test.py

Lines changed: 121 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,27 @@
1-
from datetime import datetime, timedelta
2-
31
import pytest
42
from faker import Faker
53

6-
from utils.time import datetime_str, current_datetime
74
from commons.data_access_layer.database import EventContext
85
from time_tracker_api.time_entries.time_entries_model import (
96
TimeEntryCosmosDBRepository,
107
TimeEntryCosmosDBModel,
11-
container_definition,
128
)
139

14-
fake = Faker()
15-
16-
now = current_datetime()
17-
yesterday = current_datetime() - timedelta(days=1)
18-
two_days_ago = current_datetime() - timedelta(days=2)
19-
2010

2111
def create_time_entry(
22-
start_date: datetime,
23-
end_date: datetime,
12+
start_date: str,
13+
end_date: str,
2414
owner_id: str,
2515
tenant_id: str,
2616
event_context: EventContext,
2717
time_entry_repository: TimeEntryCosmosDBRepository,
2818
) -> TimeEntryCosmosDBModel:
2919
data = {
30-
"project_id": fake.uuid4(),
31-
"activity_id": fake.uuid4(),
32-
"description": fake.paragraph(nb_sentences=2),
33-
"start_date": datetime_str(start_date),
34-
"end_date": datetime_str(end_date),
20+
"project_id": Faker().uuid4(),
21+
"activity_id": Faker().uuid4(),
22+
"description": Faker().paragraph(nb_sentences=2),
23+
"start_date": start_date,
24+
"end_date": end_date,
3525
"owner_id": owner_id,
3626
"tenant_id": tenant_id,
3727
}
@@ -43,59 +33,146 @@ def create_time_entry(
4333

4434

4535
@pytest.mark.parametrize(
46-
'start_date,end_date', [(two_days_ago, yesterday), (now, None)]
36+
'start_date,end_date,start_date_,end_date_',
37+
[
38+
(
39+
"2020-10-01T05:00:00.000Z",
40+
"2020-10-01T10:00:00.000Z",
41+
"2020-10-01T05:00:00.000Z",
42+
"2020-10-01T10:00:00.000Z",
43+
),
44+
(
45+
"2020-10-01T05:00:00.000Z",
46+
"2020-10-01T10:00:00.000Z",
47+
"2020-10-01T07:00:00.000Z",
48+
"2020-10-01T12:00:00.000Z",
49+
),
50+
(
51+
"2020-10-01T05:00:00.000Z",
52+
"2020-10-01T10:00:00.000Z",
53+
"2020-10-01T02:00:00.000Z",
54+
"2020-10-01T07:00:00.000Z",
55+
),
56+
(
57+
"2020-10-01T05:00:00.000Z",
58+
"2020-10-01T10:00:00.000Z",
59+
"2020-10-01T02:00:00.000Z",
60+
"2020-10-01T12:00:00.000Z",
61+
),
62+
],
4763
)
4864
def test_find_interception_with_date_range_should_find(
49-
start_date: datetime,
50-
end_date: datetime,
65+
start_date: str,
66+
end_date: str,
67+
start_date_: str,
68+
end_date_: str,
5169
owner_id: str,
5270
tenant_id: str,
5371
time_entry_repository: TimeEntryCosmosDBRepository,
72+
event_context: EventContext,
5473
):
55-
event_ctx = EventContext(
56-
container_definition["id"],
57-
"create",
58-
tenant_id=tenant_id,
59-
user_id=owner_id,
60-
)
61-
6274
existing_item = create_time_entry(
6375
start_date,
6476
end_date,
6577
owner_id,
6678
tenant_id,
67-
event_ctx,
79+
event_context,
6880
time_entry_repository,
6981
)
7082

7183
try:
7284
result = time_entry_repository.find_interception_with_date_range(
73-
datetime_str(yesterday), datetime_str(now), owner_id, tenant_id
85+
start_date_, end_date_, owner_id, tenant_id
7486
)
7587

7688
assert result is not None
7789
assert len(result) > 0
7890
assert any([existing_item.id == item.id for item in result])
7991
finally:
80-
time_entry_repository.delete_permanently(existing_item.id, event_ctx)
92+
time_entry_repository.delete_permanently(
93+
existing_item.id, event_context
94+
)
8195

8296

83-
def test_find_interception_should_ignore_id_of_existing_item(
97+
@pytest.mark.parametrize(
98+
'start_date,end_date,start_date_,end_date_',
99+
[
100+
(
101+
"2020-10-01T05:00:00.000Z",
102+
"2020-10-01T10:00:00.000Z",
103+
"2020-10-01T10:00:00.000Z",
104+
"2020-10-01T15:00:00.000Z",
105+
),
106+
(
107+
"2020-10-01T05:00:00.000Z",
108+
"2020-10-01T10:00:00.000Z",
109+
"2020-10-01T12:00:00.000Z",
110+
"2020-10-01T15:00:00.000Z",
111+
),
112+
(
113+
"2020-10-01T05:00:00.000Z",
114+
"2020-10-01T10:00:00.000Z",
115+
"2020-10-01T02:00:00.000Z",
116+
"2020-10-01T05:00:00.000Z",
117+
),
118+
(
119+
"2020-10-01T05:00:00.000Z",
120+
"2020-10-01T10:00:00.000Z",
121+
"2020-10-01T02:00:00.000Z",
122+
"2020-10-01T04:00:00.000Z",
123+
),
124+
],
125+
)
126+
def test_find_interception_with_date_range_should_not_find(
127+
start_date: str,
128+
end_date: str,
129+
start_date_: str,
130+
end_date_: str,
84131
owner_id: str,
85132
tenant_id: str,
86133
time_entry_repository: TimeEntryCosmosDBRepository,
134+
event_context: EventContext,
87135
):
88-
event_ctx = EventContext(
89-
container_definition["id"],
90-
"create",
91-
tenant_id=tenant_id,
92-
user_id=owner_id,
136+
existing_item = create_time_entry(
137+
start_date,
138+
end_date,
139+
owner_id,
140+
tenant_id,
141+
event_context,
142+
time_entry_repository,
93143
)
94-
start_date = datetime_str(yesterday)
95-
end_date = datetime_str(now)
144+
145+
try:
146+
result = time_entry_repository.find_interception_with_date_range(
147+
start_date_, end_date_, owner_id, tenant_id
148+
)
149+
150+
assert result == []
151+
assert len(result) == 0
152+
assert not any([existing_item.id == item.id for item in result])
153+
finally:
154+
time_entry_repository.delete_permanently(
155+
existing_item.id, event_context
156+
)
157+
158+
159+
def test_find_interception_should_ignore_id_of_existing_item(
160+
owner_id: str,
161+
tenant_id: str,
162+
time_entry_repository: TimeEntryCosmosDBRepository,
163+
event_context: EventContext,
164+
):
165+
start_date = "2020-10-01T05:00:00.000Z"
166+
end_date = "2020-10-01T10:00:00.000Z"
96167
existing_item = create_time_entry(
97-
yesterday, now, owner_id, tenant_id, event_ctx, time_entry_repository
168+
start_date,
169+
end_date,
170+
owner_id,
171+
tenant_id,
172+
event_context,
173+
time_entry_repository,
98174
)
175+
99176
try:
100177
colliding_result = time_entry_repository.find_interception_with_date_range(
101178
start_date, end_date, owner_id, tenant_id
@@ -109,15 +186,16 @@ def test_find_interception_should_ignore_id_of_existing_item(
109186
ignore_id=existing_item.id,
110187
)
111188

112-
colliding_result is not None
189+
assert colliding_result is not None
113190
assert any([existing_item.id == item.id for item in colliding_result])
114-
115-
non_colliding_result is not None
191+
assert non_colliding_result is not None
116192
assert not any(
117193
[existing_item.id == item.id for item in non_colliding_result]
118194
)
119195
finally:
120-
time_entry_repository.delete_permanently(existing_item.id, event_ctx)
196+
time_entry_repository.delete_permanently(
197+
existing_item.id, event_context
198+
)
121199

122200

123201
def test_find_running_should_return_running_time_entry(

time_tracker_api/time_entries/time_entries_model.py

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,8 @@ def find_interception_with_date_range(
308308
SELECT * FROM c
309309
WHERE ((c.start_date BETWEEN @start_date AND @end_date)
310310
OR (c.end_date BETWEEN @start_date AND @end_date))
311+
AND c.start_date!= @end_date
312+
AND c.end_date!= @start_date
311313
{conditions_clause}
312314
{ignore_id_condition}
313315
{visibility_condition}
@@ -455,8 +457,7 @@ def get_all(self, conditions: dict = None, **kwargs) -> list:
455457
conditions.update({"owner_id": event_ctx.user_id})
456458

457459
custom_query = self.build_custom_query(
458-
is_admin=event_ctx.is_admin,
459-
conditions=conditions,
460+
is_admin=event_ctx.is_admin, conditions=conditions,
460461
)
461462
date_range = self.handle_date_filter_args(args=conditions)
462463
limit = conditions.get("limit", None)
@@ -476,8 +477,7 @@ def get_all_paginated(self, conditions: dict = None, **kwargs) -> list:
476477
event_ctx = self.create_event_context("read-many")
477478
get_all_conditions.update({"owner_id": event_ctx.user_id})
478479
custom_query = self.build_custom_query(
479-
is_admin=event_ctx.is_admin,
480-
conditions=get_all_conditions,
480+
is_admin=event_ctx.is_admin, conditions=get_all_conditions,
481481
)
482482
date_range = self.handle_date_filter_args(args=get_all_conditions)
483483
records_total = self.repository.count(
@@ -488,8 +488,7 @@ def get_all_paginated(self, conditions: dict = None, **kwargs) -> list:
488488
)
489489
conditions.update({"owner_id": event_ctx.user_id})
490490
custom_query = self.build_custom_query(
491-
is_admin=event_ctx.is_admin,
492-
conditions=conditions,
491+
is_admin=event_ctx.is_admin, conditions=conditions,
493492
)
494493
date_range = self.handle_date_filter_args(args=conditions)
495494
length = conditions.get("length", None)
@@ -533,11 +532,7 @@ def update(self, id, data: dict, description=None):
533532
time_entry = self.repository.find(id, event_ctx)
534533
self.check_whether_current_user_owns_item(time_entry)
535534

536-
return self.repository.partial_update(
537-
id,
538-
data,
539-
event_ctx,
540-
)
535+
return self.repository.partial_update(id, data, event_ctx,)
541536

542537
def stop(self, id):
543538
event_ctx = self.create_event_context("update", "Stop time entry")
@@ -547,9 +542,7 @@ def stop(self, id):
547542
self.check_time_entry_is_not_stopped(time_entry)
548543

549544
return self.repository.partial_update(
550-
id,
551-
{'end_date': current_datetime_str()},
552-
event_ctx,
545+
id, {'end_date': current_datetime_str()}, event_ctx,
553546
)
554547

555548
def restart(self, id):
@@ -560,18 +553,15 @@ def restart(self, id):
560553
self.check_time_entry_is_not_started(time_entry)
561554

562555
return self.repository.partial_update(
563-
id,
564-
{'end_date': None},
565-
event_ctx,
556+
id, {'end_date': None}, event_ctx,
566557
)
567558

568559
def delete(self, id):
569560
event_ctx = self.create_event_context("delete")
570561
time_entry = self.repository.find(id, event_ctx)
571562
self.check_whether_current_user_owns_item(time_entry)
572563
self.repository.delete(
573-
id,
574-
event_ctx,
564+
id, event_ctx,
575565
)
576566

577567
def find_running(self):

0 commit comments

Comments
 (0)