parent
91d1757bde
commit
dfa43fa8a2
13 changed files with 166 additions and 588 deletions
|
|
@ -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"
|
page_size_query_param = "count"
|
||||||
max_page_size = 50
|
max_page_size = 50
|
||||||
page_size = 30
|
page_size = 30
|
||||||
|
|
@ -10,3 +10,9 @@ class ResultSetPagination(PageNumberPagination):
|
||||||
class LargeResultSetPagination(ResultSetPagination):
|
class LargeResultSetPagination(ResultSetPagination):
|
||||||
max_page_size = 100
|
max_page_size = 100
|
||||||
page_size = 50
|
page_size = 50
|
||||||
|
|
||||||
|
|
||||||
|
class CursorPagination(pagination.CursorPagination):
|
||||||
|
page_size_query_param = "count"
|
||||||
|
ordering = "-publication_date"
|
||||||
|
page_size = 30
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ export const markPostRead = (post, token) => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fetchPostsBySection = (section, page = false) => {
|
export const fetchPostsBySection = (section, next = false) => {
|
||||||
return dispatch => {
|
return dispatch => {
|
||||||
if (section.unread === 0) {
|
if (section.unread === 0) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -76,10 +76,10 @@ export const fetchPostsBySection = (section, page = false) => {
|
||||||
|
|
||||||
switch (section.type) {
|
switch (section.type) {
|
||||||
case RULE_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;
|
break;
|
||||||
case CATEGORY_TYPE:
|
case CATEGORY_TYPE:
|
||||||
url = page ? page : `/api/categories/${section.id}/posts/?read=false`;
|
url = next ? next : `/api/categories/${section.id}/posts/?read=false`;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ const mapStateToProps = state => ({
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
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);
|
export default connect(mapStateToProps, mapDispatchToProps)(PostList);
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ if (page) {
|
||||||
const settings = JSON.parse(document.getElementById('homepageSettings').textContent);
|
const settings = JSON.parse(document.getElementById('homepageSettings').textContent);
|
||||||
const { feedUrl, subredditUrl, timelineUrl, categoriesUrl } = settings;
|
const { feedUrl, subredditUrl, timelineUrl, categoriesUrl } = settings;
|
||||||
|
|
||||||
ReactDOM.render(
|
const app = (
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<App
|
<App
|
||||||
feedUrl={feedUrl.substring(1, feedUrl.length - 3)}
|
feedUrl={feedUrl.substring(1, feedUrl.length - 3)}
|
||||||
|
|
@ -24,7 +24,8 @@ if (page) {
|
||||||
timezone={settings.timezone}
|
timezone={settings.timezone}
|
||||||
autoMarking={settings.autoMarking}
|
autoMarking={settings.autoMarking}
|
||||||
/>
|
/>
|
||||||
</Provider>,
|
</Provider>
|
||||||
page
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ReactDOM.render(app, page);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,16 @@ from rest_framework import status
|
||||||
from rest_framework.generics import (
|
from rest_framework.generics import (
|
||||||
GenericAPIView,
|
GenericAPIView,
|
||||||
ListAPIView,
|
ListAPIView,
|
||||||
RetrieveUpdateDestroyAPIView,
|
RetrieveUpdateAPIView,
|
||||||
get_object_or_404,
|
get_object_or_404,
|
||||||
)
|
)
|
||||||
from rest_framework.response import Response
|
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.models import CollectionRule
|
||||||
from newsreader.news.collection.serializers import RuleSerializer
|
from newsreader.news.collection.serializers import RuleSerializer
|
||||||
from newsreader.news.core.filters import ReadFilter
|
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
|
from newsreader.news.core.serializers import PostSerializer
|
||||||
|
|
||||||
|
|
||||||
class ListRuleView(ListAPIView):
|
class DetailRuleView(RetrieveUpdateAPIView):
|
||||||
queryset = CollectionRule.objects.all()
|
queryset = CollectionRule.objects.all()
|
||||||
serializer_class = RuleSerializer
|
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):
|
class NestedRuleView(ListAPIView):
|
||||||
queryset = CollectionRule.objects.prefetch_related("posts").all()
|
queryset = CollectionRule.objects.prefetch_related("posts").all()
|
||||||
serializer_class = PostSerializer
|
serializer_class = PostSerializer
|
||||||
pagination_class = LargeResultSetPagination
|
pagination_class = CursorPagination
|
||||||
filter_backends = [ReadFilter]
|
filter_backends = [ReadFilter]
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
|
|
|
||||||
|
|
@ -121,15 +121,6 @@ class CollectionRuleDetailViewTestCase(TestCase):
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEquals(response.status_code, 200)
|
||||||
self.assertEquals(data["name"], "BBC")
|
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):
|
def test_rule_with_unauthenticated_user(self):
|
||||||
self.client.logout()
|
self.client.logout()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,137 +12,6 @@ from newsreader.news.collection.tests.factories import FeedFactory
|
||||||
from newsreader.news.core.tests.factories import CategoryFactory, FeedPostFactory
|
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):
|
class NestedRuleListViewTestCase(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.user = UserFactory(password="test")
|
self.user = UserFactory(password="test")
|
||||||
|
|
@ -157,11 +26,10 @@ class NestedRuleListViewTestCase(TestCase):
|
||||||
)
|
)
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertEqual(len(data["results"]), 5)
|
||||||
self.assertTrue("results" in data)
|
self.assertEqual(data["next"], None)
|
||||||
self.assertTrue("count" in data)
|
self.assertEqual(data["previous"], None)
|
||||||
self.assertEquals(data["count"], 5)
|
|
||||||
|
|
||||||
def test_pagination(self):
|
def test_pagination(self):
|
||||||
rule = FeedFactory.create(user=self.user)
|
rule = FeedFactory.create(user=self.user)
|
||||||
|
|
@ -178,11 +46,12 @@ class NestedRuleListViewTestCase(TestCase):
|
||||||
)
|
)
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEquals(data["count"], 80)
|
self.assertTrue(data["next"])
|
||||||
self.assertEquals(len(data["results"]), 30)
|
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]]
|
[post["id"] for post in data["results"]], [post.id for post in posts[:30]]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -194,16 +63,15 @@ class NestedRuleListViewTestCase(TestCase):
|
||||||
)
|
)
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEquals(data["count"], 0)
|
self.assertEqual(len(data["results"]), 0)
|
||||||
self.assertEquals(len(data["results"]), 0)
|
|
||||||
|
|
||||||
def test_not_known(self):
|
def test_not_known(self):
|
||||||
response = self.client.get(
|
response = self.client.get(
|
||||||
reverse("api:news:collection:rules-nested-posts", kwargs={"pk": 0})
|
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):
|
def test_post(self):
|
||||||
rule = FeedFactory.create(user=self.user)
|
rule = FeedFactory.create(user=self.user)
|
||||||
|
|
@ -215,8 +83,8 @@ class NestedRuleListViewTestCase(TestCase):
|
||||||
)
|
)
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 405)
|
self.assertEqual(response.status_code, 405)
|
||||||
self.assertEquals(data["detail"], 'Method "POST" not allowed.')
|
self.assertEqual(data["detail"], 'Method "POST" not allowed.')
|
||||||
|
|
||||||
def test_patch(self):
|
def test_patch(self):
|
||||||
rule = FeedFactory.create(user=self.user)
|
rule = FeedFactory.create(user=self.user)
|
||||||
|
|
@ -228,8 +96,8 @@ class NestedRuleListViewTestCase(TestCase):
|
||||||
)
|
)
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 405)
|
self.assertEqual(response.status_code, 405)
|
||||||
self.assertEquals(data["detail"], 'Method "PATCH" not allowed.')
|
self.assertEqual(data["detail"], 'Method "PATCH" not allowed.')
|
||||||
|
|
||||||
def test_put(self):
|
def test_put(self):
|
||||||
rule = FeedFactory.create(user=self.user)
|
rule = FeedFactory.create(user=self.user)
|
||||||
|
|
@ -241,8 +109,8 @@ class NestedRuleListViewTestCase(TestCase):
|
||||||
)
|
)
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 405)
|
self.assertEqual(response.status_code, 405)
|
||||||
self.assertEquals(data["detail"], 'Method "PUT" not allowed.')
|
self.assertEqual(data["detail"], 'Method "PUT" not allowed.')
|
||||||
|
|
||||||
def test_delete(self):
|
def test_delete(self):
|
||||||
rule = FeedFactory.create(user=self.user)
|
rule = FeedFactory.create(user=self.user)
|
||||||
|
|
@ -254,8 +122,8 @@ class NestedRuleListViewTestCase(TestCase):
|
||||||
)
|
)
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 405)
|
self.assertEqual(response.status_code, 405)
|
||||||
self.assertEquals(data["detail"], 'Method "DELETE" not allowed.')
|
self.assertEqual(data["detail"], 'Method "DELETE" not allowed.')
|
||||||
|
|
||||||
def test_rule_with_unauthenticated_user(self):
|
def test_rule_with_unauthenticated_user(self):
|
||||||
self.client.logout()
|
self.client.logout()
|
||||||
|
|
@ -266,7 +134,7 @@ class NestedRuleListViewTestCase(TestCase):
|
||||||
reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk})
|
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):
|
def test_rule_with_unauthorized_user(self):
|
||||||
other_user = UserFactory()
|
other_user = UserFactory()
|
||||||
|
|
@ -276,7 +144,7 @@ class NestedRuleListViewTestCase(TestCase):
|
||||||
reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk})
|
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):
|
def test_posts_ordering(self):
|
||||||
rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user))
|
rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user))
|
||||||
|
|
@ -310,14 +178,12 @@ class NestedRuleListViewTestCase(TestCase):
|
||||||
)
|
)
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertTrue("results" in data)
|
self.assertEqual(len(data["results"]), 3)
|
||||||
self.assertTrue("count" in data)
|
|
||||||
self.assertEquals(data["count"], 3)
|
|
||||||
|
|
||||||
self.assertEquals(data["results"][0]["id"], posts[1].pk)
|
self.assertEqual(data["results"][0]["id"], posts[1].pk)
|
||||||
self.assertEquals(data["results"][1]["id"], posts[2].pk)
|
self.assertEqual(data["results"][1]["id"], posts[2].pk)
|
||||||
self.assertEquals(data["results"][2]["id"], posts[0].pk)
|
self.assertEqual(data["results"][2]["id"], posts[0].pk)
|
||||||
|
|
||||||
def test_only_posts_from_rule_are_returned(self):
|
def test_only_posts_from_rule_are_returned(self):
|
||||||
rule = FeedFactory.create(user=self.user)
|
rule = FeedFactory.create(user=self.user)
|
||||||
|
|
@ -331,14 +197,12 @@ class NestedRuleListViewTestCase(TestCase):
|
||||||
)
|
)
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertEqual(len(data["results"]), 5)
|
||||||
self.assertTrue("results" in data)
|
|
||||||
self.assertTrue("count" in data)
|
|
||||||
self.assertEquals(data["count"], 5)
|
|
||||||
|
|
||||||
for post in data["results"]:
|
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):
|
def test_unread_posts(self):
|
||||||
rule = FeedFactory.create(user=self.user)
|
rule = FeedFactory.create(user=self.user)
|
||||||
|
|
@ -352,13 +216,13 @@ class NestedRuleListViewTestCase(TestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
data = response.json()
|
data = response.json()
|
||||||
posts = data["results"]
|
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEquals(data["count"], 10)
|
self.assertEqual(len(data["results"]), 10)
|
||||||
|
|
||||||
for post in posts:
|
for post in data["results"]:
|
||||||
self.assertEquals(post["read"], False)
|
with self.subTest(post=post):
|
||||||
|
self.assertEqual(post["read"], False)
|
||||||
|
|
||||||
def test_read_posts(self):
|
def test_read_posts(self):
|
||||||
rule = FeedFactory.create(user=self.user)
|
rule = FeedFactory.create(user=self.user)
|
||||||
|
|
@ -372,10 +236,10 @@ class NestedRuleListViewTestCase(TestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
data = response.json()
|
data = response.json()
|
||||||
posts = data["results"]
|
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEquals(data["count"], 10)
|
self.assertEqual(len(data["results"]), 10)
|
||||||
|
|
||||||
for post in posts:
|
for post in data["results"]:
|
||||||
self.assertEquals(post["read"], True)
|
with self.subTest(post=post):
|
||||||
|
self.assertEqual(post["read"], True)
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ from django.urls import path
|
||||||
|
|
||||||
from newsreader.news.collection.endpoints import (
|
from newsreader.news.collection.endpoints import (
|
||||||
DetailRuleView,
|
DetailRuleView,
|
||||||
ListRuleView,
|
|
||||||
NestedRuleView,
|
NestedRuleView,
|
||||||
RuleReadView,
|
RuleReadView,
|
||||||
)
|
)
|
||||||
|
|
@ -26,7 +25,6 @@ endpoints = [
|
||||||
path("rules/<int:pk>/", DetailRuleView.as_view(), name="rules-detail"),
|
path("rules/<int:pk>/", DetailRuleView.as_view(), name="rules-detail"),
|
||||||
path("rules/<int:pk>/posts/", NestedRuleView.as_view(), name="rules-nested-posts"),
|
path("rules/<int:pk>/posts/", NestedRuleView.as_view(), name="rules-nested-posts"),
|
||||||
path("rules/<int:pk>/read/", RuleReadView.as_view(), name="rules-read"),
|
path("rules/<int:pk>/read/", RuleReadView.as_view(), name="rules-read"),
|
||||||
path("rules/", ListRuleView.as_view(), name="rules-list"),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
from django.db.models import Q
|
|
||||||
|
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.generics import (
|
from rest_framework.generics import (
|
||||||
GenericAPIView,
|
GenericAPIView,
|
||||||
|
|
@ -13,30 +11,13 @@ from rest_framework.permissions import IsAuthenticated
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
|
||||||
from newsreader.accounts.permissions import IsPostOwner
|
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.collection.serializers import RuleSerializer
|
||||||
from newsreader.news.core.filters import ReadFilter
|
from newsreader.news.core.filters import ReadFilter
|
||||||
from newsreader.news.core.models import Category, Post
|
from newsreader.news.core.models import Category, Post
|
||||||
from newsreader.news.core.serializers import CategorySerializer, PostSerializer
|
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):
|
class DetailPostView(RetrieveUpdateAPIView):
|
||||||
queryset = Post.objects.all()
|
queryset = Post.objects.all()
|
||||||
serializer_class = PostSerializer
|
serializer_class = PostSerializer
|
||||||
|
|
@ -77,7 +58,7 @@ class NestedRuleCategoryView(ListAPIView):
|
||||||
class NestedPostCategoryView(ListAPIView):
|
class NestedPostCategoryView(ListAPIView):
|
||||||
queryset = Category.objects.prefetch_related("rules", "rules__posts").all()
|
queryset = Category.objects.prefetch_related("rules", "rules__posts").all()
|
||||||
serializer_class = PostSerializer
|
serializer_class = PostSerializer
|
||||||
pagination_class = LargeResultSetPagination
|
pagination_class = CursorPagination
|
||||||
filter_backends = [ReadFilter]
|
filter_backends = [ReadFilter]
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
|
|
@ -90,9 +71,8 @@ class NestedPostCategoryView(ListAPIView):
|
||||||
category = get_object_or_404(self.queryset, **filter_kwargs)
|
category = get_object_or_404(self.queryset, **filter_kwargs)
|
||||||
self.check_object_permissions(self.request, category)
|
self.check_object_permissions(self.request, category)
|
||||||
|
|
||||||
queryset = Post.objects.filter(
|
rules = category.rules.values_list("id", flat=True)
|
||||||
rule__in=category.rules.values_list("id", flat=True)
|
queryset = Post.objects.filter(rule__in=rules)
|
||||||
).order_by("-publication_date", "rule__name")
|
|
||||||
|
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from datetime import date, datetime, time
|
from datetime import datetime
|
||||||
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
@ -23,27 +23,21 @@ class CategoryListViewTestCase(TestCase):
|
||||||
response = self.client.get(reverse("api:news:core:categories-list"))
|
response = self.client.get(reverse("api:news:core:categories-list"))
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEquals(len(data), 3)
|
self.assertEqual(len(data), 3)
|
||||||
|
|
||||||
def test_ordering(self):
|
def test_ordering(self):
|
||||||
categories = [
|
categories = [
|
||||||
CategoryFactory(
|
CategoryFactory(
|
||||||
created=datetime.combine(
|
created=datetime(2019, 5, 20, 16, 7, 37, tzinfo=pytz.utc),
|
||||||
date(2019, 5, 20), time(hour=16, minute=7, second=37), pytz.utc
|
|
||||||
),
|
|
||||||
user=self.user,
|
user=self.user,
|
||||||
),
|
),
|
||||||
CategoryFactory(
|
CategoryFactory(
|
||||||
created=datetime.combine(
|
created=datetime(2019, 7, 20, 18, 7, 37, tzinfo=pytz.utc),
|
||||||
date(2019, 7, 20), time(hour=18, minute=7, second=37), pytz.utc
|
|
||||||
),
|
|
||||||
user=self.user,
|
user=self.user,
|
||||||
),
|
),
|
||||||
CategoryFactory(
|
CategoryFactory(
|
||||||
created=datetime.combine(
|
created=datetime(2019, 7, 20, 16, 7, 37, tzinfo=pytz.utc),
|
||||||
date(2019, 7, 20), time(hour=16, minute=7, second=37), pytz.utc
|
|
||||||
),
|
|
||||||
user=self.user,
|
user=self.user,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
@ -51,18 +45,18 @@ class CategoryListViewTestCase(TestCase):
|
||||||
response = self.client.get(reverse("api:news:core:categories-list"))
|
response = self.client.get(reverse("api:news:core:categories-list"))
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
self.assertEquals(data[0]["id"], categories[1].pk)
|
self.assertEqual(data[0]["id"], categories[1].pk)
|
||||||
self.assertEquals(data[1]["id"], categories[2].pk)
|
self.assertEqual(data[1]["id"], categories[2].pk)
|
||||||
self.assertEquals(data[2]["id"], categories[0].pk)
|
self.assertEqual(data[2]["id"], categories[0].pk)
|
||||||
|
|
||||||
def test_empty(self):
|
def test_empty(self):
|
||||||
response = self.client.get(reverse("api:news:core:categories-list"))
|
response = self.client.get(reverse("api:news:core:categories-list"))
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEquals(len(data), 0)
|
self.assertEqual(len(data), 0)
|
||||||
|
|
||||||
def test_post(self):
|
def test_post(self):
|
||||||
data = {"name": "Tech"}
|
data = {"name": "Tech"}
|
||||||
|
|
@ -74,29 +68,29 @@ class CategoryListViewTestCase(TestCase):
|
||||||
)
|
)
|
||||||
response_data = response.json()
|
response_data = response.json()
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 405)
|
self.assertEqual(response.status_code, 405)
|
||||||
self.assertEquals(response_data["detail"], 'Method "POST" not allowed.')
|
self.assertEqual(response_data["detail"], 'Method "POST" not allowed.')
|
||||||
|
|
||||||
def test_patch(self):
|
def test_patch(self):
|
||||||
response = self.client.patch(reverse("api:news:core:categories-list"))
|
response = self.client.patch(reverse("api:news:core:categories-list"))
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 405)
|
self.assertEqual(response.status_code, 405)
|
||||||
self.assertEquals(data["detail"], 'Method "PATCH" not allowed.')
|
self.assertEqual(data["detail"], 'Method "PATCH" not allowed.')
|
||||||
|
|
||||||
def test_put(self):
|
def test_put(self):
|
||||||
response = self.client.put(reverse("api:news:core:categories-list"))
|
response = self.client.put(reverse("api:news:core:categories-list"))
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 405)
|
self.assertEqual(response.status_code, 405)
|
||||||
self.assertEquals(data["detail"], 'Method "PUT" not allowed.')
|
self.assertEqual(data["detail"], 'Method "PUT" not allowed.')
|
||||||
|
|
||||||
def test_delete(self):
|
def test_delete(self):
|
||||||
response = self.client.delete(reverse("api:news:core:categories-list"))
|
response = self.client.delete(reverse("api:news:core:categories-list"))
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 405)
|
self.assertEqual(response.status_code, 405)
|
||||||
self.assertEquals(data["detail"], 'Method "DELETE" not allowed.')
|
self.assertEqual(data["detail"], 'Method "DELETE" not allowed.')
|
||||||
|
|
||||||
def test_categories_with_unauthenticated_user(self):
|
def test_categories_with_unauthenticated_user(self):
|
||||||
self.client.logout()
|
self.client.logout()
|
||||||
|
|
@ -105,7 +99,7 @@ class CategoryListViewTestCase(TestCase):
|
||||||
|
|
||||||
response = self.client.get(reverse("api:news:core:categories-list"))
|
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):
|
def test_categories_with_unauthorized_user(self):
|
||||||
other_user = UserFactory()
|
other_user = UserFactory()
|
||||||
|
|
@ -114,8 +108,8 @@ class CategoryListViewTestCase(TestCase):
|
||||||
response = self.client.get(reverse("api:news:core:categories-list"))
|
response = self.client.get(reverse("api:news:core:categories-list"))
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEquals(len(data), 0)
|
self.assertEqual(len(data), 0)
|
||||||
|
|
||||||
|
|
||||||
class NestedCategoryListViewTestCase(TestCase):
|
class NestedCategoryListViewTestCase(TestCase):
|
||||||
|
|
@ -132,8 +126,8 @@ class NestedCategoryListViewTestCase(TestCase):
|
||||||
)
|
)
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEquals(len(data), 5)
|
self.assertEqual(len(data), 5)
|
||||||
|
|
||||||
self.assertTrue("id" in data[0])
|
self.assertTrue("id" in data[0])
|
||||||
self.assertTrue("name" in data[0])
|
self.assertTrue("name" in data[0])
|
||||||
|
|
@ -149,16 +143,16 @@ class NestedCategoryListViewTestCase(TestCase):
|
||||||
)
|
)
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEquals(len(data), 0)
|
self.assertEqual(len(data), 0)
|
||||||
self.assertEquals(data, [])
|
self.assertEqual(data, [])
|
||||||
|
|
||||||
def test_not_known(self):
|
def test_not_known(self):
|
||||||
response = self.client.get(
|
response = self.client.get(
|
||||||
reverse("api:news:core:categories-nested-rules", kwargs={"pk": 100})
|
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):
|
def test_post(self):
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
|
|
@ -168,8 +162,8 @@ class NestedCategoryListViewTestCase(TestCase):
|
||||||
)
|
)
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 405)
|
self.assertEqual(response.status_code, 405)
|
||||||
self.assertEquals(data["detail"], 'Method "POST" not allowed.')
|
self.assertEqual(data["detail"], 'Method "POST" not allowed.')
|
||||||
|
|
||||||
def test_patch(self):
|
def test_patch(self):
|
||||||
category = CategoryFactory.create(user=self.user)
|
category = CategoryFactory.create(user=self.user)
|
||||||
|
|
@ -183,8 +177,8 @@ class NestedCategoryListViewTestCase(TestCase):
|
||||||
)
|
)
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 405)
|
self.assertEqual(response.status_code, 405)
|
||||||
self.assertEquals(data["detail"], 'Method "PATCH" not allowed.')
|
self.assertEqual(data["detail"], 'Method "PATCH" not allowed.')
|
||||||
|
|
||||||
def test_put(self):
|
def test_put(self):
|
||||||
category = CategoryFactory.create(user=self.user)
|
category = CategoryFactory.create(user=self.user)
|
||||||
|
|
@ -198,8 +192,8 @@ class NestedCategoryListViewTestCase(TestCase):
|
||||||
)
|
)
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 405)
|
self.assertEqual(response.status_code, 405)
|
||||||
self.assertEquals(data["detail"], 'Method "PUT" not allowed.')
|
self.assertEqual(data["detail"], 'Method "PUT" not allowed.')
|
||||||
|
|
||||||
def test_delete(self):
|
def test_delete(self):
|
||||||
category = CategoryFactory.create(user=self.user)
|
category = CategoryFactory.create(user=self.user)
|
||||||
|
|
@ -212,8 +206,8 @@ class NestedCategoryListViewTestCase(TestCase):
|
||||||
)
|
)
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 405)
|
self.assertEqual(response.status_code, 405)
|
||||||
self.assertEquals(data["detail"], 'Method "DELETE" not allowed.')
|
self.assertEqual(data["detail"], 'Method "DELETE" not allowed.')
|
||||||
|
|
||||||
def test_with_unauthenticated_user(self):
|
def test_with_unauthenticated_user(self):
|
||||||
self.client.logout()
|
self.client.logout()
|
||||||
|
|
@ -225,7 +219,7 @@ class NestedCategoryListViewTestCase(TestCase):
|
||||||
reverse("api:news:core:categories-nested-rules", kwargs={"pk": category.pk})
|
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):
|
def test_with_unauthorized_user(self):
|
||||||
other_user = UserFactory.create()
|
other_user = UserFactory.create()
|
||||||
|
|
@ -237,7 +231,7 @@ class NestedCategoryListViewTestCase(TestCase):
|
||||||
reverse("api:news:core:categories-nested-rules", kwargs={"pk": category.pk})
|
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):
|
def test_ordering(self):
|
||||||
category = CategoryFactory.create(user=self.user)
|
category = CategoryFactory.create(user=self.user)
|
||||||
|
|
@ -252,12 +246,12 @@ class NestedCategoryListViewTestCase(TestCase):
|
||||||
)
|
)
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEquals(len(data), 3)
|
self.assertEqual(len(data), 3)
|
||||||
|
|
||||||
self.assertEquals(data[0]["id"], rules[2].pk)
|
self.assertEqual(data[0]["id"], rules[2].pk)
|
||||||
self.assertEquals(data[1]["id"], rules[0].pk)
|
self.assertEqual(data[1]["id"], rules[0].pk)
|
||||||
self.assertEquals(data[2]["id"], rules[1].pk)
|
self.assertEqual(data[2]["id"], rules[1].pk)
|
||||||
|
|
||||||
def test_only_rules_from_category_are_returned(self):
|
def test_only_rules_from_category_are_returned(self):
|
||||||
other_category = CategoryFactory(user=self.user)
|
other_category = CategoryFactory(user=self.user)
|
||||||
|
|
@ -275,12 +269,12 @@ class NestedCategoryListViewTestCase(TestCase):
|
||||||
)
|
)
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEquals(len(data), 3)
|
self.assertEqual(len(data), 3)
|
||||||
|
|
||||||
self.assertEquals(data[0]["id"], rules[2].pk)
|
self.assertEqual(data[0]["id"], rules[2].pk)
|
||||||
self.assertEquals(data[1]["id"], rules[0].pk)
|
self.assertEqual(data[1]["id"], rules[0].pk)
|
||||||
self.assertEquals(data[2]["id"], rules[1].pk)
|
self.assertEqual(data[2]["id"], rules[1].pk)
|
||||||
|
|
||||||
|
|
||||||
class NestedCategoryPostView(TestCase):
|
class NestedCategoryPostView(TestCase):
|
||||||
|
|
@ -301,16 +295,15 @@ class NestedCategoryPostView(TestCase):
|
||||||
reverse("api:news:core:categories-nested-posts", kwargs={"pk": category.pk})
|
reverse("api:news:core:categories-nested-posts", kwargs={"pk": category.pk})
|
||||||
)
|
)
|
||||||
data = response.json()
|
data = response.json()
|
||||||
posts = data["results"]
|
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEquals(data["count"], 25)
|
self.assertEqual(len(data["results"]), 25)
|
||||||
|
|
||||||
self.assertTrue("id" in posts[0])
|
self.assertTrue("id" in data["results"][0])
|
||||||
self.assertTrue("title" in posts[0])
|
self.assertTrue("title" in data["results"][0])
|
||||||
self.assertTrue("body" in posts[0])
|
self.assertTrue("body" in data["results"][0])
|
||||||
self.assertTrue("rule" in posts[0])
|
self.assertTrue("rule" in data["results"][0])
|
||||||
self.assertTrue("url" in posts[0])
|
self.assertTrue("url" in data["results"][0])
|
||||||
|
|
||||||
def test_no_rules(self):
|
def test_no_rules(self):
|
||||||
category = CategoryFactory.create(user=self.user)
|
category = CategoryFactory.create(user=self.user)
|
||||||
|
|
@ -321,9 +314,9 @@ class NestedCategoryPostView(TestCase):
|
||||||
data = response.json()
|
data = response.json()
|
||||||
posts = data["results"]
|
posts = data["results"]
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEquals(data["count"], 0)
|
self.assertEqual(len(data["results"]), 0)
|
||||||
self.assertEquals(posts, [])
|
self.assertEqual(posts, [])
|
||||||
|
|
||||||
def test_no_posts(self):
|
def test_no_posts(self):
|
||||||
category = CategoryFactory.create(user=self.user)
|
category = CategoryFactory.create(user=self.user)
|
||||||
|
|
@ -335,16 +328,16 @@ class NestedCategoryPostView(TestCase):
|
||||||
data = response.json()
|
data = response.json()
|
||||||
posts = data["results"]
|
posts = data["results"]
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEquals(data["count"], 0)
|
self.assertEqual(len(data["results"]), 0)
|
||||||
self.assertEquals(posts, [])
|
self.assertEqual(posts, [])
|
||||||
|
|
||||||
def test_not_known(self):
|
def test_not_known(self):
|
||||||
response = self.client.get(
|
response = self.client.get(
|
||||||
reverse("api:news:core:categories-nested-posts", kwargs={"pk": 100})
|
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):
|
def test_post(self):
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
|
|
@ -354,8 +347,8 @@ class NestedCategoryPostView(TestCase):
|
||||||
)
|
)
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 405)
|
self.assertEqual(response.status_code, 405)
|
||||||
self.assertEquals(data["detail"], 'Method "POST" not allowed.')
|
self.assertEqual(data["detail"], 'Method "POST" not allowed.')
|
||||||
|
|
||||||
def test_patch(self):
|
def test_patch(self):
|
||||||
category = CategoryFactory.create(user=self.user)
|
category = CategoryFactory.create(user=self.user)
|
||||||
|
|
@ -369,8 +362,8 @@ class NestedCategoryPostView(TestCase):
|
||||||
)
|
)
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 405)
|
self.assertEqual(response.status_code, 405)
|
||||||
self.assertEquals(data["detail"], 'Method "PATCH" not allowed.')
|
self.assertEqual(data["detail"], 'Method "PATCH" not allowed.')
|
||||||
|
|
||||||
def test_put(self):
|
def test_put(self):
|
||||||
category = CategoryFactory.create(user=self.user)
|
category = CategoryFactory.create(user=self.user)
|
||||||
|
|
@ -384,8 +377,8 @@ class NestedCategoryPostView(TestCase):
|
||||||
)
|
)
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 405)
|
self.assertEqual(response.status_code, 405)
|
||||||
self.assertEquals(data["detail"], 'Method "PUT" not allowed.')
|
self.assertEqual(data["detail"], 'Method "PUT" not allowed.')
|
||||||
|
|
||||||
def test_delete(self):
|
def test_delete(self):
|
||||||
category = CategoryFactory.create(user=self.user)
|
category = CategoryFactory.create(user=self.user)
|
||||||
|
|
@ -398,8 +391,8 @@ class NestedCategoryPostView(TestCase):
|
||||||
)
|
)
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 405)
|
self.assertEqual(response.status_code, 405)
|
||||||
self.assertEquals(data["detail"], 'Method "DELETE" not allowed.')
|
self.assertEqual(data["detail"], 'Method "DELETE" not allowed.')
|
||||||
|
|
||||||
def test_with_unauthenticated_user(self):
|
def test_with_unauthenticated_user(self):
|
||||||
self.client.logout()
|
self.client.logout()
|
||||||
|
|
@ -410,7 +403,7 @@ class NestedCategoryPostView(TestCase):
|
||||||
reverse("api:news:core:categories-nested-posts", kwargs={"pk": category.pk})
|
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):
|
def test_with_unauthorized_user(self):
|
||||||
other_user = UserFactory.create()
|
other_user = UserFactory.create()
|
||||||
|
|
@ -420,7 +413,7 @@ class NestedCategoryPostView(TestCase):
|
||||||
reverse("api:news:core:categories-nested-posts", kwargs={"pk": category.pk})
|
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):
|
def test_ordering(self):
|
||||||
category = CategoryFactory.create(user=self.user)
|
category = CategoryFactory.create(user=self.user)
|
||||||
|
|
@ -437,16 +430,12 @@ class NestedCategoryPostView(TestCase):
|
||||||
FeedPostFactory.create(
|
FeedPostFactory.create(
|
||||||
title="Second Reuters post",
|
title="Second Reuters post",
|
||||||
rule=reuters_rule,
|
rule=reuters_rule,
|
||||||
publication_date=datetime.combine(
|
publication_date=datetime(2019, 5, 21, 15, tzinfo=pytz.utc),
|
||||||
date(2019, 5, 21), time(hour=16, minute=7, second=37), pytz.utc
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
FeedPostFactory.create(
|
FeedPostFactory.create(
|
||||||
title="First Reuters post",
|
title="First Reuters post",
|
||||||
rule=reuters_rule,
|
rule=reuters_rule,
|
||||||
publication_date=datetime.combine(
|
publication_date=datetime(2019, 5, 20, 12, tzinfo=pytz.utc),
|
||||||
date(2019, 5, 20), time(hour=16, minute=7, second=37), pytz.utc
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -454,16 +443,12 @@ class NestedCategoryPostView(TestCase):
|
||||||
FeedPostFactory.create(
|
FeedPostFactory.create(
|
||||||
title="Second Guardian post",
|
title="Second Guardian post",
|
||||||
rule=guardian_rule,
|
rule=guardian_rule,
|
||||||
publication_date=datetime.combine(
|
publication_date=datetime(2019, 5, 21, 14, tzinfo=pytz.utc),
|
||||||
date(2019, 5, 21), time(hour=16, minute=7, second=37), pytz.utc
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
FeedPostFactory.create(
|
FeedPostFactory.create(
|
||||||
title="First Guardian post",
|
title="First Guardian post",
|
||||||
rule=guardian_rule,
|
rule=guardian_rule,
|
||||||
publication_date=datetime.combine(
|
publication_date=datetime(2019, 5, 20, 11, tzinfo=pytz.utc),
|
||||||
date(2019, 5, 20), time(hour=16, minute=7, second=37), pytz.utc
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -471,16 +456,12 @@ class NestedCategoryPostView(TestCase):
|
||||||
FeedPostFactory.create(
|
FeedPostFactory.create(
|
||||||
title="Second BBC post",
|
title="Second BBC post",
|
||||||
rule=bbc_rule,
|
rule=bbc_rule,
|
||||||
publication_date=datetime.combine(
|
publication_date=datetime(2019, 5, 21, 16, tzinfo=pytz.utc),
|
||||||
date(2019, 5, 21), time(hour=16, minute=7, second=37), pytz.utc
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
FeedPostFactory.create(
|
FeedPostFactory.create(
|
||||||
title="First BBC post",
|
title="First BBC post",
|
||||||
rule=bbc_rule,
|
rule=bbc_rule,
|
||||||
publication_date=datetime.combine(
|
publication_date=datetime(2019, 5, 20, 13, tzinfo=pytz.utc),
|
||||||
date(2019, 5, 20), time(hour=16, minute=7, second=37), pytz.utc
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -490,16 +471,16 @@ class NestedCategoryPostView(TestCase):
|
||||||
data = response.json()
|
data = response.json()
|
||||||
posts = data["results"]
|
posts = data["results"]
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEquals(data["count"], 6)
|
self.assertEqual(len(data["results"]), 6)
|
||||||
|
|
||||||
self.assertEquals(posts[0]["title"], "Second BBC post")
|
self.assertEqual(posts[0]["title"], "Second BBC post")
|
||||||
self.assertEquals(posts[1]["title"], "Second Reuters post")
|
self.assertEqual(posts[1]["title"], "Second Reuters post")
|
||||||
self.assertEquals(posts[2]["title"], "Second Guardian post")
|
self.assertEqual(posts[2]["title"], "Second Guardian post")
|
||||||
|
|
||||||
self.assertEquals(posts[3]["title"], "First BBC post")
|
self.assertEqual(posts[3]["title"], "First BBC post")
|
||||||
self.assertEquals(posts[4]["title"], "First Reuters post")
|
self.assertEqual(posts[4]["title"], "First Reuters post")
|
||||||
self.assertEquals(posts[5]["title"], "First Guardian post")
|
self.assertEqual(posts[5]["title"], "First Guardian post")
|
||||||
|
|
||||||
def test_only_posts_from_category_are_returned(self):
|
def test_only_posts_from_category_are_returned(self):
|
||||||
category = CategoryFactory.create(user=self.user)
|
category = CategoryFactory.create(user=self.user)
|
||||||
|
|
@ -526,11 +507,11 @@ class NestedCategoryPostView(TestCase):
|
||||||
data = response.json()
|
data = response.json()
|
||||||
posts = data["results"]
|
posts = data["results"]
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEquals(data["count"], 2)
|
self.assertEqual(len(data["results"]), 2)
|
||||||
|
|
||||||
self.assertEquals(posts[0]["rule"], guardian_rule.pk)
|
self.assertEqual(posts[0]["rule"], guardian_rule.pk)
|
||||||
self.assertEquals(posts[1]["rule"], guardian_rule.pk)
|
self.assertEqual(posts[1]["rule"], guardian_rule.pk)
|
||||||
|
|
||||||
def test_unread_posts(self):
|
def test_unread_posts(self):
|
||||||
category = CategoryFactory.create(user=self.user)
|
category = CategoryFactory.create(user=self.user)
|
||||||
|
|
@ -549,11 +530,11 @@ class NestedCategoryPostView(TestCase):
|
||||||
data = response.json()
|
data = response.json()
|
||||||
posts = data["results"]
|
posts = data["results"]
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEquals(data["count"], 10)
|
self.assertEqual(len(data["results"]), 10)
|
||||||
|
|
||||||
for post in posts:
|
for post in posts:
|
||||||
self.assertEquals(post["read"], False)
|
self.assertEqual(post["read"], False)
|
||||||
|
|
||||||
def test_read_posts(self):
|
def test_read_posts(self):
|
||||||
category = CategoryFactory.create(user=self.user)
|
category = CategoryFactory.create(user=self.user)
|
||||||
|
|
@ -572,8 +553,8 @@ class NestedCategoryPostView(TestCase):
|
||||||
data = response.json()
|
data = response.json()
|
||||||
posts = data["results"]
|
posts = data["results"]
|
||||||
|
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEquals(data["count"], 10)
|
self.assertEqual(len(data["results"]), 10)
|
||||||
|
|
||||||
for post in posts:
|
for post in posts:
|
||||||
self.assertEquals(post["read"], True)
|
self.assertEqual(post["read"], True)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -6,7 +6,6 @@ from newsreader.news.core.endpoints import (
|
||||||
DetailCategoryView,
|
DetailCategoryView,
|
||||||
DetailPostView,
|
DetailPostView,
|
||||||
ListCategoryView,
|
ListCategoryView,
|
||||||
ListPostView,
|
|
||||||
NestedPostCategoryView,
|
NestedPostCategoryView,
|
||||||
NestedRuleCategoryView,
|
NestedRuleCategoryView,
|
||||||
)
|
)
|
||||||
|
|
@ -33,7 +32,6 @@ urlpatterns = [
|
||||||
]
|
]
|
||||||
|
|
||||||
endpoints = [
|
endpoints = [
|
||||||
path("posts/", ListPostView.as_view(), name="posts-list"),
|
|
||||||
path("posts/<int:pk>/", DetailPostView.as_view(), name="posts-detail"),
|
path("posts/<int:pk>/", DetailPostView.as_view(), name="posts-detail"),
|
||||||
path("categories/", ListCategoryView.as_view(), name="categories-list"),
|
path("categories/", ListCategoryView.as_view(), name="categories-list"),
|
||||||
path(
|
path(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue