diff --git a/src/newsreader/core/pagination.py b/src/newsreader/core/pagination.py index 5e19771..b44c862 100644 --- a/src/newsreader/core/pagination.py +++ b/src/newsreader/core/pagination.py @@ -1,7 +1,7 @@ -from rest_framework.pagination import PageNumberPagination +from rest_framework import pagination -class ResultSetPagination(PageNumberPagination): +class ResultSetPagination(pagination.PageNumberPagination): page_size_query_param = "count" max_page_size = 50 page_size = 30 @@ -10,3 +10,9 @@ class ResultSetPagination(PageNumberPagination): class LargeResultSetPagination(ResultSetPagination): max_page_size = 100 page_size = 50 + + +class CursorPagination(pagination.CursorPagination): + page_size_query_param = "count" + ordering = "-publication_date" + page_size = 30 diff --git a/src/newsreader/js/pages/homepage/actions/posts.js b/src/newsreader/js/pages/homepage/actions/posts.js index f04f3e1..826512f 100644 --- a/src/newsreader/js/pages/homepage/actions/posts.js +++ b/src/newsreader/js/pages/homepage/actions/posts.js @@ -64,7 +64,7 @@ export const markPostRead = (post, token) => { }; }; -export const fetchPostsBySection = (section, page = false) => { +export const fetchPostsBySection = (section, next = false) => { return dispatch => { if (section.unread === 0) { return; @@ -76,10 +76,10 @@ export const fetchPostsBySection = (section, page = false) => { switch (section.type) { case RULE_TYPE: - url = page ? page : `/api/rules/${section.id}/posts/?read=false`; + url = next ? next : `/api/rules/${section.id}/posts/?read=false`; break; case CATEGORY_TYPE: - url = page ? page : `/api/categories/${section.id}/posts/?read=false`; + url = next ? next : `/api/categories/${section.id}/posts/?read=false`; break; } diff --git a/src/newsreader/js/pages/homepage/components/postlist/PostList.js b/src/newsreader/js/pages/homepage/components/postlist/PostList.js index 66e3b7f..282300b 100644 --- a/src/newsreader/js/pages/homepage/components/postlist/PostList.js +++ b/src/newsreader/js/pages/homepage/components/postlist/PostList.js @@ -90,7 +90,7 @@ const mapStateToProps = state => ({ }); const mapDispatchToProps = dispatch => ({ - fetchPostsBySection: (rule, page = false) => dispatch(fetchPostsBySection(rule, page)), + fetchPostsBySection: (rule, next = false) => dispatch(fetchPostsBySection(rule, next)), }); export default connect(mapStateToProps, mapDispatchToProps)(PostList); diff --git a/src/newsreader/js/pages/homepage/index.js b/src/newsreader/js/pages/homepage/index.js index b934ad3..86cdd09 100644 --- a/src/newsreader/js/pages/homepage/index.js +++ b/src/newsreader/js/pages/homepage/index.js @@ -14,7 +14,7 @@ if (page) { const settings = JSON.parse(document.getElementById('homepageSettings').textContent); const { feedUrl, subredditUrl, timelineUrl, categoriesUrl } = settings; - ReactDOM.render( + const app = ( - , - page + ); + + ReactDOM.render(app, page); } diff --git a/src/newsreader/news/collection/endpoints.py b/src/newsreader/news/collection/endpoints.py index 7f2ede0..623283d 100644 --- a/src/newsreader/news/collection/endpoints.py +++ b/src/newsreader/news/collection/endpoints.py @@ -2,12 +2,16 @@ from rest_framework import status from rest_framework.generics import ( GenericAPIView, ListAPIView, - RetrieveUpdateDestroyAPIView, + RetrieveUpdateAPIView, get_object_or_404, ) from rest_framework.response import Response -from newsreader.core.pagination import LargeResultSetPagination, ResultSetPagination +from newsreader.core.pagination import ( + CursorPagination, + LargeResultSetPagination, + ResultSetPagination, +) from newsreader.news.collection.models import CollectionRule from newsreader.news.collection.serializers import RuleSerializer from newsreader.news.core.filters import ReadFilter @@ -15,26 +19,15 @@ from newsreader.news.core.models import Post from newsreader.news.core.serializers import PostSerializer -class ListRuleView(ListAPIView): +class DetailRuleView(RetrieveUpdateAPIView): queryset = CollectionRule.objects.all() serializer_class = RuleSerializer - pagination_class = ResultSetPagination - - def get_queryset(self): - user = self.request.user - return self.queryset.filter(user=user).order_by("-created") - - -class DetailRuleView(RetrieveUpdateDestroyAPIView): - queryset = CollectionRule.objects.all() - serializer_class = RuleSerializer - pagination_class = ResultSetPagination class NestedRuleView(ListAPIView): queryset = CollectionRule.objects.prefetch_related("posts").all() serializer_class = PostSerializer - pagination_class = LargeResultSetPagination + pagination_class = CursorPagination filter_backends = [ReadFilter] def get_queryset(self): diff --git a/src/newsreader/news/collection/tests/endpoints/rule/detail/tests.py b/src/newsreader/news/collection/tests/endpoints/rule/detail/tests.py index 8dfe6ed..0e1bc7e 100644 --- a/src/newsreader/news/collection/tests/endpoints/rule/detail/tests.py +++ b/src/newsreader/news/collection/tests/endpoints/rule/detail/tests.py @@ -121,15 +121,6 @@ class CollectionRuleDetailViewTestCase(TestCase): self.assertEquals(response.status_code, 200) self.assertEquals(data["name"], "BBC") - def test_delete(self): - rule = FeedFactory(user=self.user) - - response = self.client.delete( - reverse("api:news:collection:rules-detail", args=[rule.pk]) - ) - - self.assertEquals(response.status_code, 204) - def test_rule_with_unauthenticated_user(self): self.client.logout() diff --git a/src/newsreader/news/collection/tests/endpoints/rule/list/tests.py b/src/newsreader/news/collection/tests/endpoints/rule/list/tests.py index 44e3eaa..5a34dbc 100644 --- a/src/newsreader/news/collection/tests/endpoints/rule/list/tests.py +++ b/src/newsreader/news/collection/tests/endpoints/rule/list/tests.py @@ -12,137 +12,6 @@ from newsreader.news.collection.tests.factories import FeedFactory from newsreader.news.core.tests.factories import CategoryFactory, FeedPostFactory -class RuleListViewTestCase(TestCase): - def setUp(self): - self.user = UserFactory(password="test") - self.client.force_login(self.user) - - def test_simple(self): - FeedFactory.create_batch(size=3, user=self.user) - - response = self.client.get(reverse("api:news:collection:rules-list")) - data = response.json() - - self.assertEquals(response.status_code, 200) - self.assertTrue("results" in data) - self.assertTrue("count" in data) - self.assertEquals(data["count"], 3) - - def test_ordering(self): - rules = [ - FeedFactory( - created=datetime.combine( - date(2019, 5, 20), time(hour=16, minute=7, second=37), pytz.utc - ), - user=self.user, - ), - FeedFactory( - created=datetime.combine( - date(2019, 7, 20), time(hour=18, minute=7, second=37), pytz.utc - ), - user=self.user, - ), - FeedFactory( - created=datetime.combine( - date(2019, 7, 20), time(hour=16, minute=7, second=37), pytz.utc - ), - user=self.user, - ), - ] - - response = self.client.get(reverse("api:news:collection:rules-list")) - data = response.json() - - self.assertEquals(response.status_code, 200) - self.assertTrue("results" in data) - self.assertTrue("count" in data) - self.assertEquals(data["count"], 3) - - self.assertEquals(data["results"][0]["id"], rules[1].pk) - self.assertEquals(data["results"][1]["id"], rules[2].pk) - self.assertEquals(data["results"][2]["id"], rules[0].pk) - - def test_pagination_count(self): - FeedFactory.create_batch(size=80, user=self.user) - - response = self.client.get( - reverse("api:news:collection:rules-list"), {"count": 30} - ) - data = response.json() - - self.assertEquals(response.status_code, 200) - self.assertEquals(data["count"], 80) - self.assertEquals(len(data["results"]), 30) - - def test_empty(self): - response = self.client.get(reverse("api:news:collection:rules-list")) - data = response.json() - - self.assertEquals(response.status_code, 200) - self.assertTrue("results" in data) - self.assertTrue("count" in data) - - self.assertEquals(data["count"], 0) - self.assertEquals(len(data["results"]), 0) - - def test_post(self): - category = CategoryFactory(user=self.user) - - data = {"name": "BBC", "url": "https://www.bbc.co.uk", "category": category.pk} - - response = self.client.post( - reverse("api:news:collection:rules-list"), - data=json.dumps(data), - content_type="application/json", - ) - data = response.json() - - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "POST" not allowed.') - - def test_patch(self): - response = self.client.patch(reverse("api:news:collection:rules-list")) - data = response.json() - - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "PATCH" not allowed.') - - def test_put(self): - response = self.client.put(reverse("api:news:collection:rules-list")) - data = response.json() - - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "PUT" not allowed.') - - def test_delete(self): - response = self.client.delete(reverse("api:news:collection:rules-list")) - data = response.json() - - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "DELETE" not allowed.') - - def test_rules_with_unauthenticated_user(self): - self.client.logout() - - FeedFactory.create_batch(size=3, user=self.user) - - response = self.client.get(reverse("api:news:collection:rules-list")) - - self.assertEquals(response.status_code, 403) - - def test_rules_with_unauthorized_user(self): - other_user = UserFactory() - FeedFactory.create_batch(size=3, user=other_user) - - response = self.client.get(reverse("api:news:collection:rules-list")) - data = response.json() - - self.assertEquals(response.status_code, 200) - - self.assertEquals(data["count"], 0) - self.assertEquals(len(data["results"]), 0) - - class NestedRuleListViewTestCase(TestCase): def setUp(self): self.user = UserFactory(password="test") @@ -157,11 +26,10 @@ class NestedRuleListViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 200) - - self.assertTrue("results" in data) - self.assertTrue("count" in data) - self.assertEquals(data["count"], 5) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data["results"]), 5) + self.assertEqual(data["next"], None) + self.assertEqual(data["previous"], None) def test_pagination(self): rule = FeedFactory.create(user=self.user) @@ -178,11 +46,12 @@ class NestedRuleListViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 200) - self.assertEquals(data["count"], 80) - self.assertEquals(len(data["results"]), 30) + self.assertEqual(response.status_code, 200) + self.assertTrue(data["next"]) + self.assertFalse(data["previous"]) - self.assertEquals( + self.assertEqual(len(data["results"]), 30) + self.assertEqual( [post["id"] for post in data["results"]], [post.id for post in posts[:30]] ) @@ -194,16 +63,15 @@ class NestedRuleListViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 200) - self.assertEquals(data["count"], 0) - self.assertEquals(len(data["results"]), 0) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data["results"]), 0) def test_not_known(self): response = self.client.get( reverse("api:news:collection:rules-nested-posts", kwargs={"pk": 0}) ) - self.assertEquals(response.status_code, 404) + self.assertEqual(response.status_code, 404) def test_post(self): rule = FeedFactory.create(user=self.user) @@ -215,8 +83,8 @@ class NestedRuleListViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "POST" not allowed.') + self.assertEqual(response.status_code, 405) + self.assertEqual(data["detail"], 'Method "POST" not allowed.') def test_patch(self): rule = FeedFactory.create(user=self.user) @@ -228,8 +96,8 @@ class NestedRuleListViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "PATCH" not allowed.') + self.assertEqual(response.status_code, 405) + self.assertEqual(data["detail"], 'Method "PATCH" not allowed.') def test_put(self): rule = FeedFactory.create(user=self.user) @@ -241,8 +109,8 @@ class NestedRuleListViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "PUT" not allowed.') + self.assertEqual(response.status_code, 405) + self.assertEqual(data["detail"], 'Method "PUT" not allowed.') def test_delete(self): rule = FeedFactory.create(user=self.user) @@ -254,8 +122,8 @@ class NestedRuleListViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "DELETE" not allowed.') + self.assertEqual(response.status_code, 405) + self.assertEqual(data["detail"], 'Method "DELETE" not allowed.') def test_rule_with_unauthenticated_user(self): self.client.logout() @@ -266,7 +134,7 @@ class NestedRuleListViewTestCase(TestCase): reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk}) ) - self.assertEquals(response.status_code, 403) + self.assertEqual(response.status_code, 403) def test_rule_with_unauthorized_user(self): other_user = UserFactory() @@ -276,7 +144,7 @@ class NestedRuleListViewTestCase(TestCase): reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk}) ) - self.assertEquals(response.status_code, 403) + self.assertEqual(response.status_code, 403) def test_posts_ordering(self): rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) @@ -310,14 +178,12 @@ class NestedRuleListViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 200) - self.assertTrue("results" in data) - self.assertTrue("count" in data) - self.assertEquals(data["count"], 3) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data["results"]), 3) - self.assertEquals(data["results"][0]["id"], posts[1].pk) - self.assertEquals(data["results"][1]["id"], posts[2].pk) - self.assertEquals(data["results"][2]["id"], posts[0].pk) + self.assertEqual(data["results"][0]["id"], posts[1].pk) + self.assertEqual(data["results"][1]["id"], posts[2].pk) + self.assertEqual(data["results"][2]["id"], posts[0].pk) def test_only_posts_from_rule_are_returned(self): rule = FeedFactory.create(user=self.user) @@ -331,14 +197,12 @@ class NestedRuleListViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 200) - - self.assertTrue("results" in data) - self.assertTrue("count" in data) - self.assertEquals(data["count"], 5) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data["results"]), 5) for post in data["results"]: - self.assertEquals(post["rule"], rule.pk) + with self.subTest(post=post): + self.assertEqual(post["rule"], rule.pk) def test_unread_posts(self): rule = FeedFactory.create(user=self.user) @@ -352,13 +216,13 @@ class NestedRuleListViewTestCase(TestCase): ) data = response.json() - posts = data["results"] - self.assertEquals(response.status_code, 200) - self.assertEquals(data["count"], 10) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data["results"]), 10) - for post in posts: - self.assertEquals(post["read"], False) + for post in data["results"]: + with self.subTest(post=post): + self.assertEqual(post["read"], False) def test_read_posts(self): rule = FeedFactory.create(user=self.user) @@ -372,10 +236,10 @@ class NestedRuleListViewTestCase(TestCase): ) data = response.json() - posts = data["results"] - self.assertEquals(response.status_code, 200) - self.assertEquals(data["count"], 10) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data["results"]), 10) - for post in posts: - self.assertEquals(post["read"], True) + for post in data["results"]: + with self.subTest(post=post): + self.assertEqual(post["read"], True) diff --git a/src/newsreader/news/collection/urls.py b/src/newsreader/news/collection/urls.py index 7d883f2..e5276cb 100644 --- a/src/newsreader/news/collection/urls.py +++ b/src/newsreader/news/collection/urls.py @@ -3,7 +3,6 @@ from django.urls import path from newsreader.news.collection.endpoints import ( DetailRuleView, - ListRuleView, NestedRuleView, RuleReadView, ) @@ -26,7 +25,6 @@ endpoints = [ path("rules//", DetailRuleView.as_view(), name="rules-detail"), path("rules//posts/", NestedRuleView.as_view(), name="rules-nested-posts"), path("rules//read/", RuleReadView.as_view(), name="rules-read"), - path("rules/", ListRuleView.as_view(), name="rules-list"), ] urlpatterns = [ diff --git a/src/newsreader/news/core/endpoints.py b/src/newsreader/news/core/endpoints.py index f5a48bc..ab47cca 100644 --- a/src/newsreader/news/core/endpoints.py +++ b/src/newsreader/news/core/endpoints.py @@ -1,5 +1,3 @@ -from django.db.models import Q - from rest_framework import status from rest_framework.generics import ( GenericAPIView, @@ -13,30 +11,13 @@ from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from newsreader.accounts.permissions import IsPostOwner -from newsreader.core.pagination import LargeResultSetPagination +from newsreader.core.pagination import CursorPagination from newsreader.news.collection.serializers import RuleSerializer from newsreader.news.core.filters import ReadFilter from newsreader.news.core.models import Category, Post from newsreader.news.core.serializers import CategorySerializer, PostSerializer -class ListPostView(ListAPIView): - queryset = Post.objects.all() - serializer_class = PostSerializer - pagination_class = LargeResultSetPagination - filter_backends = [ReadFilter] - - def get_queryset(self): - user = self.request.user - queryset = ( - self.queryset.filter(rule__user=user) - .filter(Q(rule__category=None) | Q(rule__category__user=user)) - .order_by("rule", "-publication_date", "-created") - ) - - return queryset - - class DetailPostView(RetrieveUpdateAPIView): queryset = Post.objects.all() serializer_class = PostSerializer @@ -77,7 +58,7 @@ class NestedRuleCategoryView(ListAPIView): class NestedPostCategoryView(ListAPIView): queryset = Category.objects.prefetch_related("rules", "rules__posts").all() serializer_class = PostSerializer - pagination_class = LargeResultSetPagination + pagination_class = CursorPagination filter_backends = [ReadFilter] def get_queryset(self): @@ -90,9 +71,8 @@ class NestedPostCategoryView(ListAPIView): category = get_object_or_404(self.queryset, **filter_kwargs) self.check_object_permissions(self.request, category) - queryset = Post.objects.filter( - rule__in=category.rules.values_list("id", flat=True) - ).order_by("-publication_date", "rule__name") + rules = category.rules.values_list("id", flat=True) + queryset = Post.objects.filter(rule__in=rules) return queryset diff --git a/src/newsreader/news/core/tests/endpoints/category/list/tests.py b/src/newsreader/news/core/tests/endpoints/category/list/tests.py index 15fb166..c822950 100644 --- a/src/newsreader/news/core/tests/endpoints/category/list/tests.py +++ b/src/newsreader/news/core/tests/endpoints/category/list/tests.py @@ -1,6 +1,6 @@ import json -from datetime import date, datetime, time +from datetime import datetime from django.test import TestCase from django.urls import reverse @@ -23,27 +23,21 @@ class CategoryListViewTestCase(TestCase): response = self.client.get(reverse("api:news:core:categories-list")) data = response.json() - self.assertEquals(response.status_code, 200) - self.assertEquals(len(data), 3) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data), 3) def test_ordering(self): categories = [ CategoryFactory( - created=datetime.combine( - date(2019, 5, 20), time(hour=16, minute=7, second=37), pytz.utc - ), + created=datetime(2019, 5, 20, 16, 7, 37, tzinfo=pytz.utc), user=self.user, ), CategoryFactory( - created=datetime.combine( - date(2019, 7, 20), time(hour=18, minute=7, second=37), pytz.utc - ), + created=datetime(2019, 7, 20, 18, 7, 37, tzinfo=pytz.utc), user=self.user, ), CategoryFactory( - created=datetime.combine( - date(2019, 7, 20), time(hour=16, minute=7, second=37), pytz.utc - ), + created=datetime(2019, 7, 20, 16, 7, 37, tzinfo=pytz.utc), user=self.user, ), ] @@ -51,18 +45,18 @@ class CategoryListViewTestCase(TestCase): response = self.client.get(reverse("api:news:core:categories-list")) data = response.json() - self.assertEquals(response.status_code, 200) + self.assertEqual(response.status_code, 200) - self.assertEquals(data[0]["id"], categories[1].pk) - self.assertEquals(data[1]["id"], categories[2].pk) - self.assertEquals(data[2]["id"], categories[0].pk) + self.assertEqual(data[0]["id"], categories[1].pk) + self.assertEqual(data[1]["id"], categories[2].pk) + self.assertEqual(data[2]["id"], categories[0].pk) def test_empty(self): response = self.client.get(reverse("api:news:core:categories-list")) data = response.json() - self.assertEquals(response.status_code, 200) - self.assertEquals(len(data), 0) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data), 0) def test_post(self): data = {"name": "Tech"} @@ -74,29 +68,29 @@ class CategoryListViewTestCase(TestCase): ) response_data = response.json() - self.assertEquals(response.status_code, 405) - self.assertEquals(response_data["detail"], 'Method "POST" not allowed.') + self.assertEqual(response.status_code, 405) + self.assertEqual(response_data["detail"], 'Method "POST" not allowed.') def test_patch(self): response = self.client.patch(reverse("api:news:core:categories-list")) data = response.json() - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "PATCH" not allowed.') + self.assertEqual(response.status_code, 405) + self.assertEqual(data["detail"], 'Method "PATCH" not allowed.') def test_put(self): response = self.client.put(reverse("api:news:core:categories-list")) data = response.json() - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "PUT" not allowed.') + self.assertEqual(response.status_code, 405) + self.assertEqual(data["detail"], 'Method "PUT" not allowed.') def test_delete(self): response = self.client.delete(reverse("api:news:core:categories-list")) data = response.json() - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "DELETE" not allowed.') + self.assertEqual(response.status_code, 405) + self.assertEqual(data["detail"], 'Method "DELETE" not allowed.') def test_categories_with_unauthenticated_user(self): self.client.logout() @@ -105,7 +99,7 @@ class CategoryListViewTestCase(TestCase): response = self.client.get(reverse("api:news:core:categories-list")) - self.assertEquals(response.status_code, 403) + self.assertEqual(response.status_code, 403) def test_categories_with_unauthorized_user(self): other_user = UserFactory() @@ -114,8 +108,8 @@ class CategoryListViewTestCase(TestCase): response = self.client.get(reverse("api:news:core:categories-list")) data = response.json() - self.assertEquals(response.status_code, 200) - self.assertEquals(len(data), 0) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data), 0) class NestedCategoryListViewTestCase(TestCase): @@ -132,8 +126,8 @@ class NestedCategoryListViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 200) - self.assertEquals(len(data), 5) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data), 5) self.assertTrue("id" in data[0]) self.assertTrue("name" in data[0]) @@ -149,16 +143,16 @@ class NestedCategoryListViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 200) - self.assertEquals(len(data), 0) - self.assertEquals(data, []) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data), 0) + self.assertEqual(data, []) def test_not_known(self): response = self.client.get( reverse("api:news:core:categories-nested-rules", kwargs={"pk": 100}) ) - self.assertEquals(response.status_code, 404) + self.assertEqual(response.status_code, 404) def test_post(self): response = self.client.post( @@ -168,8 +162,8 @@ class NestedCategoryListViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "POST" not allowed.') + self.assertEqual(response.status_code, 405) + self.assertEqual(data["detail"], 'Method "POST" not allowed.') def test_patch(self): category = CategoryFactory.create(user=self.user) @@ -183,8 +177,8 @@ class NestedCategoryListViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "PATCH" not allowed.') + self.assertEqual(response.status_code, 405) + self.assertEqual(data["detail"], 'Method "PATCH" not allowed.') def test_put(self): category = CategoryFactory.create(user=self.user) @@ -198,8 +192,8 @@ class NestedCategoryListViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "PUT" not allowed.') + self.assertEqual(response.status_code, 405) + self.assertEqual(data["detail"], 'Method "PUT" not allowed.') def test_delete(self): category = CategoryFactory.create(user=self.user) @@ -212,8 +206,8 @@ class NestedCategoryListViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "DELETE" not allowed.') + self.assertEqual(response.status_code, 405) + self.assertEqual(data["detail"], 'Method "DELETE" not allowed.') def test_with_unauthenticated_user(self): self.client.logout() @@ -225,7 +219,7 @@ class NestedCategoryListViewTestCase(TestCase): reverse("api:news:core:categories-nested-rules", kwargs={"pk": category.pk}) ) - self.assertEquals(response.status_code, 403) + self.assertEqual(response.status_code, 403) def test_with_unauthorized_user(self): other_user = UserFactory.create() @@ -237,7 +231,7 @@ class NestedCategoryListViewTestCase(TestCase): reverse("api:news:core:categories-nested-rules", kwargs={"pk": category.pk}) ) - self.assertEquals(response.status_code, 403) + self.assertEqual(response.status_code, 403) def test_ordering(self): category = CategoryFactory.create(user=self.user) @@ -252,12 +246,12 @@ class NestedCategoryListViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 200) - self.assertEquals(len(data), 3) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data), 3) - self.assertEquals(data[0]["id"], rules[2].pk) - self.assertEquals(data[1]["id"], rules[0].pk) - self.assertEquals(data[2]["id"], rules[1].pk) + self.assertEqual(data[0]["id"], rules[2].pk) + self.assertEqual(data[1]["id"], rules[0].pk) + self.assertEqual(data[2]["id"], rules[1].pk) def test_only_rules_from_category_are_returned(self): other_category = CategoryFactory(user=self.user) @@ -275,12 +269,12 @@ class NestedCategoryListViewTestCase(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 200) - self.assertEquals(len(data), 3) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data), 3) - self.assertEquals(data[0]["id"], rules[2].pk) - self.assertEquals(data[1]["id"], rules[0].pk) - self.assertEquals(data[2]["id"], rules[1].pk) + self.assertEqual(data[0]["id"], rules[2].pk) + self.assertEqual(data[1]["id"], rules[0].pk) + self.assertEqual(data[2]["id"], rules[1].pk) class NestedCategoryPostView(TestCase): @@ -301,16 +295,15 @@ class NestedCategoryPostView(TestCase): reverse("api:news:core:categories-nested-posts", kwargs={"pk": category.pk}) ) data = response.json() - posts = data["results"] - self.assertEquals(response.status_code, 200) - self.assertEquals(data["count"], 25) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data["results"]), 25) - self.assertTrue("id" in posts[0]) - self.assertTrue("title" in posts[0]) - self.assertTrue("body" in posts[0]) - self.assertTrue("rule" in posts[0]) - self.assertTrue("url" in posts[0]) + self.assertTrue("id" in data["results"][0]) + self.assertTrue("title" in data["results"][0]) + self.assertTrue("body" in data["results"][0]) + self.assertTrue("rule" in data["results"][0]) + self.assertTrue("url" in data["results"][0]) def test_no_rules(self): category = CategoryFactory.create(user=self.user) @@ -321,9 +314,9 @@ class NestedCategoryPostView(TestCase): data = response.json() posts = data["results"] - self.assertEquals(response.status_code, 200) - self.assertEquals(data["count"], 0) - self.assertEquals(posts, []) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data["results"]), 0) + self.assertEqual(posts, []) def test_no_posts(self): category = CategoryFactory.create(user=self.user) @@ -335,16 +328,16 @@ class NestedCategoryPostView(TestCase): data = response.json() posts = data["results"] - self.assertEquals(response.status_code, 200) - self.assertEquals(data["count"], 0) - self.assertEquals(posts, []) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data["results"]), 0) + self.assertEqual(posts, []) def test_not_known(self): response = self.client.get( reverse("api:news:core:categories-nested-posts", kwargs={"pk": 100}) ) - self.assertEquals(response.status_code, 404) + self.assertEqual(response.status_code, 404) def test_post(self): response = self.client.post( @@ -354,8 +347,8 @@ class NestedCategoryPostView(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "POST" not allowed.') + self.assertEqual(response.status_code, 405) + self.assertEqual(data["detail"], 'Method "POST" not allowed.') def test_patch(self): category = CategoryFactory.create(user=self.user) @@ -369,8 +362,8 @@ class NestedCategoryPostView(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "PATCH" not allowed.') + self.assertEqual(response.status_code, 405) + self.assertEqual(data["detail"], 'Method "PATCH" not allowed.') def test_put(self): category = CategoryFactory.create(user=self.user) @@ -384,8 +377,8 @@ class NestedCategoryPostView(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "PUT" not allowed.') + self.assertEqual(response.status_code, 405) + self.assertEqual(data["detail"], 'Method "PUT" not allowed.') def test_delete(self): category = CategoryFactory.create(user=self.user) @@ -398,8 +391,8 @@ class NestedCategoryPostView(TestCase): ) data = response.json() - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "DELETE" not allowed.') + self.assertEqual(response.status_code, 405) + self.assertEqual(data["detail"], 'Method "DELETE" not allowed.') def test_with_unauthenticated_user(self): self.client.logout() @@ -410,7 +403,7 @@ class NestedCategoryPostView(TestCase): reverse("api:news:core:categories-nested-posts", kwargs={"pk": category.pk}) ) - self.assertEquals(response.status_code, 403) + self.assertEqual(response.status_code, 403) def test_with_unauthorized_user(self): other_user = UserFactory.create() @@ -420,7 +413,7 @@ class NestedCategoryPostView(TestCase): reverse("api:news:core:categories-nested-posts", kwargs={"pk": category.pk}) ) - self.assertEquals(response.status_code, 403) + self.assertEqual(response.status_code, 403) def test_ordering(self): category = CategoryFactory.create(user=self.user) @@ -437,16 +430,12 @@ class NestedCategoryPostView(TestCase): FeedPostFactory.create( title="Second Reuters post", rule=reuters_rule, - publication_date=datetime.combine( - date(2019, 5, 21), time(hour=16, minute=7, second=37), pytz.utc - ), + publication_date=datetime(2019, 5, 21, 15, tzinfo=pytz.utc), ), FeedPostFactory.create( title="First Reuters post", rule=reuters_rule, - publication_date=datetime.combine( - date(2019, 5, 20), time(hour=16, minute=7, second=37), pytz.utc - ), + publication_date=datetime(2019, 5, 20, 12, tzinfo=pytz.utc), ), ] @@ -454,16 +443,12 @@ class NestedCategoryPostView(TestCase): FeedPostFactory.create( title="Second Guardian post", rule=guardian_rule, - publication_date=datetime.combine( - date(2019, 5, 21), time(hour=16, minute=7, second=37), pytz.utc - ), + publication_date=datetime(2019, 5, 21, 14, tzinfo=pytz.utc), ), FeedPostFactory.create( title="First Guardian post", rule=guardian_rule, - publication_date=datetime.combine( - date(2019, 5, 20), time(hour=16, minute=7, second=37), pytz.utc - ), + publication_date=datetime(2019, 5, 20, 11, tzinfo=pytz.utc), ), ] @@ -471,16 +456,12 @@ class NestedCategoryPostView(TestCase): FeedPostFactory.create( title="Second BBC post", rule=bbc_rule, - publication_date=datetime.combine( - date(2019, 5, 21), time(hour=16, minute=7, second=37), pytz.utc - ), + publication_date=datetime(2019, 5, 21, 16, tzinfo=pytz.utc), ), FeedPostFactory.create( title="First BBC post", rule=bbc_rule, - publication_date=datetime.combine( - date(2019, 5, 20), time(hour=16, minute=7, second=37), pytz.utc - ), + publication_date=datetime(2019, 5, 20, 13, tzinfo=pytz.utc), ), ] @@ -490,16 +471,16 @@ class NestedCategoryPostView(TestCase): data = response.json() posts = data["results"] - self.assertEquals(response.status_code, 200) - self.assertEquals(data["count"], 6) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data["results"]), 6) - self.assertEquals(posts[0]["title"], "Second BBC post") - self.assertEquals(posts[1]["title"], "Second Reuters post") - self.assertEquals(posts[2]["title"], "Second Guardian post") + self.assertEqual(posts[0]["title"], "Second BBC post") + self.assertEqual(posts[1]["title"], "Second Reuters post") + self.assertEqual(posts[2]["title"], "Second Guardian post") - self.assertEquals(posts[3]["title"], "First BBC post") - self.assertEquals(posts[4]["title"], "First Reuters post") - self.assertEquals(posts[5]["title"], "First Guardian post") + self.assertEqual(posts[3]["title"], "First BBC post") + self.assertEqual(posts[4]["title"], "First Reuters post") + self.assertEqual(posts[5]["title"], "First Guardian post") def test_only_posts_from_category_are_returned(self): category = CategoryFactory.create(user=self.user) @@ -526,11 +507,11 @@ class NestedCategoryPostView(TestCase): data = response.json() posts = data["results"] - self.assertEquals(response.status_code, 200) - self.assertEquals(data["count"], 2) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data["results"]), 2) - self.assertEquals(posts[0]["rule"], guardian_rule.pk) - self.assertEquals(posts[1]["rule"], guardian_rule.pk) + self.assertEqual(posts[0]["rule"], guardian_rule.pk) + self.assertEqual(posts[1]["rule"], guardian_rule.pk) def test_unread_posts(self): category = CategoryFactory.create(user=self.user) @@ -549,11 +530,11 @@ class NestedCategoryPostView(TestCase): data = response.json() posts = data["results"] - self.assertEquals(response.status_code, 200) - self.assertEquals(data["count"], 10) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data["results"]), 10) for post in posts: - self.assertEquals(post["read"], False) + self.assertEqual(post["read"], False) def test_read_posts(self): category = CategoryFactory.create(user=self.user) @@ -572,8 +553,8 @@ class NestedCategoryPostView(TestCase): data = response.json() posts = data["results"] - self.assertEquals(response.status_code, 200) - self.assertEquals(data["count"], 10) + self.assertEqual(response.status_code, 200) + self.assertEqual(len(data["results"]), 10) for post in posts: - self.assertEquals(post["read"], True) + self.assertEqual(post["read"], True) diff --git a/src/newsreader/news/core/tests/endpoints/post/list/__init__.py b/src/newsreader/news/core/tests/endpoints/post/list/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/newsreader/news/core/tests/endpoints/post/list/tests.py b/src/newsreader/news/core/tests/endpoints/post/list/tests.py deleted file mode 100644 index 3bf9d17..0000000 --- a/src/newsreader/news/core/tests/endpoints/post/list/tests.py +++ /dev/null @@ -1,234 +0,0 @@ -from datetime import date, datetime, time - -from django.test import TestCase -from django.urls import reverse - -import pytz - -from newsreader.accounts.tests.factories import UserFactory -from newsreader.news.collection.tests.factories import FeedFactory -from newsreader.news.core.tests.factories import CategoryFactory, FeedPostFactory - - -class PostListViewTestCase(TestCase): - def setUp(self): - self.user = UserFactory(is_staff=True, password="test") - self.client.force_login(self.user) - - def test_simple(self): - rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) - FeedPostFactory.create_batch(size=3, rule=rule) - - response = self.client.get(reverse("api:news:core:posts-list")) - data = response.json() - - self.assertEquals(response.status_code, 200) - self.assertTrue("results" in data) - self.assertTrue("count" in data) - self.assertEquals(data["count"], 3) - - def test_ordering(self): - rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) - - posts = [ - FeedPostFactory( - title="I'm the first post", - rule=rule, - publication_date=datetime.combine( - date(2019, 5, 20), time(hour=16, minute=7, second=37), pytz.utc - ), - ), - FeedPostFactory( - title="I'm the second post", - rule=rule, - publication_date=datetime.combine( - date(2019, 7, 20), time(hour=18, minute=7, second=37), pytz.utc - ), - ), - FeedPostFactory( - title="I'm the third post", - rule=rule, - publication_date=datetime.combine( - date(2019, 7, 20), time(hour=16, minute=7, second=37), pytz.utc - ), - ), - ] - - response = self.client.get(reverse("api:news:core:posts-list")) - data = response.json() - - self.assertEquals(response.status_code, 200) - self.assertTrue("results" in data) - self.assertTrue("count" in data) - self.assertEquals(data["count"], 3) - - self.assertEquals(data["results"][0]["id"], posts[1].pk) - self.assertEquals(data["results"][1]["id"], posts[2].pk) - self.assertEquals(data["results"][2]["id"], posts[0].pk) - - def test_pagination_count(self): - rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) - FeedPostFactory.create_batch(size=80, rule=rule) - page_size = 50 - - response = self.client.get(reverse("api:news:core:posts-list"), {"count": 50}) - data = response.json() - - self.assertEquals(response.status_code, 200) - self.assertEquals(data["count"], 80) - self.assertEquals(len(data["results"]), page_size) - - def test_empty(self): - response = self.client.get(reverse("api:news:core:posts-list")) - data = response.json() - - self.assertEquals(response.status_code, 200) - self.assertTrue("results" in data) - self.assertTrue("count" in data) - - self.assertEquals(data["count"], 0) - self.assertEquals(len(data["results"]), 0) - - def test_post(self): - response = self.client.post(reverse("api:news:core:posts-list")) - data = response.json() - - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "POST" not allowed.') - - def test_patch(self): - response = self.client.patch(reverse("api:news:core:posts-list")) - data = response.json() - - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "PATCH" not allowed.') - - def test_put(self): - response = self.client.put(reverse("api:news:core:posts-list")) - data = response.json() - - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "PUT" not allowed.') - - def test_delete(self): - response = self.client.delete(reverse("api:news:core:posts-list")) - data = response.json() - - self.assertEquals(response.status_code, 405) - self.assertEquals(data["detail"], 'Method "DELETE" not allowed.') - - def test_posts_with_unauthenticated_user_without_category(self): - self.client.logout() - - FeedPostFactory.create_batch(size=3, rule=FeedFactory(user=self.user)) - - response = self.client.get(reverse("api:news:core:posts-list")) - - self.assertEquals(response.status_code, 403) - - def test_posts_with_unauthenticated_user_with_category(self): - self.client.logout() - - category = CategoryFactory(user=self.user) - - FeedPostFactory.create_batch( - size=3, rule=FeedFactory(user=self.user, category=category) - ) - - response = self.client.get(reverse("api:news:core:posts-list")) - - self.assertEquals(response.status_code, 403) - - def test_posts_with_unauthorized_user_without_category(self): - other_user = UserFactory() - - rule = FeedFactory(user=other_user, category=None) - FeedPostFactory.create_batch(size=3, rule=rule) - - response = self.client.get(reverse("api:news:core:posts-list")) - data = response.json() - - self.assertEquals(response.status_code, 200) - self.assertEquals(len(data["results"]), 0) - self.assertEquals(data["count"], 0) - - def test_posts_with_unauthorized_user_with_category(self): - other_user = UserFactory() - category = CategoryFactory(user=other_user) - - FeedPostFactory.create_batch( - size=3, rule=FeedFactory(user=other_user, category=category) - ) - - response = self.client.get(reverse("api:news:core:posts-list")) - data = response.json() - - self.assertEquals(response.status_code, 200) - self.assertEquals(len(data["results"]), 0) - self.assertEquals(data["count"], 0) - - # Note that this situation should not be possible, due to the user not being able - # to specify the user when creating categories/rules - def test_posts_with_authorized_rule_unauthorized_category(self): - other_user = UserFactory() - - rule = FeedFactory(user=self.user, category=CategoryFactory(user=other_user)) - FeedPostFactory.create_batch(size=3, rule=rule) - - response = self.client.get(reverse("api:news:core:posts-list")) - data = response.json() - - self.assertEquals(response.status_code, 200) - self.assertTrue("results" in data) - self.assertTrue("count" in data) - self.assertEquals(data["count"], 0) - - def test_posts_with_authorized_user_without_category(self): - rule = FeedFactory(user=self.user, category=None) - FeedPostFactory.create_batch(size=3, rule=rule) - - response = self.client.get(reverse("api:news:core:posts-list")) - data = response.json() - - self.assertEquals(response.status_code, 200) - self.assertTrue("results" in data) - self.assertTrue("count" in data) - self.assertEquals(data["count"], 3) - - def test_unread_posts(self): - rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) - - FeedPostFactory.create_batch(size=10, rule=rule, read=False) - FeedPostFactory.create_batch(size=10, rule=rule, read=True) - - response = self.client.get( - reverse("api:news:core:posts-list"), {"read": "false"} - ) - - data = response.json() - posts = data["results"] - - self.assertEquals(response.status_code, 200) - self.assertEquals(data["count"], 10) - - for post in posts: - self.assertEquals(post["read"], False) - - def test_read_posts(self): - rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user)) - - FeedPostFactory.create_batch(size=20, rule=rule, read=False) - FeedPostFactory.create_batch(size=10, rule=rule, read=True) - - response = self.client.get( - reverse("api:news:core:posts-list"), {"read": "true"} - ) - - data = response.json() - posts = data["results"] - - self.assertEquals(response.status_code, 200) - self.assertEquals(data["count"], 10) - - for post in posts: - self.assertEquals(post["read"], True) diff --git a/src/newsreader/news/core/urls.py b/src/newsreader/news/core/urls.py index 8096cf8..21db59d 100644 --- a/src/newsreader/news/core/urls.py +++ b/src/newsreader/news/core/urls.py @@ -6,7 +6,6 @@ from newsreader.news.core.endpoints import ( DetailCategoryView, DetailPostView, ListCategoryView, - ListPostView, NestedPostCategoryView, NestedRuleCategoryView, ) @@ -33,7 +32,6 @@ urlpatterns = [ ] endpoints = [ - path("posts/", ListPostView.as_view(), name="posts-list"), path("posts//", DetailPostView.as_view(), name="posts-detail"), path("categories/", ListCategoryView.as_view(), name="categories-list"), path(