oncall-engine/engine/apps/api/tests/test_shift_swaps.py
Joey Orlando 33364b63c6
Google Calendar Out of Office events - autogenerated shift swap requests (#4104)
# What this PR does

## Which issue(s) this PR closes

Closes https://github.com/grafana/oncall-private/issues/2590

## Checklist

- [x] Unit, integration, and e2e (if applicable) tests updated
- [x] Documentation added (or `pr:no public docs` PR label added if not
required) - will be done in
https://github.com/grafana/oncall-private/issues/2591
- [x] Added the relevant release notes label (see labels prefixed w/
`release:`). These labels dictate how your PR will
show up in the autogenerated release notes. - will be done in
https://github.com/grafana/oncall-private/issues/2591

---------

Co-authored-by: Dominik <dominik.broj@grafana.com>
Co-authored-by: Maxim Mordasov <maxim.mordasov@grafana.com>
2024-04-02 20:10:16 +00:00

802 lines
30 KiB
Python

import datetime
import json
from unittest.mock import patch
import pytest
from django.urls import reverse
from django.utils import timezone
from rest_framework import status
from rest_framework.response import Response
from rest_framework.test import APIClient
from apps.api.permissions import LegacyAccessControlRole
from apps.schedules.models import CustomOnCallShift, OnCallScheduleWeb, ShiftSwapRequest
from common.api_helpers.utils import serialize_datetime_as_utc_timestamp
from common.insight_log import EntityEvent
description = "my shift swap request"
tomorrow = timezone.now().replace(hour=0, minute=0, second=0, microsecond=0) + datetime.timedelta(days=1)
two_days_from_now = tomorrow + datetime.timedelta(days=1)
mock_success_response = Response(status=status.HTTP_200_OK)
@pytest.fixture
def ssr_setup(
make_schedule, make_organization_and_user_with_plugin_token, make_user_for_organization, make_shift_swap_request
):
def _ssr_setup(beneficiary_role=None, benefactor_role=None, **kwargs):
organization, beneficiary, token = make_organization_and_user_with_plugin_token(role=beneficiary_role)
benefactor = make_user_for_organization(organization, role=benefactor_role)
schedule = make_schedule(organization, schedule_class=OnCallScheduleWeb)
ssr = make_shift_swap_request(schedule, beneficiary, swap_start=tomorrow, swap_end=two_days_from_now, **kwargs)
return ssr, beneficiary, token, benefactor
return _ssr_setup
def _construct_serialized_object(
ssr: ShiftSwapRequest, status="open", description=None, benefactor=None, list_response=False, expand_users=False
):
data = {
"id": ssr.public_primary_key,
"created_at": serialize_datetime_as_utc_timestamp(ssr.created_at),
"updated_at": serialize_datetime_as_utc_timestamp(ssr.updated_at),
"schedule": ssr.schedule.public_primary_key,
"swap_start": serialize_datetime_as_utc_timestamp(ssr.swap_start),
"swap_end": serialize_datetime_as_utc_timestamp(ssr.swap_end),
"beneficiary": ssr.beneficiary.public_primary_key,
"status": status,
"benefactor": benefactor,
"description": description,
}
if expand_users:
def _serialized_user(u):
if u:
return {
"display_name": u.username,
"email": u.email,
"pk": u.public_primary_key,
"avatar_full": u.avatar_full_url,
}
data["beneficiary"] = _serialized_user(ssr.beneficiary)
data["benefactor"] = _serialized_user(ssr.benefactor)
if not list_response:
data["shifts"] = ssr.shifts()
return data
def _build_expected_update_response(ssr, modified_data, updated_at_ts, **kwargs):
"""
updated_at timestamp will obviously be bumped when we do a PUT/PATCH
"""
return _construct_serialized_object(ssr, **kwargs) | modified_data | {"updated_at": updated_at_ts}
@pytest.mark.django_db
@pytest.mark.parametrize("expand_users", ["true", "false", None])
def test_list(ssr_setup, make_user_auth_headers, expand_users):
ssr, beneficiary, token, _ = ssr_setup(description=description)
client = APIClient()
url = reverse("api-internal:shift_swap-list")
if expand_users is not None:
url += "?expand_users={}".format(expand_users)
expected_payload = {
"next": None,
"previous": None,
"page_size": 50,
"count": 1,
"current_page_number": 1,
"total_pages": 1,
"results": [
_construct_serialized_object(
ssr, description=description, list_response=True, expand_users=expand_users == "true"
),
],
}
response = client.get(url, format="json", **make_user_auth_headers(beneficiary, token))
assert response.status_code == status.HTTP_200_OK
assert response.json() == expected_payload
@patch("apps.api.views.shift_swap.ShiftSwapViewSet.list", return_value=mock_success_response)
@pytest.mark.django_db
@pytest.mark.parametrize(
"role,expected_status",
[
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
(LegacyAccessControlRole.NONE, status.HTTP_403_FORBIDDEN),
],
)
def test_list_permissions(
mock_endpoint_handler,
ssr_setup,
make_user_auth_headers,
role,
expected_status,
):
_, beneficiary, token, _ = ssr_setup(beneficiary_role=role)
client = APIClient()
url = reverse("api-internal:shift_swap-list")
response = client.get(url, format="json", **make_user_auth_headers(beneficiary, token))
assert response.status_code == expected_status
@pytest.mark.django_db
@pytest.mark.parametrize("expand_users", ["true", "false", None])
def test_retrieve(ssr_setup, make_user_auth_headers, expand_users):
ssr, beneficiary, token, _ = ssr_setup(description=description)
client = APIClient()
url = reverse("api-internal:shift_swap-detail", kwargs={"pk": ssr.public_primary_key})
if expand_users is not None:
url += "?expand_users={}".format(expand_users)
response = client.get(url, format="json", **make_user_auth_headers(beneficiary, token))
assert response.status_code == status.HTTP_200_OK
assert response.json() == _construct_serialized_object(
ssr, description=description, expand_users=expand_users == "true"
)
@patch("apps.api.views.shift_swap.ShiftSwapViewSet.retrieve", return_value=mock_success_response)
@pytest.mark.django_db
@pytest.mark.parametrize(
"role,expected_status",
[
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
(LegacyAccessControlRole.NONE, status.HTTP_403_FORBIDDEN),
],
)
def test_retrieve_permissions(
mock_endpoint_handler,
ssr_setup,
make_user_auth_headers,
role,
expected_status,
):
ssr, _, token, benefactor = ssr_setup(benefactor_role=role)
client = APIClient()
url = reverse("api-internal:shift_swap-detail", kwargs={"pk": ssr.public_primary_key})
response = client.get(url, format="json", **make_user_auth_headers(benefactor, token))
assert response.status_code == expected_status
@patch("apps.schedules.models.shift_swap_request.write_resource_insight_log")
@patch("apps.schedules.tasks.shift_swaps.create_shift_swap_request_message")
@pytest.mark.django_db
def test_create(
mock_create_shift_swap_request_message,
mock_write_resource_insight_log,
make_organization_and_user_with_plugin_token,
make_schedule,
make_user_auth_headers,
):
organization, user, token = make_organization_and_user_with_plugin_token()
schedule = make_schedule(organization, schedule_class=OnCallScheduleWeb)
client = APIClient()
url = reverse("api-internal:shift_swap-list")
data = {
"schedule": schedule.public_primary_key,
"description": "hellooooo world",
"swap_start": tomorrow,
"swap_end": two_days_from_now,
}
response = client.post(url, data, format="json", **make_user_auth_headers(user, token))
ssr = ShiftSwapRequest.objects.get(public_primary_key=response.json()["id"])
expected_response = _construct_serialized_object(ssr) | {
**data,
"swap_start": serialize_datetime_as_utc_timestamp(tomorrow),
"swap_end": serialize_datetime_as_utc_timestamp(two_days_from_now),
}
assert response.status_code == status.HTTP_201_CREATED
assert response.json() == expected_response
mock_write_resource_insight_log.assert_called_once_with(instance=ssr, author=user, event=EntityEvent.CREATED)
mock_create_shift_swap_request_message.apply_async.assert_called_once_with((ssr.pk,))
@pytest.mark.django_db
@pytest.mark.parametrize(
"swap_start,expected_persisted_value",
[
# UTC format
("2285-07-20T12:00:00Z", "2285-07-20T12:00:00.000000Z"),
# UTC format w/ microseconds
("2285-07-20T12:00:00.245652Z", "2285-07-20T12:00:00.245652Z"),
# UTC offset w/ colons + no microseconds
("2285-07-20T12:00:00+07:00", "2285-07-20T05:00:00.000000Z"),
# UTC offset w/ colons + microseconds
("2285-07-20T12:00:00.245652+07:00", "2285-07-20T05:00:00.245652Z"),
# UTC offset w/ no colons + no microseconds
("2285-07-20T12:00:00+0700", "2285-07-20T05:00:00.000000Z"),
# UTC offset w/ no colons + microseconds
("2285-07-20T12:00:00.245652+0700", "2285-07-20T05:00:00.245652Z"),
("2285-07-20 12:00:00", None),
("22850720T120000Z", None),
],
)
def test_create_swap_start_and_swap_end_must_include_time_zone(
make_organization_and_user_with_plugin_token,
make_schedule,
make_user_auth_headers,
swap_start,
expected_persisted_value,
):
organization, user, token = make_organization_and_user_with_plugin_token()
schedule = make_schedule(organization, schedule_class=OnCallScheduleWeb)
client = APIClient()
url = reverse("api-internal:shift_swap-list")
start_year = "2285"
end_year = "2286"
swap_end = swap_start.replace(start_year, end_year)
data = {
"schedule": schedule.public_primary_key,
"description": "hellooooo world",
"swap_start": swap_start,
"swap_end": swap_end,
}
response = client.post(url, data, format="json", **make_user_auth_headers(user, token))
if expected_persisted_value:
ssr = ShiftSwapRequest.objects.get(public_primary_key=response.json()["id"])
assert response.status_code == status.HTTP_201_CREATED
assert response.json() == _construct_serialized_object(ssr) | {
**data,
"swap_start": expected_persisted_value,
"swap_end": expected_persisted_value.replace(start_year, end_year),
}
else:
assert response.status_code == status.HTTP_400_BAD_REQUEST
@patch("apps.api.views.shift_swap.ShiftSwapViewSet.create", return_value=mock_success_response)
@pytest.mark.django_db
@pytest.mark.parametrize(
"role,expected_status",
[
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
(LegacyAccessControlRole.NONE, status.HTTP_403_FORBIDDEN),
],
)
def test_create_permissions(
mock_endpoint_handler,
ssr_setup,
make_user_auth_headers,
role,
expected_status,
):
_, _, token, benefactor = ssr_setup(benefactor_role=role)
client = APIClient()
url = reverse("api-internal:shift_swap-list")
response = client.post(url, format="json", **make_user_auth_headers(benefactor, token))
assert response.status_code == expected_status
@patch("apps.api.views.shift_swap.write_resource_insight_log")
@patch("apps.api.views.shift_swap.update_shift_swap_request_message")
@pytest.mark.django_db
def test_update(
mock_update_shift_swap_request_message, mock_write_resource_insight_log, ssr_setup, make_user_auth_headers
):
ssr, beneficiary, token, _ = ssr_setup(description=description)
insights_log_prev_state = ssr.insight_logs_serialized
client = APIClient()
url = reverse("api-internal:shift_swap-detail", kwargs={"pk": ssr.public_primary_key})
auth_headers = make_user_auth_headers(beneficiary, token)
data = {
"description": "hellooooo world",
"schedule": ssr.schedule.public_primary_key,
"swap_start": serialize_datetime_as_utc_timestamp(ssr.swap_start),
"swap_end": serialize_datetime_as_utc_timestamp(ssr.swap_end),
}
response = client.put(url, data=json.dumps(data), content_type="application/json", **auth_headers)
response_json = response.json()
expected_response = _build_expected_update_response(ssr, data, response_json["updated_at"], description=description)
assert response.status_code == status.HTTP_200_OK
assert response_json == expected_response
response = client.get(url, format="json", **auth_headers)
assert response.status_code == status.HTTP_200_OK
assert response.json() == expected_response
ssr.refresh_from_db()
mock_write_resource_insight_log.assert_called_once_with(
instance=ssr,
author=beneficiary,
event=EntityEvent.UPDATED,
prev_state=insights_log_prev_state,
new_state=ssr.insight_logs_serialized,
)
mock_update_shift_swap_request_message.apply_async.assert_called_once_with((ssr.pk,))
@pytest.mark.django_db
@pytest.mark.parametrize(
"swap_start,expected_persisted_value",
[
# UTC format
("2285-07-20T12:00:00Z", "2285-07-20T12:00:00.000000Z"),
# UTC format w/ microseconds
("2285-07-20T12:00:00.245652Z", "2285-07-20T12:00:00.245652Z"),
# UTC offset w/ colons + no microseconds
("2285-07-20T12:00:00+07:00", "2285-07-20T05:00:00.000000Z"),
# UTC offset w/ colons + microseconds
("2285-07-20T12:00:00.245652+07:00", "2285-07-20T05:00:00.245652Z"),
# UTC offset w/ no colons + no microseconds
("2285-07-20T12:00:00+0700", "2285-07-20T05:00:00.000000Z"),
# UTC offset w/ no colons + microseconds
("2285-07-20T12:00:00.245652+0700", "2285-07-20T05:00:00.245652Z"),
("2285-07-20 12:00:00", None),
("22850720T120000Z", None),
],
)
def test_update_swap_start_and_swap_end_must_include_time_zone(
ssr_setup,
make_user_auth_headers,
swap_start,
expected_persisted_value,
):
ssr, beneficiary, token, _ = ssr_setup()
client = APIClient()
url = reverse("api-internal:shift_swap-detail", kwargs={"pk": ssr.public_primary_key})
start_year = "2285"
end_year = "2286"
swap_end = swap_start.replace(start_year, end_year)
data = {
"schedule": ssr.schedule.public_primary_key,
"swap_start": swap_start,
"swap_end": swap_end,
}
response = client.put(url, data, format="json", **make_user_auth_headers(beneficiary, token))
if expected_persisted_value:
ssr = ShiftSwapRequest.objects.get(public_primary_key=response.json()["id"])
assert response.status_code == status.HTTP_200_OK
assert response.json() == _construct_serialized_object(ssr) | {
**data,
"swap_start": expected_persisted_value,
"swap_end": expected_persisted_value.replace(start_year, end_year),
}
else:
assert response.status_code == status.HTTP_400_BAD_REQUEST
@pytest.mark.django_db
@pytest.mark.parametrize(
"role,expected_status",
[
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
(LegacyAccessControlRole.NONE, status.HTTP_403_FORBIDDEN),
],
)
def test_update_own_ssr_permissions(ssr_setup, make_user_auth_headers, role, expected_status):
ssr, beneficiary, token, _ = ssr_setup(beneficiary_role=role)
client = APIClient()
url = reverse("api-internal:shift_swap-detail", kwargs={"pk": ssr.public_primary_key})
data = {
"description": "hellooooo world",
"schedule": ssr.schedule.public_primary_key,
"swap_start": serialize_datetime_as_utc_timestamp(ssr.swap_start),
"swap_end": serialize_datetime_as_utc_timestamp(ssr.swap_end),
}
response = client.put(
url, data=json.dumps(data), content_type="application/json", **make_user_auth_headers(beneficiary, token)
)
assert response.status_code == expected_status
@pytest.mark.django_db
def test_update_others_ssr_permissions(ssr_setup, make_user_auth_headers):
ssr, _, token, benefactor = ssr_setup()
assert benefactor.role == LegacyAccessControlRole.ADMIN
client = APIClient()
url = reverse("api-internal:shift_swap-detail", kwargs={"pk": ssr.public_primary_key})
response = client.put(
url, data=json.dumps({}), content_type="application/json", **make_user_auth_headers(benefactor, token)
)
assert response.status_code == status.HTTP_403_FORBIDDEN
@patch("apps.api.views.shift_swap.write_resource_insight_log")
@patch("apps.api.views.shift_swap.update_shift_swap_request_message")
@pytest.mark.django_db
def test_partial_update(
mock_update_shift_swap_request_message, mock_write_resource_insight_log, ssr_setup, make_user_auth_headers
):
ssr, beneficiary, token, _ = ssr_setup(description=description)
insights_log_prev_state = ssr.insight_logs_serialized
client = APIClient()
url = reverse("api-internal:shift_swap-detail", kwargs={"pk": ssr.public_primary_key})
auth_headers = make_user_auth_headers(beneficiary, token)
data = {"description": "this is a shift swap request"}
response = client.patch(url, data=json.dumps(data), content_type="application/json", **auth_headers)
response_json = response.json()
expected_response = _build_expected_update_response(ssr, data, response_json["updated_at"], description=description)
assert response.status_code == status.HTTP_200_OK
assert response.json() == expected_response
response = client.get(url, format="json", **auth_headers)
assert response.status_code == status.HTTP_200_OK
assert response.json() == expected_response
ssr.refresh_from_db()
mock_write_resource_insight_log.assert_called_once_with(
instance=ssr,
author=beneficiary,
event=EntityEvent.UPDATED,
prev_state=insights_log_prev_state,
new_state=ssr.insight_logs_serialized,
)
mock_update_shift_swap_request_message.apply_async.assert_called_once_with((ssr.pk,))
@pytest.mark.django_db
def test_partial_update_time_related_fields(ssr_setup, make_user_auth_headers):
ssr, beneficiary, token, _ = ssr_setup()
client = APIClient()
url = reverse("api-internal:shift_swap-detail", kwargs={"pk": ssr.public_primary_key})
auth_headers = make_user_auth_headers(beneficiary, token)
# but if we do PATCH a time related field, we must specify all the time fields
swap_start = {"swap_start": serialize_datetime_as_utc_timestamp(tomorrow + datetime.timedelta(days=5))}
swap_end = {"swap_end": serialize_datetime_as_utc_timestamp(tomorrow + datetime.timedelta(days=10))}
valid = swap_start | swap_end
for case in [swap_start, swap_end]:
response = client.patch(url, data=json.dumps(case), content_type="application/json", **auth_headers)
assert response.status_code == status.HTTP_400_BAD_REQUEST
# valid way to patch time related fields
response = client.patch(url, data=json.dumps(valid), content_type="application/json", **auth_headers)
response_json = response.json()
expected_response = _build_expected_update_response(ssr, valid, response_json["updated_at"])
assert response.status_code == status.HTTP_200_OK
assert response.json() == expected_response
response = client.get(url, format="json", **auth_headers)
assert response.status_code == status.HTTP_200_OK
assert response.json() == expected_response
@pytest.mark.django_db
def test_related_shifts(ssr_setup, make_on_call_shift, make_user_auth_headers):
ssr, beneficiary, token, _ = ssr_setup()
schedule = ssr.schedule
organization = schedule.organization
user = beneficiary
today = timezone.now().replace(hour=0, minute=0, second=0, microsecond=0)
start = today + timezone.timedelta(days=1)
duration = timezone.timedelta(hours=8)
data = {
"start": start,
"rotation_start": start,
"duration": duration,
"priority_level": 1,
"frequency": CustomOnCallShift.FREQUENCY_DAILY,
"schedule": schedule,
}
on_call_shift = make_on_call_shift(
organization=organization, shift_type=CustomOnCallShift.TYPE_ROLLING_USERS_EVENT, **data
)
on_call_shift.add_rolling_users([[user]])
client = APIClient()
url = reverse("api-internal:shift_swap-detail", kwargs={"pk": ssr.public_primary_key})
auth_headers = make_user_auth_headers(beneficiary, token)
response = client.get(url, **auth_headers)
assert response.status_code == status.HTTP_200_OK
response_json = response.json()
expected = [
# start, end, user, swap request ID
(
start.strftime("%Y-%m-%dT%H:%M:%SZ"),
(start + duration).strftime("%Y-%m-%dT%H:%M:%SZ"),
user.public_primary_key,
ssr.public_primary_key,
),
]
returned_events = [
(e["start"], e["end"], e["users"][0]["pk"], e["users"][0]["swap_request"]["pk"])
for e in response_json["shifts"]
]
assert returned_events == expected
@pytest.mark.django_db
@pytest.mark.parametrize(
"role,expected_status",
[
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
(LegacyAccessControlRole.NONE, status.HTTP_403_FORBIDDEN),
],
)
def test_partial_update_own_ssr_permissions(ssr_setup, make_user_auth_headers, role, expected_status):
ssr, beneficiary, token, _ = ssr_setup(beneficiary_role=role)
client = APIClient()
url = reverse("api-internal:shift_swap-detail", kwargs={"pk": ssr.public_primary_key})
response = client.patch(
url,
data=json.dumps({"description": "foo"}),
content_type="application/json",
**make_user_auth_headers(beneficiary, token),
)
assert response.status_code == expected_status
@pytest.mark.django_db
def test_partial_update_others_ssr_permissions(ssr_setup, make_user_auth_headers):
ssr, _, token, benefactor = ssr_setup()
assert benefactor.role == LegacyAccessControlRole.ADMIN
client = APIClient()
url = reverse("api-internal:shift_swap-detail", kwargs={"pk": ssr.public_primary_key})
response = client.patch(
url, data=json.dumps({}), content_type="application/json", **make_user_auth_headers(benefactor, token)
)
assert response.status_code == status.HTTP_403_FORBIDDEN
@pytest.mark.django_db
def test_benefactor_and_beneficiary_are_read_only_fields(ssr_setup, make_user_auth_headers):
ssr, beneficiary, token, benefactor = ssr_setup(description=description)
client = APIClient()
list_url = reverse("api-internal:shift_swap-list")
detail_url = reverse("api-internal:shift_swap-detail", kwargs={"pk": ssr.public_primary_key})
auth_headers = make_user_auth_headers(beneficiary, token)
base_data = {
"description": "hellooooo world",
"schedule": ssr.schedule.public_primary_key,
"swap_start": serialize_datetime_as_utc_timestamp(ssr.swap_start),
"swap_end": serialize_datetime_as_utc_timestamp(ssr.swap_end),
}
update_beneficiary = {"beneficiary": benefactor.public_primary_key}
update_benefactor = {"benefactor": beneficiary.public_primary_key}
def _assert_beneficiary_hasnt_changed(resp):
assert resp.json()["beneficiary"] == beneficiary.public_primary_key
def _assert_benefactor_is_still_none(resp):
assert resp.json()["benefactor"] is None
response = client.post(
list_url, data=json.dumps(base_data | update_beneficiary), content_type="application/json", **auth_headers
)
_assert_beneficiary_hasnt_changed(response)
response = client.post(
list_url, data=json.dumps(base_data | update_benefactor), content_type="application/json", **auth_headers
)
_assert_benefactor_is_still_none(response)
response = client.put(
detail_url, data=json.dumps(base_data | update_beneficiary), content_type="application/json", **auth_headers
)
_assert_beneficiary_hasnt_changed(response)
response = client.put(
detail_url, data=json.dumps(base_data | update_benefactor), content_type="application/json", **auth_headers
)
_assert_benefactor_is_still_none(response)
response = client.patch(
detail_url, data=json.dumps(base_data | update_beneficiary), content_type="application/json", **auth_headers
)
_assert_beneficiary_hasnt_changed(response)
response = client.patch(
detail_url, data=json.dumps(base_data | update_benefactor), content_type="application/json", **auth_headers
)
_assert_benefactor_is_still_none(response)
@patch("apps.api.views.shift_swap.write_resource_insight_log")
@patch("apps.api.views.shift_swap.update_shift_swap_request_message")
@pytest.mark.django_db
def test_delete(
mock_update_shift_swap_request_message, mock_write_resource_insight_log, ssr_setup, make_user_auth_headers
):
ssr, beneficiary, token, _ = ssr_setup()
client = APIClient()
url = reverse("api-internal:shift_swap-detail", kwargs={"pk": ssr.public_primary_key})
auth_headers = make_user_auth_headers(beneficiary, token)
response = client.delete(url, **auth_headers)
assert response.status_code == status.HTTP_204_NO_CONTENT
response = client.get(url, format="json", **auth_headers)
assert response.status_code == status.HTTP_404_NOT_FOUND
mock_write_resource_insight_log.assert_called_once_with(
instance=ssr,
author=beneficiary,
event=EntityEvent.DELETED,
)
mock_update_shift_swap_request_message.apply_async.assert_called_once_with((ssr.pk,))
@pytest.mark.django_db
@pytest.mark.parametrize(
"role,expected_status",
[
(LegacyAccessControlRole.ADMIN, status.HTTP_204_NO_CONTENT),
(LegacyAccessControlRole.EDITOR, status.HTTP_204_NO_CONTENT),
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
(LegacyAccessControlRole.NONE, status.HTTP_403_FORBIDDEN),
],
)
def test_delete_own_ssr_permissions(ssr_setup, make_user_auth_headers, role, expected_status):
ssr, beneficiary, token, _ = ssr_setup(beneficiary_role=role)
client = APIClient()
url = reverse("api-internal:shift_swap-detail", kwargs={"pk": ssr.public_primary_key})
response = client.delete(url, format="json", **make_user_auth_headers(beneficiary, token))
assert response.status_code == expected_status
@pytest.mark.django_db
def test_delete_others_ssr_permissions(ssr_setup, make_user_auth_headers):
ssr, _, token, benefactor = ssr_setup()
assert benefactor.role == LegacyAccessControlRole.ADMIN
client = APIClient()
url = reverse("api-internal:shift_swap-detail", kwargs={"pk": ssr.public_primary_key})
response = client.delete(url, format="json", **make_user_auth_headers(benefactor, token))
assert response.status_code == status.HTTP_403_FORBIDDEN
@pytest.mark.django_db
def test_take(ssr_setup, make_user_auth_headers):
ssr, _, token, benefactor = ssr_setup()
client = APIClient()
url = reverse("api-internal:shift_swap-take", kwargs={"pk": ssr.public_primary_key})
auth_headers = make_user_auth_headers(benefactor, token)
response = client.post(url, format="json", **auth_headers)
response_json = response.json()
updated_at = response_json["updated_at"]
expected_response = _build_expected_update_response(
ssr, {}, updated_at, status="taken", benefactor=benefactor.public_primary_key
)
assert response.status_code == status.HTTP_200_OK
assert response_json == expected_response
assert updated_at != serialize_datetime_as_utc_timestamp(
ssr.updated_at
) # validate that updated_at is auto-updated on take
url = reverse("api-internal:shift_swap-detail", kwargs={"pk": ssr.public_primary_key})
response = client.get(url, format="json", **auth_headers)
response_json = response.json()
assert response.status_code == status.HTTP_200_OK
assert response_json == expected_response
@pytest.mark.django_db
def test_benficiary_tries_to_take_their_own_ssr(ssr_setup, make_user_auth_headers):
ssr, beneficiary, token, _ = ssr_setup()
client = APIClient()
url = reverse("api-internal:shift_swap-take", kwargs={"pk": ssr.public_primary_key})
response = client.post(url, format="json", **make_user_auth_headers(beneficiary, token))
assert response.status_code == status.HTTP_400_BAD_REQUEST
@pytest.mark.django_db
def test_take_already_taken_ssr(ssr_setup, make_user_auth_headers):
ssr, _, token, benefactor = ssr_setup()
client = APIClient()
url = reverse("api-internal:shift_swap-take", kwargs={"pk": ssr.public_primary_key})
response = client.post(url, format="json", **make_user_auth_headers(benefactor, token))
assert response.status_code == status.HTTP_200_OK
# try to take the SSR again
response = client.post(url, format="json", **make_user_auth_headers(benefactor, token))
assert response.status_code == status.HTTP_400_BAD_REQUEST
@pytest.mark.django_db
def test_take_past_due_ssr(ssr_setup, make_user_auth_headers):
ssr, _, token, benefactor = ssr_setup()
client = APIClient()
url = reverse("api-internal:shift_swap-take", kwargs={"pk": ssr.public_primary_key})
ssr.swap_start = tomorrow - datetime.timedelta(days=5)
ssr.save()
response = client.post(url, format="json", **make_user_auth_headers(benefactor, token))
assert response.status_code == status.HTTP_400_BAD_REQUEST
@pytest.mark.django_db
def test_take_deleted_ssr(ssr_setup, make_user_auth_headers):
ssr, _, token, benefactor = ssr_setup()
client = APIClient()
url = reverse("api-internal:shift_swap-take", kwargs={"pk": ssr.public_primary_key})
ssr.delete()
response = client.post(url, format="json", **make_user_auth_headers(benefactor, token))
assert response.status_code == status.HTTP_404_NOT_FOUND
@patch("apps.api.views.shift_swap.ShiftSwapViewSet.take", return_value=mock_success_response)
@pytest.mark.django_db
@pytest.mark.parametrize(
"role,expected_status",
[
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
(LegacyAccessControlRole.VIEWER, status.HTTP_403_FORBIDDEN),
(LegacyAccessControlRole.NONE, status.HTTP_403_FORBIDDEN),
],
)
def test_take_permissions(
mock_endpoint_handler,
ssr_setup,
make_user_auth_headers,
role,
expected_status,
):
ssr, _, token, benefactor = ssr_setup(benefactor_role=role)
client = APIClient()
url = reverse("api-internal:shift_swap-take", kwargs={"pk": ssr.public_primary_key})
response = client.post(url, format="json", **make_user_auth_headers(benefactor, token))
assert response.status_code == expected_status