Skip to content

Commit d919059

Browse files
committed
Fix a bug in first reservable time calc pagination optimization
1 parent d369174 commit d919059

3 files changed

Lines changed: 76 additions & 32 deletions

File tree

reservation_units/utils/first_reservable_time_helper/first_reservable_time_helper.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -288,8 +288,6 @@ def _get_reservation_unit_queryset_for_calculation(self) -> ReservationUnitQuery
288288
self.original_reservation_unit_queryset.defer(None)
289289
.select_related(None)
290290
.prefetch_related(None)
291-
# ReservationUnits are not reservable without a HaukiResource
292-
.exclude(origin_hauki_resource__isnull=True)
293291
.prefetch_related(
294292
# Required for calculating first reservable time
295293
models.Prefetch(

reservation_units/utils/first_reservable_time_helper/first_reservable_time_reservation_unit_helper.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ def __init__(self, parent: FirstReservableTimeHelper, reservation_unit: Reservat
9898
def calculate_first_reservable_time(self) -> ReservableTimeOutput:
9999
self.is_reservation_unit_closed = True
100100

101+
# ReservationUnits are not reservable without a HaukiResource
102+
if self.reservation_unit.origin_hauki_resource is None:
103+
return ReservableTimeOutput(is_closed=self.is_reservation_unit_closed, first_reservable_time=None)
104+
101105
# Go through each ReservableTimeSpan individually one-by-one until a suitable time span is found.
102106
for reservable_time_span in self.reservation_unit.origin_hauki_resource.reservable_time_spans.all():
103107
helper = ReservableTimeSpanFirstReservableTimeHelper(parent=self, reservable_time_span=reservable_time_span)

tests/test_graphql_api/test_reservation_unit/test_query_first_reservable_time.py

Lines changed: 72 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ def apply_reservation_unit_override_settings(
205205

206206

207207
@freezegun.freeze_time(NOW)
208-
def test__query_reservation_unit__first_reservable_time__no_results_time_spans_dont_exist(graphql, reservation_unit):
208+
def test__reservation_unit__first_reservable_time__no_results_time_spans_dont_exist(graphql, reservation_unit):
209209
"""When there are no time spans, the first reservable time should be None."""
210210
response = graphql(reservation_units_reservable_query())
211211

@@ -261,9 +261,7 @@ def test__query_reservation_unit__first_reservable_time__no_results_time_spans_d
261261
)
262262
)
263263
@freezegun.freeze_time(NOW)
264-
def test__query_reservation_unit__first_reservable_time__filters__invalid_values(
265-
graphql, reservation_unit, filters, result
266-
):
264+
def test__reservation_unit__first_reservable_time__filters__invalid_values(graphql, reservation_unit, filters, result):
267265
"""Invalid filter values should return an error"""
268266
response = graphql(reservation_units_reservable_query(**asdict(filters)))
269267

@@ -330,7 +328,7 @@ def test__query_reservation_unit__first_reservable_time__filters__invalid_values
330328
)
331329
)
332330
@freezegun.freeze_time(NOW)
333-
def test__query_reservation_unit__first_reservable_time__filters__too_strict_causes_no_first_reservable_time_exists(
331+
def test__reservation_unit__first_reservable_time__filters__too_strict_causes_no_first_reservable_time_exists(
334332
graphql, reservation_unit, filters, result
335333
):
336334
"""
@@ -576,7 +574,7 @@ def test__query_reservation_unit_reservable__filters__should_not_exclude_time_sp
576574
)
577575
)
578576
@freezegun.freeze_time(NOW)
579-
def test__query_reservation_unit__first_reservable_time__filters__simple(graphql, reservation_unit, filters, result):
577+
def test__reservation_unit__first_reservable_time__filters__simple(graphql, reservation_unit, filters, result):
580578
"""
581579
Filters with 'simple' values, which are a bit more advanced than the previous test.
582580
@@ -673,7 +671,7 @@ def test__query_reservation_unit__first_reservable_time__filters__simple(graphql
673671
)
674672
)
675673
@freezegun.freeze_time(NOW)
676-
def test__query_reservation_unit__first_reservable_time__filters__multiple_time_spans_on_the_same_day(
674+
def test__reservation_unit__first_reservable_time__filters__multiple_time_spans_on_the_same_day(
677675
graphql, reservation_unit, filters, result
678676
):
679677
"""
@@ -757,7 +755,7 @@ def test__query_reservation_unit__first_reservable_time__filters__multiple_time_
757755
)
758756
)
759757
@freezegun.freeze_time(NOW)
760-
def test__query_reservation_unit__first_reservable_time__filters__multiple_days_long_time_span(
758+
def test__reservation_unit__first_reservable_time__filters__multiple_days_long_time_span(
761759
graphql, reservation_unit, filters, result
762760
):
763761
"""
@@ -890,7 +888,7 @@ def test__query_reservation_unit__first_reservable_time__filters__multiple_days_
890888
)
891889
)
892890
@freezegun.freeze_time(NOW)
893-
def test__query_reservation_unit__first_reservable_time__reservation_unit_settings(
891+
def test__reservation_unit__first_reservable_time__reservation_unit_settings(
894892
graphql, reservation_unit, filters, result, reservation_unit_settings
895893
):
896894
"""
@@ -1025,7 +1023,7 @@ def test__query_reservation_unit__first_reservable_time__reservation_unit_settin
10251023
)
10261024
)
10271025
@freezegun.freeze_time(NOW)
1028-
def test__query_reservation_unit__first_reservable_time__filters_and_reservation_unit_settings_combined(
1026+
def test__reservation_unit__first_reservable_time__filters_and_reservation_unit_settings_combined(
10291027
graphql, reservation_unit, reservation_unit_settings, filters, result
10301028
):
10311029
"""
@@ -1208,7 +1206,7 @@ def test__query_reservation_unit__first_reservable_time__filters_and_reservation
12081206
)
12091207
)
12101208
@freezegun.freeze_time(NOW)
1211-
def test__query_reservation_unit__first_reservable_time__application_rounds(
1209+
def test__reservation_unit__first_reservable_time__application_rounds(
12121210
graphql, reservation_unit, application_round_params, filters, result
12131211
):
12141212
"""
@@ -1248,7 +1246,7 @@ def test__query_reservation_unit__first_reservable_time__application_rounds(
12481246

12491247

12501248
@freezegun.freeze_time(NOW)
1251-
def test__query_reservation_unit__first_reservable_time__filters__application_rounds__start_date_at_round_last_day(
1249+
def test__reservation_unit__first_reservable_time__filters__application_rounds__start_date_at_round_last_day(
12521250
graphql, reservation_unit
12531251
):
12541252
"""
@@ -1307,7 +1305,7 @@ def test__query_reservation_unit__first_reservable_time__filters__application_ro
13071305

13081306

13091307
@freezegun.freeze_time(NOW)
1310-
def test__query_reservation_unit__first_reservable_time__reservations__own_reservation_block_reservable_time(
1308+
def test__reservation_unit__first_reservable_time__reservations__own_reservation_block_reservable_time(
13111309
graphql, reservation_unit
13121310
):
13131311
"""
@@ -1350,7 +1348,7 @@ def test__query_reservation_unit__first_reservable_time__reservations__own_reser
13501348

13511349

13521350
@freezegun.freeze_time(NOW)
1353-
def test__query_reservation_unit__first_reservable_time__reservations__unrelated_reservation_should_not_affect(
1351+
def test__reservation_unit__first_reservable_time__reservations__unrelated_reservation_should_not_affect(
13541352
graphql, reservation_unit
13551353
):
13561354
"""
@@ -1397,7 +1395,7 @@ def test__query_reservation_unit__first_reservable_time__reservations__unrelated
13971395

13981396

13991397
@freezegun.freeze_time(NOW)
1400-
def test__query_reservation_unit__first_reservable_time__reservations__dont_include_cancelled_or_denied_reservations(
1398+
def test__reservation_unit__first_reservable_time__reservations__dont_include_cancelled_or_denied_reservations(
14011399
graphql,
14021400
reservation_unit,
14031401
):
@@ -1454,7 +1452,7 @@ def test__query_reservation_unit__first_reservable_time__reservations__dont_incl
14541452

14551453

14561454
@freezegun.freeze_time(NOW)
1457-
def test__query_reservation_unit__first_reservable_time__reservations__date_filters_on_same_day_as_reservation(
1455+
def test__reservation_unit__first_reservable_time__reservations__date_filters_on_same_day_as_reservation(
14581456
graphql, reservation_unit
14591457
):
14601458
"""
@@ -1507,7 +1505,7 @@ def test__query_reservation_unit__first_reservable_time__reservations__date_filt
15071505

15081506

15091507
@freezegun.freeze_time(NOW)
1510-
def test__query_reservation_unit__first_reservable_time__reservations__filter_start_time_at_reservation_start(
1508+
def test__reservation_unit__first_reservable_time__reservations__filter_start_time_at_reservation_start(
15111509
graphql,
15121510
reservation_unit,
15131511
):
@@ -1562,7 +1560,7 @@ def test__query_reservation_unit__first_reservable_time__reservations__filter_st
15621560

15631561

15641562
@freezegun.freeze_time(NOW)
1565-
def test__query_reservation_unit__first_reservable_time__reservations__in_common_hierarchy__by_space(
1563+
def test__reservation_unit__first_reservable_time__reservations__in_common_hierarchy__by_space(
15661564
graphql, reservation_unit
15671565
):
15681566
"""
@@ -1625,7 +1623,7 @@ def test__query_reservation_unit__first_reservable_time__reservations__in_common
16251623

16261624

16271625
@freezegun.freeze_time(NOW)
1628-
def test__query_reservation_unit__first_reservable_time__reservations__in_common_hierarchy__by_resource(
1626+
def test__reservation_unit__first_reservable_time__reservations__in_common_hierarchy__by_resource(
16291627
graphql, reservation_unit
16301628
):
16311629
"""
@@ -1688,7 +1686,7 @@ def test__query_reservation_unit__first_reservable_time__reservations__in_common
16881686

16891687

16901688
@freezegun.freeze_time(NOW)
1691-
def test__query_reservation_unit__first_reservable_time__reservations__in_common_hierarchy__by_resource__and_filters(
1689+
def test__reservation_unit__first_reservable_time__reservations__in_common_hierarchy__by_resource__and_filters(
16921690
graphql, reservation_unit
16931691
):
16941692
"""
@@ -1757,7 +1755,7 @@ def test__query_reservation_unit__first_reservable_time__reservations__in_common
17571755

17581756

17591757
@freezegun.freeze_time(NOW)
1760-
def test__query_reservation_unit__first_reservable_time__buffers__goes_over_closed_time(graphql, reservation_unit):
1758+
def test__reservation_unit__first_reservable_time__buffers__goes_over_closed_time(graphql, reservation_unit):
17611759
"""
17621760
ReservationUnits before and after buffers should not affect reservability when the buffer is in a closed time span.
17631761
@@ -1823,7 +1821,7 @@ def test__query_reservation_unit__first_reservable_time__buffers__goes_over_clos
18231821
)
18241822
)
18251823
@freezegun.freeze_time(NOW)
1826-
def test__query_reservation_unit__first_reservable_time__buffers__different_length_buffers_are_overlapping(
1824+
def test__reservation_unit__first_reservable_time__buffers__different_length_buffers_are_overlapping(
18271825
graphql, reservation_unit, filters, result, reservation_unit_settings
18281826
):
18291827
"""
@@ -1901,7 +1899,7 @@ def test__query_reservation_unit__first_reservable_time__buffers__different_leng
19011899

19021900

19031901
@freezegun.freeze_time(NOW)
1904-
def test__query_reservation_unit__first_reservable_time__buffers__start_and_end_same_time_different_after_buffers(
1902+
def test__reservation_unit__first_reservable_time__buffers__start_and_end_same_time_different_after_buffers(
19051903
graphql, reservation_unit
19061904
):
19071905
"""
@@ -1988,7 +1986,7 @@ def test__query_reservation_unit__first_reservable_time__buffers__start_and_end_
19881986

19891987

19901988
@freezegun.freeze_time(NOW)
1991-
def test__query_reservation_unit__first_reservable_time__buffers__different_before_buffers__before_reservation(
1989+
def test__reservation_unit__first_reservable_time__buffers__different_before_buffers__before_reservation(
19921990
graphql, reservation_unit
19931991
):
19941992
"""
@@ -2081,7 +2079,7 @@ def test__query_reservation_unit__first_reservable_time__buffers__different_befo
20812079

20822080

20832081
@freezegun.freeze_time(NOW)
2084-
def test__query_reservation_unit__first_reservable_time__buffers__different_before_buffers__before_closed_time(
2082+
def test__reservation_unit__first_reservable_time__buffers__different_before_buffers__before_closed_time(
20852083
graphql, reservation_unit
20862084
):
20872085
"""
@@ -2166,9 +2164,7 @@ def test__query_reservation_unit__first_reservable_time__buffers__different_befo
21662164

21672165

21682166
@freezegun.freeze_time(datetime(NEXT_YEAR, 1, 1, 10, microsecond=1).astimezone(DEFAULT_TIMEZONE))
2169-
def test__query_reservation_unit__first_reservable_time__round_current_time_to_the_next_minute(
2170-
graphql, reservation_unit
2171-
):
2167+
def test__reservation_unit__first_reservable_time__round_current_time_to_the_next_minute(graphql, reservation_unit):
21722168
"""
21732169
This is a regression test for a bug that was found during manual testing.
21742170
@@ -2200,7 +2196,7 @@ def test__query_reservation_unit__first_reservable_time__round_current_time_to_t
22002196

22012197

22022198
@freezegun.freeze_time(NOW)
2203-
def test__query_reservation_unit__first_reservable_time__extra_long_interval(graphql, reservation_unit):
2199+
def test__reservation_unit__first_reservable_time__extra_long_interval(graphql, reservation_unit):
22042200
"""
22052201
Make sure that:
22062202
- The correct first reservable time is returned even with a long interval
@@ -2266,7 +2262,7 @@ def test__query_reservation_unit__first_reservable_time__extra_long_interval(gra
22662262

22672263

22682264
@freezegun.freeze_time(NOW)
2269-
def test__query_reservation_unit__first_reservable_time__blocked_type_reservation_can_overlap_with_buffer(
2265+
def test__reservation_unit__first_reservable_time__blocked_type_reservation_can_overlap_with_buffer(
22702266
graphql, reservation_unit
22712267
):
22722268
"""
@@ -2416,3 +2412,49 @@ def test_reservation_unit__first_reservable_time__duration_exactly_min_but_buffe
24162412
assert response.has_errors is False, response
24172413
assert is_closed(response) is False
24182414
assert frt(response) == dt(hour=21, minute=30)
2415+
2416+
2417+
########################################################################################################################
2418+
2419+
2420+
@freezegun.freeze_time(NOW)
2421+
def test__reservation_unit__first_reservable_time__no_bug_in_pagination_from_hauki_resource(graphql, reservation_unit):
2422+
"""
2423+
This is a regression test for a bug that was found during manual testing.
2424+
2425+
Tests that when pagination optimization occurs when calculating first reservable time (FRT),
2426+
reservation units with no HaukiResource do not affect the results of the calculation.
2427+
2428+
Previously, errors would occur when the previous page had reservation units with no HaukiResource.
2429+
The FRT calculation would not include these reservation units when calculating, meaning it would
2430+
start from the wrong reservation unit, which would throw off the page size calculation.
2431+
2432+
In this test, we have 1 reservation unit with a HaukiResource, and 1 without one.
2433+
With the given ordering, the one with no HaukiResource is "in the previous page", since we use an offset of 1.
2434+
But during the FRT calculation, given the previous bug, the offset would have been applied on top of removing
2435+
all reservation units with no HaukiResource from the query, meaning we start calculating FRTs from an imaginary
2436+
3rd reservation unit. This means that we never calculate the FRT for the 2nd reservation unit, meaning
2437+
it would be None. The tests asserts that this should not happen.
2438+
"""
2439+
common_space = SpaceFactory.create()
2440+
2441+
ReservationUnitFactory(name="A", spaces=[common_space], unit=reservation_unit.unit)
2442+
2443+
reservation_unit.name = "B"
2444+
reservation_unit.spaces.set([common_space])
2445+
reservation_unit.save()
2446+
2447+
ReservableTimeSpanFactory.create(
2448+
resource=reservation_unit.origin_hauki_resource,
2449+
start_datetime=_datetime(hour=10),
2450+
end_datetime=_datetime(hour=12),
2451+
)
2452+
2453+
query = reservation_units_reservable_query(order_by="nameFiAsc", offset=1)
2454+
response = graphql(query)
2455+
assert response.has_errors is False, response
2456+
2457+
assert len(response) == 1
2458+
2459+
assert frt(response) == dt(hour=10)
2460+
assert is_closed(response) is False

0 commit comments

Comments
 (0)