oncall-engine/engine/apps/api/tests/test_shift_swaps.py
Yulya Artyukhina 64bf1e5096
Speed up internal api endpoints (#4830)
# What this PR does
Reduces number of calls to db for `/schedules`, `/alertgroups` and
`/users` endpoints.
Fixes the issue when there was an additional call to db to get
organization url to build user avatar full link.

## Which issue(s) this PR closes

Related to [issue link here]

<!--
*Note*: If you want the issue to be auto-closed once the PR is merged,
change "Related to" to "Closes" in the line above.
If you have more than one GitHub issue that this PR closes, be sure to
preface
each issue link with a [closing
keyword](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/using-keywords-in-issues-and-pull-requests#linking-a-pull-request-to-an-issue).
This ensures that the issue(s) are auto-closed once the PR has been
merged.
-->

## Checklist

- [x] Unit, integration, and e2e (if applicable) tests updated
- [x] Documentation added (or `pr:no public docs` PR label added if not
required)
- [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.
2024-08-15 14:20:55 +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(u.organization),
}
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