Skip to content

Commit

Permalink
Add recurring reservation deny mutation endpoint
Browse files Browse the repository at this point in the history
Also fix issues related to reservee being able to modify
other user's reservations.
  • Loading branch information
matti-lamppu committed Nov 14, 2024
1 parent e566359 commit 8f4d410
Show file tree
Hide file tree
Showing 15 changed files with 462 additions and 20 deletions.
27 changes: 23 additions & 4 deletions tests/factories/recurring_reservation.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import datetime
from typing import Any
from typing import TYPE_CHECKING, Any

from factory import LazyAttribute, fuzzy

from tilavarauspalvelu.enums import ReservationStartInterval, ReservationStateChoice, WeekdayChoice
from tilavarauspalvelu.models import RecurringReservation
from tilavarauspalvelu.models import RecurringReservation, Reservation
from utils.date_utils import DEFAULT_TIMEZONE, get_periods_between, local_datetime

from ._base import (
Expand All @@ -15,6 +15,9 @@
ReverseForeignKeyFactory,
)

if TYPE_CHECKING:
from django.db import models

__all__ = [
"RecurringReservationFactory",
]
Expand Down Expand Up @@ -70,6 +73,8 @@ def create_with_matching_reservations(cls, **kwargs: Any) -> RecurringReservatio
if not weekdays:
weekdays = [series.begin_date.weekday()]

reservations: list[Reservation] = []

for weekday in weekdays:
delta: int = weekday - series.begin_date.weekday()
if delta < 0:
Expand All @@ -86,12 +91,26 @@ def create_with_matching_reservations(cls, **kwargs: Any) -> RecurringReservatio
tzinfo=DEFAULT_TIMEZONE,
)
for begin, end in periods:
ReservationFactory.create(
reservation = ReservationFactory.build(
recurring_reservation=series,
reservation_units=[series.reservation_unit],
user=series.user,
begin=begin,
end=end,
**sub_kwargs,
)
reservations.append(reservation)

Reservation.objects.bulk_create(reservations)

# Add reservation units.
ReservationReservationUnit: type[models.Model] = Reservation.reservation_units.through # noqa: N806
reservation_reservation_units: list[ReservationReservationUnit] = [
ReservationReservationUnit(
reservation=reservation,
reservationunit=series.reservation_unit,
)
for reservation in reservations
]
ReservationReservationUnit.objects.bulk_create(reservation_reservation_units)

return series
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
CREATE_SERIES_MUTATION = build_mutation("createReservationSeries", "ReservationSeriesCreateMutation")
UPDATE_SERIES_MUTATION = build_mutation("updateReservationSeries", "ReservationSeriesUpdateMutation")
RESCHEDULE_SERIES_MUTATION = build_mutation("rescheduleReservationSeries", "ReservationSeriesRescheduleMutation")
DENY_SERIES_MUTATION = build_mutation("denyReservationSeries", "ReservationSeriesDenyMutation", fields="denied future")


def get_minimal_series_data(reservation_unit: ReservationUnit, user: User, **overrides: Any) -> dict[str, Any]:
Expand Down
108 changes: 108 additions & 0 deletions tests/test_graphql_api/test_recurring_reservation/test_deny_series.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import pytest
from freezegun import freeze_time

from tests.factories import ReservationDenyReasonFactory
from tilavarauspalvelu.enums import ReservationStateChoice
from utils.date_utils import local_datetime

from .helpers import DENY_SERIES_MUTATION, create_reservation_series

# Applied to all tests
pytestmark = [
pytest.mark.django_db,
]


@freeze_time(local_datetime(year=2024, month=1, day=1))
def test_recurring_reservations__deny_series(graphql):
reason = ReservationDenyReasonFactory.create()

reservation_series = create_reservation_series()

data = {
"pk": reservation_series.pk,
"denyReason": reason.pk,
"handlingDetails": "Handling details",
}

graphql.login_with_superuser()
response = graphql(DENY_SERIES_MUTATION, input_data=data)

assert response.has_errors is False, response.errors

assert response.first_query_object == {"denied": 5, "future": 5}
assert reservation_series.reservations.count() == 9

future_reservations = reservation_series.reservations.filter(begin__gt=local_datetime())
past_reservations = reservation_series.reservations.filter(begin__lte=local_datetime())

assert all(reservation.state == ReservationStateChoice.DENIED for reservation in future_reservations)
assert all(reservation.deny_reason == reason for reservation in future_reservations)
assert all(reservation.handling_details == "Handling details" for reservation in future_reservations)
assert all(reservation.handled_at == local_datetime() for reservation in future_reservations)

assert all(reservation.state != ReservationStateChoice.DENIED for reservation in past_reservations)
assert all(reservation.deny_reason != reason for reservation in past_reservations)
assert all(reservation.handling_details != "Handling details" for reservation in past_reservations)
assert all(reservation.handled_at is None for reservation in past_reservations)


@freeze_time(local_datetime(year=2024, month=1, day=1))
def test_recurring_reservations__deny_series__dont_need_handling_details(graphql):
reason = ReservationDenyReasonFactory.create()

reservation_series = create_reservation_series()

data = {
"pk": reservation_series.pk,
"denyReason": reason.pk,
}

graphql.login_with_superuser()
response = graphql(DENY_SERIES_MUTATION, input_data=data)

assert response.has_errors is False, response.errors

assert response.first_query_object == {"denied": 5, "future": 5}


@freeze_time(local_datetime(year=2024, month=1, day=1))
def test_recurring_reservations__deny_series__reason_missing(graphql):
reservation_series = create_reservation_series()

data = {
"pk": reservation_series.pk,
"denyReason": 1,
"handlingDetails": "Handling details",
}

graphql.login_with_superuser()
response = graphql(DENY_SERIES_MUTATION, input_data=data)

assert response.error_message() == "Mutation was unsuccessful."
assert response.field_error_messages("denyReason") == ["Deny reason with pk 1 does not exist."]


@freeze_time(local_datetime(year=2024, month=1, day=1))
def test_recurring_reservations__deny_series__only_deny_certain_states(graphql):
reason = ReservationDenyReasonFactory.create()

reservation_series = create_reservation_series()

last_reservation = reservation_series.reservations.order_by("begin").last()
last_reservation.state = ReservationStateChoice.WAITING_FOR_PAYMENT
last_reservation.save()

data = {
"pk": reservation_series.pk,
"denyReason": reason.pk,
"handlingDetails": "Handling details",
}

graphql.login_with_superuser()
response = graphql(DENY_SERIES_MUTATION, input_data=data)

assert response.has_errors is False, response.errors

# Last reservation is denied since it's waiting for payment
assert response.first_query_object == {"denied": 4, "future": 5}
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import pytest
from freezegun import freeze_time

from tests.factories import ReservationDenyReasonFactory, UnitRoleFactory, UserFactory
from tilavarauspalvelu.enums import UserRoleChoice
from utils.date_utils import local_datetime

from .helpers import DENY_SERIES_MUTATION, create_reservation_series

pytestmark = [
pytest.mark.django_db,
]


@freeze_time(local_datetime(year=2024, month=1, day=1))
def test_recurring_reservations__deny_series__regular_user(graphql):
reason = ReservationDenyReasonFactory.create()

reservation_series = create_reservation_series()

data = {
"pk": reservation_series.pk,
"denyReason": reason.pk,
}

graphql.login_with_regular_user()

response = graphql(DENY_SERIES_MUTATION, input_data=data)

assert response.error_message() == "No permission to update."


@freeze_time(local_datetime(year=2024, month=1, day=1))
def test_recurring_reservations__deny_series__general_admin(graphql):
reason = ReservationDenyReasonFactory.create()

reservation_series = create_reservation_series()

data = {
"pk": reservation_series.pk,
"denyReason": reason.pk,
}

graphql.login_user_with_role(role=UserRoleChoice.ADMIN)

response = graphql(DENY_SERIES_MUTATION, input_data=data)

assert response.has_errors is False, response.errors


@freeze_time(local_datetime(year=2024, month=1, day=1))
def test_recurring_reservations__deny_series__unit_admin(graphql):
reason = ReservationDenyReasonFactory.create()

reservation_series = create_reservation_series()

data = {
"pk": reservation_series.pk,
"denyReason": reason.pk,
}

unit = reservation_series.reservation_unit.unit
user = UserFactory.create_with_unit_role(role=UserRoleChoice.ADMIN, units=[unit])
graphql.force_login(user)

response = graphql(DENY_SERIES_MUTATION, input_data=data)

assert response.has_errors is False, response.errors


@freeze_time(local_datetime(year=2024, month=1, day=1))
def test_recurring_reservations__deny_series__unit_handler(graphql):
reason = ReservationDenyReasonFactory.create()

reservation_series = create_reservation_series()

data = {
"pk": reservation_series.pk,
"denyReason": reason.pk,
}

unit = reservation_series.reservation_unit.unit
user = UserFactory.create_with_unit_role(role=UserRoleChoice.HANDLER, units=[unit])
graphql.force_login(user)

response = graphql(DENY_SERIES_MUTATION, input_data=data)

assert response.has_errors is False, response.errors


@freeze_time(local_datetime(year=2024, month=1, day=1))
def test_recurring_reservations__deny_series__unit_reserver__own_reservation(graphql):
reason = ReservationDenyReasonFactory.create()

user = UserFactory.create()

reservation_series = create_reservation_series(user=user)

unit = reservation_series.reservation_unit.unit
UnitRoleFactory.create(user=user, role=UserRoleChoice.RESERVER, units=[unit])

data = {
"pk": reservation_series.pk,
"denyReason": reason.pk,
}

graphql.force_login(user)

response = graphql(DENY_SERIES_MUTATION, input_data=data)

assert response.has_errors is False, response.errors


@freeze_time(local_datetime(year=2024, month=1, day=1))
def test_recurring_reservations__deny_series__unit_reserver__other_user_reservation(graphql):
reason = ReservationDenyReasonFactory.create()

reservation_series = create_reservation_series()

data = {
"pk": reservation_series.pk,
"denyReason": reason.pk,
}

unit = reservation_series.reservation_unit.unit
user = UserFactory.create_with_unit_role(role=UserRoleChoice.RESERVER, units=[unit])
graphql.force_login(user)

response = graphql(DENY_SERIES_MUTATION, input_data=data)

assert response.error_message() == "No permission to update."
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from tests.factories import RecurringReservationFactory, ReservationFactory
from tilavarauspalvelu.enums import ReservationStateChoice, WeekdayChoice
from tilavarauspalvelu.models import AffectingTimeSpan, Reservation, ReservationStatistic, ReservationUnitHierarchy
from tilavarauspalvelu.tasks import create_or_update_reservation_statistics
from utils.date_utils import DEFAULT_TIMEZONE, combine, local_date, local_datetime, local_time

from .helpers import RESCHEDULE_SERIES_MUTATION, create_reservation_series, get_minimal_reschedule_data
Expand Down Expand Up @@ -747,6 +748,8 @@ def test_recurring_reservations__reschedule_series__create_statistics(graphql, s

recurring_reservation = create_reservation_series()

create_or_update_reservation_statistics(recurring_reservation.reservations.values_list("pk", flat=True))

# We have 9 reservations, so there should be 9 reservation statistics.
assert ReservationStatistic.objects.count() == 9

Expand All @@ -773,6 +776,8 @@ def test_recurring_reservations__reschedule_series__create_statistics__partial(g

recurring_reservation = create_reservation_series()

create_or_update_reservation_statistics(recurring_reservation.reservations.values_list("pk", flat=True))

# We have 9 reservations, so there should be 9 reservation statistics.
assert ReservationStatistic.objects.count() == 9

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pytest
from freezegun import freeze_time

from tests.factories import UserFactory
from tests.factories import GeneralRoleFactory, UnitRoleFactory, UserFactory
from tilavarauspalvelu.enums import UserRoleChoice
from utils.date_utils import local_datetime

Expand All @@ -19,7 +19,7 @@
(UserRoleChoice.ADMIN, True),
(UserRoleChoice.HANDLER, True),
(UserRoleChoice.VIEWER, False),
(UserRoleChoice.RESERVER, True),
(UserRoleChoice.RESERVER, False),
(UserRoleChoice.NOTIFICATION_MANAGER, False),
],
)
Expand All @@ -34,13 +34,26 @@ def test_recurring_reservations__reschedule_series__general_role(graphql, role,
assert response.has_errors is not has_permission


@freeze_time(local_datetime(year=2023, month=12, day=1))
def test_recurring_reservations__reschedule_series__general_reserver__own_reservation(graphql):
user = UserFactory.create()
series = create_reservation_series(user=user)
GeneralRoleFactory.create(user=user, role=UserRoleChoice.RESERVER)

graphql.force_login(user)
data = get_minimal_reschedule_data(series)
response = graphql(RESCHEDULE_SERIES_MUTATION, input_data=data)

assert response.has_errors is False


@pytest.mark.parametrize(
("role", "has_permission"),
[
(UserRoleChoice.ADMIN, True),
(UserRoleChoice.HANDLER, True),
(UserRoleChoice.VIEWER, False),
(UserRoleChoice.RESERVER, True),
(UserRoleChoice.RESERVER, False),
(UserRoleChoice.NOTIFICATION_MANAGER, False),
],
)
Expand All @@ -54,3 +67,16 @@ def test_recurring_reservations__reschedule_series__unit_role(graphql, role, has
response = graphql(RESCHEDULE_SERIES_MUTATION, input_data=data)

assert response.has_errors is not has_permission


@freeze_time(local_datetime(year=2023, month=12, day=1))
def test_recurring_reservations__reschedule_series__unit_reserver__own_reservation(graphql):
user = UserFactory.create()
series = create_reservation_series(user=user)
UnitRoleFactory.create(user=user, role=UserRoleChoice.RESERVER, units=[series.reservation_unit.unit])

graphql.force_login(user)
data = get_minimal_reschedule_data(series)
response = graphql(RESCHEDULE_SERIES_MUTATION, input_data=data)

assert response.has_errors is False
Loading

0 comments on commit 8f4d410

Please sign in to comment.