From 5da26fb1b52bf52d13785c49e96821d6c14d4de4 Mon Sep 17 00:00:00 2001 From: chestnut90 Date: Sun, 29 Oct 2023 17:20:13 +0900 Subject: [PATCH 1/5] =?UTF-8?q?:white=5Fcheck=5Fmark:Test=20:=20=EA=B2=8C?= =?UTF-8?q?=EC=8B=9C=EB=AC=BC=20=EA=B3=B5=EC=9C=A0=20api,=20url=20?= =?UTF-8?q?=EC=8B=A4=ED=8C=A8=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BC=80?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4#12?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - TestCase Red - /shares// url에 대한 리소스 찾을 수 없는 에러 발생 Related to #12 --- shares/tests.py | 1 - shares/tests/__init__.py | 0 shares/tests/test_shares_api.py | 13 +++++++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) delete mode 100644 shares/tests.py create mode 100644 shares/tests/__init__.py create mode 100644 shares/tests/test_shares_api.py diff --git a/shares/tests.py b/shares/tests.py deleted file mode 100644 index a39b155..0000000 --- a/shares/tests.py +++ /dev/null @@ -1 +0,0 @@ -# Create your tests here. diff --git a/shares/tests/__init__.py b/shares/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/shares/tests/test_shares_api.py b/shares/tests/test_shares_api.py new file mode 100644 index 0000000..b892ff5 --- /dev/null +++ b/shares/tests/test_shares_api.py @@ -0,0 +1,13 @@ +from django.urls import reverse +from rest_framework.test import APITestCase + + +class SharesAPITestCase(APITestCase): + """ + /shares// post 에 대한 테스트 케이스 + """ + + viewname = "shares" + + def test_post_failure_no_api(self): + path = reverse(self.viewname, kwargs={"content_id": None}) From c3a1a5475ee07e0402be9f4fb3babd8842dc9505 Mon Sep 17 00:00:00 2001 From: chestnut90 Date: Sun, 29 Oct 2023 17:26:51 +0900 Subject: [PATCH 2/5] =?UTF-8?q?:sparkles:Feat=20:=20=EA=B2=8C=EC=8B=9C?= =?UTF-8?q?=EB=AC=BC=20=EC=A2=8B=EC=95=84=EC=9A=94=20api=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20#12?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 게시물 좋아요 api, /shares// 에 대한 view 인터페이스 추가 Related to #12 --- config/urls.py | 10 +++++++--- shares/urls.py | 7 +++++++ shares/views.py | 9 ++++++++- 3 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 shares/urls.py diff --git a/config/urls.py b/config/urls.py index 76d2b78..c81be31 100644 --- a/config/urls.py +++ b/config/urls.py @@ -26,10 +26,14 @@ path("api/users/", include("users.urls")), path("api/posts/", include("posts.urls")), path("api/common/", include("common.urls")), - # Swagger - path("swagger/docs/", schema_view.with_ui("swagger", cache_timeout=0), name="schema-swagger-ui"), - # Likes path("api/likes/", include("likes.urls")), + path("api/shares/", include("shares.urls")), + # Swagger + path( + "swagger/docs/", + schema_view.with_ui("swagger", cache_timeout=0), + name="schema-swagger-ui", + ), ] urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/shares/urls.py b/shares/urls.py new file mode 100644 index 0000000..b912074 --- /dev/null +++ b/shares/urls.py @@ -0,0 +1,7 @@ +from django.urls import path + +from shares.views import SharesAPIView + +urlpatterns = [ + path("/", SharesAPIView.as_view(), name="shares"), +] diff --git a/shares/views.py b/shares/views.py index 60f00ef..1997775 100644 --- a/shares/views.py +++ b/shares/views.py @@ -1 +1,8 @@ -# Create your views here. +from drf_yasg.utils import swagger_auto_schema +from rest_framework.views import APIView + + +class SharesAPIView(APIView): + @swagger_auto_schema(operation_summary="게시물 좋아요") + def post(self, request, content_id): + pass From dc83f456bc25df2cfa2b7b492780cf473f9fd951 Mon Sep 17 00:00:00 2001 From: chestnut90 Date: Sun, 29 Oct 2023 17:30:28 +0900 Subject: [PATCH 3/5] =?UTF-8?q?:white=5Fcheck=5Fmark:Test=20:=20=EA=B2=8C?= =?UTF-8?q?=EC=8B=9C=EB=AC=BC=20=EC=A2=8B=EC=95=84=EC=9A=94=20api=EC=97=90?= =?UTF-8?q?=20=EB=8C=80=ED=95=9C=20post=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=8B=A4=ED=8C=A8=20#12?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 인증되지 않은 사용자가 게시물에 post 요청을 수행 시 실패 테스트 케이스 - 인증된 사용자가 게시물에 post 요청을 수행 시 실패 테스트 케이스 Related to #12 --- shares/tests/test_shares_api.py | 40 +++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/shares/tests/test_shares_api.py b/shares/tests/test_shares_api.py index b892ff5..74d674c 100644 --- a/shares/tests/test_shares_api.py +++ b/shares/tests/test_shares_api.py @@ -1,6 +1,10 @@ +from django.contrib.auth import get_user_model from django.urls import reverse +from rest_framework import status from rest_framework.test import APITestCase +from posts.models import Post + class SharesAPITestCase(APITestCase): """ @@ -11,3 +15,39 @@ class SharesAPITestCase(APITestCase): def test_post_failure_no_api(self): path = reverse(self.viewname, kwargs={"content_id": None}) + + def test_post_without_auth(self): + """ + 인증되지 않은 사용자가 shares에 post 요청을 전달하고, + 401(unauth) 응답을 얻습니다. + """ + + post = Post.objects.create(title="title") + self.client.logout() + response = self.client.post( + path=reverse(self.viewname, kwargs={"content_id": post.content_id}), + data=None, + ) + + self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) # status 401 + + def test_post_with_auth(self): + """ + 인증된 사용자가 shares에 post 요청을 전달하고, + 200(Ok) 응답을 얻으며 share_count가 증가합니다. + """ + + post = Post.objects.create(title="title") + + user = get_user_model().objects.create(username="username") + self.client.force_authenticate(user=user) + + response = self.client.post( + path=reverse(self.viewname, kwargs={"content_id": post.content_id}), + data=None, + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) # status 200 + + post_after_response = Post.objects.get(content_id=post.content_id) + self.assertEqual(post.share_count + 1, post_after_response.share_count) # share_count diff 1 From 77178c9b7173d062fb64bdddcb976e63731ae4df Mon Sep 17 00:00:00 2001 From: chestnut90 Date: Sun, 29 Oct 2023 18:01:24 +0900 Subject: [PATCH 4/5] =?UTF-8?q?:recycle:Refact=20:=20=EA=B2=8C=EC=8B=9C?= =?UTF-8?q?=EB=AC=BC=20=EC=A2=8B=EC=95=84=EC=9A=94=20api=EC=97=90=20?= =?UTF-8?q?=EB=8C=80=ED=95=9C=20=EA=B8=B0=EB=8A=A5=20=EC=99=84=EB=A3=8C=20?= =?UTF-8?q?#12?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 게시물 좋아요 api에 대한 Post 메시지 요청 시 게시물의 공유수를 1 증가한다. Related to #12 --- shares/serializers.py | 9 +++++++++ shares/views.py | 38 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 shares/serializers.py diff --git a/shares/serializers.py b/shares/serializers.py new file mode 100644 index 0000000..3b198f5 --- /dev/null +++ b/shares/serializers.py @@ -0,0 +1,9 @@ +from rest_framework.serializers import ModelSerializer + +from posts.models import Post + + +class PostShareCountIncrementSerializer(ModelSerializer): + class Meta: + model = Post + fields = ("share_count",) diff --git a/shares/views.py b/shares/views.py index 1997775..591895a 100644 --- a/shares/views.py +++ b/shares/views.py @@ -1,8 +1,42 @@ +from django.shortcuts import get_object_or_404 +from drf_yasg import openapi from drf_yasg.utils import swagger_auto_schema +from rest_framework import status +from rest_framework.permissions import IsAuthenticatedOrReadOnly +from rest_framework.response import Response from rest_framework.views import APIView +from posts.models import Post +from shares.serializers import PostShareCountIncrementSerializer + class SharesAPIView(APIView): - @swagger_auto_schema(operation_summary="게시물 좋아요") + permission_classes = [IsAuthenticatedOrReadOnly] + + @swagger_auto_schema( + operation_summary="게시물 좋아요", + responses={ + status.HTTP_200_OK: openapi.Response(description="ok"), + status.HTTP_401_UNAUTHORIZED: openapi.Response(description="unauthorized"), + }, + ) def post(self, request, content_id): - pass + """ + 게시물(content_id)에 대해 공유 요청을 합니다. + """ + + post = get_object_or_404(Post, content_id=content_id) + + # TODO : how to increment share_count using serializer not passing data. + serializer = PostShareCountIncrementSerializer( + instance=post, + data={ + "share_count": post.share_count + 1, + }, + partial=True, + ) + + serializer.is_valid(raise_exception=True) + serializer.save() + + return Response(status=status.HTTP_200_OK) From 66ad1059b97125cdbabba0bcb4779e207353678c Mon Sep 17 00:00:00 2001 From: chestnut90 Date: Sun, 29 Oct 2023 18:13:26 +0900 Subject: [PATCH 5/5] =?UTF-8?q?:white=5Fcheck=5Fmark:Test=20:=20=EA=B2=8C?= =?UTF-8?q?=EC=8B=9C=EB=AC=BC=20=EA=B3=B5=EC=9C=A0=20api=EC=9D=98=20no=20a?= =?UTF-8?q?pi=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BC=80=EC=9D=B4=EC=8A=A4?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=20#12?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - flake8에 의해 사용되지 않는 변수 오류가 발생됨. Related to #12 --- shares/tests/test_shares_api.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/shares/tests/test_shares_api.py b/shares/tests/test_shares_api.py index 74d674c..07ec3bf 100644 --- a/shares/tests/test_shares_api.py +++ b/shares/tests/test_shares_api.py @@ -14,7 +14,10 @@ class SharesAPITestCase(APITestCase): viewname = "shares" def test_post_failure_no_api(self): - path = reverse(self.viewname, kwargs={"content_id": None}) + """ + 해당 view가 없으면 NoReverseMatch 예외가 수반됩니다. + """ + reverse(self.viewname, kwargs={"content_id": None}) def test_post_without_auth(self): """ @@ -46,7 +49,6 @@ def test_post_with_auth(self): path=reverse(self.viewname, kwargs={"content_id": post.content_id}), data=None, ) - self.assertEqual(response.status_code, status.HTTP_200_OK) # status 200 post_after_response = Post.objects.get(content_id=post.content_id)