diff --git a/v1/accounts/tests/__init__.py b/v1/accounts/tests/__init__.py new file mode 100644 index 0000000..6f804bc --- /dev/null +++ b/v1/accounts/tests/__init__.py @@ -0,0 +1,2 @@ +#!/usr/bin/env python +# encoding: utf-8 diff --git a/v1/accounts/tests.py b/v1/accounts/tests/test_account.py similarity index 69% rename from v1/accounts/tests.py rename to v1/accounts/tests/test_account.py index 6beaa57..b82e556 100644 --- a/v1/accounts/tests.py +++ b/v1/accounts/tests/test_account.py @@ -4,13 +4,15 @@ from django.test import TestCase from rest_framework_jwt.settings import api_settings -jwt_decode_handler = api_settings.JWT_DECODE_HANDLER - class AccountTests(TestCase): - fixtures=['test/users.json'] + fixtures = ['test/users.json'] + + def setUp(self): + self.jwt_decode_handler = api_settings.JWT_DECODE_HANDLER def test_obtain_authtoken_success(self): + """ Try and login and confirm that the login was successful """ resp = self.client.post( '/api/v1/accounts/obtain-auth-token/', { @@ -21,11 +23,12 @@ def test_obtain_authtoken_success(self): self.assertEqual(resp.status_code, 200) self.assertTrue(resp.json()['id'] == 1) - decoded_token = jwt_decode_handler(resp.json()['token']) + decoded_token = self.jwt_decode_handler(resp.json()['token']) self.assertTrue(decoded_token.get('user_id') == 1) self.assertTrue(decoded_token.get('username') == 'testuser1') def test_obtain_authtoken_wrong_password(self): + """ Try and login and confirm that the login was unsuccessful """ resp = self.client.post( '/api/v1/accounts/obtain-auth-token/', { @@ -34,4 +37,4 @@ def test_obtain_authtoken_wrong_password(self): } ) - self.assertEqual(resp.status_code, 400) \ No newline at end of file + self.assertEqual(resp.status_code, 400) diff --git a/v1/common/tests/__init__.py b/v1/common/tests/__init__.py new file mode 100644 index 0000000..6f804bc --- /dev/null +++ b/v1/common/tests/__init__.py @@ -0,0 +1,2 @@ +#!/usr/bin/env python +# encoding: utf-8 diff --git a/v1/common/tests/test_permission.py b/v1/common/tests/test_permission.py new file mode 100644 index 0000000..9cbf3a7 --- /dev/null +++ b/v1/common/tests/test_permission.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +# encoding: utf-8 + +from django.contrib.auth.models import AnonymousUser, User +from django.test import TestCase, RequestFactory +from v1.common.permissions import IsAdminOrReadOnly, IsOwnerOrReadOnly + + +class PermissionTest(TestCase): + def setUp(self): + # Every test needs access to the request factory. + self.factory = RequestFactory() + # Create a staff user. + self.user = User.objects.create_user( + username='jacob', email='jacob@gmail.com', password='top_secret', is_staff=True + ) + + def test_is_owner_or_read_only(self): + # Try and access something as an admin user. + # Both get and post should have access. + request = self.factory.get('/admin') + request.user = self.user + self.assertTrue( + IsOwnerOrReadOnly().has_permission(request, None) + ) + self.assertTrue( + IsOwnerOrReadOnly().has_object_permission(request, None, None) + ) + request = self.factory.post('/admin') + request.user = self.user + self.assertTrue( + IsOwnerOrReadOnly().has_permission(request, None) + ) + + # Try and access something as an anonymous user. + # Both get should have access but post shouldn't. + request = self.factory.get('/admin') + request.user = AnonymousUser() + self.assertTrue( + IsOwnerOrReadOnly().has_permission(request, None) + ) + self.assertTrue( + IsOwnerOrReadOnly().has_object_permission(request, None, None) + ) + request = self.factory.post('/admin') + request.user = AnonymousUser() + self.assertFalse( + IsOwnerOrReadOnly().has_permission(request, None) + ) + + def test_is_admin_or_read_only(self): + # Try and access something as an admin user. + # Both get and post should have access. + request = self.factory.get('/admin') + request.user = self.user + self.assertTrue( + IsAdminOrReadOnly().has_permission(request, None) + ) + request = self.factory.post('/admin') + request.user = self.user + self.assertTrue( + IsAdminOrReadOnly().has_permission(request, None) + ) + + # Try and access something as an anonymous user. + # Both get should have access but post shouldn't. + request = self.factory.get('/admin') + request.user = AnonymousUser() + self.assertTrue( + IsAdminOrReadOnly().has_permission(request, None) + ) + request = self.factory.post('/admin') + request.user = AnonymousUser() + self.assertFalse( + IsAdminOrReadOnly().has_permission(request, None) + ) diff --git a/v1/common/tests/test_search.py b/v1/common/tests/test_search.py new file mode 100644 index 0000000..39a316a --- /dev/null +++ b/v1/common/tests/test_search.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python +# encoding: utf-8 + +from django.test import TestCase +from v1.recipe.models import Recipe +from v1.common.recipe_search import get_search_results + + +class GetSearchResultsTests(TestCase): + fixtures = [ + 'test/users.json', + 'course_data.json', + 'cuisine_data.json', + 'recipe_data.json', + 'ing_data.json' + ] + + def test_get_search_results(self): + """ Run a search that will return data """ + query = get_search_results( + ['title', 'ingredient_groups__ingredients__title', 'tags__title'], + Recipe.objects, + 'chili' + ).distinct() + + self.assertTrue(len(query.all()) > 0) + + def test_get_search_no_results(self): + """ Run a search that will return no data """ + query = get_search_results( + ['title', 'ingredient_groups__ingredients__title', 'tags__title'], + Recipe.objects, + 'blue berry' + ).distinct() + + self.assertTrue(len(query.all()) == 0) diff --git a/v1/fixtures/news_data.json b/v1/fixtures/news_data.json index 34ad47c..8aed17b 100644 --- a/v1/fixtures/news_data.json +++ b/v1/fixtures/news_data.json @@ -5,7 +5,7 @@ "fields": { "content": "OpenEats2 is an open source recipe management site. You can share recipes with friends, rate recipes, store your favorite recipes to find easily, and more. If you want to run your own personal OpenEats site visit the Github page", "image": "", - "pub_date": "2010-10-10 13:32:47", + "pub_date": "2010-10-10 13:32:47+00:00", "slug": "default-news-entry", "title": "OpenEats 2", "frontpage": 1 diff --git a/v1/fixtures/recipe_data.json b/v1/fixtures/recipe_data.json index 7c3b6d4..be5ee22 100644 --- a/v1/fixtures/recipe_data.json +++ b/v1/fixtures/recipe_data.json @@ -11,8 +11,8 @@ "course": 2, "servings": 8, "rating": 3, - "update_date": "2011-05-21 07:35:32", - "pub_date": "2011-05-21 07:35:32", + "update_date": "2011-05-21 07:35:32+03:00", + "pub_date": "2011-05-21 07:35:32+03:00", "slug": "tasty-chili", "prep_time": 60, "cook_time": 60 @@ -29,8 +29,8 @@ "course": 2, "servings": 8, "rating": 3, - "update_date": "2011-05-21 07:35:32", - "pub_date": "2011-05-21 07:35:32", + "update_date": "2011-05-21 07:35:32+03:00", + "pub_date": "2011-05-21 07:35:32+03:00", "slug": "tasty-chili-2", "prep_time": 60, "cook_time": 60 @@ -47,8 +47,8 @@ "course": 2, "servings": 8, "rating": 3, - "update_date": "2011-05-21 07:35:32", - "pub_date": "2011-05-21 07:35:32", + "update_date": "2011-05-21 07:35:32+03:00", + "pub_date": "2011-05-21 07:35:32+03:00", "slug": "tasty-chili-3", "prep_time": 60, "cook_time": 60 @@ -65,8 +65,8 @@ "course": 2, "servings": 8, "rating": 3, - "update_date": "2011-05-21 07:35:32", - "pub_date": "2011-05-21 07:35:32", + "update_date": "2011-05-21 07:35:32+03:00", + "pub_date": "2011-05-21 07:35:32+03:00", "slug": "tasghjghjty-chili-4", "prep_time": 60, "cook_time": 60 @@ -83,8 +83,8 @@ "course": 2, "servings": 8, "rating": 3, - "update_date": "2011-05-21 07:35:32", - "pub_date": "2011-05-21 07:35:32", + "update_date": "2011-05-21 07:35:32+03:00", + "pub_date": "2011-05-21 07:35:32+03:00", "slug": "taghjksty-chili-4", "prep_time": 60, "cook_time": 60 @@ -101,8 +101,8 @@ "course": 2, "servings": 8, "rating": 3, - "update_date": "2011-05-21 07:35:32", - "pub_date": "2011-05-21 07:35:32", + "update_date": "2011-05-21 07:35:32+03:00", + "pub_date": "2011-05-21 07:35:32+03:00", "slug": "tastsdfsdfy-chili-4", "prep_time": 60, "cook_time": 60 @@ -119,8 +119,8 @@ "course": 2, "servings": 8, "rating": 3, - "update_date": "2011-05-21 07:35:32", - "pub_date": "2011-05-21 07:35:32", + "update_date": "2011-05-21 07:35:32+03:00", + "pub_date": "2011-05-21 07:35:32+03:00", "slug": "tastzsdfsery-chili-4", "prep_time": 60, "cook_time": 60 @@ -137,8 +137,8 @@ "course": 2, "servings": 8, "rating": 3, - "update_date": "2011-05-21 07:35:32", - "pub_date": "2011-05-21 07:35:32", + "update_date": "2011-05-21 07:35:32+03:00", + "pub_date": "2011-05-21 07:35:32+03:00", "slug": "tasty-asdfchili-4", "prep_time": 60, "cook_time": 60 @@ -155,8 +155,8 @@ "course": 2, "servings": 8, "rating": 3, - "update_date": "2011-05-21 07:35:32", - "pub_date": "2011-05-21 07:35:32", + "update_date": "2011-05-21 07:35:32+03:00", + "pub_date": "2011-05-21 07:35:32+03:00", "slug": "tasty-werwerchili-4", "prep_time": 60, "cook_time": 60 @@ -173,8 +173,8 @@ "course": 2, "servings": 8, "rating": 3, - "update_date": "2011-05-21 07:35:32", - "pub_date": "2011-05-21 07:35:32", + "update_date": "2011-05-21 07:35:32+03:00", + "pub_date": "2011-05-21 07:35:32+03:00", "slug": "tastysdfsdf-chili-4", "prep_time": 60, "cook_time": 60 @@ -191,8 +191,8 @@ "course": 2, "servings": 8, "rating": 3, - "update_date": "2011-05-21 07:35:32", - "pub_date": "2011-05-21 07:35:32", + "update_date": "2011-05-21 07:35:32+03:00", + "pub_date": "2011-05-21 07:35:32+03:00", "slug": "tasty-xcvchili-4", "prep_time": 60, "cook_time": 60 @@ -209,8 +209,8 @@ "course": 2, "servings": 8, "rating": 3, - "update_date": "2011-05-21 07:35:32", - "pub_date": "2011-05-21 07:35:32", + "update_date": "2011-05-21 07:35:32+03:00", + "pub_date": "2011-05-21 07:35:32+03:00", "slug": "tastysdf-chili-4", "prep_time": 60, "cook_time": 60 @@ -227,8 +227,8 @@ "course": 2, "servings": 8, "rating": 3, - "update_date": "2011-05-21 07:35:32", - "pub_date": "2011-05-21 07:35:32", + "update_date": "2011-05-21 07:35:32+03:00", + "pub_date": "2011-05-21 07:35:32+03:00", "slug": "tasty-werchili-4", "prep_time": 60, "cook_time": 60 @@ -245,8 +245,8 @@ "course": 2, "servings": 8, "rating": 3, - "update_date": "2011-05-21 07:35:32", - "pub_date": "2011-05-21 07:35:32", + "update_date": "2011-05-21 07:35:32+03:00", + "pub_date": "2011-05-21 07:35:32+03:00", "slug": "tasty-chertili-4", "prep_time": 60, "cook_time": 60 @@ -263,8 +263,8 @@ "course": 2, "servings": 8, "rating": 3, - "update_date": "2011-05-21 07:35:32", - "pub_date": "2011-05-21 07:35:32", + "update_date": "2011-05-21 07:35:32+03:00", + "pub_date": "2011-05-21 07:35:32+03:00", "slug": "tasty-chicxvbxcvbli-4", "prep_time": 60, "cook_time": 60 @@ -281,8 +281,8 @@ "course": 2, "servings": 8, "rating": 3, - "update_date": "2011-05-21 07:35:32", - "pub_date": "2011-05-21 07:35:32", + "update_date": "2011-05-21 07:35:32+03:00", + "pub_date": "2011-05-21 07:35:32+03:00", "slug": "tasdasdasty-chili-4", "prep_time": 60, "cook_time": 60 @@ -299,8 +299,8 @@ "course": 2, "servings": 8, "rating": 3, - "update_date": "2011-05-21 07:35:32", - "pub_date": "2011-05-21 07:35:32", + "update_date": "2011-05-21 07:35:32+03:00", + "pub_date": "2011-05-21 07:35:32+03:00", "slug": "werwertasty-chili-4", "prep_time": 60, "cook_time": 60 @@ -317,8 +317,8 @@ "course": 2, "servings": 8, "rating": 3, - "update_date": "2011-05-21 07:35:32", - "pub_date": "2011-05-21 07:35:32", + "update_date": "2011-05-21 07:35:32+03:00", + "pub_date": "2011-05-21 07:35:32+03:00", "slug": "twerasty-chili-4", "prep_time": 60, "cook_time": 60 @@ -335,8 +335,8 @@ "course": 2, "servings": 8, "rating": 3, - "update_date": "2011-05-21 07:35:32", - "pub_date": "2011-05-21 07:35:32", + "update_date": "2011-05-21 07:35:32+03:00", + "pub_date": "2011-05-21 07:35:32+03:00", "slug": "tasty-ertchili-4", "prep_time": 60, "cook_time": 60 @@ -353,8 +353,8 @@ "course": 2, "servings": 8, "rating": 3, - "update_date": "2011-05-21 07:35:32", - "pub_date": "2011-05-21 07:35:32", + "update_date": "2011-05-21 07:35:32+03:00", + "pub_date": "2011-05-21 07:35:32+03:00", "slug": "taergtsty-chili-4", "prep_time": 60, "cook_time": 60 @@ -371,8 +371,8 @@ "course": 2, "servings": 8, "rating": 3, - "update_date": "2011-05-21 07:35:32", - "pub_date": "2011-05-21 07:35:32", + "update_date": "2011-05-21 07:35:32+03:00", + "pub_date": "2011-05-21 07:35:32+03:00", "slug": "tasty-chxcvili-4", "prep_time": 60, "cook_time": 60 @@ -389,8 +389,8 @@ "course": 2, "servings": 8, "rating": 3, - "update_date": "2011-05-21 07:35:32", - "pub_date": "2011-05-21 07:35:32", + "update_date": "2011-05-21 07:35:32+03:00", + "pub_date": "2011-05-21 07:35:32+03:00", "slug": "tasty-chighli-4", "prep_time": 60, "cook_time": 60 @@ -407,8 +407,8 @@ "course": 2, "servings": 8, "rating": 3, - "update_date": "2011-05-21 07:35:32", - "pub_date": "2011-05-21 07:35:32", + "update_date": "2011-05-21 07:35:32+03:00", + "pub_date": "2011-05-21 07:35:32+03:00", "slug": "tsdfasty-chili-4", "prep_time": 60, "cook_time": 60 @@ -425,8 +425,8 @@ "course": 2, "servings": 8, "rating": 3, - "update_date": "2011-05-21 07:35:32", - "pub_date": "2011-05-21 07:35:32", + "update_date": "2011-05-21 07:35:32+03:00", + "pub_date": "2011-05-21 07:35:32+03:00", "slug": "taertsty-chili-4", "prep_time": 60, "cook_time": 60 @@ -443,8 +443,8 @@ "course": 2, "servings": 8, "rating": 3, - "update_date": "2011-05-21 07:35:32", - "pub_date": "2011-05-21 07:35:32", + "update_date": "2011-05-21 07:35:32+03:00", + "pub_date": "2011-05-21 07:35:32+03:00", "slug": "tastbxcvy-chili-4", "prep_time": 60, "cook_time": 60 @@ -461,8 +461,8 @@ "course": 2, "servings": 8, "rating": 3, - "update_date": "2011-05-21 07:35:32", - "pub_date": "2011-05-21 07:35:32", + "update_date": "2011-05-21 07:35:32+03:00", + "pub_date": "2011-05-21 07:35:32+03:00", "slug": "tasty-sdfgchili-4", "prep_time": 60, "cook_time": 60 @@ -479,8 +479,8 @@ "course": 2, "servings": 8, "rating": 3, - "update_date": "2011-05-21 07:35:32", - "pub_date": "2011-05-21 07:35:32", + "update_date": "2011-05-21 07:35:32+03:00", + "pub_date": "2011-05-21 07:35:32+03:00", "slug": "tasty-6chili-4", "prep_time": 60, "cook_time": 60 @@ -497,8 +497,8 @@ "course": 2, "servings": 8, "rating": 3, - "update_date": "2011-05-21 07:35:32", - "pub_date": "2011-05-21 07:35:32", + "update_date": "2011-05-21 07:35:32+03:00", + "pub_date": "2011-05-21 07:35:32+03:00", "slug": "tasty-5chili-4", "prep_time": 60, "cook_time": 60 @@ -515,8 +515,8 @@ "course": 2, "servings": 8, "rating": 3, - "update_date": "2011-05-21 07:35:32", - "pub_date": "2011-05-21 07:35:32", + "update_date": "2011-05-21 07:35:32+03:00", + "pub_date": "2011-05-21 07:35:32+03:00", "slug": "tasty-chi234li-4", "prep_time": 60, "cook_time": 60 @@ -533,8 +533,8 @@ "course": 2, "servings": 8, "rating": 3, - "update_date": "2011-05-21 07:35:32", - "pub_date": "2011-05-21 07:35:32", + "update_date": "2011-05-21 07:35:32+03:00", + "pub_date": "2011-05-21 07:35:32+03:00", "slug": "tasty-chili-4234", "prep_time": 60, "cook_time": 60 @@ -551,8 +551,8 @@ "course": 2, "servings": 8, "rating": 3, - "update_date": "2011-05-21 07:35:32", - "pub_date": "2011-05-21 07:35:32", + "update_date": "2011-05-21 07:35:32+03:00", + "pub_date": "2011-05-21 07:35:32+03:00", "slug": "tasty-chili-4123", "prep_time": 60, "cook_time": 60 diff --git a/v1/fixtures/test/course.json b/v1/fixtures/test/course.json deleted file mode 100644 index e8dac61..0000000 --- a/v1/fixtures/test/course.json +++ /dev/null @@ -1,35 +0,0 @@ -[ - { - "pk": 1, - "model": "recipe_groups.course", - "fields": { - "title": "Course 1", - "slug": "course-1", - "author": "1" - } - },{ - "pk": 2, - "model": "recipe_groups.course", - "fields": { - "title": "Course 2", - "slug": "course-2", - "author": "1" - } - },{ - "pk": 3, - "model": "recipe_groups.course", - "fields": { - "title": "Course 3", - "slug": "course-3", - "author": "1" - } - },{ - "pk": 4, - "model": "recipe_groups.course", - "fields": { - "title": "Course 4", - "slug": "course-4", - "author": "1" - } - } -] \ No newline at end of file diff --git a/v1/fixtures/test/cuisine.json b/v1/fixtures/test/cuisine.json deleted file mode 100644 index fb09d93..0000000 --- a/v1/fixtures/test/cuisine.json +++ /dev/null @@ -1,35 +0,0 @@ -[ - { - "pk": 1, - "model": "recipe_groups.cuisine", - "fields": { - "title": "Cuisine 1", - "slug": "cuisine-1", - "author": "1" - } - },{ - "pk": 2, - "model": "recipe_groups.cuisine", - "fields": { - "title": "Cuisine 2", - "slug": "cuisine-2", - "author": "1" - } - },{ - "pk": 3, - "model": "recipe_groups.cuisine", - "fields": { - "title": "Cuisine 3", - "slug": "cuisine-3", - "author": "1" - } - },{ - "pk": 4, - "model": "recipe_groups.cuisine", - "fields": { - "title": "Cuisine 4", - "slug": "cuisine-4", - "author": "1" - } - } -] \ No newline at end of file diff --git a/v1/fixtures/test/food.jpg b/v1/fixtures/test/food.jpg new file mode 100644 index 0000000..1cda9a5 Binary files /dev/null and b/v1/fixtures/test/food.jpg differ diff --git a/v1/fixtures/test/recipes.json b/v1/fixtures/test/recipes.json deleted file mode 100644 index 82f9c7d..0000000 --- a/v1/fixtures/test/recipes.json +++ /dev/null @@ -1,110 +0,0 @@ -[ - { - "pk": 1, - "model": "recipe.recipe", - "fields": { - "title": "Recipe 1", - "slug": "recipe-1", - "author": "1", - "cuisine": "1", - "course": "1", - "info": "Recipe info", - "prep_time": "1", - "cook_time": "1", - "servings": "1", - "rating": 0, - "pub_date": "2017-01-01 00:00:00+00:00", - "update_date": "2017-01-01 00:00:00+00:00" - } - }, - { - "pk": 2, - "model": "recipe.recipe", - "fields": { - "title": "Recipe 2", - "slug": "recipe-2", - "author": "1", - "cuisine": "1", - "course": "2", - "info": "Recipe info", - "prep_time": "1", - "cook_time": "1", - "servings": "1", - "rating": 0, - "pub_date": "2017-01-01 00:00:00+00:00", - "update_date": "2017-01-01 00:00:00+00:00" - } - }, - { - "pk": 3, - "model": "recipe.recipe", - "fields": { - "title": "Recipe 3", - "slug": "recipe-3", - "author": "1", - "cuisine": "2", - "course": "1", - "info": "Recipe info", - "prep_time": "1", - "cook_time": "1", - "servings": "1", - "rating": 1, - "pub_date": "2017-01-01 00:00:00+00:00", - "update_date": "2017-01-01 00:00:00+00:00" - } - }, - { - "pk": 4, - "model": "recipe.recipe", - "fields": { - "title": "Recipe 4", - "slug": "recipe-4", - "author": "1", - "cuisine": "1", - "course": "3", - "info": "Recipe info", - "prep_time": "1", - "cook_time": "1", - "servings": "1", - "rating": 1, - "pub_date": "2017-01-01 00:00:00+00:00", - "update_date": "2017-01-01 00:00:00+00:00" - } - }, - { - "pk": 5, - "model": "recipe.recipe", - "fields": { - "title": "Recipe 5", - "slug": "recipe-5", - "author": "1", - "cuisine": "2", - "course": "2", - "info": "Recipe info", - "prep_time": "1", - "cook_time": "1", - "servings": "1", - "rating": 2, - "pub_date": "2017-01-01 00:00:00+00:00", - "update_date": "2017-01-01 00:00:00+00:00" - } - }, - { - "pk": 6, - "model": "recipe.recipe", - "fields": { - "title": "Recipe 6", - "slug": "recipe-6", - "author": "1", - "cuisine": "3", - "course": "1", - "info": "Recipe info", - "prep_time": "1", - "cook_time": "1", - "servings": "1", - "rating": 2, - "pub_date": "2017-01-01 00:00:00+00:00", - "update_date": "2017-01-01 00:00:00+00:00" - } - } -] \ No newline at end of file diff --git a/v1/list/tests/test_list_item.py b/v1/list/tests/test_list_item.py index 82279a6..af875bc 100644 --- a/v1/list/tests/test_list_item.py +++ b/v1/list/tests/test_list_item.py @@ -1,10 +1,12 @@ #!/usr/bin/env python # encoding: utf-8 +import json from django.test import TestCase from django.contrib.auth.models import User from rest_framework.test import APIRequestFactory from v1.list import views +from v1.list.models import GroceryItem class ListTests(TestCase): @@ -21,3 +23,23 @@ def test_list_item_count(self): response = view(request) self.assertEqual(response.data.get('count'), 7) + + def test_list_bulk_delete(self): + """Test to make sure the count is right for items after we delete""" + view = views.BulkGroceryItemViewSet.as_view() + request = self.factory.delete( + '/api/v1/list/bulk_item', + data=json.dumps([19, 20, 21]), + content_type='application/json' + ) + request.user = User.objects.get(pk=1) + response = view(request) + self.assertEqual(response.status_code, 204) + + self.assertEqual(len(GroceryItem.objects.filter(list_id=8)), 4) + + view = views.GroceryItemViewSet.as_view({'get': 'list'}) + request = self.factory.get('/api/v1/list/items/?list=8') + request.user = User.objects.get(pk=1) + response = view(request) + self.assertEqual(response.data.get('count'), 4) diff --git a/v1/list/tests/test_permission.py b/v1/list/tests/test_permission.py new file mode 100644 index 0000000..38c0004 --- /dev/null +++ b/v1/list/tests/test_permission.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python +# encoding: utf-8 + +from django.contrib.auth.models import AnonymousUser, User +from django.test import TestCase, RequestFactory +from v1.list.permissions import IsListOwner, IsItemOwner +from v1.list.models import GroceryList, GroceryItem + + +class PermissionTest(TestCase): + def setUp(self): + # Every test needs access to the request factory. + self.factory = RequestFactory() + # Create a staff user. + self.staff = User.objects.create_user( + username='staff', email='staff@gmail.com', password='top_secret', is_superuser=True + ) + self.user = User.objects.create_user( + username='jacob', email='jacob@gmail.com', password='top_secret' + ) + self.list = GroceryList.objects.create(title='food', author=self.user) + self.item = GroceryItem.objects.create(title='bacon', list=self.list) + + def test_is_list_owner_or_read_only(self): + # Try and access something as an admin user. + # Both get and post should have access. + request = self.factory.get('/admin') + request.user = self.staff + self.assertTrue(IsListOwner().has_object_permission(request, None, None)) + self.assertTrue(IsListOwner().has_object_permission(request, None, self.list)) + request = self.factory.post('/admin') + request.user = self.staff + self.assertTrue(IsListOwner().has_object_permission(request, None, None)) + self.assertTrue(IsListOwner().has_object_permission(request, None, self.list)) + + # Try and access something as an user who created th lists. + # Both get and post should have access. + request = self.factory.get('/admin') + request.user = self.user + self.assertTrue(IsListOwner().has_object_permission(request, None, self.list)) + request = self.factory.post('/admin') + request.user = self.user + self.assertTrue(IsListOwner().has_object_permission(request, None, self.list)) + + # Try and access something as an anonymous user. + # Both get and post should not have access. + request = self.factory.get('/admin') + request.user = AnonymousUser() + self.assertFalse(IsListOwner().has_object_permission(request, None, self.list)) + request = self.factory.post('/admin') + request.user = AnonymousUser() + self.assertFalse(IsListOwner().has_object_permission(request, None, self.list)) + + def test_is_item_owner_or_read_only(self): + # Try and access something as an admin user. + # Both get and post should have access. + request = self.factory.get('/admin') + request.user = self.staff + self.assertTrue(IsItemOwner().has_object_permission(request, None, None)) + self.assertTrue(IsItemOwner().has_object_permission(request, None, self.item)) + request = self.factory.post('/admin') + request.user = self.staff + self.assertTrue(IsItemOwner().has_object_permission(request, None, None)) + self.assertTrue(IsItemOwner().has_object_permission(request, None, self.item)) + + # Try and access something as an user who created th lists. + # Both get and post should have access. + request = self.factory.get('/admin') + request.user = self.user + self.assertTrue(IsItemOwner().has_object_permission(request, None, self.item)) + request = self.factory.post('/admin') + request.user = self.user + self.assertTrue(IsItemOwner().has_object_permission(request, None, self.item)) + + # Try and access something as an anonymous user. + # Both get and post should not have access. + request = self.factory.get('/admin') + request.user = AnonymousUser() + self.assertFalse(IsItemOwner().has_object_permission(request, None, self.item)) + request = self.factory.post('/admin') + request.user = AnonymousUser() + self.assertFalse(IsItemOwner().has_object_permission(request, None, self.item)) diff --git a/v1/recipe/tests/__init__.py b/v1/recipe/tests/__init__.py new file mode 100644 index 0000000..6f804bc --- /dev/null +++ b/v1/recipe/tests/__init__.py @@ -0,0 +1,2 @@ +#!/usr/bin/env python +# encoding: utf-8 diff --git a/v1/recipe/tests/test_create_recipe.py b/v1/recipe/tests/test_create_recipe.py new file mode 100644 index 0000000..dd1c3d4 --- /dev/null +++ b/v1/recipe/tests/test_create_recipe.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python +# encoding: utf-8 + +import os +from django.test import TestCase +from django.contrib.auth.models import User +from django.core.files.uploadedfile import SimpleUploadedFile +from django.conf import settings +from rest_framework.test import APIRequestFactory +from v1.recipe import views + + +class RecipeSerializerTests(TestCase): + fixtures = [ + 'test/users.json', + 'course_data.json', + 'cuisine_data.json', + ] + + def setUp(self): + self.factory = APIRequestFactory() + self.staff = User.objects.create_user( + username='staff', email='staff@gmail.com', password='top_secret', is_superuser=True + ) + + def test_simple_create_recipe(self): + """Test to make sure we have the right fields""" + view = views.RecipeViewSet.as_view({'post': 'create'}) + data = { + "ingredient_groups": [ + { + "id": 3, + "title": "", + "ingredients": [] + }, + { + "id": 4, + "title": "Veges", + "ingredients": [ + { + "id": 13, + "numerator": 1.0, + "denominator": 2.0, + "measurement": "dash", + "title": "black pepper" + }, + { + "id": 14, + "numerator": 4.0, + "denominator": 1.0, + "measurement": "tablespoons", + "title": "chili powder" + }, + { + "id": 15, + "numerator": 1.0, + "denominator": 1.0, + "measurement": "tablespoon", + "title": "cumin" + }, + { + "id": 16, + "numerator": 1.0, + "denominator": 1.0, + "measurement": "can", + "title": "dark kidney beans" + }, + { + "id": 17, + "numerator": 2.0, + "denominator": 1.0, + "measurement": "cans", + "title": "diced tomatos" + }, + { + "id": 18, + "numerator": 1.0, + "denominator": 1.0, + "measurement": "whole", + "title": "green bell pepper" + }, + { + "id": 19, + "numerator": 1.0, + "denominator": 1.0, + "measurement": "can", + "title": "light kidney beans" + }, + { + "id": 20, + "numerator": 1.0, + "denominator": 1.0, + "measurement": "whole", + "title": "serrano pepper" + }, + { + "id": 21, + "numerator": 1.0, + "denominator": 1.0, + "measurement": "whole", + "title": "white onion" + } + ] + }, + { + "id": 5, + "title": "Beef", + "ingredients": [ + { + "id": 22, + "numerator": 1.0, + "denominator": 1.0, + "measurement": "pound", + "title": "ground pork" + }, + { + "id": 23, + "numerator": 1.0, + "denominator": 1.0, + "measurement": "pound", + "title": "ground sirloin" + }, + { + "id": 24, + "numerator": 1.0, + "denominator": 1.0, + "measurement": "dash", + "title": "kosher salt" + } + ] + } + ], + "directions": '', + "tags": ['hi', 'hello'], + "title": "Recipe name", + "info": "Recipe info", + "source": "google.com", + "prep_time": 60, + "cook_time": 60, + "servings": 8, + "rating": 0, + "cuisine": 1, + "course": 2 + } + request = self.factory.post('/api/v1/recipe/recipes/', data=data) + request.user = self.staff + + root_path = os.path.join(settings.PROJECT_PATH, 'v1', 'fixtures', 'test', 'food.jpg') + with open(root_path, 'rb') as f: + request.FILES['photo'] = SimpleUploadedFile( + root_path, + f.read(), + content_type='multipart/form-data' + ) + response = view(request) + + self.assertTrue(response.data.get('id', True)) diff --git a/v1/recipe/tests/test_delete_recipe.py b/v1/recipe/tests/test_delete_recipe.py new file mode 100644 index 0000000..91c0b3a --- /dev/null +++ b/v1/recipe/tests/test_delete_recipe.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python +# encoding: utf-8 + +from django.test import TestCase +from django.contrib.auth.models import User +from rest_framework.test import APIRequestFactory +from v1.recipe import views + + +class RecipeSerializerTests(TestCase): + fixtures = [ + 'test/users.json', + 'course_data.json', + 'cuisine_data.json', + 'ing_data.json', + 'recipe_data.json' + ] + + def setUp(self): + self.factory = APIRequestFactory() + self.staff = User.objects.create_user( + username='staff', email='staff@gmail.com', password='top_secret', is_superuser=True + ) + + def test_simple_delete_recipe(self): + """Test to make sure we have the right fields""" + view = views.RecipeViewSet.as_view({'delete': 'destroy'}) + request = self.factory.delete('/api/v1/recipe/recipes/tasty-chili') + request.user = self.staff + response = view(request, slug='tasty-chili') + + self.assertEqual(response.status_code, 204) diff --git a/v1/recipe/tests/test_field_limiter.py b/v1/recipe/tests/test_field_limiter.py new file mode 100644 index 0000000..4be5644 --- /dev/null +++ b/v1/recipe/tests/test_field_limiter.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python +# encoding: utf-8 + +from django.test import TestCase +from rest_framework.test import APIRequestFactory +from v1.recipe import views + + +class RecipeSerializerTests(TestCase): + fixtures = [ + 'test/users.json', + 'course_data.json', + 'cuisine_data.json', + 'ing_data.json', + 'recipe_data.json' + ] + + def setUp(self): + self.factory = APIRequestFactory() + + def test_view_limiter(self): + """Test to make sure we have the right fields""" + view = views.RecipeViewSet.as_view({'get': 'list'}) + request = self.factory.get('/api/v1/recipe/recipes/tasty-chili?fields=id') + response = view(request) + + self.assertTrue(response.data.get('id', True)) + self.assertFalse(response.data.get('title', False)) + + view = views.RecipeViewSet.as_view({'get': 'list'}) + request = self.factory.get('/api/v1/recipe/recipes/tasty-chili?fields=id,title,photo') + response = view(request) + + self.assertTrue(response.data.get('id', True)) + self.assertTrue(response.data.get('title', True)) + self.assertTrue(response.data.get('photo', True)) + self.assertFalse(response.data.get('directions', False)) + self.assertFalse(response.data.get('author', False)) diff --git a/v1/recipe/tests.py b/v1/recipe/tests/test_ratings.py similarity index 95% rename from v1/recipe/tests.py rename to v1/recipe/tests/test_ratings.py index da2de5c..a52db19 100644 --- a/v1/recipe/tests.py +++ b/v1/recipe/tests/test_ratings.py @@ -1,15 +1,15 @@ #!/usr/bin/env python # encoding: utf-8 -import unittest import mock +from django.test import TestCase from copy import deepcopy from v1.recipe.serializers import RecipeSerializer from v1.recipe.models import Recipe -class RecipeSerializerTests(unittest.TestCase): +class RecipeSerializerTests(TestCase): def setUp(self): self.serializer = RecipeSerializer() self.data = { diff --git a/v1/recipe/tests/test_read_recipe.py b/v1/recipe/tests/test_read_recipe.py new file mode 100644 index 0000000..47dcabb --- /dev/null +++ b/v1/recipe/tests/test_read_recipe.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# encoding: utf-8 + +import json +from django.test import TestCase +from rest_framework.test import APIRequestFactory +from v1.recipe import views + + +class RecipeSerializerTests(TestCase): + fixtures = [ + 'test/users.json', + 'course_data.json', + 'cuisine_data.json', + 'ing_data.json', + 'recipe_data.json' + ] + + def setUp(self): + self.factory = APIRequestFactory() + + def test_retrieve_view(self): + """Try and read the view of a recipe""" + view = views.RecipeViewSet.as_view({'get': 'retrieve'}) + request = self.factory.get('/api/v1/recipe/recipes/tasty-chili') + response = view(request, slug='tasty-chili') + + self.assertTrue(response.data.get('id') == 1) + self.assertTrue(response.data.get('title') == 'Tasty Chili') + self.assertTrue(response.data.get('prep_time') == 60) + self.assertTrue(response.data.get('servings') == 8) + + def test_list_view(self): + """Try and read the view of a recipe""" + view = views.RecipeViewSet.as_view({'get': 'list'}) + request = self.factory.get('/api/v1/recipe/recipes/tasty-chili?course=entry&cuisine=american&rating=3') + response = view(request) + + self.assertEqual(len(response.data.get('results')), 31) + + def test_mini_browse(self): + """Try and read the view of a recipe""" + view = views.MiniBrowseViewSet.as_view({'get': 'list'}) + request = self.factory.get('/api/v1/recipe/mini-browse') + response = view(request) + self.assertEqual(len(response.data), 4) + + def get_mini_browse(limit): + v = views.MiniBrowseViewSet.as_view({'get': 'list'}) + r = self.factory.get('/api/v1/recipe/mini-browse/?limit=%s' % limit) + results = json.loads(json.dumps(v(r).data)).get('results') + self.assertEqual(len(results), limit) + for r in results: + self.assertTrue(r.get('id', False)) + self.assertTrue(r.get('slug', False)) + self.assertTrue(r.get('title', False)) + self.assertTrue(r.get('pub_date', False)) + self.assertTrue(r.get('rating', False)) + self.assertTrue(r.get('info', False)) + + get_mini_browse(2) + get_mini_browse(4) + get_mini_browse(6) diff --git a/v1/recipe/tests/test_update_recipe.py b/v1/recipe/tests/test_update_recipe.py new file mode 100644 index 0000000..c50c98f --- /dev/null +++ b/v1/recipe/tests/test_update_recipe.py @@ -0,0 +1,273 @@ +#!/usr/bin/env python +# encoding: utf-8 + +from django.test import TestCase +from django.contrib.auth.models import User +from rest_framework.test import APIRequestFactory +from v1.recipe import views + + +class RecipeSerializerTests(TestCase): + fixtures = [ + 'test/users.json', + 'course_data.json', + 'cuisine_data.json', + 'ing_data.json', + 'recipe_data.json' + ] + + def setUp(self): + self.factory = APIRequestFactory() + self.staff = User.objects.create_user( + username='staff', email='staff@gmail.com', password='top_secret', is_superuser=True + ) + + def test_simple_patch_recipe(self): + """Test to make sure we have the right fields""" + view = views.RecipeViewSet.as_view({'patch': 'update'}) + data = { + "ingredient_groups": [ + { + "id": 3, + "title": "", + "ingredients": [] + }, + { + "id": 4, + "title": "Veges", + "ingredients": [ + { + "id": 13, + "numerator": 1.0, + "denominator": 2.0, + "measurement": "dash", + "title": "black pepper" + }, + { + "id": 14, + "numerator": 4.0, + "denominator": 1.0, + "measurement": "tablespoons", + "title": "chili powder" + }, + { + "id": 15, + "numerator": 1.0, + "denominator": 1.0, + "measurement": "tablespoon", + "title": "cumin" + }, + { + "id": 16, + "numerator": 1.0, + "denominator": 1.0, + "measurement": "can", + "title": "dark kidney beans" + }, + { + "id": 17, + "numerator": 2.0, + "denominator": 1.0, + "measurement": "cans", + "title": "diced tomatos" + }, + { + "id": 18, + "numerator": 1.0, + "denominator": 1.0, + "measurement": "whole", + "title": "green bell pepper" + }, + { + "id": 19, + "numerator": 1.0, + "denominator": 1.0, + "measurement": "can", + "title": "light kidney beans" + }, + { + "id": 20, + "numerator": 1.0, + "denominator": 1.0, + "measurement": "whole", + "title": "serrano pepper" + }, + { + "id": 21, + "numerator": 1.0, + "denominator": 1.0, + "measurement": "whole", + "title": "white onion" + } + ] + }, + { + "id": 5, + "title": "Beef", + "ingredients": [ + { + "id": 22, + "numerator": 1.0, + "denominator": 1.0, + "measurement": "pound", + "title": "ground pork" + }, + { + "id": 23, + "numerator": 1.0, + "denominator": 1.0, + "measurement": "pound", + "title": "ground sirloin" + }, + { + "id": 24, + "numerator": 1.0, + "denominator": 1.0, + "measurement": "dash", + "title": "kosher salt" + } + ] + } + ], + "directions": '', + "tags": ['hi', 'hello'], + "title": "Recipe name", + "info": "Recipe info", + "source": "google.com", + "prep_time": 60, + "cook_time": 60, + "servings": 8, + "rating": 0, + "cuisine": 1, + "course": 2 + } + request = self.factory.patch('/api/v1/recipe/recipes/tasty-chili', data=data) + request.user = self.staff + response = view(request, slug='tasty-chili') + + self.assertTrue(response.data.get('id', True)) + + def test_put_recipe(self): + """Test to make sure we have the right fields""" + view = views.RecipeViewSet.as_view({'put': 'update'}) + data = { + "ingredient_groups": [ + { + "id": 3, + "title": "", + "ingredients": [] + }, + { + "id": 4, + "title": "Veges", + "ingredients": [ + { + "id": 13, + "numerator": 1.0, + "denominator": 2.0, + "measurement": "dash", + "title": "black pepper" + }, + { + "id": 14, + "numerator": 4.0, + "denominator": 1.0, + "measurement": "tablespoons", + "title": "chili powder" + }, + { + "id": 15, + "numerator": 1.0, + "denominator": 1.0, + "measurement": "tablespoon", + "title": "cumin" + }, + { + "id": 16, + "numerator": 1.0, + "denominator": 1.0, + "measurement": "can", + "title": "dark kidney beans" + }, + { + "id": 17, + "numerator": 2.0, + "denominator": 1.0, + "measurement": "cans", + "title": "diced tomatos" + }, + { + "id": 18, + "numerator": 1.0, + "denominator": 1.0, + "measurement": "whole", + "title": "green bell pepper" + }, + { + "id": 19, + "numerator": 1.0, + "denominator": 1.0, + "measurement": "can", + "title": "light kidney beans" + }, + { + "id": 20, + "numerator": 1.0, + "denominator": 1.0, + "measurement": "whole", + "title": "serrano pepper" + }, + { + "id": 21, + "numerator": 1.0, + "denominator": 1.0, + "measurement": "whole", + "title": "white onion" + } + ] + }, + { + "id": 5, + "title": "Beef", + "ingredients": [ + { + "id": 22, + "numerator": 1.0, + "denominator": 1.0, + "measurement": "pound", + "title": "ground pork" + }, + { + "id": 23, + "numerator": 1.0, + "denominator": 1.0, + "measurement": "pound", + "title": "ground sirloin" + }, + { + "id": 24, + "numerator": 1.0, + "denominator": 1.0, + "measurement": "dash", + "title": "kosher salt" + } + ] + } + ], + "directions": '', + "tags": ['hi', 'hello'], + "title": "Recipe name", + "info": "Recipe info", + "source": "google.com", + "prep_time": 60, + "cook_time": 60, + "servings": 8, + "rating": 0, + "cuisine": 1, + "course": 2 + } + request = self.factory.put('/api/v1/recipe/recipes/tasty-chili', data=data) + request.user = self.staff + response = view(request, slug='tasty-chili') + + self.assertTrue(response.data.get('id', True)) diff --git a/v1/recipe_groups/tests/__init__.py b/v1/recipe_groups/tests/__init__.py new file mode 100644 index 0000000..6f804bc --- /dev/null +++ b/v1/recipe_groups/tests/__init__.py @@ -0,0 +1,2 @@ +#!/usr/bin/env python +# encoding: utf-8 diff --git a/v1/recipe_groups/tests.py b/v1/recipe_groups/tests/tests.py similarity index 84% rename from v1/recipe_groups/tests.py rename to v1/recipe_groups/tests/tests.py index 59833cf..c0bb939 100644 --- a/v1/recipe_groups/tests.py +++ b/v1/recipe_groups/tests/tests.py @@ -8,7 +8,12 @@ class RecipeGroupsTests(TestCase): - fixtures = ['test/users.json', 'test/cuisine.json', 'test/course.json', 'test/recipes.json'] + fixtures = [ + 'test/users.json', + 'course_data.json', + 'cuisine_data.json', + 'recipe_data.json' + ] def setUp(self): self.factory = APIRequestFactory() @@ -18,10 +23,10 @@ def test_cuisine_all(self): request = self.factory.get('/api/v1/recipe_groups/cuisine-count/') response = view(request) - self.assertEqual(response.data.get('count'), 3) + self.assertEqual(response.data.get('count'), 1) results = response.data.get('results') - totals = {"cuisine-1": 3, "cuisine-2": 2, "cuisine-3": 1} + totals = {"american": 31} for item in results: self.assertEquals(totals[item.get('slug')], item.get('total')) @@ -31,30 +36,30 @@ def test_course_all(self): request = self.factory.get('/api/v1/recipe_groups/course-count/') response = view(request) - self.assertEqual(response.data.get('count'), 3) + self.assertEqual(response.data.get('count'), 1) results = response.data.get('results') - totals = {"course-1": 3, "course-2": 2, "course-3": 1} + totals = {"entry": 31} for item in results: self.assertEquals(totals[item.get('slug')], item.get('total')) def test_cuisine_with_filters(self): view = views.CuisineCountViewSet.as_view({'get': 'list'}) - request = self.factory.get('/api/v1/recipe_groups/cuisine-count/?course=course-1&rating=0') + request = self.factory.get('/api/v1/recipe_groups/cuisine-count/?course=entry&rating=3') response = view(request) self.assertEqual(response.data.get('count'), 1) results = response.data.get('results') - totals = {"cuisine-1": 1} + totals = {"american": 31} for item in results: self.assertEquals(totals[item.get('slug')], item.get('total')) def test_cuisine_with_course_filter_no_results(self): view = views.CuisineCountViewSet.as_view({'get': 'list'}) - request = self.factory.get('/api/v1/recipe_groups/cuisine-count/?course=course-4&rating=0') + request = self.factory.get('/api/v1/recipe_groups/cuisine-count/?course=entry&rating=0') response = view(request) self.assertEqual(response.data.get('count'), 0) @@ -68,20 +73,20 @@ def test_cuisine_with_non_existent_course(self): def test_course_with_filters(self): view = views.CourseCountViewSet.as_view({'get': 'list'}) - request = self.factory.get('/api/v1/recipe_groups/course-count/?cuisine=cuisine-1&rating=1') + request = self.factory.get('/api/v1/recipe_groups/course-count/?cuisine=american&rating=3') response = view(request) self.assertEqual(response.data.get('count'), 1) results = response.data.get('results') - totals = {"course-3": 1} + totals = {"entry": 31} for item in results: self.assertEquals(totals[item.get('slug')], item.get('total')) def test_course_with_cuisine_filter_no_results(self): view = views.CourseCountViewSet.as_view({'get': 'list'}) - request = self.factory.get('/api/v1/recipe_groups/course-count/?cuisine=cuisine-4&rating=0') + request = self.factory.get('/api/v1/recipe_groups/course-count/?cuisine=american&rating=0') response = view(request) self.assertEqual(response.data.get('count'), 0)