From 14edd062de3503230714822994de8746a5176909 Mon Sep 17 00:00:00 2001 From: Mads Nylund <73914541+MadsNyl@users.noreply.github.com> Date: Tue, 1 Oct 2024 09:28:35 +0200 Subject: [PATCH 1/2] Description to forms (#894) added description to form --- app/forms/migrations/0014_form_description.py | 18 ++++++++++++++++++ app/forms/models/forms.py | 1 + app/forms/serializers/forms.py | 1 + 3 files changed, 20 insertions(+) create mode 100644 app/forms/migrations/0014_form_description.py diff --git a/app/forms/migrations/0014_form_description.py b/app/forms/migrations/0014_form_description.py new file mode 100644 index 000000000..3cecfc4ad --- /dev/null +++ b/app/forms/migrations/0014_form_description.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.16 on 2024-10-01 06:25 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("forms", "0013_alter_field_type"), + ] + + operations = [ + migrations.AddField( + model_name="form", + name="description", + field=models.TextField(blank=True, default=""), + ), + ] diff --git a/app/forms/models/forms.py b/app/forms/models/forms.py index 08e2ff97d..642654ec8 100644 --- a/app/forms/models/forms.py +++ b/app/forms/models/forms.py @@ -24,6 +24,7 @@ class Form(PolymorphicModel, BasePermissionModel): write_access = (*AdminGroup.admin(), AdminGroup.NOK) id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) title = models.CharField(max_length=400) + description = models.TextField(blank=True, default="") template = models.BooleanField(default=False) viewer_has_answered = None diff --git a/app/forms/serializers/forms.py b/app/forms/serializers/forms.py index 8d88a2fb3..8944a112f 100644 --- a/app/forms/serializers/forms.py +++ b/app/forms/serializers/forms.py @@ -51,6 +51,7 @@ class Meta: fields = ( "id", "title", + "description", "fields", "template", "resource_type", From 1a49b08572b3088247117a070fdddca8da638bf8 Mon Sep 17 00:00:00 2001 From: Josefine Arntsen <128137082+josefinearntsen@users.noreply.github.com> Date: Fri, 11 Oct 2024 10:13:10 +0200 Subject: [PATCH 2/2] Bug report system (#865) * Created New App named Index * created model * Refactor: Change admin file * admin * added serializer and viewsets for list * fixed typing error for permission_classes * Started on the create serializer for feedback * Made tests for create feedback * Implemented update serializer for feedback * made destroy method and testing create method as member * Made destroy method and tests * Fixed linting * Fixed linting --------- Co-authored-by: Tam Le Co-authored-by: Josefine Arntsen Co-authored-by: Mads Nylund Co-authored-by: Mads Nylund <73914541+MadsNyl@users.noreply.github.com> --- CHANGELOG.md | 2 +- app/content/serializers/user.py | 1 + app/index/__init__.py | 0 app/index/admin/__init__.py | 0 app/index/admin/admin.py | 5 + app/index/app.py | 5 + app/index/enums.py | 8 + app/index/exceptions.py | 0 app/index/factories/__init__.py | 2 + app/index/factories/bug_factory.py | 13 ++ app/index/factories/idea_factory.py | 13 ++ app/index/filters/__init__.py | 0 app/index/migrations/0001_initial.py | 112 ++++++++++++ app/index/migrations/__init__.py | 0 app/index/mixins.py | 0 app/index/models/__init__.py | 3 + app/index/models/bug.py | 5 + app/index/models/feedback.py | 76 ++++++++ app/index/models/idea.py | 5 + app/index/serializers/__init__.py | 3 + app/index/serializers/bug.py | 17 ++ app/index/serializers/feedback.py | 80 +++++++++ app/index/serializers/idea.py | 17 ++ app/index/tasks/__init__.py | 0 app/index/tests/__init__.py | 0 app/index/urls.py | 8 + app/index/util/__init__.py | 0 app/index/views/__init__.py | 0 app/index/views/feedback.py | 81 +++++++++ app/settings.py | 1 + app/tests/conftest.py | 11 ++ app/tests/index/__init__.py | 0 app/tests/index/test_feedback_integration.py | 174 +++++++++++++++++++ app/urls.py | 1 + 34 files changed, 642 insertions(+), 1 deletion(-) create mode 100644 app/index/__init__.py create mode 100644 app/index/admin/__init__.py create mode 100644 app/index/admin/admin.py create mode 100644 app/index/app.py create mode 100644 app/index/enums.py create mode 100644 app/index/exceptions.py create mode 100644 app/index/factories/__init__.py create mode 100644 app/index/factories/bug_factory.py create mode 100644 app/index/factories/idea_factory.py create mode 100644 app/index/filters/__init__.py create mode 100644 app/index/migrations/0001_initial.py create mode 100644 app/index/migrations/__init__.py create mode 100644 app/index/mixins.py create mode 100644 app/index/models/__init__.py create mode 100644 app/index/models/bug.py create mode 100644 app/index/models/feedback.py create mode 100644 app/index/models/idea.py create mode 100644 app/index/serializers/__init__.py create mode 100644 app/index/serializers/bug.py create mode 100644 app/index/serializers/feedback.py create mode 100644 app/index/serializers/idea.py create mode 100644 app/index/tasks/__init__.py create mode 100644 app/index/tests/__init__.py create mode 100644 app/index/urls.py create mode 100644 app/index/util/__init__.py create mode 100644 app/index/views/__init__.py create mode 100644 app/index/views/feedback.py create mode 100644 app/tests/index/__init__.py create mode 100644 app/tests/index/test_feedback_integration.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 04e14785b..8c92abd33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ --- ## Neste versjon - +- ✨**Tilbakemelding-funksjon**. Man kan nå opprette tilbakemeldinger for bugs og idé. ## Versjon 2024.09.25 - ✨**Codex arrangementer**. Det kan nå opprettes arrangementer på Codex, som medlemmer av Codex kan melde seg på. diff --git a/app/content/serializers/user.py b/app/content/serializers/user.py index 2378c5624..9150dc28e 100644 --- a/app/content/serializers/user.py +++ b/app/content/serializers/user.py @@ -127,6 +127,7 @@ class Meta: "user_id", "first_name", "last_name", + "image", ) diff --git a/app/index/__init__.py b/app/index/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/app/index/admin/__init__.py b/app/index/admin/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/app/index/admin/admin.py b/app/index/admin/admin.py new file mode 100644 index 000000000..ebcfc4b4c --- /dev/null +++ b/app/index/admin/admin.py @@ -0,0 +1,5 @@ +from django.contrib import admin + +from app.index import models + +admin.site.register(models.Bug) diff --git a/app/index/app.py b/app/index/app.py new file mode 100644 index 000000000..fa3e063ef --- /dev/null +++ b/app/index/app.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class IndexConfig(AppConfig): + name = "app.index" diff --git a/app/index/enums.py b/app/index/enums.py new file mode 100644 index 000000000..5adae939a --- /dev/null +++ b/app/index/enums.py @@ -0,0 +1,8 @@ +from django.db import models + + +class Status(models.TextChoices): + OPEN = "OPEN", "Åpen" + CLOSED = "CLOSED", "Lukket" + IN_PROGRESS = "IN_PROGRESS", "Under arbeid" + REJECTED = "REJECTED", "Avvist" diff --git a/app/index/exceptions.py b/app/index/exceptions.py new file mode 100644 index 000000000..e69de29bb diff --git a/app/index/factories/__init__.py b/app/index/factories/__init__.py new file mode 100644 index 000000000..e4c63edf6 --- /dev/null +++ b/app/index/factories/__init__.py @@ -0,0 +1,2 @@ +from app.index.factories.bug_factory import BugFactory +from app.index.factories.idea_factory import IdeaFactory diff --git a/app/index/factories/bug_factory.py b/app/index/factories/bug_factory.py new file mode 100644 index 000000000..cd84aa3f3 --- /dev/null +++ b/app/index/factories/bug_factory.py @@ -0,0 +1,13 @@ +import factory +from factory.django import DjangoModelFactory + +from app.content.factories import UserFactory +from app.index.models.bug import Bug + + +class BugFactory(DjangoModelFactory): + class Meta: + model = Bug + + title = factory.Sequence(lambda n: f"Bug{n}") + author = factory.SubFactory(UserFactory) diff --git a/app/index/factories/idea_factory.py b/app/index/factories/idea_factory.py new file mode 100644 index 000000000..643207704 --- /dev/null +++ b/app/index/factories/idea_factory.py @@ -0,0 +1,13 @@ +import factory +from factory.django import DjangoModelFactory + +from app.content.factories import UserFactory +from app.index.models.idea import Idea + + +class IdeaFactory(DjangoModelFactory): + class Meta: + model = Idea + + title = factory.Sequence(lambda n: f"Idea{n}") + author = factory.SubFactory(UserFactory) diff --git a/app/index/filters/__init__.py b/app/index/filters/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/app/index/migrations/0001_initial.py b/app/index/migrations/0001_initial.py new file mode 100644 index 000000000..fb78c0271 --- /dev/null +++ b/app/index/migrations/0001_initial.py @@ -0,0 +1,112 @@ +# Generated by Django 4.2.5 on 2024-09-26 14:54 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ("contenttypes", "0002_remove_content_type_name"), + ] + + operations = [ + migrations.CreateModel( + name="Feedback", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ("title", models.CharField(max_length=100)), + ("description", models.TextField(blank=True, default="")), + ( + "status", + models.CharField( + default="OPEN", + max_length=20, + verbose_name=[ + ("OPEN", "Åpen"), + ("CLOSED", "Lukket"), + ("IN_PROGRESS", "Under arbeid"), + ("REJECTED", "Avvist"), + ], + ), + ), + ( + "author", + models.ForeignKey( + blank=True, + default=None, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "polymorphic_ctype", + models.ForeignKey( + editable=False, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="polymorphic_%(app_label)s.%(class)s_set+", + to="contenttypes.contenttype", + ), + ), + ], + options={ + "ordering": ("created_at",), + }, + ), + migrations.CreateModel( + name="Bug", + fields=[ + ( + "feedback_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="index.feedback", + ), + ), + ], + options={ + "abstract": False, + }, + bases=("index.feedback",), + ), + migrations.CreateModel( + name="Idea", + fields=[ + ( + "feedback_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="index.feedback", + ), + ), + ], + options={ + "abstract": False, + }, + bases=("index.feedback",), + ), + ] diff --git a/app/index/migrations/__init__.py b/app/index/migrations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/app/index/mixins.py b/app/index/mixins.py new file mode 100644 index 000000000..e69de29bb diff --git a/app/index/models/__init__.py b/app/index/models/__init__.py new file mode 100644 index 000000000..8f1a9108b --- /dev/null +++ b/app/index/models/__init__.py @@ -0,0 +1,3 @@ +from app.index.models.bug import Bug +from app.index.models.feedback import Feedback +from app.index.models.idea import Idea diff --git a/app/index/models/bug.py b/app/index/models/bug.py new file mode 100644 index 000000000..2ba1c0b7b --- /dev/null +++ b/app/index/models/bug.py @@ -0,0 +1,5 @@ +from app.index.models.feedback import Feedback + + +class Bug(Feedback): + pass diff --git a/app/index/models/feedback.py b/app/index/models/feedback.py new file mode 100644 index 000000000..f91157c85 --- /dev/null +++ b/app/index/models/feedback.py @@ -0,0 +1,76 @@ +from django.db import models + +from polymorphic.models import PolymorphicModel + +from app.common.enums import AdminGroup, Groups +from app.common.permissions import BasePermissionModel, check_has_access +from app.content.models.user import User +from app.index.enums import Status +from app.util.models import BaseModel + + +class Feedback(BaseModel, BasePermissionModel, PolymorphicModel): + + read_access = (Groups.TIHLDE,) + write_access = (Groups.TIHLDE,) + + title = models.CharField(max_length=100) + description = models.TextField(default="", blank=True) + + author = models.ForeignKey( + User, blank=True, null=True, default=None, on_delete=models.SET_NULL + ) + status = models.CharField(Status.choices, default=Status.OPEN, max_length=20) + + def __str__(self): + return f"{self.title} - {self.status}" + + class Meta: + ordering = ("created_at",) + + @classmethod + def has_read_permission(cls, request): + return super().has_read_permission(request) + + @classmethod + def has_write_permission(cls, request): + return super().has_write_permission(request) + + @classmethod + def has_retrieve_permission(cls, request): + return cls.has_read_permission(request) + + @classmethod + def has_create_permission(cls, request): + return cls.has_write_permission(request) + + @classmethod + def has_update_permission(cls, request): + return cls.has_write_permission(request) + + @classmethod + def has_destroy_permission(cls, request): + return cls.has_write_permission(request) + + @classmethod + def has_list_permission(cls, request): + return cls.has_read_permission(request) + + def has_object_read_permission(self, request): + return self.has_read_permission(request) + + def has_object_write_permission(self, request): + return self.has_write_permission(request) + + def has_object_retrieve_permission(self, request): + return self.has_object_read_permission(request) + + def has_object_update_permission(self, request): + return ( + check_has_access([AdminGroup.INDEX], request) or self.author == request.user + ) + + def has_object_destroy_permission(self, request): + return ( + check_has_access([AdminGroup.INDEX], request) or self.author == request.user + ) diff --git a/app/index/models/idea.py b/app/index/models/idea.py new file mode 100644 index 000000000..1a504a7da --- /dev/null +++ b/app/index/models/idea.py @@ -0,0 +1,5 @@ +from app.index.models.feedback import Feedback + + +class Idea(Feedback): + pass diff --git a/app/index/serializers/__init__.py b/app/index/serializers/__init__.py new file mode 100644 index 000000000..6c05e1f4f --- /dev/null +++ b/app/index/serializers/__init__.py @@ -0,0 +1,3 @@ +from app.index.serializers.bug import BugListSerializer +from app.index.serializers.idea import IdeaListSerializer +from app.index.serializers.feedback import FeedbackListPolymorphicSerializer diff --git a/app/index/serializers/bug.py b/app/index/serializers/bug.py new file mode 100644 index 000000000..9d2ab12ab --- /dev/null +++ b/app/index/serializers/bug.py @@ -0,0 +1,17 @@ +from app.common.serializers import BaseModelSerializer +from app.content.serializers.user import SimpleUserSerializer +from app.index.models.bug import Bug + + +class BugListSerializer(BaseModelSerializer): + author = SimpleUserSerializer(read_only=True) + + class Meta: + model = Bug + fields = ( + "id", + "title", + "status", + "created_at", + "author", + ) diff --git a/app/index/serializers/feedback.py b/app/index/serializers/feedback.py new file mode 100644 index 000000000..df512adb7 --- /dev/null +++ b/app/index/serializers/feedback.py @@ -0,0 +1,80 @@ +from rest_polymorphic.serializers import PolymorphicSerializer + +from app.common.serializers import BaseModelSerializer +from app.index.models import Bug, Feedback, Idea +from app.index.serializers import BugListSerializer, IdeaListSerializer + + +class FeedbackListPolymorphicSerializer(PolymorphicSerializer, BaseModelSerializer): + resource_type_field_name = "feedback_type" + + model_serializer_mapping = { + Bug: BugListSerializer, + Idea: IdeaListSerializer, + } + + class Meta: + model = Feedback + fields = ( + "id", + "title", + "status", + "created_at", + "author", + ) + + +class IdeaCreateSerializer(BaseModelSerializer): + class Meta: + model = Idea + fields = ( + "title", + "description", + ) + + def create(self, validated_data): + user = self.context["request"].user + validated_data["author"] = user + + return super().create(validated_data) + + +class BugCreateSerializer(BaseModelSerializer): + class Meta: + model = Bug + fields = ( + "title", + "description", + ) + + def create(self, validated_data): + user = self.context["request"].user + validated_data["author"] = user + + return super().create(validated_data) + + +class IdeaUpdateSerializer(BaseModelSerializer): + class Meta: + model = Feedback + fields = ( + "title", + "description", + "status", + ) + + def update(self, instance, validated_data): + return super().update(instance, validated_data) + + +class BugUpdateSerializer(BaseModelSerializer): + class Meta: + model = Feedback + fields = ( + "title", + "description", + "status", + ) + + def update(self, instance, validated_data): + return super().update(instance, validated_data) diff --git a/app/index/serializers/idea.py b/app/index/serializers/idea.py new file mode 100644 index 000000000..344087f63 --- /dev/null +++ b/app/index/serializers/idea.py @@ -0,0 +1,17 @@ +from app.common.serializers import BaseModelSerializer +from app.content.serializers.user import SimpleUserSerializer +from app.index.models.idea import Idea + + +class IdeaListSerializer(BaseModelSerializer): + author = SimpleUserSerializer(read_only=True) + + class Meta: + model = Idea + fields = ( + "id", + "title", + "status", + "created_at", + "author", + ) diff --git a/app/index/tasks/__init__.py b/app/index/tasks/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/app/index/tests/__init__.py b/app/index/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/app/index/urls.py b/app/index/urls.py new file mode 100644 index 000000000..17a8202e3 --- /dev/null +++ b/app/index/urls.py @@ -0,0 +1,8 @@ +from django.urls import include, re_path +from rest_framework import routers + +from app.index.views.feedback import FeedbackViewSet + +router = routers.DefaultRouter() +router.register("feedbacks", FeedbackViewSet) +urlpatterns = [re_path(r"", include(router.urls))] diff --git a/app/index/util/__init__.py b/app/index/util/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/app/index/views/__init__.py b/app/index/views/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/app/index/views/feedback.py b/app/index/views/feedback.py new file mode 100644 index 000000000..dcf5a2bdf --- /dev/null +++ b/app/index/views/feedback.py @@ -0,0 +1,81 @@ +from rest_framework import status +from rest_framework.response import Response + +from app.common.pagination import BasePagination +from app.common.permissions import BasicViewPermission +from app.common.viewsets import BaseViewSet +from app.index.models.feedback import Feedback +from app.index.serializers.feedback import ( + BugCreateSerializer, + BugUpdateSerializer, + FeedbackListPolymorphicSerializer, + IdeaCreateSerializer, + IdeaUpdateSerializer, +) + + +class FeedbackViewSet(BaseViewSet): + serializer_class = FeedbackListPolymorphicSerializer + queryset = Feedback.objects.select_related("author") + pagination_class = BasePagination + permission_classes = [BasicViewPermission] + + def create(self, request, *_args, **_kwargs): + data = request.data + + feedback_type = data.get("feedback_type") + + if feedback_type == "Idea": + serializer = IdeaCreateSerializer(data=data, context={"request": request}) + + elif feedback_type == "Bug": + serializer = BugCreateSerializer(data=data, context={"request": request}) + + else: + return Response( + {"detail": "Ugyldig type tilbakemelding"}, + status=status.HTTP_400_BAD_REQUEST, + ) + + if serializer.is_valid(): + feedback = self.perform_create(serializer) + data = FeedbackListPolymorphicSerializer(feedback).data + return Response( + data, + status=status.HTTP_201_CREATED, + ) + return Response( + serializer.errors, + status=status.HTTP_400_BAD_REQUEST, + ) + + def update(self, request, *_args, **_kwargs): + instance = self.get_object() + data = request.data + + feedback_type = instance.feedback_type + + if feedback_type == "Idea": + serializer = IdeaUpdateSerializer(instance, data=data) + + elif feedback_type == "Bug": + serializer = BugUpdateSerializer(instance, data=data) + + if serializer.is_valid(): + super().perform_update(serializer) + return Response( + serializer.data, + status=status.HTTP_200_OK, + ) + + return Response( + serializer.errors, + status=status.HTTP_400_BAD_REQUEST, + ) + + def destroy(self, request, *_args, **_kwargs): + super().destroy(request, *_args, **_kwargs) + return Response( + {"detail": "Tilbakemeldingen ble slettet"}, + status=status.HTTP_200_OK, + ) diff --git a/app/settings.py b/app/settings.py index d7a79e4d0..90c264b6d 100644 --- a/app/settings.py +++ b/app/settings.py @@ -106,6 +106,7 @@ "app.payment", "app.kontres", "app.emoji", + "app.index", "app.codex", ] diff --git a/app/tests/conftest.py b/app/tests/conftest.py index e2efd0300..1473ca03e 100644 --- a/app/tests/conftest.py +++ b/app/tests/conftest.py @@ -36,6 +36,7 @@ from app.group.factories import GroupFactory, MembershipFactory from app.group.factories.fine_factory import FineFactory from app.group.factories.membership_factory import MembershipHistoryFactory +from app.index.factories import BugFactory, IdeaFactory from app.kontres.factories import BookableItemFactory, ReservationFactory from app.payment.factories.order_factory import OrderFactory from app.payment.factories.paid_event_factory import PaidEventFactory @@ -311,3 +312,13 @@ def codex_event(): @pytest.fixture() def codex_event_registration(): return CodexEventRegistrationFactory() + + +@pytest.fixture() +def feedback_bug(): + return BugFactory() + + +@pytest.fixture() +def feedback_idea(): + return IdeaFactory() diff --git a/app/tests/index/__init__.py b/app/tests/index/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/app/tests/index/test_feedback_integration.py b/app/tests/index/test_feedback_integration.py new file mode 100644 index 000000000..6535abc59 --- /dev/null +++ b/app/tests/index/test_feedback_integration.py @@ -0,0 +1,174 @@ +from rest_framework import status + +import pytest + +from app.util.test_utils import get_api_client + +FEEDBACK_BASE_URL = "/index/feedbacks/" + + +def get_data(type): + return { + "feedback_type": type, + "title": "This is a type title", + "description": f"This is a {type} report", + } + + +@pytest.mark.django_db +def test_list_feedback_with_both_bug_and_idea_as_member( + member, feedback_bug, feedback_idea +): + """All members should be able to list all types of feedbacks.""" + + url = FEEDBACK_BASE_URL + client = get_api_client(member) + response = client.get(url) + + data = response.data + results = data["results"] + bug_type = list(filter(lambda x: "Bug" == x["feedback_type"], results)) + idea_type = list(filter(lambda x: "Idea" == x["feedback_type"], results)) + + assert response.status_code == status.HTTP_200_OK + assert data["count"] == 2 + assert bug_type + assert idea_type + + +@pytest.mark.django_db +def test_list_feedback_as_anonymous_user(default_client): + """Non TIHLDE users should not be able to list feedbacks""" + + url = FEEDBACK_BASE_URL + response = default_client.get(url) + + assert response.status_code == status.HTTP_403_FORBIDDEN + + +@pytest.mark.django_db +@pytest.mark.parametrize( + "type", + ["Bug", "Idea"], +) +def test_create_feedback_with_both_bug_and_idea_as_member(member, type): + """All members should be able to create a bug and a idea feedback""" + + url = FEEDBACK_BASE_URL + client = get_api_client(member) + data = get_data(type) + response = client.post(url, data=data) + data = response.data + + assert response.status_code == status.HTTP_201_CREATED + assert data["feedback_type"] == type + + +@pytest.mark.django_db +def test_create_feedback_with_wrong_type_as_member(member): + """No members should be able to create feedback of another type than bug and idea""" + + url = FEEDBACK_BASE_URL + client = get_api_client(member) + data = get_data("wrong_type") + response = client.post(url, data=data) + data = response.data + + assert response.status_code == status.HTTP_400_BAD_REQUEST + + +@pytest.mark.django_db +@pytest.mark.parametrize( + "type", + ["Bug", "Idea"], +) +def test_create_feedback_as_anonymous_user(default_client, type): + """Non TIHLDE users should not be able to create feedbacks""" + + url = FEEDBACK_BASE_URL + data = get_data(type) + response = default_client.post(url, data=data) + + assert response.status_code == status.HTTP_403_FORBIDDEN + + +@pytest.mark.django_db +@pytest.mark.parametrize( + "type", + ["Bug", "Idea"], +) +def test_update_feedback_with_both_bug_and_idea_as_member(member, type): + """All members should be able to update their own bug and idea feedback""" + + url = FEEDBACK_BASE_URL + data = get_data(type) + client = get_api_client(member) + response = client.post(url, data=data) + data = response.data + + assert response.status_code == status.HTTP_201_CREATED + assert data["feedback_type"] == type + + +@pytest.mark.django_db +@pytest.mark.parametrize( + "type", + ["Bug", "Idea"], +) +def test_destroy_feedback_as_index_user(index_member, type): + """An Index user should be able to delete other members feedback""" + + url = FEEDBACK_BASE_URL + data = get_data(type) + client = get_api_client(user=index_member) + + initial_response = client.post(url, data=data) + + feedback_id = initial_response.data["id"] + + url = f"{FEEDBACK_BASE_URL}{feedback_id}/" + + response = client.delete(url) + + assert response.status_code == status.HTTP_200_OK + + +@pytest.mark.django_db +@pytest.mark.parametrize( + "type", + ["Bug", "Idea"], +) +def test_destroy_your_own_feedback_as_member(member, type): + """A user should be able to delete their own feedback as a member""" + + url = FEEDBACK_BASE_URL + data = get_data(type) + client = get_api_client(user=member) + + initial_response = client.post(url, data=data) + + print(initial_response.data) + print(f"Author: {initial_response.data['author']}") + + feedback_id = initial_response.data["id"] + + url = f"{FEEDBACK_BASE_URL}{feedback_id}/" + + response = client.delete(url) + + assert response.status_code == status.HTTP_200_OK + + +@pytest.mark.django_db +@pytest.mark.parametrize( + "type", + ["Bug", "Idea"], +) +def test_destroy_feedback_as_anonymous_user(default_client, type): + """Non TIHLDE users should not be able to delete feedbacks""" + + url = FEEDBACK_BASE_URL + data = get_data(type) + response = default_client.delete(url, data=data) + + assert response.status_code == status.HTTP_403_FORBIDDEN diff --git a/app/urls.py b/app/urls.py index d4fb286ba..eabb182f5 100644 --- a/app/urls.py +++ b/app/urls.py @@ -55,4 +55,5 @@ path("kontres/", include("app.kontres.urls")), path("emojis/", include("app.emoji.urls")), path("codex/", include("app.codex.urls")), + path("index/", include("app.index.urls")), ]