Django Api-Key with unit test
I am trying to implement unit tests to an existing project, the existing project uses Api-Key's to access and authenticate against the Api endpoints.
if I do the following via postman or command line:
curl --location --request GET 'http://127.0.0.1:8000/api/user_db' \
--header 'Authorization: Api-Key REDACTED' \
--header 'Content-Type: application/json' \
--data-raw '{
"username" : "test@testing.local"
}'
This will call the following view function and return the user details with the corresponding oid (json response) without error.
from django.shortcuts import render
from rest_framework_api_key.permissions import HasAPIKey
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from user_api.classes.UserController import (
GetBusinessUser,
CreateBusinessUser,
UpdateBusinessUser,
DeleteBusinesssUser
)
from celery.utils.log import get_task_logger
import environ
logger = get_task_logger(__name__)
env = environ.Env()
class ProcessUserRequest(APIView):
permission_classes = [HasAPIKey |IsAuthenticated ]
def get(self, request):
logger.info("Get Business User Request Received")
result = GetBusinessUser(request)
return Response(result["result"],
content_type='application/json charset=utf-8',
status=result["statuscode"]
This additionally calls the following shortened function:
def GetBusinessUser(request) -> Dict[str, Union[str, int]]:
logger.info(f"Processing Get Username Request: {request.data}")
valid_serializer = ValidateGetBusinessUserFormSerializer(data=request.data)
valid_serializer.is_valid(raise_exception=True)
username = valid_serializer.validated_data['username']
return BusinessUser.objects.filter(username=username).first()
As I wish to make unit test cases to ensure I can validate prior to deployment, I have implemented the following in the modules tests.py file:
from rest_framework.test import APITestCase, APIClient
from rest_framework_api_key.models import APIKey
from user_api.classes.UserController import GetBusinessUser
from django.urls import reverse
# Class Method for GetBusinessUser (truncated)
# try except handling and other user checks removed for stack
class ProcessUserRequestTest(APITestCase):
def setUp(self):
self.client = APIClient()
# have also tried: self.headers = {'HTTP_AUTHORIZATION': f'Api-Key {self.api_key.key}'}
self.client.credentials(HTTP_AUTHORIZATION='Api-Key SomeApiKeyValue')
self.url = reverse('business_user')
self.valid_payload = {'username': 'test@testing.local'}
self.invalid_payload = {'param1': '', 'param2': 'value2'}
def test_get_business_user_request(self):
# also tried based on above:
# response = self.client.get(self.url, **self.headers, format='json')
response = self.client.get(self.url, data=self.valid_payload, format='json')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data, GetBusinessUser(response.data).data)
No matter what I seem to do the following is always returned, so it appears from testing adding authentication headers or using the client.credentials
does not work with Authorization: Api-Key somekey
as a header?
creating test database for alias 'default'...
System check identified no issues (0 silenced).
{'detail': ErrorDetail(string='Authentication credentials were not provided.', code='not_authenticated')}
F
======================================================================
FAIL: test_get_business_user_request (user_api.tests.ProcessUserRequestTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File "../truncated/tests.py", line 19, in in test_get_business_user_request
self.assertEqual(response.status_code, 200)
AssertionError: 403 != 200
----------------------------------------------------------------------
Ran 1 test in 0.018s
FAILED (failures=1)
Destroying test database for alias 'default'...
Has this been encountered before and is there a workable solution so I can create unit tests?
I've been struggling with this for a while but figured it out, below is an example that worked for me.
_, key = APIKey.objects.create_key(name="test")
authorization = f"Api-Key {key}"
response = self.client.put(url, data, HTTP_AUTHORIZATION=authorization, format="json")
Now, in your case, I think it would look more like:
from rest_framework.test import APITestCase, APIClient
from rest_framework_api_key.models import APIKey
from user_api.classes.UserController import GetBusinessUser
from django.urls import reverse
class ProcessUserRequestTest(APITestCase):
def setUp(self):
self.client = APIClient()
self.url = reverse('business_user')
self.valid_payload = {'username': 'test@testing.local'}
self.invalid_payload = {'param1': '', 'param2': 'value2'}
def test_get_business_user_request(self):
_, key = APIKey.objects.create_key(name="test")
authorization = f"Api-Key {key}"
response = self.client.get(self.url, data=self.valid_payload, HTTP_AUTHORIZATION=authorization, format='json')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data, GetBusinessUser(response.data).data)
I haven't tested your code, see it as pseudo-code. And please let me know if you where able to make it work, and then I can adjust my pseudo-code accordingly.
Cheers
I think so you have to test API-KEY manually but when you have to access API you have just to authenticate the existing user whom to be requested.
force_authenticate(request, user=user) # to docs
self.client.force_authenticate(user=self.user) # im using like this it works
refer to the https://www.django-rest-framework.org/api-guide/testing/
- Forcing authentication
check out this point on given link to orignal documentation of DRF