diff --git a/cardie/authentication/test_authentication.py b/cardie/authentication/test_authentication.py new file mode 100644 index 0000000..5d0c0e2 --- /dev/null +++ b/cardie/authentication/test_authentication.py @@ -0,0 +1,127 @@ +import pytest +from django.test import RequestFactory +from django.urls import reverse +from django.utils import timezone +from main.models import Server + +from authentication.models import User +from authentication.views import create_account, sign_in + + +@pytest.fixture +def server(): + server = Server.objects.create(ip='127.0.0.1', production=False, allow_create_accounts=True, allow_sign_in=True) + return server + +@pytest.mark.django_db +def test_create_user(server): + # Create a new user + user_data = { + 'Username': 'testuser', + 'Password': 'testpassword', + 'Email': 'testuser@example.com', + 'date_created': timezone.now() + } + # Create a mock request object + factory = RequestFactory() + request = factory.post(reverse('createaccount'), user_data) + request.headers=user_data + request.headers['Internal'] = True + # Call the create_account view function with the mock request object + response = create_account(request) + + assert response.status_code == 200 + +@pytest.mark.django_db +def test_create_user_with_no_username( server): + # Create a new user + user_data = { + 'Username': 'testuser', + 'Password': '', + 'Email': 'testuser@example.com', + 'date_created': timezone.now() + } + # Create a mock request object + factory = RequestFactory() + request = factory.post(reverse('createaccount'), user_data) + request.headers=user_data + # Call the create_account view function with the mock request object + response = create_account(request) + + # Assert that the response status code is not 200 + assert response.status_code != 200 + +@pytest.mark.django_db +def test_create_user_with_no_email( server): + # Create a new user + user_data = { + 'Username': 'testuser', + 'Password': 'testpassword', + 'Email': '', + 'date_created': timezone.now() + } + # Create a mock request object + factory = RequestFactory() + request = factory.post(reverse('createaccount'), user_data) + request.headers=user_data + # Call the create_account view function with the mock request object + response = create_account(request) + + # Assert that the response status code is not 200 + assert response.status_code != 200 + +@pytest.mark.django_db +def test_create_user_with_no_password( server): + # Create a mock request object + factory = RequestFactory() + request = factory.post(reverse('createaccount'), {'Username': '', 'Password': 'password', 'Email': 'abc@gmail.com'}) + request.headers={ 'Username': '', 'Password': 'password', 'Email': 'abc@gmail.com' } + # Call the create_account view function with the mock request object + response = create_account(request) + + # Assert that the response status code is not 200 + assert response.status_code != 200 + +@pytest.mark.django_db +def test_create_user_duplicate_username(server): + user_data = { + 'Username': 'testuser', + 'Password': 'testpassword', + 'Email': 'testuser@example.com', + 'date_created': timezone.now(), + } + # First create a user + User.objects.create(username=user_data['Username'], email=user_data['Email'], password=user_data['Password'], date_created=user_data['date_created']) + + # Create a mock request object + factory = RequestFactory() + request = factory.post(reverse('createaccount'), user_data) + request.headers=user_data + + # Call the create_account view function with the mock request object + response = create_account(request) + + # Assert that the response status code is not 200 + assert response.status_code != 200 + +@pytest.mark.django_db +def test_signin_user(server): + # Try to create a user with a duplicate email + user_data = { + 'Username': 'testuser', + 'Password': 'testpassword', + 'Email': 'testuser@example.com', + 'date_created': timezone.now(), + } + # First create a user + User.objects.create(username=user_data['Username'], email=user_data['Email'], password=user_data['Password'], date_created=user_data['date_created']) + + # Create a mock request object + factory = RequestFactory() + request = factory.post(reverse('sign_in'), user_data) + request.headers=user_data + + # Call the create_account view function with the mock request object + response = sign_in(request) + + assert response.status_code == 200 \ No newline at end of file diff --git a/cardie/authentication/tests.py b/cardie/authentication/tests.py deleted file mode 100644 index 4929020..0000000 --- a/cardie/authentication/tests.py +++ /dev/null @@ -1,2 +0,0 @@ - -# Create your tests here. diff --git a/cardie/authentication/views.py b/cardie/authentication/views.py index 7dfcf6f..53d379f 100644 --- a/cardie/authentication/views.py +++ b/cardie/authentication/views.py @@ -1,23 +1,23 @@ +import uuid + +from django.contrib.auth.hashers import check_password, make_password from django.shortcuts import HttpResponse -from django.contrib.auth.hashers import make_password, check_password +from django.utils import timezone +from main import views +from main.models import Card, Server, TempCard from authentication.models import User -from main.models import Server, Card, TempCard -from main import views -from django.utils import timezone -import uuid def sign_in(request): server = Server.objects.all()[0] # TODO: What if there is multiple server objects? - + if server.allow_sign_in: if "Username" in request.headers and "Password" in request.headers: username = request.headers["Username"] password = request.headers["Password"] signed_in = True - else: try: username = request.session["username"] @@ -26,10 +26,8 @@ def sign_in(request): signed_in = True except KeyError: - print("Missing headers and no session!") - return HttpResponse("error_missing_headers_and_session") + return HttpResponse("error_missing_headers_and_session",status_code=400) - if signed_in: users = User.objects.filter(username=username) @@ -53,7 +51,7 @@ def sign_in(request): except KeyError: pass - + if request.headers["Internal"] == "true": return HttpResponse("success") @@ -74,27 +72,26 @@ def sign_in(request): def create_account(request): server = Server.objects.all()[0] # TODO: What if there is multiple server objects? - + print(server.allow_create_accounts) if server.allow_create_accounts: if "Username" in request.headers and "Password" in request.headers and "Email" in request.headers: username = request.headers["Username"] password = request.headers["Password"] email = request.headers["Email"] - + if username == "": - return HttpResponse("no_username") + return HttpResponse("no_username",status=400) if password == "": - return HttpResponse("no_password") - + return HttpResponse("no_password",status=400) + if email == "": - return HttpResponse("no_email") - + return HttpResponse("no_email",status=400) users = User.objects.filter(username=username) - + if len(users) > 0: - return HttpResponse("error_account_already_exists") + return HttpResponse("error_account_already_exists",status=400) else: hashed_password = make_password(password) @@ -102,12 +99,14 @@ def create_account(request): user = User(username=username, password=hashed_password, email=email, date_created=timezone.now()) user.save() - request.session["username"] = username - request.session["password"] = password - + request.session = { + "username": username, + "password": password + } + return sign_in(request) else: - return HttpResponse("error_missing_headers") + return HttpResponse("error_missing_headers",status=400) else: - return HttpResponse("error_create_account_disabled") \ No newline at end of file + return HttpResponse("error_create_account_disabled",status=400) \ No newline at end of file diff --git a/cardie/main/test_card.py b/cardie/main/test_card.py new file mode 100644 index 0000000..5932138 --- /dev/null +++ b/cardie/main/test_card.py @@ -0,0 +1,139 @@ +import uuid + +import pytest +from authentication.models import User +from authentication.views import sign_in +from django.test import RequestFactory +from django.urls import reverse +from django.utils import timezone + +from main.models import Server +from main.views import check_card, create_card, delete_card + +CARD_VIEWS = { + 'CHECK_CARD' : 'checkcard', + 'CREATE_CARD' : 'createcard', + 'DELETE_CARD' : 'deletecard', + 'SIGN_IN' : 'signin', + 'RENAME_CARD' : 'renamecard' +} + +# NOTE:Tests can only access the data in current session. +# In accordance with DRY principle create a fixture to avoid repetition in tests +@pytest.fixture +def server(): + print("Running server fixture") + server = Server.objects.create(ip='127.0.0.1', production=False, allow_create_accounts=True, allow_sign_in=True) + return server + +@pytest.fixture +def user(server): + print("server in user fixture",server) + ## Try to create a user with a duplicate email + user_data = { + 'Username': 'testuser', + 'Password': 'testpassword', + 'Email': 'testuser@example.com', + 'date_created': timezone.now(), + } + # First create a user + User.objects.create(username=user_data['Username'], email=user_data['Email'], password=user_data['Password'], date_created=user_data['date_created']) + + # Create a mock request object + factory = RequestFactory() + request = factory.post(reverse(CARD_VIEWS['SIGN_IN']), user_data) + request.headers=user_data + request.headers['Internal'] = True + + # Call the create_account view function with the mock request object + response = sign_in(request) + return (user_data, response) # Return the user data and response + +@pytest.fixture +def card(user): + user_data = user[0] + uuid_data = {'UUID' : uuid.uuid4()} + # Create card for signed in user + factory = RequestFactory() + request = factory.post(reverse(CARD_VIEWS['CREATE_CARD']), uuid_data) + request.session = {'username' : user_data['Username'],'password' : user_data['Password']} + request.headers = uuid_data + + response = create_card(request) + return (user_data, uuid_data, response) # Return the user data, uuid and response + +@pytest.mark.django_db +def test_create_card(server,user): + user_data = user[0] + # Create card for signed in user + factory = RequestFactory() + request = factory.post(reverse(CARD_VIEWS['CREATE_CARD']), {'UUID': 'testuuid'}) + request.session = {'username' : user_data['Username'],'password' : user_data['Password']} + request.headers = {'UUID' : uuid.uuid4()} + + response = create_card(request) + + # Assert that create_card returns 200 + assert response.status_code == 200 + +@pytest.mark.django_db +def test_check_created_card(server,card): + user_data = card[0] + uuid_data = card[1] + + # Check the created card exists + factory = RequestFactory() + request = factory.post(reverse(CARD_VIEWS['CHECK_CARD']), uuid_data) + request.session = {'username' : user_data['Username'],'password' : user_data['Password']} + request.headers = uuid_data + + response = check_card(request) + + # Assert that create_card returns 200 + assert response.status_code == 200 + +@pytest.mark.django_db +def test_delete_card(server,card): + user_data = card[0] + uuid_data = card[1] + print("uuid_data", uuid_data) + # Check the created card exists + factory = RequestFactory() + request = factory.post(reverse(CARD_VIEWS['DELETE_CARD']), uuid_data) + request.session = {'username' : user_data['Username'],'password' : user_data['Password']} + request.headers = uuid_data + + response = delete_card(request) + + # Assert that create_card returns 200 + assert response.status_code == 200 + +@pytest.mark.django_db +def test_delete_card_that_doesnt_exist(server,card): + user_data = card[0] + uuid_data = {'UUID' : uuid.uuid4()} + # Check the created card exists + factory = RequestFactory() + request = factory.post(reverse(CARD_VIEWS['DELETE_CARD']), uuid_data) + request.session = {'username' : user_data['Username'],'password' : user_data['Password']} + request.headers = uuid_data + + response = delete_card(request) + + # Assert that create_card returns 404 (not found) + assert response.status_code == 404 + +@pytest.mark.django_db +def test_rename_card(server,card): + user_data = card[0] + uuid_data = card[1] + # Check the created card exists + factory = RequestFactory() + request = factory.post(reverse(CARD_VIEWS['RENAME_CARD']), uuid_data) + request.session = {'username' : user_data['Username'],'password' : user_data['Password']} + request.headers = uuid_data + + response = delete_card(request) + + # Assert that create_card returns 404 (not found) + assert response.status_code == 404 \ No newline at end of file diff --git a/cardie/main/views.py b/cardie/main/views.py index df1e010..b1d92ae 100644 --- a/cardie/main/views.py +++ b/cardie/main/views.py @@ -1,11 +1,12 @@ import json +import authentication.views as Auth from authentication.models import User -from authentication.views import sign_in from django.http import JsonResponse from django.shortcuts import HttpResponse, render from django.utils import timezone from django.views.decorators.csrf import csrf_exempt + from main.icons import icons from main.models import Card, TempCard @@ -28,7 +29,8 @@ def authentication(request): request.session["username"] request.session["password"] - return sign_in(request) + # Refactored to avoid circular import in tests + return Auth.sign_in(request) except KeyError: print("No session data on authentication page!") @@ -99,11 +101,10 @@ def create_card(request): card_created_on=timezone.now(), ) card.save() - - return HttpResponse("Done") + return HttpResponse("Done",status=200) else: - return HttpResponse("Request is not a POST request") + return HttpResponse("Request is not a POST request",status=400) @csrf_exempt @@ -117,7 +118,7 @@ def check_card(request): # Card exists on the server if card[0].owner == me: # You own this card - return JsonResponse(card[0].data, safe=False) + return JsonResponse(card[0].data, safe=False,status=200) else: # You don't have permission to access this card @@ -233,19 +234,23 @@ def create_temp_card(request): @csrf_exempt def delete_card(request): if request.method == "POST": - if request.headers["uuid"]: - # TODO: What if there are two accounts with that username? - me = User.objects.filter(username=request.session["username"])[0] - - card = Card.objects.filter(uuid=request.headers["uuid"], owner=me)[0] - - if card: - card.delete() - return HttpResponse("Success") + if request.headers["UUID"]: + try: + # TODO: What if there are two accounts with that username? + me = User.objects.filter(username=request.session["username"])[0] + + #This throws IndexError if there are no cards for the current user with that UUID + card = Card.objects.filter(uuid=request.headers["UUID"], owner=me)[0] + + if card: + card.delete() + return HttpResponse("Success",status=200) - else: - return HttpResponse("Card not found") + else: + return HttpResponse("Card not found",status=404) + except IndexError: + return HttpResponse("No Cards created for current user",status=404) else: return HttpResponse("Missing headers") diff --git a/requirements.txt b/requirements.txt index a8efd0b..8025ec7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -25,3 +25,4 @@ sqlparse==0.4.4 typing_extensions==4.12.2 urllib3==2.2.2 Werkzeug==3.0.3 +pytest==8.3.3 \ No newline at end of file