Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: PBI-Profil #4

Merged
merged 38 commits into from
Feb 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
5b69146
Create new workflow for Build and Test for all branch without SonarQu…
JohannesSetiawan Feb 22, 2024
4673003
Remove all Todo (models, views, urls, test), create AppUser model to …
JohannesSetiawan Feb 22, 2024
09721ae
[RED] Start working on Register functionality. Add base code for the …
JohannesSetiawan Feb 22, 2024
0d473d8
[GREEN] Implemented register feature and pass the test.
JohannesSetiawan Feb 22, 2024
215b389
[RED] Adding new test for email format not valid.
JohannesSetiawan Feb 22, 2024
73accb2
[GREEN] Finish handling worng email format
JohannesSetiawan Feb 22, 2024
eb6cdb6
[RED] Add test for empty input.
JohannesSetiawan Feb 22, 2024
f1d7385
[GREEN] Passed test for empty input.
JohannesSetiawan Feb 22, 2024
cd22c06
[RED] Create test for username and/or email already used.
JohannesSetiawan Feb 22, 2024
96d12b1
[GREEN] Passed test for email/username already exist.
JohannesSetiawan Feb 22, 2024
5c49d94
[REFACTOR] Refactor input validators to increase reusabilty
JohannesSetiawan Feb 22, 2024
58e1696
[RED] Add base code and test for Login feature.
JohannesSetiawan Feb 22, 2024
331cbf5
[RED] Fix test for login feature.
JohannesSetiawan Feb 22, 2024
c40d7c5
[GREEN] Finish implementing login feature to pass the test.
JohannesSetiawan Feb 22, 2024
5b303ec
[RED] Add test for failed log in.
JohannesSetiawan Feb 22, 2024
6d4f744
[GREEN] Finish implementing failed ogin due to wrong username/password.
JohannesSetiawan Feb 22, 2024
e5174fd
[RED] Add test for Logout feature.
JohannesSetiawan Feb 22, 2024
4215204
[RED] Fix test for logout.
JohannesSetiawan Feb 22, 2024
4eae1f2
[GREEN] Finishing logout feature.
JohannesSetiawan Feb 22, 2024
b0c3423
Chore: Adding django-rest-framework and simplejwt in Django to prepar…
JohannesSetiawan Feb 23, 2024
a8b3778
[REFACTOR] Remove logout functionality and test due to using JWT toke…
JohannesSetiawan Feb 23, 2024
3691048
[RED] Add skeletons and test for SendVerificationEmail feature. Chang…
JohannesSetiawan Feb 23, 2024
d40bdb3
[GREEN] Finishing SendVerificationEmail feature. Change workflows to …
JohannesSetiawan Feb 23, 2024
26c6598
[CHORE]: Fix workflows for django-build-test.yml.
JohannesSetiawan Feb 23, 2024
e4c084f
[REFACTOR] Changes some attribute/function names. Change email content.
JohannesSetiawan Feb 23, 2024
902840c
[REFACTOR] Fix naming in tests.py
JohannesSetiawan Feb 23, 2024
fa5c506
[RED] Create test and skeleton for SendRecoverPasswordEmail feature
JohannesSetiawan Feb 23, 2024
abd788b
[GREEN] Finish implementing SendRecoverPasswordEmail feature
JohannesSetiawan Feb 23, 2024
f132b80
fix: work
carlenee Feb 24, 2024
f299676
[RED] Add tests for profile update functionality
carlenee Feb 24, 2024
e72d434
[GREEN] Finishing Update Profile feature.
carlenee Feb 24, 2024
f49c6a8
Merge branch 'staging' of https://github.com/RevelioStartup/revelio-b…
carlenee Feb 24, 2024
8effe40
[GREEN] Finishing Update Profile feature.
carlenee Feb 24, 2024
10f6563
[GREEN] Freeze requirements
carlenee Feb 25, 2024
8f5c51a
[RED] Fix test assets file
carlenee Feb 25, 2024
7444170
fix:tests and view name
carlenee Feb 25, 2024
6192701
fix: file path
carlenee Feb 25, 2024
26dc312
fix: add .gitignore
carlenee Feb 25, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,5 @@ GitHub.sublime-settings
!.vscode/extensions.json
.history
.vercel

profile_pictures/
1 change: 1 addition & 0 deletions assets/invalid_img.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/logo.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions authentication/migrations/0002_profile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 4.2.9 on 2024-02-23 14:31

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('authentication', '0001_initial'),
]

operations = [
migrations.CreateModel(
name='Profile',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('bio', models.TextField(blank=True)),
('profile_picture', models.ImageField(blank=True, upload_to='profile_pictures/')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]
7 changes: 6 additions & 1 deletion authentication/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,9 @@
from django.contrib.auth.models import AbstractUser

class AppUser(AbstractUser):
is_verified_user = models.BooleanField(default=False)
is_verified_user = models.BooleanField(default=False)

class Profile(models.Model):
user = models.OneToOneField(AppUser, on_delete=models.CASCADE)
bio = models.TextField(blank=True)
profile_picture = models.ImageField(upload_to='profile_pictures/', blank=True)
7 changes: 7 additions & 0 deletions authentication/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from rest_framework import serializers
from .models import Profile

class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ['bio', 'profile_picture'] # Add other fields as needed
73 changes: 68 additions & 5 deletions authentication/tests.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from django.test import TestCase
from authentication.models import AppUser
from authentication.models import AppUser, Profile
from django.urls import reverse
import json
from django.core import mail
from rest_framework.test import APIClient
from .tokens import account_token
from django.core.files.uploadedfile import SimpleUploadedFile
import os

REGISTER_LINK = reverse('authentication:register')
LOGIN_LINK = reverse('authentication:login')
Expand Down Expand Up @@ -68,6 +70,7 @@ def test_missing_fields(self):
self.assertEqual(response.status_code, 400)
self.assertEqual(response.json()['msg'],"One or more fields are missing!")


class LoginTest(TestCase):
def setUp(self):
self.client = APIClient()
Expand All @@ -83,7 +86,7 @@ def test_login_failed(self):
response = self.client.post(LOGIN_LINK, json.dumps(data), content_type='application/json')
self.assertEqual(response.status_code, 400)
self.assertEqual(response.json()['msg'],"Wrong username/password!")

def test_missing_fields(self):
data = {
"username":"user1"
Expand Down Expand Up @@ -113,11 +116,12 @@ def test_valid_verification_token(self):
def test_invalid_verification_token(self):
response = self.client.post((EMAIL_VERIFICATION_LINK), {'token': 'invalid token'})
self.assertEqual(response.status_code, 400)

def test_missing_verification_token(self):
response = self.client.post((EMAIL_VERIFICATION_LINK), {})
self.assertEqual(response.status_code, 400)


class SendRecoverPasswordEmailTest(TestCase):
def setUp(self):
self.client = APIClient()
Expand All @@ -132,11 +136,11 @@ def test_sent_email_recover_password(self):
def test_sent_wrong_email_recover_password(self):
response = self.client.post((RECOVER_PASSWORD_LINK), {'email':'[email protected]'})
self.assertEqual(response.status_code, 400)

def test_sent_missing_email_recover_password(self):
response = self.client.post((RECOVER_PASSWORD_LINK), {})
self.assertEqual(response.status_code, 400)

def test_change_password(self):
token = account_token.make_token(self.user)
response = self.client.put((RECOVER_PASSWORD_LINK),
Expand All @@ -159,3 +163,62 @@ def test_change_password_missing_fields(self):
{'email':'[email protected]', 'token': 'wrong token'})
self.assertEqual(response.status_code, 400)

class ProfileUpdateTest(TestCase):
def setUp(self):
self.client = APIClient()
self.user = AppUser.objects.create_user(email='[email protected]', username='testuser', password='test')
self.client.force_authenticate(user=self.user)
self.profile = Profile.objects.create(user=self.user, bio='Old bio')

def test_update_profile_with_valid_data(self):
# Test updating profile with valid data
data = {'bio': 'New bio'}
response = self.client.put(reverse('authentication:profile'), data)
self.assertEqual(response.status_code, 200)
self.assertEqual(Profile.objects.get(user=self.user).bio, 'New bio')

def test_update_profile_with_empty_data(self):
# Test updating profile with empty data
data = {'bio': ''}
response = self.client.put(reverse('authentication:profile'), data)
self.assertEqual(response.status_code, 200)
self.assertEqual(Profile.objects.get(user=self.user).bio, '') # Ensure bio is empty

def test_update_profile_picture(self):
# Test uploading profile picture
# Replace image_path with the path to a valid image file
image_path = 'assets/logo.jpg'
with open(image_path, 'rb') as f:
image = SimpleUploadedFile('image.jpg', f.read(), content_type='image/jpeg')
data = {'profile_picture': image}
response = self.client.put(reverse('authentication:profile'), data)
self.assertEqual(response.status_code, 200)
self.assertTrue(Profile.objects.get(user=self.user).profile_picture) # Ensure profile picture is uploaded

def test_update_profile_with_invalid_data(self):
# Test updating profile with invalid data (uploading an image as bio)
image_path = 'assets/invalid_img.jpg' # Path to an invalid image file
with open(image_path, 'rb') as f:
image = SimpleUploadedFile(os.path.basename(image_path), f.read(), content_type='image/jpeg')
data = {'bio': 'New bio', 'profile_picture': image}
response = self.client.put(reverse('authentication:profile'), data)
self.assertEqual(response.status_code, 400) # Expect a bad request response


def test_update_profile_unauthenticated(self):
# Test updating profile when unauthenticated
self.client.logout()
data = {'bio': 'New bio'}
response = self.client.put(reverse('authentication:profile'), data)
self.assertEqual(response.status_code, 401) # Expect unauthorized response

def test_update_profile_picture_unauthenticated(self):
# Test uploading profile picture when unauthenticated
self.client.logout()
# Replace image_path with the path to a valid image file
image_path = 'assets/logo.jpg'
with open(image_path, 'rb') as f:
image = SimpleUploadedFile('image.jpg', f.read(), content_type='image/jpeg')
data = {'profile_picture': image}
response = self.client.put(reverse('authentication:profile'), data)
self.assertEqual(response.status_code, 401) # Expect unauthorized response
5 changes: 4 additions & 1 deletion authentication/urls.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
from django.urls import path
from authentication.views import RegisterView, LoginView, SendVerificationEmailView, SendRecoverPasswordEmailView
from authentication.views import RegisterView, LoginView, SendVerificationEmailView, SendRecoverPasswordEmailView, ProfileView
from rest_framework_simplejwt.views import TokenObtainPairView
from rest_framework_simplejwt.views import TokenRefreshView

app_name = 'authentication'

urlpatterns = [
path('register/', RegisterView.as_view(), name='register'),
path('login/', LoginView.as_view(), name='login'),
path('token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
path('verify/', SendVerificationEmailView.as_view(), name='verify_email'),
path('recover/', SendRecoverPasswordEmailView.as_view(), name='recover_password'),
path('profile/', ProfileView.as_view(), name='profile'),
]
16 changes: 15 additions & 1 deletion authentication/views.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from authentication.models import AppUser
from authentication.models import AppUser, Profile
from django.contrib.auth import authenticate
from rest_framework.views import APIView
from rest_framework.response import Response
Expand All @@ -10,6 +10,8 @@
from django.template.loader import render_to_string
from .tokens import account_token
from django.core.mail import EmailMessage
from .serializers import ProfileSerializer
from rest_framework import status

class RegisterView(APIView):

Expand Down Expand Up @@ -125,3 +127,15 @@ def put(self, request):
return Response({'msg': 'Invalid verification token!'}, status=400)
else:
return Response({'msg': "User doesn't exist!"}, status=400)

class ProfileView(APIView):
permission_classes = [IsAuthenticated]
def put(self, request):
user = request.user
profile = user.profile
serializer = ProfileSerializer(instance=profile, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response({'msg': 'Profile updated successfully!'})
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Binary file modified requirements.txt
Binary file not shown.
Loading