Skip to content

Commit

Permalink
ft(favourite-articles): Add functionality to enable user
Browse files Browse the repository at this point in the history
- 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
Show file tree
Hide file tree
Showing 10 changed files with 549 additions and 0 deletions.
Empty file.
6 changes: 6 additions & 0 deletions authors/apps/favourite/admin.py
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)
5 changes: 5 additions & 0 deletions authors/apps/favourite/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from django.apps import AppConfig


class FavouriteConfig(AppConfig):
name = 'favourite'
28 changes: 28 additions & 0 deletions authors/apps/favourite/models.py
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')
20 changes: 20 additions & 0 deletions authors/apps/favourite/serializers.py
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__')
287 changes: 287 additions & 0 deletions authors/apps/favourite/tests/test_favourite.py
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)
19 changes: 19 additions & 0 deletions authors/apps/favourite/urls.py
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'),
]
Loading

0 comments on commit c504ea6

Please sign in to comment.