Add tests for DashboardApiView and ApiTokenRequiredMixin (#230)

* test(core): fix factories

* test(api): Add tests for DashboardApiView and ApiTokenRequiredMixin

* refactor(api): sort imports and ran black on api app
This commit is contained in:
Sumit Singh 2023-09-23 08:51:29 +05:30 committed by GitHub
parent 120ea02fde
commit 03f8cbfe7b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 196 additions and 38 deletions

View File

@ -1,7 +1,10 @@
from django.http import JsonResponse from http import HTTPStatus
from django.contrib.auth.models import AnonymousUser
from core.models import User from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser
from django.http import JsonResponse
User = get_user_model()
class ApiTokenRequiredMixin: class ApiTokenRequiredMixin:
@ -11,13 +14,13 @@ class ApiTokenRequiredMixin:
return AnonymousUser() return AnonymousUser()
token = token.split(" ")[1] token = token.split(" ")[1]
user = User.objects.filter(api_token=token).first() user: User = User.objects.filter(api_token=token).first()
return user or AnonymousUser()
return user if user else AnonymousUser()
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
request.user = self._get_user_by_token(request) request.user = self._get_user_by_token(request)
if not request.user.is_authenticated: return (
return JsonResponse(data={}, status=403) super().dispatch(request, *args, **kwargs)
if request.user.is_authenticated
return super().dispatch(request, *args, **kwargs) else JsonResponse(data={}, status=HTTPStatus.FORBIDDEN)
)

View File

@ -1,3 +0,0 @@
from django.test import TestCase
# Create your tests here.

View File

View File

@ -0,0 +1,77 @@
from http import HTTPStatus
from django.test import TestCase, RequestFactory
from django.views import View
from api.mixins import ApiTokenRequiredMixin
from core.factories import UserFactory
from core.models import _default_api_token, Service
class TestApiTokenRequiredMixin(TestCase):
class DummyView(ApiTokenRequiredMixin, View):
model = Service
template_name = "dashboard/pages/service.html"
def setUp(self):
super().setUp()
self.user = UserFactory()
self.request = RequestFactory().get("/fake-path")
# Setup request and view.
self.factory = RequestFactory()
self.view = self.DummyView()
def test_get_user_by_token_without_authorization_token(self):
"""
GIVEN: A request without Authorization header
WHEN: get_user_by_token is called
THEN: It should return AnonymousUser
"""
user = self.view._get_user_by_token(self.request)
self.assertEqual(user.is_anonymous, True)
def test_get_user_by_token_with_invalid_authorization_token(self):
"""
GIVEN: A request with invalid Authorization header
WHEN: get_user_by_token is called
THEN: It should return AnonymousUser
"""
self.request.META["HTTP_AUTHORIZATION"] = "Bearer invalid-token"
user = self.view._get_user_by_token(self.request)
self.assertEqual(user.is_anonymous, True)
def test_get_user_by_token_with_invalid_token(self):
"""
GIVEN: A request with invalid token
WHEN: get_user_by_token is called
THEN: It should return AnonymousUser
"""
self.request.META["HTTP_AUTHORIZATION"] = f"Token {_default_api_token()}"
user = self.view._get_user_by_token(self.request)
self.assertEqual(user.is_anonymous, True)
def test_get_user_by_token_with_valid_token(self):
"""
GIVEN: A request with valid token
WHEN: get_user_by_token is called
THEN: It should return the user
"""
self.request.META["HTTP_AUTHORIZATION"] = f"Token {self.user.api_token}"
user = self.view._get_user_by_token(self.request)
self.assertEqual(user, self.user)
def test_dispatch_with_unauthenticated_user(self):
"""
GIVEN: A request with unauthenticated user
WHEN: dispatch is called
THEN: It should return 403
"""
self.request.META["HTTP_AUTHORIZATION"] = f"Token {_default_api_token()}"
response = self.view.dispatch(self.request)
self.assertEqual(response.status_code, HTTPStatus.FORBIDDEN)

View File

@ -0,0 +1,79 @@
import json
from http import HTTPStatus
from django.contrib.auth import get_user_model
from django.test import TestCase, RequestFactory
from django.urls import reverse
from api.views import DashboardApiView
from core.factories import UserFactory, ServiceFactory
from core.models import Service
User = get_user_model()
class TestDashboardApiView(TestCase):
def setUp(self) -> None:
super().setUp()
self.user: User = UserFactory()
self.service_1: Service = ServiceFactory(owner=self.user)
self.service_2: Service = ServiceFactory(owner=self.user)
self.url = reverse("api:services")
self.factory = RequestFactory()
def test_get_with_unauthenticated_user(self):
"""
GIVEN: An unauthenticated user
WHEN: The user makes a GET request to the dashboard API view
THEN: It should return 403
"""
response = self.client.get(self.url)
self.assertEqual(response.status_code, HTTPStatus.FORBIDDEN)
def test_get_returns_400(self):
"""
GIVEN: An authenticated user
WHEN: The user makes a GET request to the dashboard API view with an invalid date format
THEN: It should return 400
"""
request = self.factory.get(self.url, {"startDate": "01/01/2000"})
request.META["HTTP_AUTHORIZATION"] = f"Token {self.user.api_token}"
response = DashboardApiView.as_view()(request)
self.assertEqual(response.status_code, HTTPStatus.BAD_REQUEST)
data = json.loads(response.content)
self.assertEqual(data["error"], "Invalid date format. Use YYYY-MM-DD.")
def test_get_with_authenticated_user(self):
"""
GIVEN: An authenticated user
WHEN: The user makes a GET request to the dashboard API view
THEN: It should return 200
"""
request = self.factory.get(self.url)
request.META["HTTP_AUTHORIZATION"] = f"Token {self.user.api_token}"
response = DashboardApiView.as_view()(request)
self.assertEqual(response.status_code, HTTPStatus.OK)
data = json.loads(response.content)
self.assertEqual(len(data["services"]), 2)
def test_get_with_service_uuid(self):
"""
GIVEN: An authenticated user
WHEN: The user makes a GET request to the dashboard API view with a service UUID
THEN: It should return 200 and a single service
"""
request = self.factory.get(self.url, {"uuid": str(self.service_1.uuid)})
request.META["HTTP_AUTHORIZATION"] = f"Token {self.user.api_token}"
response = DashboardApiView.as_view()(request)
self.assertEqual(response.status_code, HTTPStatus.OK)
data = json.loads(response.content)
self.assertEqual(len(data["services"]), 1)
self.assertEqual(data["services"][0]["uuid"], str(self.service_1.uuid))
self.assertEqual(data["services"][0]["name"], str(self.service_1.name))

View File

@ -1,54 +1,46 @@
import uuid from http import HTTPStatus
from django.http import JsonResponse
from django.db.models import Q from django.db.models import Q
from django.db.models.query import QuerySet from django.db.models.query import QuerySet
from django.http import JsonResponse
from django.views.generic import View from django.views.generic import View
from dashboard.mixins import DateRangeMixin
from core.models import Service from core.models import Service
from core.utils import is_valid_uuid
from dashboard.mixins import DateRangeMixin
from .mixins import ApiTokenRequiredMixin from .mixins import ApiTokenRequiredMixin
def is_valid_uuid(value):
try:
uuid.UUID(value)
return True
except ValueError:
return False
class DashboardApiView(ApiTokenRequiredMixin, DateRangeMixin, View): class DashboardApiView(ApiTokenRequiredMixin, DateRangeMixin, View):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
services = Service.objects.filter( services = Service.objects.filter(Q(owner=request.user) | Q(collaborators__in=[request.user])).distinct()
Q(owner=request.user) | Q(collaborators__in=[request.user])
).distinct()
uuid = request.GET.get("uuid") uuid_ = request.GET.get("uuid")
if uuid and is_valid_uuid(uuid): if uuid_ and is_valid_uuid(uuid_):
services = services.filter(uuid=uuid) services = services.filter(uuid=uuid_)
try: try:
start = self.get_start_date() start = self.get_start_date()
end = self.get_end_date() end = self.get_end_date()
except ValueError: except ValueError:
return JsonResponse(status=400, data={"error": "Invalid date format"}) return JsonResponse(status=HTTPStatus.BAD_REQUEST, data={"error": "Invalid date format. Use YYYY-MM-DD."})
service: Service
services_data = [ services_data = [
{ {
"name": s.name, "name": service.name,
"uuid": s.uuid, "uuid": service.uuid,
"link": s.link, "link": service.link,
"stats": s.get_core_stats(start, end), "stats": service.get_core_stats(start, end),
} }
for s in services for service in services
] ]
services_data = self._convert_querysets_to_lists(services_data) services_data = self._convert_querysets_to_lists(services_data)
return JsonResponse(data={"services": services_data}) return JsonResponse(data={"services": services_data})
def _convert_querysets_to_lists(self, services_data): def _convert_querysets_to_lists(self, services_data: list[dict]) -> list[dict]:
for service_data in services_data: for service_data in services_data:
for key, value in service_data["stats"].items(): for key, value in service_data["stats"].items():
if isinstance(value, QuerySet): if isinstance(value, QuerySet):

10
shynet/core/utils.py Normal file
View File

@ -0,0 +1,10 @@
import uuid
def is_valid_uuid(value: str) -> bool:
"""Check if a string is a valid UUID."""
try:
uuid.UUID(value)
return True
except ValueError:
return False