Replace rest_framework_swagger with drf_yasg

rest_framework is deprecated see https://github.com/marcgibbons/django-rest-swagger#django-rest-swagger-deprecated-2019-06-04
This commit is contained in:
sonny 2020-07-22 23:49:18 +02:00
parent ec45a661e5
commit a6827e604d
24 changed files with 2728 additions and 311 deletions

View file

@ -6,7 +6,7 @@ import { isEqual } from 'lodash';
import { fetchCategories } from './actions/categories';
import Sidebar from './components/sidebar/Sidebar.js';
import FeedList from './components/feedlist/FeedList.js';
import PostList from './components/postlist/PostList.js';
import PostModal from './components/PostModal.js';
import Messages from '../../components/Messages.js';
@ -19,7 +19,7 @@ class App extends React.Component {
return (
<>
<Sidebar />
<FeedList />
<PostList />
{this.props.error && (
<Messages messages={[{ type: 'error', text: this.props.error.message }]} />

View file

@ -3,6 +3,7 @@ import { connect } from 'react-redux';
import Cookies from 'js-cookie';
import { unSelectPost, markPostRead } from '../actions/posts.js';
import { CATEGORY_TYPE, RULE_TYPE, FEED, SUBREDDIT } from '../constants.js';
import { formatDatetime } from '../../../utils.js';
class PostModal extends React.Component {
@ -43,6 +44,10 @@ class PostModal extends React.Component {
const post = this.props.post;
const publicationDate = formatDatetime(post.publicationDate);
const titleClassName = post.read ? 'post__title post__title--read' : 'post__title';
const ruleUrl =
this.props.rule.type === FEED
? `/collection/rules/${this.props.rule.id}/`
: `/collection/rules/subreddits/${this.props.rule.id}/`;
return (
<div className="modal post-modal">
@ -70,11 +75,7 @@ class PostModal extends React.Component {
</span>
)}
<span className="badge post__rule" title={this.props.rule.name}>
<a
href={`/collection/rules/${this.props.rule.id}/`}
target="_blank"
rel="noopener noreferrer"
>
<a href={ruleUrl} target="_blank" rel="noopener noreferrer">
{this.props.rule.name}
</a>
</span>

View file

@ -1,7 +1,7 @@
import React from 'react';
import { connect } from 'react-redux';
import { CATEGORY_TYPE, RULE_TYPE } from '../../constants.js';
import { CATEGORY_TYPE, RULE_TYPE, FEED, SUBREDDIT } from '../../constants.js';
import { selectPost } from '../../actions/posts.js';
import { formatDatetime } from '../../../../utils.js';
@ -14,6 +14,11 @@ class PostItem extends React.Component {
? 'posts__header posts__header--read'
: 'posts__header';
const ruleUrl =
rule.type === FEED
? `/collection/rules/${rule.id}/`
: `/collection/rules/subreddits/${rule.id}/`;
return (
<li className="posts__item">
<h5
@ -30,11 +35,7 @@ class PostItem extends React.Component {
</span>
{this.props.selected.type == CATEGORY_TYPE && (
<span className="badge">
<a
href={`/collection/rules/${rule.id}/`}
target="_blank"
rel="noopener noreferrer"
>
<a href={ruleUrl} target="_blank" rel="noopener noreferrer">
{rule.name}
</a>
</span>

View file

@ -8,7 +8,7 @@ import { filterPosts } from './filters.js';
import LoadingIndicator from '../../../../components/LoadingIndicator.js';
import PostItem from './PostItem.js';
class FeedList extends React.Component {
class PostList extends React.Component {
checkScrollHeight = ::this.checkScrollHeight;
componentDidMount() {
@ -83,4 +83,4 @@ const mapDispatchToProps = dispatch => ({
fetchPostsBySection: (rule, page = false) => dispatch(fetchPostsBySection(rule, page)),
});
export default connect(mapStateToProps, mapDispatchToProps)(FeedList);
export default connect(mapStateToProps, mapDispatchToProps)(PostList);

View file

@ -1,2 +1,5 @@
export const RULE_TYPE = 'RULE';
export const CATEGORY_TYPE = 'CATEGORY';
export const SUBREDDIT = 'subreddit';
export const FEED = 'feed';

View file

@ -6,7 +6,14 @@ from newsreader.news.collection.models import CollectionRule
class CollectionRuleAdmin(admin.ModelAdmin):
fields = ("url", "name", "timezone", "category", "favicon", "user")
list_display = ("name", "category", "url", "last_suceeded", "succeeded")
list_display = (
"name",
"type_display",
"category",
"url",
"last_suceeded",
"succeeded",
)
list_filter = ("user",)
def save_model(self, request, obj, form, change):
@ -14,5 +21,8 @@ class CollectionRuleAdmin(admin.ModelAdmin):
obj.user = request.user
obj.save()
def type_display(self, rule):
return rule.get_type_display()
admin.site.register(CollectionRule, CollectionRuleAdmin)

View file

@ -1,4 +1,5 @@
from django import forms
from django.core.exceptions import ValidationError
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _
@ -6,15 +7,17 @@ import pytz
from newsreader.news.collection.choices import RuleTypeChoices
from newsreader.news.collection.models import CollectionRule
from newsreader.news.collection.reddit import REDDIT_API_URL
from newsreader.news.core.models import Category
def get_reddit_help_text():
return mark_safe(
"Only subreddits are supported. For example: "
"<a className='link' target='_blank' rel='noopener noreferrer'"
" href='https://reddit.com/r/aww'>https://www.reddit.com/r/aww</a>."
" Note that subreddit urls should NOT include 'www' because Reddit is picky."
"Only subreddits are supported"
" see the 'listings' section in <a className='link' target='_blank' rel='noopener noreferrer'"
" href='https://www.reddit.com/dev/api#section_listings'>the reddit API docs</a>."
" For example: <a className='link' target='_blank' rel='noopener noreferrer'"
" href='https://oauth.reddit.com/r/aww'>https://oauth.reddit.com/r/aww</a>"
)
@ -65,15 +68,19 @@ class SubRedditRuleForm(CollectionRuleForm):
timezone = None
def clean_url(self):
url = self.cleaned_data["url"]
if not url.startswith(REDDIT_API_URL):
raise ValidationError(_("This does not look like an Reddit API URL"))
return url
def save(self, commit=True):
instance = super().save(commit=False)
instance.type = RuleTypeChoices.subreddit
instance.timezone = str(pytz.utc)
instance.user = self.user
if not instance.url.endswith(".json"):
instance.url = f"{instance.url}.json"
if commit:
instance.save()

View file

@ -10,6 +10,7 @@ from uuid import uuid4
from django.conf import settings
from django.core.cache import cache
from django.utils import timezone
from django.utils.html import format_html
import bleach
import pytz
@ -42,6 +43,12 @@ REDDIT_API_URL = "https://oauth.reddit.com"
RATE_LIMIT = 60
RATE_LIMIT_DURATION = timedelta(seconds=60)
REDDIT_IMAGE_EXTENSIONS = (".jpg", ".png", ".gif")
REDDIT_VIDEO_EXTENSIONS = (".mp4", ".gifv", ".webm")
# see type prefixes on https://www.reddit.com/dev/api/
REDDIT_POST = "t3"
def get_reddit_authorization_url(user):
state = str(uuid4())
@ -114,18 +121,23 @@ class RedditBuilder(Builder):
results = {}
for post in posts:
if not "data" in post:
if not "data" in post or post["kind"] != REDDIT_POST:
continue
remote_identifier = post["data"]["id"]
title = truncate_text(Post, "title", post["data"]["title"])
author = truncate_text(Post, "author", post["data"]["author"])
url_fragment = f"{post['data']['permalink']}"
data = post["data"]
remote_identifier = data["id"]
title = truncate_text(Post, "title", data["title"])
author = truncate_text(Post, "author", data["author"])
post_url_fragment = data["permalink"]
direct_url = data["url"]
is_text_post = data["is_self"]
if remote_identifier in results:
continue
uncleaned_body = post["data"]["selftext_html"]
if is_text_post:
uncleaned_body = data["selftext_html"]
unescaped_body = unescape(uncleaned_body) if uncleaned_body else ""
body = (
bleach.clean(
@ -138,6 +150,43 @@ class RedditBuilder(Builder):
if unescaped_body
else ""
)
elif direct_url.endswith(REDDIT_IMAGE_EXTENSIONS):
body = format_html(
"<div><img alt='{title}' src='{url}' loading='lazy' /></div>",
url=direct_url,
title=title,
)
elif data["is_video"]:
video_info = data["secure_media"]["reddit_video"]
body = format_html(
"<div><video controls muted><source src='{url}' type='video/mp4' /></video></div>",
url=video_info["fallback_url"],
)
elif direct_url.endswith(REDDIT_VIDEO_EXTENSIONS):
extension = next(
extension.replace(".", "")
for extension in REDDIT_VIDEO_EXTENSIONS
if direct_url.endswith(extension)
)
if extension == "gifv":
body = format_html(
"<div><video controls muted><source src='{url}' type='video/mp4' /></video></div>",
url=direct_url.replace(extension, "mp4"),
)
else:
body = format_html(
"<div><video controls muted><source src='{url}' type='video/{extension}' /></video></div>",
url=direct_url,
extension=extension,
)
else:
body = format_html(
"<div><a target='_blank' rel='noopener noreferrer' alt='{title}' href='{url}' class='link'>Direct url</a></div>",
url=direct_url,
title=title,
)
try:
parsed_date = datetime.fromtimestamp(post["data"]["created_utc"])
@ -146,12 +195,12 @@ class RedditBuilder(Builder):
logging.warning(f"Failed parsing timestamp from {url_fragment}")
created_date = timezone.now()
data = {
post_data = {
"remote_identifier": remote_identifier,
"title": title,
"body": body,
"author": author,
"url": f"{REDDIT_URL}{url_fragment}",
"url": f"{REDDIT_URL}{post_url_fragment}",
"publication_date": created_date,
"rule": rule,
}
@ -159,13 +208,13 @@ class RedditBuilder(Builder):
if remote_identifier in self.existing_posts:
existing_post = self.existing_posts[remote_identifier]
for key, value in data.items():
for key, value in post_data.items():
setattr(existing_post, key, value)
results[existing_post.remote_identifier] = existing_post
continue
results[remote_identifier] = Post(**data)
results[remote_identifier] = Post(**post_data)
return results.values()
@ -271,7 +320,7 @@ class RedditClient(Client):
yield response_data
except StreamDeniedException as e:
logger.exception(
logger.warning(
f"Access token expired for user {stream.user.pk}"
)

View file

@ -12,4 +12,4 @@ class RuleSerializer(serializers.ModelSerializer):
class Meta:
model = CollectionRule
fields = ("id", "name", "url", "favicon", "category", "user", "unread")
fields = ("id", "type", "name", "url", "favicon", "category", "user", "unread")

View file

@ -4,9 +4,9 @@ from django.test import TestCase
from django.urls import reverse
from newsreader.accounts.tests.factories import UserFactory
from newsreader.news.collection.tests.factories import CollectionRuleFactory
from newsreader.news.collection.tests.factories import FeedFactory
from newsreader.news.core.models import Post
from newsreader.news.core.tests.factories import CategoryFactory, PostFactory
from newsreader.news.core.tests.factories import CategoryFactory, FeedPostFactory
class CollectionRuleDetailViewTestCase(TestCase):
@ -15,7 +15,7 @@ class CollectionRuleDetailViewTestCase(TestCase):
self.client.force_login(self.user)
def test_simple(self):
rule = CollectionRuleFactory(user=self.user)
rule = FeedFactory(user=self.user)
response = self.client.get(
reverse("api:news:collection:rules-detail", args=[rule.pk])
@ -29,6 +29,7 @@ class CollectionRuleDetailViewTestCase(TestCase):
self.assertTrue("url" in data)
self.assertTrue("favicon" in data)
self.assertTrue("category" in data)
self.assertTrue("type" in data)
def test_not_known(self):
response = self.client.get(
@ -40,7 +41,7 @@ class CollectionRuleDetailViewTestCase(TestCase):
self.assertEquals(data["detail"], "Not found.")
def test_post(self):
rule = CollectionRuleFactory(user=self.user)
rule = FeedFactory(user=self.user)
response = self.client.post(
reverse("api:news:collection:rules-detail", args=[rule.pk])
@ -51,7 +52,7 @@ class CollectionRuleDetailViewTestCase(TestCase):
self.assertEquals(data["detail"], 'Method "POST" not allowed.')
def test_patch(self):
rule = CollectionRuleFactory(name="BBC", user=self.user)
rule = FeedFactory(name="BBC", user=self.user)
response = self.client.patch(
reverse("api:news:collection:rules-detail", args=[rule.pk]),
@ -67,7 +68,7 @@ class CollectionRuleDetailViewTestCase(TestCase):
old_category = CategoryFactory(user=self.user)
new_category = CategoryFactory(user=self.user)
rule = CollectionRuleFactory(name="BBC", category=old_category, user=self.user)
rule = FeedFactory(name="BBC", category=old_category, user=self.user)
response = self.client.patch(
reverse("api:news:collection:rules-detail", args=[rule.pk]),
@ -80,7 +81,7 @@ class CollectionRuleDetailViewTestCase(TestCase):
self.assertEquals(data["category"], new_category.pk)
def test_identifier_cannot_be_changed(self):
rule = CollectionRuleFactory(user=self.user)
rule = FeedFactory(user=self.user)
response = self.client.patch(
reverse("api:news:collection:rules-detail", args=[rule.pk]),
@ -93,7 +94,7 @@ class CollectionRuleDetailViewTestCase(TestCase):
self.assertEquals(data["id"], rule.pk)
def test_category_change(self):
rule = CollectionRuleFactory(user=self.user)
rule = FeedFactory(user=self.user)
category = CategoryFactory(user=self.user)
response = self.client.patch(
@ -108,7 +109,7 @@ class CollectionRuleDetailViewTestCase(TestCase):
self.assertEquals(data["category"], category.pk)
def test_put(self):
rule = CollectionRuleFactory(name="BBC", user=self.user)
rule = FeedFactory(name="BBC", user=self.user)
response = self.client.put(
reverse("api:news:collection:rules-detail", args=[rule.pk]),
@ -121,7 +122,7 @@ class CollectionRuleDetailViewTestCase(TestCase):
self.assertEquals(data["name"], "BBC")
def test_delete(self):
rule = CollectionRuleFactory(user=self.user)
rule = FeedFactory(user=self.user)
response = self.client.delete(
reverse("api:news:collection:rules-detail", args=[rule.pk])
@ -132,7 +133,7 @@ class CollectionRuleDetailViewTestCase(TestCase):
def test_rule_with_unauthenticated_user(self):
self.client.logout()
rule = CollectionRuleFactory(name="BBC", user=self.user)
rule = FeedFactory(name="BBC", user=self.user)
response = self.client.patch(
reverse("api:news:collection:rules-detail", args=[rule.pk]),
@ -144,7 +145,7 @@ class CollectionRuleDetailViewTestCase(TestCase):
def test_rule_with_unauthorized_user(self):
other_user = UserFactory()
rule = CollectionRuleFactory(name="BBC", user=other_user)
rule = FeedFactory(name="BBC", user=other_user)
response = self.client.patch(
reverse("api:news:collection:rules-detail", args=[rule.pk]),
@ -155,10 +156,10 @@ class CollectionRuleDetailViewTestCase(TestCase):
self.assertEquals(response.status_code, 403)
def test_read_count(self):
rule = CollectionRuleFactory(user=self.user)
rule = FeedFactory(user=self.user)
PostFactory.create_batch(size=20, read=False, rule=rule)
PostFactory.create_batch(size=20, read=True, rule=rule)
FeedPostFactory.create_batch(size=20, read=False, rule=rule)
FeedPostFactory.create_batch(size=20, read=True, rule=rule)
response = self.client.get(
reverse("api:news:collection:rules-detail", args=[rule.pk])
@ -175,9 +176,9 @@ class CollectionRuleReadTestCase(TestCase):
self.client.force_login(self.user)
def test_rule_read(self):
rule = CollectionRuleFactory(user=self.user)
rule = FeedFactory(user=self.user)
PostFactory.create_batch(size=20, read=False, rule=rule)
FeedPostFactory.create_batch(size=20, read=False, rule=rule)
response = self.client.post(
reverse("api:news:collection:rules-read", args=[rule.pk])
@ -197,9 +198,9 @@ class CollectionRuleReadTestCase(TestCase):
def test_unauthenticated_user(self):
self.client.logout()
rule = CollectionRuleFactory(user=self.user)
rule = FeedFactory(user=self.user)
PostFactory.create_batch(size=20, read=False, rule=rule)
FeedPostFactory.create_batch(size=20, read=False, rule=rule)
response = self.client.post(
reverse("api:news:collection:rules-read", args=[rule.pk])
@ -209,9 +210,9 @@ class CollectionRuleReadTestCase(TestCase):
def test_unauthorized_user(self):
other_user = UserFactory()
rule = CollectionRuleFactory(user=other_user)
rule = FeedFactory(user=other_user)
PostFactory.create_batch(size=20, read=False, rule=rule)
FeedPostFactory.create_batch(size=20, read=False, rule=rule)
response = self.client.post(
reverse("api:news:collection:rules-read", args=[rule.pk])
@ -221,7 +222,7 @@ class CollectionRuleReadTestCase(TestCase):
self.assertEquals(Post.objects.filter(read=False).count(), 20)
def test_get(self):
rule = CollectionRuleFactory(user=self.user)
rule = FeedFactory(user=self.user)
response = self.client.get(
reverse("api:news:collection:rules-read", args=[rule.pk])
@ -230,7 +231,7 @@ class CollectionRuleReadTestCase(TestCase):
self.assertEquals(response.status_code, 405)
def test_patch(self):
rule = CollectionRuleFactory(name="BBC", user=self.user)
rule = FeedFactory(name="BBC", user=self.user)
response = self.client.patch(
reverse("api:news:collection:rules-read", args=[rule.pk]),
@ -241,7 +242,7 @@ class CollectionRuleReadTestCase(TestCase):
self.assertEquals(response.status_code, 405)
def test_put(self):
rule = CollectionRuleFactory(name="BBC", user=self.user)
rule = FeedFactory(name="BBC", user=self.user)
response = self.client.put(
reverse("api:news:collection:rules-read", args=[rule.pk]),
@ -252,7 +253,7 @@ class CollectionRuleReadTestCase(TestCase):
self.assertEquals(response.status_code, 405)
def test_delete(self):
rule = CollectionRuleFactory(user=self.user)
rule = FeedFactory(user=self.user)
response = self.client.delete(
reverse("api:news:collection:rules-read", args=[rule.pk])

View file

@ -8,8 +8,8 @@ from django.urls import reverse
import pytz
from newsreader.accounts.tests.factories import UserFactory
from newsreader.news.collection.tests.factories import CollectionRuleFactory
from newsreader.news.core.tests.factories import CategoryFactory, PostFactory
from newsreader.news.collection.tests.factories import FeedFactory
from newsreader.news.core.tests.factories import CategoryFactory, FeedPostFactory
class RuleListViewTestCase(TestCase):
@ -18,7 +18,7 @@ class RuleListViewTestCase(TestCase):
self.client.force_login(self.user)
def test_simple(self):
CollectionRuleFactory.create_batch(size=3, user=self.user)
FeedFactory.create_batch(size=3, user=self.user)
response = self.client.get(reverse("api:news:collection:rules-list"))
data = response.json()
@ -30,19 +30,19 @@ class RuleListViewTestCase(TestCase):
def test_ordering(self):
rules = [
CollectionRuleFactory(
FeedFactory(
created=datetime.combine(
date(2019, 5, 20), time(hour=16, minute=7, second=37), pytz.utc
),
user=self.user,
),
CollectionRuleFactory(
FeedFactory(
created=datetime.combine(
date(2019, 7, 20), time(hour=18, minute=7, second=37), pytz.utc
),
user=self.user,
),
CollectionRuleFactory(
FeedFactory(
created=datetime.combine(
date(2019, 7, 20), time(hour=16, minute=7, second=37), pytz.utc
),
@ -63,7 +63,7 @@ class RuleListViewTestCase(TestCase):
self.assertEquals(data["results"][2]["id"], rules[0].pk)
def test_pagination_count(self):
CollectionRuleFactory.create_batch(size=80, user=self.user)
FeedFactory.create_batch(size=80, user=self.user)
response = self.client.get(
reverse("api:news:collection:rules-list"), {"count": 30}
@ -124,7 +124,7 @@ class RuleListViewTestCase(TestCase):
def test_rules_with_unauthenticated_user(self):
self.client.logout()
CollectionRuleFactory.create_batch(size=3, user=self.user)
FeedFactory.create_batch(size=3, user=self.user)
response = self.client.get(reverse("api:news:collection:rules-list"))
@ -132,7 +132,7 @@ class RuleListViewTestCase(TestCase):
def test_rules_with_unauthorized_user(self):
other_user = UserFactory()
CollectionRuleFactory.create_batch(size=3, user=other_user)
FeedFactory.create_batch(size=3, user=other_user)
response = self.client.get(reverse("api:news:collection:rules-list"))
data = response.json()
@ -149,8 +149,8 @@ class NestedRuleListViewTestCase(TestCase):
self.client.force_login(self.user)
def test_simple(self):
rule = CollectionRuleFactory.create(user=self.user)
PostFactory.create_batch(size=5, rule=rule)
rule = FeedFactory.create(user=self.user)
FeedPostFactory.create_batch(size=5, rule=rule)
response = self.client.get(
reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk})
@ -164,8 +164,8 @@ class NestedRuleListViewTestCase(TestCase):
self.assertEquals(data["count"], 5)
def test_pagination(self):
rule = CollectionRuleFactory.create(user=self.user)
PostFactory.create_batch(size=80, rule=rule)
rule = FeedFactory.create(user=self.user)
FeedPostFactory.create_batch(size=80, rule=rule)
response = self.client.get(
reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk}),
@ -178,7 +178,7 @@ class NestedRuleListViewTestCase(TestCase):
self.assertEquals(len(data["results"]), 30)
def test_empty(self):
rule = CollectionRuleFactory.create(user=self.user)
rule = FeedFactory.create(user=self.user)
response = self.client.get(
reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk})
@ -197,7 +197,7 @@ class NestedRuleListViewTestCase(TestCase):
self.assertEquals(response.status_code, 404)
def test_post(self):
rule = CollectionRuleFactory.create(user=self.user)
rule = FeedFactory.create(user=self.user)
response = self.client.post(
reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk}),
@ -210,7 +210,7 @@ class NestedRuleListViewTestCase(TestCase):
self.assertEquals(data["detail"], 'Method "POST" not allowed.')
def test_patch(self):
rule = CollectionRuleFactory.create(user=self.user)
rule = FeedFactory.create(user=self.user)
response = self.client.patch(
reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk}),
@ -223,7 +223,7 @@ class NestedRuleListViewTestCase(TestCase):
self.assertEquals(data["detail"], 'Method "PATCH" not allowed.')
def test_put(self):
rule = CollectionRuleFactory.create(user=self.user)
rule = FeedFactory.create(user=self.user)
response = self.client.put(
reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk}),
@ -236,7 +236,7 @@ class NestedRuleListViewTestCase(TestCase):
self.assertEquals(data["detail"], 'Method "PUT" not allowed.')
def test_delete(self):
rule = CollectionRuleFactory.create(user=self.user)
rule = FeedFactory.create(user=self.user)
response = self.client.delete(
reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk}),
@ -251,7 +251,7 @@ class NestedRuleListViewTestCase(TestCase):
def test_rule_with_unauthenticated_user(self):
self.client.logout()
rule = CollectionRuleFactory(user=self.user)
rule = FeedFactory(user=self.user)
response = self.client.get(
reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk})
@ -261,7 +261,7 @@ class NestedRuleListViewTestCase(TestCase):
def test_rule_with_unauthorized_user(self):
other_user = UserFactory()
rule = CollectionRuleFactory(user=other_user)
rule = FeedFactory(user=other_user)
response = self.client.get(
reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk})
@ -270,26 +270,24 @@ class NestedRuleListViewTestCase(TestCase):
self.assertEquals(response.status_code, 403)
def test_posts_ordering(self):
rule = CollectionRuleFactory(
user=self.user, category=CategoryFactory(user=self.user)
)
rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user))
posts = [
PostFactory(
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
),
),
PostFactory(
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
),
),
PostFactory(
FeedPostFactory(
title="I'm the third post",
rule=rule,
publication_date=datetime.combine(
@ -313,11 +311,11 @@ class NestedRuleListViewTestCase(TestCase):
self.assertEquals(data["results"][2]["id"], posts[0].pk)
def test_only_posts_from_rule_are_returned(self):
rule = CollectionRuleFactory.create(user=self.user)
other_rule = CollectionRuleFactory.create(user=self.user)
rule = FeedFactory.create(user=self.user)
other_rule = FeedFactory.create(user=self.user)
PostFactory.create_batch(size=5, rule=rule)
PostFactory.create_batch(size=5, rule=other_rule)
FeedPostFactory.create_batch(size=5, rule=rule)
FeedPostFactory.create_batch(size=5, rule=other_rule)
response = self.client.get(
reverse("api:news:collection:rules-nested-posts", kwargs={"pk": rule.pk})
@ -334,10 +332,10 @@ class NestedRuleListViewTestCase(TestCase):
self.assertEquals(post["rule"], rule.pk)
def test_unread_posts(self):
rule = CollectionRuleFactory.create(user=self.user)
rule = FeedFactory.create(user=self.user)
PostFactory.create_batch(size=10, rule=rule, read=False)
PostFactory.create_batch(size=10, rule=rule, read=True)
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:collection:rules-nested-posts", kwargs={"pk": rule.pk}),
@ -354,10 +352,10 @@ class NestedRuleListViewTestCase(TestCase):
self.assertEquals(post["read"], False)
def test_read_posts(self):
rule = CollectionRuleFactory.create(user=self.user)
rule = FeedFactory.create(user=self.user)
PostFactory.create_batch(size=20, rule=rule, read=False)
PostFactory.create_batch(size=10, rule=rule, read=True)
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:collection:rules-nested-posts", kwargs={"pk": rule.pk}),

View file

@ -11,7 +11,7 @@ from freezegun import freeze_time
from newsreader.news.collection.feed import FeedBuilder
from newsreader.news.collection.tests.factories import FeedFactory
from newsreader.news.core.models import Post
from newsreader.news.core.tests.factories import PostFactory
from newsreader.news.core.tests.factories import FeedPostFactory
from .mocks import *
@ -287,11 +287,11 @@ class FeedBuilderTestCase(TestCase):
rule = FeedFactory()
mock_stream = MagicMock(rule=rule)
existing_first_post = PostFactory.create(
existing_first_post = FeedPostFactory.create(
remote_identifier="28f79ae4-8f9a-11e9-b143-00163ef6bee7", rule=rule
)
existing_second_post = PostFactory.create(
existing_second_post = FeedPostFactory.create(
remote_identifier="a5479c66-8fae-11e9-8422-00163ef6bee7", rule=rule
)

View file

@ -21,7 +21,7 @@ from newsreader.news.collection.feed import FeedCollector
from newsreader.news.collection.tests.factories import FeedFactory
from newsreader.news.collection.utils import build_publication_date
from newsreader.news.core.models import Post
from newsreader.news.core.tests.factories import PostFactory
from newsreader.news.core.tests.factories import FeedPostFactory
from .mocks import duplicate_mock, empty_mock, multiple_mock, multiple_update_mock
@ -143,7 +143,7 @@ class FeedCollectorTestCase(TestCase):
struct_time((2019, 5, 20, 16, 7, 37, 0, 140, 0)), pytz.utc
)
first_post = PostFactory(
first_post = FeedPostFactory(
url="https://www.bbc.co.uk/news/world-us-canada-48338168",
title="Trump's 'genocidal taunts' will not end Iran - Zarif",
body="Foreign Minister Mohammad Javad Zarif says the US "
@ -156,7 +156,7 @@ class FeedCollectorTestCase(TestCase):
struct_time((2019, 5, 20, 12, 19, 19, 0, 140, 0)), pytz.utc
)
second_post = PostFactory(
second_post = FeedPostFactory(
url="https://www.bbc.co.uk/news/technology-48334739",
title="Huawei's Android loss: How it affects you",
body="Google's move to end business ties with Huawei will "
@ -169,7 +169,7 @@ class FeedCollectorTestCase(TestCase):
struct_time((2019, 5, 20, 16, 32, 38, 0, 140, 0)), pytz.utc
)
third_post = PostFactory(
third_post = FeedPostFactory(
url="https://www.bbc.co.uk/news/uk-england-birmingham-48339080",
title="Birmingham head teacher threatened over LGBT lessons",
body="Police are investigating the messages while an MP "
@ -194,7 +194,7 @@ class FeedCollectorTestCase(TestCase):
self.mocked_parse.return_value = multiple_update_mock
rule = FeedFactory()
first_post = PostFactory(
first_post = FeedPostFactory(
remote_identifier="https://www.bbc.co.uk/news/world-us-canada-48338168",
url="https://www.bbc.co.uk/",
title="Trump",
@ -203,7 +203,7 @@ class FeedCollectorTestCase(TestCase):
rule=rule,
)
second_post = PostFactory(
second_post = FeedPostFactory(
remote_identifier="https://www.bbc.co.uk/news/technology-48334739",
url="https://www.bbc.co.uk/",
title="Huawei's Android loss: How it affects you",
@ -212,7 +212,7 @@ class FeedCollectorTestCase(TestCase):
rule=rule,
)
third_post = PostFactory(
third_post = FeedPostFactory(
remote_identifier="https://www.bbc.co.uk/news/uk-england-birmingham-48339080",
url="https://www.bbc.co.uk/news/uk-england-birmingham-48339080",
title="Birmingham head teacher threatened over LGBT lessons",

View file

@ -8,7 +8,7 @@ from freezegun import freeze_time
from newsreader.news.collection.feed import FeedDuplicateHandler
from newsreader.news.collection.tests.factories import FeedFactory
from newsreader.news.core.models import Post
from newsreader.news.core.tests.factories import PostFactory
from newsreader.news.core.tests.factories import FeedPostFactory
@freeze_time("2019-10-30 12:30:00")
@ -19,17 +19,17 @@ class FeedDuplicateHandlerTestCase(TestCase):
def test_duplicate_entries_with_remote_identifiers(self):
rule = FeedFactory()
existing_post = PostFactory.create(
existing_post = FeedPostFactory.create(
remote_identifier="28f79ae4-8f9a-11e9-b143-00163ef6bee7", rule=rule
)
new_posts = PostFactory.build_batch(
new_posts = FeedPostFactory.build_batch(
remote_identifier="28f79ae4-8f9a-11e9-b143-00163ef6bee7",
publication_date=timezone.now() - timedelta(days=7),
rule=rule,
size=5,
)
last_post = PostFactory.build(
last_post = FeedPostFactory.build(
remote_identifier="28f79ae4-8f9a-11e9-b143-00163ef6bee7",
publication_date=timezone.now(),
rule=rule,
@ -54,7 +54,7 @@ class FeedDuplicateHandlerTestCase(TestCase):
def test_duplicate_entries_with_different_remote_identifiers(self):
rule = FeedFactory()
existing_post = PostFactory(
existing_post = FeedPostFactory(
remote_identifier="28f79ae4-8f9a-11e9-b143-00163ef6bee7",
url="https://bbc.com",
title="New post",
@ -63,7 +63,7 @@ class FeedDuplicateHandlerTestCase(TestCase):
rule=rule,
)
new_posts = PostFactory.build_batch(
new_posts = FeedPostFactory.build_batch(
remote_identifier="28f79ae4-8f9a-11e9-b143-00163ef6bee7Q",
url="https://bbc.com",
title="New post",
@ -72,7 +72,7 @@ class FeedDuplicateHandlerTestCase(TestCase):
rule=rule,
size=5,
)
last_post = PostFactory.build(
last_post = FeedPostFactory.build(
remote_identifier="28f79ae4-8f9a-11e9-b143-00163ef6bee7Q",
url="https://bbc.com",
title="New post",
@ -100,7 +100,7 @@ class FeedDuplicateHandlerTestCase(TestCase):
def test_duplicate_entries_in_recent_database(self):
rule = FeedFactory()
existing_post = PostFactory(
existing_post = FeedPostFactory(
url="https://www.bbc.co.uk/news/uk-england-birmingham-48339080",
title="Birmingham head teacher threatened over LGBT lessons",
body="Google's move to end business ties with Huawei will affect current devices",
@ -109,7 +109,7 @@ class FeedDuplicateHandlerTestCase(TestCase):
rule=rule,
)
new_posts = PostFactory.build_batch(
new_posts = FeedPostFactory.build_batch(
url="https://www.bbc.co.uk/news/uk-england-birmingham-48339080",
title="Birmingham head teacher threatened over LGBT lessons",
body="Google's move to end business ties with Huawei will affect current devices",
@ -119,7 +119,7 @@ class FeedDuplicateHandlerTestCase(TestCase):
size=5,
)
last_post = PostFactory.build(
last_post = FeedPostFactory.build(
url="https://www.bbc.co.uk/news/uk-england-birmingham-48339080",
title="Birmingham head teacher threatened over LGBT lessons",
body="Google's move to end business ties with Huawei will affect current devices",
@ -147,17 +147,17 @@ class FeedDuplicateHandlerTestCase(TestCase):
def test_multiple_existing_entries_with_identifier(self):
rule = FeedFactory()
PostFactory.create_batch(
FeedPostFactory.create_batch(
remote_identifier="28f79ae4-8f9a-11e9-b143-00163ef6bee7", rule=rule, size=5
)
new_posts = PostFactory.build_batch(
new_posts = FeedPostFactory.build_batch(
remote_identifier="28f79ae4-8f9a-11e9-b143-00163ef6bee7",
publication_date=timezone.now() - timedelta(hours=5),
rule=rule,
size=5,
)
last_post = PostFactory.build(
last_post = FeedPostFactory.build(
remote_identifier="28f79ae4-8f9a-11e9-b143-00163ef6bee7",
publication_date=timezone.now() - timedelta(minutes=5),
rule=rule,
@ -189,7 +189,7 @@ class FeedDuplicateHandlerTestCase(TestCase):
def test_duplicate_entries_outside_time_slot(self):
rule = FeedFactory()
existing_post = PostFactory(
existing_post = FeedPostFactory(
url="https://www.bbc.co.uk/news/uk-england-birmingham-48339080",
title="Birmingham head teacher threatened over LGBT lessons",
body="Google's move to end business ties with Huawei will affect current devices",
@ -198,7 +198,7 @@ class FeedDuplicateHandlerTestCase(TestCase):
rule=rule,
)
new_posts = PostFactory.build_batch(
new_posts = FeedPostFactory.build_batch(
url="https://www.bbc.co.uk/news/uk-england-birmingham-48339080",
title="Birmingham head teacher threatened over LGBT lessons",
body="Google's move to end business ties with Huawei will affect current devices",
@ -207,7 +207,7 @@ class FeedDuplicateHandlerTestCase(TestCase):
rule=rule,
size=5,
)
last_post = PostFactory.build(
last_post = FeedPostFactory.build(
url="https://www.bbc.co.uk/news/uk-england-birmingham-48339080",
title="Birmingham head teacher threatened over LGBT lessons",
body="Google's move to end business ties with Huawei will affect current devices",
@ -235,14 +235,14 @@ class FeedDuplicateHandlerTestCase(TestCase):
def test_duplicate_entries_in_collected_entries(self):
rule = FeedFactory()
post_1 = PostFactory.build(
post_1 = FeedPostFactory.build(
title="title got updated",
body="body",
url="https://bbc.com",
publication_date=timezone.now(),
rule=rule,
)
duplicate_post_1 = PostFactory.build(
duplicate_post_1 = FeedPostFactory.build(
title="title got updated",
body="body",
url="https://bbc.com",
@ -250,11 +250,11 @@ class FeedDuplicateHandlerTestCase(TestCase):
rule=rule,
)
post_2 = PostFactory.build(
post_2 = FeedPostFactory.build(
remote_identifier="28f79ae4-8f9a-11e9-b143-00163ef6bee7",
publication_date=timezone.now(),
)
duplicate_post_2 = PostFactory.build(
duplicate_post_2 = FeedPostFactory.build(
remote_identifier="28f79ae4-8f9a-11e9-b143-00163ef6bee7",
publication_date=timezone.now() - timedelta(minutes=5),
)

File diff suppressed because it is too large Load diff

View file

@ -86,7 +86,7 @@ class RedditBuilderTestCase(TestCase):
def test_update_posts(self):
subreddit = SubredditFactory()
existing_post = RedditPostFactory(
remote_identifier="hngsj8",
remote_identifier="hm0qct",
author="Old author",
title="Old title",
body="Old body",
@ -108,17 +108,24 @@ class RedditBuilderTestCase(TestCase):
existing_post.refresh_from_db()
self.assertEquals(existing_post.remote_identifier, "hngsj8")
self.assertEquals(existing_post.author, "nixcraft")
self.assertEquals(existing_post.title, "KeePassXC 2.6.0 released")
self.assertEquals(existing_post.body, "")
self.assertEquals(existing_post.remote_identifier, "hm0qct")
self.assertEquals(existing_post.author, "AutoModerator")
self.assertEquals(
existing_post.title,
"Linux Experiences/Rants or Education/Certifications thread - July 06, 2020",
)
self.assertIn(
"This megathread is also to hear opinions from anyone just starting out "
"with Linux or those that have used Linux (GNU or otherwise) for a long time.",
existing_post.body,
)
self.assertEquals(
existing_post.publication_date,
pytz.utc.localize(datetime(2020, 7, 8, 15, 11, 6)),
pytz.utc.localize(datetime(2020, 7, 6, 6, 11, 22)),
)
self.assertEquals(
existing_post.url,
"https://www.reddit.com/r/linux/comments/hngsj8/" "keepassxc_260_released/",
"https://www.reddit.com/r/linux/comments/hm0qct/linux_experiencesrants_or_educationcertifications/",
)
def test_html_sanitizing(self):
@ -219,3 +226,183 @@ class RedditBuilderTestCase(TestCase):
duplicate_post.title,
"Linux Experiences/Rants or Education/Certifications thread - July 06, 2020",
)
def test_image_post(self):
builder = RedditBuilder
subreddit = SubredditFactory()
mock_stream = MagicMock(rule=subreddit)
with builder((image_mock, mock_stream)) as builder:
builder.save()
posts = {post.remote_identifier: post for post in Post.objects.all()}
self.assertCountEqual(("hr64xh", "hr4bxo", "hr14y5", "hr2fv0"), posts.keys())
post = posts["hr64xh"]
title = (
"Yall, I just cant... this is my "
"son, Judah. My wife and I have no "
"idea how we created such a "
"beautiful child."
)
url = "https://i.redd.it/cm2qybia1va51.jpg"
self.assertEquals(
"https://www.reddit.com/r/aww/comments/hr64xh/yall_i_just_cant_this_is_my_son_judah_my_wife_and/",
post.url,
)
self.assertEquals(
f"<div><img alt='{title}' src='{url}' loading='lazy' /></div>", post.body
)
def test_external_image_post(self):
builder = RedditBuilder
subreddit = SubredditFactory()
mock_stream = MagicMock(rule=subreddit)
with builder((external_image_mock, mock_stream)) as builder:
builder.save()
posts = {post.remote_identifier: post for post in Post.objects.all()}
self.assertCountEqual(("hr41am", "huoldn"), posts.keys())
post = posts["hr41am"]
url = "http://gfycat.com/thatalivedogwoodclubgall"
title = "Excited cows have a new brush!"
self.assertEquals(
f"<div><a target='_blank' rel='noopener noreferrer' alt='{title}' href='{url}' class='link'>Direct url</a></div>",
post.body,
)
self.assertEquals(
"https://www.reddit.com/r/aww/comments/hr41am/excited_cows_have_a_new_brush/",
post.url,
)
post = posts["huoldn"]
url = "https://i.imgur.com/usfMVUJ.jpg"
title = "Novosibirsk Zoo welcomes 16 cobalt-eyed Pallass cat kittens"
self.assertEquals(
f"<div><img alt='{title}' src='{url}' loading='lazy' /></div>", post.body
)
self.assertEquals(
"https://www.reddit.com/r/aww/comments/huoldn/novosibirsk_zoo_welcomes_16_cobalteyed_pallass/",
post.url,
)
def test_video_post(self):
builder = RedditBuilder
subreddit = SubredditFactory()
mock_stream = MagicMock(rule=subreddit)
with builder((video_mock, mock_stream)) as builder:
builder.save()
posts = {post.remote_identifier: post for post in Post.objects.all()}
self.assertCountEqual(("hr32jf", "hr1r00", "hqy0ny", "hr0uzh"), posts.keys())
post = posts["hr1r00"]
url = "https://v.redd.it/eyvbxaeqtta51/DASH_480.mp4?source=fallback"
self.assertEquals(
post.url,
"https://www.reddit.com/r/aww/comments/hr1r00/cool_catt_and_his_clingy_girlfriend/",
)
self.assertEquals(
f"<div><video controls muted><source src='{url}' type='video/mp4' /></video></div>",
post.body,
)
def test_external_video_post(self):
builder = RedditBuilder
subreddit = SubredditFactory()
mock_stream = MagicMock(rule=subreddit)
with builder((external_video_mock, mock_stream)) as builder:
builder.save()
post = Post.objects.get()
self.assertEquals(post.remote_identifier, "hulh8k")
self.assertEquals(
post.url,
"https://www.reddit.com/r/aww/comments/hulh8k/dog_splashing_in_water/",
)
title = "Dog splashing in water"
url = "https://gfycat.com/excellentinfantileamericanwigeon"
self.assertEquals(
f"<div><a target='_blank' rel='noopener noreferrer' alt='{title}' href='{url}' class='link'>Direct url</a></div>",
post.body,
)
def test_external_gifv_video_post(self):
builder = RedditBuilder
subreddit = SubredditFactory()
mock_stream = MagicMock(rule=subreddit)
with builder((external_gifv_mock, mock_stream)) as builder:
builder.save()
post = Post.objects.get()
self.assertEquals(post.remote_identifier, "humdlf")
self.assertEquals(
post.url, "https://www.reddit.com/r/aww/comments/humdlf/if_i_fits_i_sits/"
)
self.assertEquals(
"<div><video controls muted><source src='https://i.imgur.com/grVh2AG.mp4' type='video/mp4' /></video></div>",
post.body,
)
def test_link_only_post(self):
builder = RedditBuilder
subreddit = SubredditFactory()
mock_stream = MagicMock(rule=subreddit)
with builder((simple_mock, mock_stream)) as builder:
builder.save()
post = Post.objects.get(remote_identifier="hngsj8")
title = "KeePassXC 2.6.0 released"
url = "https://keepassxc.org/blog/2020-07-07-2.6.0-released/"
self.assertIn(
f"<div><a target='_blank' rel='noopener noreferrer' alt='{title}' href='{url}' class='link'>Direct url</a></div>",
post.body,
)
self.assertEquals(
post.url,
"https://www.reddit.com/r/linux/comments/hngsj8/keepassxc_260_released/",
)
def test_skip_not_known_post_type(self):
builder = RedditBuilder
subreddit = SubredditFactory()
mock_stream = MagicMock(rule=subreddit)
with builder((unknown_mock, mock_stream)) as builder:
builder.save()
self.assertEquals(Post.objects.count(), 0)

View file

@ -1,11 +1,12 @@
from django.test import TestCase
from django.urls import reverse
from django.utils.translation import gettext as _
import pytz
from newsreader.news.collection.choices import RuleTypeChoices
from newsreader.news.collection.models import CollectionRule
from newsreader.news.collection.reddit import REDDIT_URL
from newsreader.news.collection.reddit import REDDIT_API_URL, REDDIT_URL
from newsreader.news.collection.tests.factories import SubredditFactory
from newsreader.news.collection.tests.views.base import CollectionRuleViewTestCase
from newsreader.news.core.tests.factories import CategoryFactory
@ -17,7 +18,7 @@ class SubRedditCreateViewTestCase(CollectionRuleViewTestCase, TestCase):
self.form_data = {
"name": "new rule",
"url": "https://www.reddit.com/r/aww",
"url": f"{REDDIT_API_URL}/r/aww",
"category": str(self.category.pk),
}
@ -31,12 +32,19 @@ class SubRedditCreateViewTestCase(CollectionRuleViewTestCase, TestCase):
rule = CollectionRule.objects.get(name="new rule")
self.assertEquals(rule.type, RuleTypeChoices.subreddit)
self.assertEquals(rule.url, "https://www.reddit.com/r/aww.json")
self.assertEquals(rule.url, f"{REDDIT_API_URL}/r/aww")
self.assertEquals(rule.timezone, str(pytz.utc))
self.assertEquals(rule.favicon, None)
self.assertEquals(rule.category.pk, self.category.pk)
self.assertEquals(rule.user.pk, self.user.pk)
def test_regular_reddit_url(self):
self.form_data.update(url=f"{REDDIT_URL}/r/aww")
response = self.client.post(self.url, self.form_data)
self.assertContains(response, _("This does not look like an Reddit API URL"))
class SubRedditUpdateViewTestCase(CollectionRuleViewTestCase, TestCase):
def setUp(self):
@ -44,7 +52,7 @@ class SubRedditUpdateViewTestCase(CollectionRuleViewTestCase, TestCase):
self.rule = SubredditFactory(
name="Python",
url=f"{REDDIT_URL}/r/python.json",
url=f"{REDDIT_API_URL}/r/python.json",
user=self.user,
category=self.category,
type=RuleTypeChoices.subreddit,
@ -97,7 +105,7 @@ class SubRedditUpdateViewTestCase(CollectionRuleViewTestCase, TestCase):
self.assertEquals(response.status_code, 404)
def test_url_change(self):
self.form_data.update(name="aww", url=f"{REDDIT_URL}/r/aww")
self.form_data.update(name="aww", url=f"{REDDIT_API_URL}/r/aww")
response = self.client.post(self.url, self.form_data)
@ -106,8 +114,15 @@ class SubRedditUpdateViewTestCase(CollectionRuleViewTestCase, TestCase):
rule = CollectionRule.objects.get(name="aww")
self.assertEquals(rule.type, RuleTypeChoices.subreddit)
self.assertEquals(rule.url, f"{REDDIT_URL}/r/aww.json")
self.assertEquals(rule.url, f"{REDDIT_API_URL}/r/aww")
self.assertEquals(rule.timezone, str(pytz.utc))
self.assertEquals(rule.favicon, None)
self.assertEquals(rule.category.pk, self.category.pk)
self.assertEquals(rule.user.pk, self.user.pk)
def test_regular_reddit_url(self):
self.form_data.update(url=f"{REDDIT_URL}/r/aww")
response = self.client.post(self.url, self.form_data)
self.assertContains(response, _("This does not look like an Reddit API URL"))

View file

@ -4,8 +4,8 @@ from django.test import TestCase
from django.urls import reverse
from newsreader.accounts.tests.factories import UserFactory
from newsreader.news.collection.tests.factories import CollectionRuleFactory
from newsreader.news.core.tests.factories import CategoryFactory, PostFactory
from newsreader.news.collection.tests.factories import FeedFactory
from newsreader.news.core.tests.factories import CategoryFactory, FeedPostFactory
class CategoryDetailViewTestCase(TestCase):
@ -116,11 +116,11 @@ class CategoryDetailViewTestCase(TestCase):
def test_read_count(self):
category = CategoryFactory(user=self.user)
unread_rule = CollectionRuleFactory(category=category)
read_rule = CollectionRuleFactory(category=category)
unread_rule = FeedFactory(category=category)
read_rule = FeedFactory(category=category)
PostFactory.create_batch(size=20, read=False, rule=unread_rule)
PostFactory.create_batch(size=20, read=True, rule=read_rule)
FeedPostFactory.create_batch(size=20, read=False, rule=unread_rule)
FeedPostFactory.create_batch(size=20, read=True, rule=read_rule)
response = self.client.get(
reverse("api:news:core:categories-detail", args=[category.pk])
@ -139,8 +139,8 @@ class CategoryReadTestCase(TestCase):
def test_category_read(self):
category = CategoryFactory(user=self.user)
rules = [
PostFactory.create_batch(size=5, read=False, rule=rule)
for rule in CollectionRuleFactory.create_batch(size=5, category=category)
FeedPostFactory.create_batch(size=5, read=False, rule=rule)
for rule in FeedFactory.create_batch(size=5, category=category)
]
response = self.client.post(
@ -165,8 +165,8 @@ class CategoryReadTestCase(TestCase):
category = CategoryFactory(user=self.user)
rules = [
PostFactory.create_batch(size=5, read=False, rule=rule)
for rule in CollectionRuleFactory.create_batch(
FeedPostFactory.create_batch(size=5, read=False, rule=rule)
for rule in FeedFactory.create_batch(
size=5, category=category, user=self.user
)
]
@ -182,8 +182,8 @@ class CategoryReadTestCase(TestCase):
category = CategoryFactory(user=other_user)
rules = [
PostFactory.create_batch(size=5, read=False, rule=rule)
for rule in CollectionRuleFactory.create_batch(
FeedPostFactory.create_batch(size=5, read=False, rule=rule)
for rule in FeedFactory.create_batch(
size=5, category=category, user=other_user
)
]

View file

@ -8,8 +8,8 @@ from django.urls import reverse
import pytz
from newsreader.accounts.tests.factories import UserFactory
from newsreader.news.collection.tests.factories import CollectionRuleFactory
from newsreader.news.core.tests.factories import CategoryFactory, PostFactory
from newsreader.news.collection.tests.factories import FeedFactory
from newsreader.news.core.tests.factories import CategoryFactory, FeedPostFactory
class CategoryListViewTestCase(TestCase):
@ -125,7 +125,7 @@ class NestedCategoryListViewTestCase(TestCase):
def test_simple(self):
category = CategoryFactory.create(user=self.user)
rules = CollectionRuleFactory.create_batch(size=5, category=category)
rules = FeedFactory.create_batch(size=5, category=category)
response = self.client.get(
reverse("api:news:core:categories-nested-rules", kwargs={"pk": category.pk})
@ -219,7 +219,7 @@ class NestedCategoryListViewTestCase(TestCase):
self.client.logout()
category = CategoryFactory.create(user=self.user)
rules = CollectionRuleFactory.create_batch(size=5, category=category)
rules = FeedFactory.create_batch(size=5, category=category)
response = self.client.get(
reverse("api:news:core:categories-nested-rules", kwargs={"pk": category.pk})
@ -231,7 +231,7 @@ class NestedCategoryListViewTestCase(TestCase):
other_user = UserFactory.create()
category = CategoryFactory.create(user=other_user)
rules = CollectionRuleFactory.create_batch(size=5, category=category)
rules = FeedFactory.create_batch(size=5, category=category)
response = self.client.get(
reverse("api:news:core:categories-nested-rules", kwargs={"pk": category.pk})
@ -242,9 +242,9 @@ class NestedCategoryListViewTestCase(TestCase):
def test_ordering(self):
category = CategoryFactory.create(user=self.user)
rules = [
CollectionRuleFactory.create(category=category, name="Durp"),
CollectionRuleFactory.create(category=category, name="Slurp"),
CollectionRuleFactory.create(category=category, name="Burp"),
FeedFactory.create(category=category, name="Durp"),
FeedFactory.create(category=category, name="Slurp"),
FeedFactory.create(category=category, name="Burp"),
]
response = self.client.get(
@ -261,13 +261,13 @@ class NestedCategoryListViewTestCase(TestCase):
def test_only_rules_from_category_are_returned(self):
other_category = CategoryFactory(user=self.user)
CollectionRuleFactory.create_batch(size=5, category=other_category)
FeedFactory.create_batch(size=5, category=other_category)
category = CategoryFactory.create(user=self.user)
rules = [
CollectionRuleFactory.create(category=category, name="Durp"),
CollectionRuleFactory.create(category=category, name="Slurp"),
CollectionRuleFactory.create(category=category, name="Burp"),
FeedFactory.create(category=category, name="Durp"),
FeedFactory.create(category=category, name="Slurp"),
FeedFactory.create(category=category, name="Burp"),
]
response = self.client.get(
@ -291,8 +291,8 @@ class NestedCategoryPostView(TestCase):
def test_simple(self):
category = CategoryFactory.create(user=self.user)
rules = {
rule.pk: PostFactory.create_batch(size=5, rule=rule)
for rule in CollectionRuleFactory.create_batch(
rule.pk: FeedPostFactory.create_batch(size=5, rule=rule)
for rule in FeedFactory.create_batch(
size=5, category=category, user=self.user
)
}
@ -327,9 +327,7 @@ class NestedCategoryPostView(TestCase):
def test_no_posts(self):
category = CategoryFactory.create(user=self.user)
rules = CollectionRuleFactory.create_batch(
size=5, user=self.user, category=category
)
rules = FeedFactory.create_batch(size=5, user=self.user, category=category)
response = self.client.get(
reverse("api:news:core:categories-nested-posts", kwargs={"pk": category.pk})
@ -427,25 +425,23 @@ class NestedCategoryPostView(TestCase):
def test_ordering(self):
category = CategoryFactory.create(user=self.user)
bbc_rule = CollectionRuleFactory.create(
name="BBC", category=category, user=self.user
)
guardian_rule = CollectionRuleFactory.create(
bbc_rule = FeedFactory.create(name="BBC", category=category, user=self.user)
guardian_rule = FeedFactory.create(
name="The Guardian", category=category, user=self.user
)
reuters_rule = CollectionRuleFactory.create(
reuters_rule = FeedFactory.create(
name="Reuters", category=category, user=self.user
)
reuters_rule = [
PostFactory.create(
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
),
),
PostFactory.create(
FeedPostFactory.create(
title="First Reuters post",
rule=reuters_rule,
publication_date=datetime.combine(
@ -455,14 +451,14 @@ class NestedCategoryPostView(TestCase):
]
guardian_posts = [
PostFactory.create(
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
),
),
PostFactory.create(
FeedPostFactory.create(
title="First Guardian post",
rule=guardian_rule,
publication_date=datetime.combine(
@ -472,14 +468,14 @@ class NestedCategoryPostView(TestCase):
]
bbc_posts = [
PostFactory.create(
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
),
),
PostFactory.create(
FeedPostFactory.create(
title="First BBC post",
rule=bbc_rule,
publication_date=datetime.combine(
@ -509,19 +505,19 @@ class NestedCategoryPostView(TestCase):
category = CategoryFactory.create(user=self.user)
other_category = CategoryFactory.create(user=self.user)
guardian_rule = CollectionRuleFactory.create(
guardian_rule = FeedFactory.create(
name="BBC", category=category, user=self.user
)
other_rule = CollectionRuleFactory.create(name="The Guardian", user=self.user)
other_rule = FeedFactory.create(name="The Guardian", user=self.user)
guardian_posts = [
PostFactory.create(rule=guardian_rule),
PostFactory.create(rule=guardian_rule),
FeedPostFactory.create(rule=guardian_rule),
FeedPostFactory.create(rule=guardian_rule),
]
other_posts = [
PostFactory.create(rule=other_rule),
PostFactory.create(rule=other_rule),
FeedPostFactory.create(rule=other_rule),
FeedPostFactory.create(rule=other_rule),
]
response = self.client.get(
@ -538,10 +534,10 @@ class NestedCategoryPostView(TestCase):
def test_unread_posts(self):
category = CategoryFactory.create(user=self.user)
rule = CollectionRuleFactory(category=category)
rule = FeedFactory(category=category)
PostFactory.create_batch(size=10, rule=rule, read=False)
PostFactory.create_batch(size=10, rule=rule, read=True)
FeedPostFactory.create_batch(size=10, rule=rule, read=False)
FeedPostFactory.create_batch(size=10, rule=rule, read=True)
response = self.client.get(
reverse(
@ -561,10 +557,10 @@ class NestedCategoryPostView(TestCase):
def test_read_posts(self):
category = CategoryFactory.create(user=self.user)
rule = CollectionRuleFactory(category=category)
rule = FeedFactory(category=category)
PostFactory.create_batch(size=20, rule=rule, read=False)
PostFactory.create_batch(size=10, rule=rule, read=True)
FeedPostFactory.create_batch(size=20, rule=rule, read=False)
FeedPostFactory.create_batch(size=10, rule=rule, read=True)
response = self.client.get(
reverse(

View file

@ -4,8 +4,8 @@ from django.test import TestCase
from django.urls import reverse
from newsreader.accounts.tests.factories import UserFactory
from newsreader.news.collection.tests.factories import CollectionRuleFactory
from newsreader.news.core.tests.factories import CategoryFactory, PostFactory
from newsreader.news.collection.tests.factories import FeedFactory
from newsreader.news.core.tests.factories import CategoryFactory, FeedPostFactory
class PostDetailViewTestCase(TestCase):
@ -14,10 +14,8 @@ class PostDetailViewTestCase(TestCase):
self.client.force_login(self.user)
def test_simple(self):
rule = CollectionRuleFactory(
user=self.user, category=CategoryFactory(user=self.user)
)
post = PostFactory(rule=rule)
rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user))
post = FeedPostFactory(rule=rule)
response = self.client.get(
reverse("api:news:core:posts-detail", args=[post.pk])
@ -43,10 +41,8 @@ class PostDetailViewTestCase(TestCase):
self.assertEquals(data["detail"], "Not found.")
def test_post(self):
rule = CollectionRuleFactory(
user=self.user, category=CategoryFactory(user=self.user)
)
post = PostFactory(rule=rule)
rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user))
post = FeedPostFactory(rule=rule)
response = self.client.post(
reverse("api:news:core:posts-detail", args=[post.pk])
@ -57,10 +53,8 @@ class PostDetailViewTestCase(TestCase):
self.assertEquals(data["detail"], 'Method "POST" not allowed.')
def test_patch(self):
rule = CollectionRuleFactory(
user=self.user, category=CategoryFactory(user=self.user)
)
post = PostFactory(title="This is clickbait for sure", rule=rule)
rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user))
post = FeedPostFactory(title="This is clickbait for sure", rule=rule)
response = self.client.patch(
reverse("api:news:core:posts-detail", args=[post.pk]),
@ -73,10 +67,8 @@ class PostDetailViewTestCase(TestCase):
self.assertEquals(data["title"], "This title is very accurate")
def test_identifier_cannot_be_changed(self):
rule = CollectionRuleFactory(
user=self.user, category=CategoryFactory(user=self.user)
)
post = PostFactory(title="This is clickbait for sure", rule=rule)
rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user))
post = FeedPostFactory(title="This is clickbait for sure", rule=rule)
response = self.client.patch(
reverse("api:news:core:posts-detail", args=[post.pk]),
@ -89,13 +81,9 @@ class PostDetailViewTestCase(TestCase):
self.assertEquals(data["id"], post.pk)
def test_rule_cannot_be_changed(self):
rule = CollectionRuleFactory(
user=self.user, category=CategoryFactory(user=self.user)
)
new_rule = CollectionRuleFactory(
user=self.user, category=CategoryFactory(user=self.user)
)
post = PostFactory(title="This is clickbait for sure", rule=rule)
rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user))
new_rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user))
post = FeedPostFactory(title="This is clickbait for sure", rule=rule)
response = self.client.patch(
reverse("api:news:core:posts-detail", args=[post.pk]),
@ -115,10 +103,8 @@ class PostDetailViewTestCase(TestCase):
self.assertTrue(data["rule"], rule.pk)
def test_put(self):
rule = CollectionRuleFactory(
user=self.user, category=CategoryFactory(user=self.user)
)
post = PostFactory(title="This is clickbait for sure", rule=rule)
rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user))
post = FeedPostFactory(title="This is clickbait for sure", rule=rule)
response = self.client.put(
reverse("api:news:core:posts-detail", args=[post.pk]),
@ -131,10 +117,8 @@ class PostDetailViewTestCase(TestCase):
self.assertEquals(data["title"], "This title is very accurate")
def test_delete(self):
rule = CollectionRuleFactory(
user=self.user, category=CategoryFactory(user=self.user)
)
post = PostFactory(rule=rule)
rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user))
post = FeedPostFactory(rule=rule)
response = self.client.delete(
reverse("api:news:core:posts-detail", args=[post.pk])
@ -147,8 +131,8 @@ class PostDetailViewTestCase(TestCase):
def test_post_with_unauthenticated_user_without_category(self):
self.client.logout()
rule = CollectionRuleFactory(user=self.user, category=None)
post = PostFactory(rule=rule)
rule = FeedFactory(user=self.user, category=None)
post = FeedPostFactory(rule=rule)
response = self.client.get(
reverse("api:news:core:posts-detail", args=[post.pk])
@ -159,10 +143,8 @@ class PostDetailViewTestCase(TestCase):
def test_post_with_unauthenticated_user_with_category(self):
self.client.logout()
rule = CollectionRuleFactory(
user=self.user, category=CategoryFactory(user=self.user)
)
post = PostFactory(rule=rule)
rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user))
post = FeedPostFactory(rule=rule)
response = self.client.get(
reverse("api:news:core:posts-detail", args=[post.pk])
@ -172,8 +154,8 @@ class PostDetailViewTestCase(TestCase):
def test_post_with_unauthorized_user_without_category(self):
other_user = UserFactory()
rule = CollectionRuleFactory(user=other_user, category=None)
post = PostFactory(rule=rule)
rule = FeedFactory(user=other_user, category=None)
post = FeedPostFactory(rule=rule)
response = self.client.get(
reverse("api:news:core:posts-detail", args=[post.pk])
@ -183,10 +165,8 @@ class PostDetailViewTestCase(TestCase):
def test_post_with_unauthorized_user_with_category(self):
other_user = UserFactory()
rule = CollectionRuleFactory(
user=other_user, category=CategoryFactory(user=other_user)
)
post = PostFactory(rule=rule)
rule = FeedFactory(user=other_user, category=CategoryFactory(user=other_user))
post = FeedPostFactory(rule=rule)
response = self.client.get(
reverse("api:news:core:posts-detail", args=[post.pk])
@ -196,10 +176,8 @@ class PostDetailViewTestCase(TestCase):
def test_post_with_different_user_for_category_and_rule(self):
other_user = UserFactory()
rule = CollectionRuleFactory(
user=self.user, category=CategoryFactory(user=other_user)
)
post = PostFactory(rule=rule)
rule = FeedFactory(user=self.user, category=CategoryFactory(user=other_user))
post = FeedPostFactory(rule=rule)
response = self.client.get(
reverse("api:news:core:posts-detail", args=[post.pk])
@ -208,10 +186,8 @@ class PostDetailViewTestCase(TestCase):
self.assertEquals(response.status_code, 403)
def test_mark_read(self):
rule = CollectionRuleFactory(
user=self.user, category=CategoryFactory(user=self.user)
)
post = PostFactory(rule=rule, read=False)
rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user))
post = FeedPostFactory(rule=rule, read=False)
response = self.client.patch(
reverse("api:news:core:posts-detail", args=[post.pk]),
@ -224,10 +200,8 @@ class PostDetailViewTestCase(TestCase):
self.assertEquals(data["read"], True)
def test_mark_unread(self):
rule = CollectionRuleFactory(
user=self.user, category=CategoryFactory(user=self.user)
)
post = PostFactory(rule=rule, read=True)
rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user))
post = FeedPostFactory(rule=rule, read=True)
response = self.client.patch(
reverse("api:news:core:posts-detail", args=[post.pk]),

View file

@ -6,8 +6,8 @@ from django.urls import reverse
import pytz
from newsreader.accounts.tests.factories import UserFactory
from newsreader.news.collection.tests.factories import CollectionRuleFactory
from newsreader.news.core.tests.factories import CategoryFactory, PostFactory
from newsreader.news.collection.tests.factories import FeedFactory
from newsreader.news.core.tests.factories import CategoryFactory, FeedPostFactory
class PostListViewTestCase(TestCase):
@ -16,10 +16,8 @@ class PostListViewTestCase(TestCase):
self.client.force_login(self.user)
def test_simple(self):
rule = CollectionRuleFactory(
user=self.user, category=CategoryFactory(user=self.user)
)
PostFactory.create_batch(size=3, rule=rule)
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()
@ -30,26 +28,24 @@ class PostListViewTestCase(TestCase):
self.assertEquals(data["count"], 3)
def test_ordering(self):
rule = CollectionRuleFactory(
user=self.user, category=CategoryFactory(user=self.user)
)
rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user))
posts = [
PostFactory(
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
),
),
PostFactory(
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
),
),
PostFactory(
FeedPostFactory(
title="I'm the third post",
rule=rule,
publication_date=datetime.combine(
@ -71,10 +67,8 @@ class PostListViewTestCase(TestCase):
self.assertEquals(data["results"][2]["id"], posts[0].pk)
def test_pagination_count(self):
rule = CollectionRuleFactory(
user=self.user, category=CategoryFactory(user=self.user)
)
PostFactory.create_batch(size=80, rule=rule)
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})
@ -126,7 +120,7 @@ class PostListViewTestCase(TestCase):
def test_posts_with_unauthenticated_user_without_category(self):
self.client.logout()
PostFactory.create_batch(size=3, rule=CollectionRuleFactory(user=self.user))
FeedPostFactory.create_batch(size=3, rule=FeedFactory(user=self.user))
response = self.client.get(reverse("api:news:core:posts-list"))
@ -137,8 +131,8 @@ class PostListViewTestCase(TestCase):
category = CategoryFactory(user=self.user)
PostFactory.create_batch(
size=3, rule=CollectionRuleFactory(user=self.user, category=category)
FeedPostFactory.create_batch(
size=3, rule=FeedFactory(user=self.user, category=category)
)
response = self.client.get(reverse("api:news:core:posts-list"))
@ -148,8 +142,8 @@ class PostListViewTestCase(TestCase):
def test_posts_with_unauthorized_user_without_category(self):
other_user = UserFactory()
rule = CollectionRuleFactory(user=other_user, category=None)
PostFactory.create_batch(size=3, rule=rule)
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()
@ -162,8 +156,8 @@ class PostListViewTestCase(TestCase):
other_user = UserFactory()
category = CategoryFactory(user=other_user)
PostFactory.create_batch(
size=3, rule=CollectionRuleFactory(user=other_user, category=category)
FeedPostFactory.create_batch(
size=3, rule=FeedFactory(user=other_user, category=category)
)
response = self.client.get(reverse("api:news:core:posts-list"))
@ -178,10 +172,8 @@ class PostListViewTestCase(TestCase):
def test_posts_with_authorized_rule_unauthorized_category(self):
other_user = UserFactory()
rule = CollectionRuleFactory(
user=self.user, category=CategoryFactory(user=other_user)
)
PostFactory.create_batch(size=3, rule=rule)
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()
@ -192,8 +184,8 @@ class PostListViewTestCase(TestCase):
self.assertEquals(data["count"], 0)
def test_posts_with_authorized_user_without_category(self):
rule = CollectionRuleFactory(user=self.user, category=None)
PostFactory.create_batch(size=3, rule=rule)
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()
@ -204,12 +196,10 @@ class PostListViewTestCase(TestCase):
self.assertEquals(data["count"], 3)
def test_unread_posts(self):
rule = CollectionRuleFactory(
user=self.user, category=CategoryFactory(user=self.user)
)
rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user))
PostFactory.create_batch(size=10, rule=rule, read=False)
PostFactory.create_batch(size=10, rule=rule, read=True)
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"}
@ -225,12 +215,10 @@ class PostListViewTestCase(TestCase):
self.assertEquals(post["read"], False)
def test_read_posts(self):
rule = CollectionRuleFactory(
user=self.user, category=CategoryFactory(user=self.user)
)
rule = FeedFactory(user=self.user, category=CategoryFactory(user=self.user))
PostFactory.create_batch(size=20, rule=rule, read=False)
PostFactory.create_batch(size=10, rule=rule, read=True)
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"}

View file

@ -3,7 +3,7 @@ import factory.fuzzy
import pytz
from newsreader.accounts.tests.factories import UserFactory
from newsreader.news.collection.reddit import REDDIT_URL
from newsreader.news.collection.reddit import REDDIT_API_URL
from newsreader.news.core.models import Category, Post
@ -33,9 +33,12 @@ class PostFactory(factory.django.DjangoModelFactory):
model = Post
class FeedPostFactory(PostFactory):
rule = factory.SubFactory("newsreader.news.collection.tests.factories.FeedFactory")
class RedditPostFactory(PostFactory):
remote_identifier = factory.Faker("uuid4")
url = factory.fuzzy.FuzzyText(length=10, prefix=f"{REDDIT_URL}/")
url = factory.fuzzy.FuzzyText(length=10, prefix=f"{REDDIT_API_URL}/")
rule = factory.SubFactory(
"newsreader.news.collection.tests.factories.SubredditFactory"
)

View file

@ -68,14 +68,9 @@
margin: 20px 0 5px 0;
}
& img {
padding: 10px 10px 30px 10px;
max-width: 70%;
width: inherit;
height: 100%;
align-self: center;
& img, video {
padding: 10px 0;
max-width: 100%;
}
}