-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ft(favourite-articles): Add functionality to enable user
- Favourite and unfavourite an article - A user can get all favourites - A user can get an article's favourites [Delivers #161967019]
- Loading branch information
EmmanuelChayu
committed
Dec 18, 2018
1 parent
f1a2664
commit c504ea6
Showing
10 changed files
with
549 additions
and
0 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from django.contrib import admin | ||
|
||
from authors.apps.favourite.models import Favourite | ||
# Register your models here. | ||
|
||
admin.site.register(Favourite) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from django.apps import AppConfig | ||
|
||
|
||
class FavouriteConfig(AppConfig): | ||
name = 'favourite' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
""" | ||
Models for Favourite app | ||
""" | ||
from django.db import models | ||
|
||
# local imports | ||
from authors.apps.articles.models import Articles | ||
from authors.apps.profiles.models import Profile | ||
|
||
|
||
# Create your models here. | ||
class Favourite(models.Model): | ||
""" | ||
Define the model for Favourite | ||
""" | ||
# Favourite can only be either 1 or -1 | ||
FAVOURITE_CHOICES = ( | ||
(1, 'FAVOURITE'), | ||
(-1, 'UNFAVOURITE'), | ||
) | ||
user = models.ForeignKey(Profile, on_delete=models.CASCADE, default=0) | ||
article = models.ForeignKey( | ||
Articles, on_delete=models.CASCADE, default=0) | ||
favourite = models.IntegerField(choices=FAVOURITE_CHOICES) | ||
set_on = models.DateTimeField(auto_now_add=True) | ||
|
||
class Meta: | ||
unique_together = ('user', 'article', 'favourite') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
""" | ||
Serializes `Favourite` model | ||
""" | ||
from rest_framework import serializers | ||
|
||
# local imports | ||
from authors.apps.favourite.models import Favourite | ||
|
||
|
||
class FavouriteSerializer(serializers.ModelSerializer): | ||
""" | ||
Favourite serializer | ||
""" | ||
class Meta: | ||
""" | ||
Class Meta | ||
""" | ||
model = Favourite | ||
|
||
fields = ('__all__') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,287 @@ | ||
""" | ||
Module contains the unittests for the `favourite` app | ||
""" | ||
import json | ||
from rest_framework import status | ||
from rest_framework.test import APITestCase | ||
from rest_framework.reverse import reverse | ||
|
||
# local imports | ||
from authors.apps.authentication.models import User | ||
|
||
|
||
class TestFavouriteModel(APITestCase): | ||
""" | ||
UNITTESTS for Favourite Model | ||
""" | ||
|
||
def setUp(self): | ||
""" | ||
Set up | ||
""" | ||
# Generate a test client for sending API requests | ||
# Define the endpoints for register, login | ||
self.register_endpoint = reverse('register') | ||
self.post_article_endpoint = reverse('articles') | ||
self.favourites_endpoint = reverse('favourites') | ||
|
||
# A sample user to use in the test | ||
|
||
# POSTS articles | ||
self.user = { | ||
"user": { | ||
"username": "EmmanuelChayu", | ||
"email": "emmanuelchayu@andela.com", | ||
"password": "#1Emmcodes" | ||
} | ||
} | ||
# reacts | ||
self.another_user = { | ||
"user": { | ||
"username": "EmmanuelBeja", | ||
"email": "beja.emmanuel@gmail.com", | ||
"password": "#1Emmcodes" | ||
} | ||
} | ||
|
||
# reacts | ||
self.third_user = { | ||
"user": { | ||
"username": "Emmbeja", | ||
"email": "emmcodes@gmail.com", | ||
"password": "#1Emmcodes" | ||
} | ||
} | ||
|
||
# A sample article to use in the tests | ||
self.article = { | ||
"title": "Django Unchained", | ||
"description": "Django without chains", | ||
"body": "The chains were removed from the Django", | ||
"author": self.user['user']['username'], | ||
"tagList": "tag, list" | ||
} | ||
self.article_too = { | ||
"title": "War is not It", | ||
"description": "Civil War and Stuff", | ||
"body": "The civil war happened and yes", | ||
"author": self.another_user['user']['username'], | ||
"tagList": "civil, war" | ||
} | ||
|
||
# Sample favourite input data to use in the tests | ||
self.favourite = { | ||
"slug": "django-unchained", | ||
"favourite": 1 | ||
} | ||
|
||
self.favourite_too = { | ||
"slug": "war-is-not-it", | ||
"favourite": -1 | ||
} | ||
|
||
self.fake_favourite = { | ||
"slug": "war-is-not-it", | ||
"favourite": 4 | ||
} | ||
|
||
def register_user_helper(self, user_data): | ||
""" | ||
Helper method for registering a user and returning a user | ||
""" | ||
# Register a user to generate a token | ||
register_response = self.client.post( | ||
self.register_endpoint, user_data, format='json') | ||
|
||
# Activate user account manually | ||
user = User.objects.get(username=user_data['user']['username']) | ||
user.is_active = True | ||
user.save() | ||
user = User.objects.get(username=user_data['user']['username']) | ||
|
||
# Decode response and extract user | ||
user = json.loads( | ||
register_response.content.decode('utf-8'))['user'] | ||
return user | ||
|
||
def post_an_article_helper(self, article, user): | ||
""" | ||
Helper method for posting an article | ||
""" | ||
user = self.register_user_helper(user) | ||
user_token = user['token'] | ||
|
||
# Send a POST request to create an article with token | ||
# Authorize | ||
self.client.credentials(HTTP_AUTHORIZATION='Token ' + user_token) | ||
|
||
# Post an article | ||
post_article_response = self.client.post( | ||
self.post_article_endpoint, article, format='json' | ||
) | ||
return post_article_response | ||
|
||
def test_user_can_favourite_an_article(self): | ||
""" | ||
Test a logged in user can like an existing article | ||
""" | ||
# Create an article | ||
self.post_an_article_helper(self.article, self.user) | ||
|
||
# register a user and send a favourite to the article | ||
user = self.register_user_helper(self.another_user) | ||
user_token = user['token'] | ||
|
||
# Send request to like article with auth token | ||
self.client.credentials(HTTP_AUTHORIZATION='Token ' + user_token) | ||
response = self.client.post( | ||
self.favourites_endpoint, self.favourite, format='json' | ||
) | ||
|
||
# extract contents of response | ||
response_data = json.loads( | ||
response.content.decode('utf-8')) | ||
|
||
# Assertions | ||
# Check the status code of the response | ||
self.assertEqual(response.status_code, status.HTTP_201_CREATED) | ||
|
||
# Check the contents of the response | ||
self.assertTrue(response_data["favourite"]) | ||
self.assertEqual( | ||
response_data["favourite"]["favourite"], | ||
self.favourite["favourite"]) | ||
|
||
def test_user_can_unlike_a_liked_article(self): | ||
""" | ||
Test a logged in user can like an existing article | ||
""" | ||
# Create an article | ||
self.post_an_article_helper(self.article, self.user) | ||
|
||
# register a user and send a favourite to the article | ||
user = self.register_user_helper(self.another_user) | ||
user_token = user['token'] | ||
|
||
# Send request to like article with auth token | ||
self.client.credentials(HTTP_AUTHORIZATION='Token ' + user_token) | ||
# LIKE to an article | ||
self.client.post( | ||
self.favourites_endpoint, self.favourite, format='json' | ||
) | ||
# Send similar request to UNLIKE | ||
response = self.client.post( | ||
self.favourites_endpoint, self.favourite, format='json' | ||
) | ||
|
||
# extract contents of response | ||
response_data = json.loads( | ||
response.content.decode('utf-8')) | ||
|
||
# Assertions | ||
# Check the status code of the response | ||
self.assertEqual(response.status_code, status.HTTP_200_OK) | ||
|
||
# Check the contents of the response | ||
self.assertEqual( | ||
response_data['message'], | ||
"You no longer `FAVOURITE` this article") | ||
|
||
def test_user_can_not_react_to_non_existent_article(self): | ||
""" | ||
Test that an error is reported when request is sent to react | ||
to a non-existent article | ||
""" | ||
|
||
# register a user and send a favourite to the article | ||
user = self.register_user_helper(self.another_user) | ||
user_token = user['token'] | ||
|
||
# Send request to like article with auth token | ||
self.client.credentials(HTTP_AUTHORIZATION='Token ' + user_token) | ||
response = self.client.post( | ||
self.favourites_endpoint, self.favourite, format='json' | ||
) | ||
|
||
# extract contents of response | ||
response_data = json.loads( | ||
response.content.decode('utf-8')) | ||
|
||
# Assertions | ||
# Check the status code of the response | ||
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) | ||
|
||
# Check the contents of the response | ||
self.assertEqual( | ||
response_data["detail"], | ||
"Article with slug `{}` does not exist".format( | ||
self.favourite["slug"])) | ||
|
||
def test_user_view_all_favourites_on_all_articles(self): | ||
""" | ||
Test a logged in user can view all | ||
favourites on all existing articles | ||
""" | ||
# Create an article | ||
self.post_an_article_helper(self.article, self.user) | ||
# Create another article | ||
self.post_an_article_helper(self.article_too, self.another_user) | ||
|
||
user = self.register_user_helper(self.third_user) | ||
user_token = user['token'] | ||
|
||
# Send request to like article with auth token | ||
self.client.credentials(HTTP_AUTHORIZATION='Token ' + user_token) | ||
self.client.post( | ||
self.favourites_endpoint, self.favourite, format='json' | ||
) | ||
# Dislike another article | ||
self.client.post( | ||
self.favourites_endpoint, self.favourite_too, format='json' | ||
) | ||
# import pdb; pdb.set_trace() | ||
# Retrieve all FAVOURITES | ||
response = self.client.get( | ||
self.favourites_endpoint, format='json') | ||
|
||
# extract contents of response | ||
response_data = json.loads( | ||
response.content.decode('utf-8')) | ||
|
||
# Assertions | ||
# Check the status code of the response | ||
self.assertEqual(response.status_code, status.HTTP_200_OK) | ||
|
||
# Check the contents of the response | ||
self.assertTrue(response_data["favourites"]) | ||
self.assertEqual(len(response_data["favourites"]), 2) | ||
self.assertTrue( | ||
response_data["favourites"][0]["favourite"], | ||
self.favourite["favourite"]) | ||
self.assertTrue( | ||
response_data["favourites"][1]["favourite"], | ||
self.favourite_too["favourite"]) | ||
|
||
def test_unlogged_in_user_cannot_react_to_article(self): | ||
""" | ||
Test that an unlogged in user cannot view the profile | ||
""" | ||
# Send a GET request to view profile | ||
response = self.client.post( | ||
self.favourites_endpoint, self.favourite, format='json' | ||
) | ||
|
||
# extract contents of response | ||
response_data = json.loads( | ||
response.content.decode('utf-8')) | ||
|
||
response_message = response_data['detail'] | ||
|
||
# Assertions | ||
# assert that the response message is as below | ||
self.assertEqual( | ||
response_message, "Authentication credentials were not provided.") | ||
|
||
# Check that the reponse status code is 401 | ||
self.assertEqual( | ||
response.status_code, status.HTTP_401_UNAUTHORIZED) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
""" | ||
Define the urls where the views for `profiles` are accessible | ||
""" | ||
from django.urls import path | ||
|
||
# local imports | ||
from authors.apps.favourite.views import ( | ||
FavouriteView, ArticleFavouritesView | ||
) | ||
|
||
|
||
urlpatterns = [ | ||
path( | ||
'all/', FavouriteView.as_view(), | ||
name='favourites'), | ||
path( | ||
'single/<str:slug>', ArticleFavouritesView.as_view(), | ||
name='article_favourites'), | ||
] |
Oops, something went wrong.