def test_comparison(other_subreddit, other_post_ids, identical): """Test the comparison of `Post` objects.""" posts = [ Post( name="episode_{0}".format(post_id), submit_at=datetime(2018, 1, 6, 17, 0, 0), subreddit="anime", title="Slow Start - Episode {0} Discussion".format(post_id), body_template="*Slow Start*, Episode {0}".format(post_id), ) for post_id in range(1, 3) ] schedule = Schedule(subreddit="anime", posts=posts) other_posts = [ Post( name="episode_{0}".format(post_id), submit_at=datetime(2018, 1, 6, 17, 0, 0), subreddit="anime", title="Slow Start - Episode {0} Discussion".format(post_id), body_template="*Slow Start*, Episode {0}".format(post_id), ) for post_id in other_post_ids ] other_schedule = Schedule(subreddit=other_subreddit, posts=other_posts) if identical: assert schedule == other_schedule else: assert schedule != other_schedule assert schedule.__eq__("schedule") is NotImplemented # noqa: WPS609
def prepare_thumbnail(self, post: Post) -> None: """ Prepare the post for the submission with thumbnail. Converted the post to the Rich Text JSON format so that Reddit creates a thumbnail for the post. """ log.debug("scheduled_post_convert") try: post.body_rtjson = self.post_converter.convert_to_rtjson( post.body_md, ) except ImageNotFound: log.warning("scheduled_post_missing_image", post=str(post)) post.submit_with_thumbnail = False click.echo( click.style( "Warning: No image found in the post.\n" + "To submit a post without image set the " + "'submit_with_thumbnail' option to False.", fg="red", )) except (RedditError, PostConversionError) as exception: log.exception("scheduled_post_convert_error") exception.hint = ( "To avoid further issues you can submit a post without " + "image by setting the 'submit_with_thumbnail' option to False." ) raise
def test_comparison(): """Test the comparison of `Post` objects.""" post = Post( name="episode_01", submit_at=datetime(2018, 1, 6, 17, 0, 0), subreddit="anime", title="Slow Start - Episode 1 Discussion", body_template="*Slow Start*, Episode 1: The First Butterflies", ) identical_post = Post( name="episode_01", submit_at=datetime(2018, 1, 6, 17, 0, 0), subreddit="anime", title="Slow Start - Episode 1 Discussion", body_template="*Slow Start*, Episode 1: The First Butterflies", ) different_post = Post( name="episode_02", submit_at=datetime(2018, 1, 13, 12, 0, 0), subreddit="anime", title="Slow Start - Episode 2 Discussion", body_template="*Slow Start*, Episode 2: Exercise Wears Me Out ", ) assert post == identical_post assert post != different_post assert post.__eq__("post") is NotImplemented # noqa: WPS609
def test_build_navigation_links( mock_substitute_navigation_links, post_helper_config, reddit, ): """Test the preparation of the Navigation Links.""" posts = [ Post( name="e{0:02}".format(post), submit_at=datetime(2018, 1, 6, 17, 0, 0), subreddit="anime", title="Slow Start - Episode {0} Discussion".format(post), body_template="1:$e01|2:$e02|3:$e03", navigation_scheduled="-", navigation_submitted="$link", navigation_current="*", ) for post in range(1, 4) ] post_helper = PostHelper(post_helper_config, reddit) post_helper.build_navigation_links(posts[0], posts) assert mock_substitute_navigation_links.call_args == call(None, None) posts[0].submission_id = "id_1" post_helper.build_navigation_links(posts[1], posts) assert mock_substitute_navigation_links.call_args == call("id_1", None) posts[1].submission_id = "id_2" post_helper.build_navigation_links(posts[0], posts) assert mock_substitute_navigation_links.call_args == call(None, "id_2") posts[2].submission_id = "id_3" post_helper.build_navigation_links(posts[1], posts) assert mock_substitute_navigation_links.call_args == call("id_1", "id_3")
def post(): """Return an example Post.""" body_template = """*Slow Start*, Episode 1: The First Butterflies --- **Streams:** * [Crunchyroll](https://www.crunchyroll.com/slow-start/episode-1-the-first-butterflies-759027) * [VRV](https://vrv.co/watch/G69P1KQ0Y/Slow-Start:The-First-Butterflies) --- **Show Information:** * [MyAnimeList](https://myanimelist.net/anime/35540) * [AniDB](http://anidb.net/perl-bin/animedb.pl?show=anime&aid=13160) * [AniList](https://anilist.co/anime/98693/SlowStart) * [Official Website](http://slow-start.com) * [US Website](http://slowstart-usa.com/) * [Twitter](https://twitter.com/slosta_anime) * [US Facebook Page](https://www.facebook.com/SlowStartUSA/) """ # noqa: E501 return Post( name="episode_01", submit_at=datetime(2018, 1, 6, 17, 0, 0), subreddit="anime", title="Slow Start - Episode 1 Discussion", body_template=body_template, submit_with_thumbnail=True, )
def test_create(): """Test creating a `Post` instance.""" post = Post( name="episode_01", submit_at=datetime(2018, 1, 6, 17, 0, 0), subreddit="anime", title="Slow Start - Episode 1 Discussion", body_template="*Slow Start*, Episode 1", ) assert post.submit_at == datetime(2018, 1, 6, 17, 0, 0) assert post.subreddit == "anime" assert post.title == "Slow Start - Episode 1 Discussion" assert post.body_template == "*Slow Start*, Episode 1" assert ( post.submit_with_thumbnail, post.flair_id, post.navigation_submitted, post.navigation_current, post.navigation_scheduled, post.submission_id, post.body_md, post.body_rtjson, ) == ( True, # Set to True by default None, "$link", # Default value "", # Default value "", # Default value None, None, None, )
def test_create_with_empty_field(): """Test that the `Post` cannot be instantiated with empty attributes.""" with pytest.raises(AttributeError): Post( name="episode_01", submit_at=datetime(2018, 1, 6, 17, 0, 0), subreddit="anime", title="Slow Start - Episode 1 Discussion", body_template=None, # type: ignore )
def test_string_representation(): """Test the string representation of a `Post` instance.""" post = Post( name="episode_01", submit_at=datetime(2018, 1, 6, 17, 0, 0), subreddit="anime", title="Slow Start - Episode 1 Discussion", body_template="*Slow Start*, Episode 1: The First Butterflies", ) assert "/r/anime Post at 2018-01-06 17:00:00: Slow Start" in str(post)
def schedule(): """Return the `Schedule` with 3 posts.""" posts = [ Post( name="episode_{0}".format(index + 1), submit_at=datetime(2018, 1, 6 + index * 7, 17, 0, 0), subreddit="anime", title="Slow Start - Episode {0} Discussion".format(index + 1), body_template="*Slow Start*, Episode {0}".format(index + 1), ) for index in range(0, 3) ] return Schedule(subreddit="anime", posts=posts)
def submit_post(self, post: Post) -> Submission: """ Submit the post to Reddit. If the post is set to be submitted with thumbnail, submit its body in the Rich Text JSON format. Otherwise submit the Markdown content using regular method of `PRAW`. """ log.info("post_submit", post=str(post)) try: if post.submit_with_thumbnail and post.body_rtjson: submission = self.reddit_helper.submit_post_rtjson( subreddit=post.subreddit, title=post.title, body_rtjson=post.body_rtjson, flair_id=post.flair_id, ) else: submission = self.reddit.subreddit( post.subreddit, ).submit( title=post.title, selftext=post.body_md, flair_id=post.flair_id, ) except PrawcoreException as exception: log.exception("post_submit_error") raise RedditError( "Failed to submit the post.", ) from exception post.submission_id = submission.id log.debug("post_submit_result", permalink=submission.permalink) if not post.submit_with_thumbnail: return submission delay = self.post_update_delay log.debug("post_update_delay", delay=delay) click.echo( click.style( "Waiting {0}s before updating the post.".format(delay / 1000), fg="cyan", ), ) time.sleep(delay / 1000) return self.update_post(post, submission)
def test_create(): """Test creating a `Post` instance.""" posts = [ Post( name="episode_{0}".format(post_id), submit_at=datetime(2018, 1, 6, 17, 0, 0), subreddit="anime", title="Slow Start - Episode {0} Discussion".format(post_id), body_template="*Slow Start*, Episode {0}".format(post_id), ) for post_id in range(1, 4) ] schedule = Schedule(subreddit="anime", posts=posts) assert schedule.subreddit == "anime" assert schedule.posts[0].subreddit == "anime" assert schedule.posts[1].title == "Slow Start - Episode 2 Discussion" assert schedule.posts[2].submit_with_thumbnail # Set to True by default
def schedule(): """ Return mock `Schedule`. The first Post has `submission_id` filled in. """ posts = [ Post( name="episode_{0}".format(post_id), submit_at=datetime(2018, 1, 6, 17, 0, 0), subreddit="anime", title="Slow Start - Episode {0} Discussion".format(post_id), body_template="*Slow Start*, Episode {0}".format(post_id), ) for post_id in range(1, 4) ] posts[0].submission_id = "cute_id" return Schedule(subreddit="anime", posts=posts)
def test_submit_post_without_thumbnail( mock_reddit, mock_reddit_helper, mock_update_post, reddit_cutifier_config, post: Post, ): """ Test submitting a post without thumbnail. 1. Test successful submission: Set :attr:`Post.submit_with_thumbnail` to False (to make the test more obvious even when the `body_rtjson` is empty). Check that :meth:`Reddit.subreddit().submit()` is called with the correct arguments. Check that :meth:`RedditHelper.submit_post_rtjson()` is not called (used only for posts with thumbnail). 2. Test handling of an exception raised by `PRAW`. """ post.submit_with_thumbnail = False reddit_cutifier = RedditCutifier(reddit_cutifier_config) subreddit = mock_reddit.return_value.subreddit.return_value subreddit.submit.return_value.permalink = "slow_start_link" assert reddit_cutifier.submit_post(post).permalink == "slow_start_link" assert subreddit.submit.call_args == call( title=post.title, selftext=post.body_md, flair_id=post.flair_id, ) assert not mock_reddit_helper.return_value.submit_post_rtjson.called subreddit.submit.side_effect = PrawcoreException with pytest.raises(RedditError): reddit_cutifier.submit_post(post) assert not mock_update_post.called
def test_prepare_post( mock_prepare_thumbnail, mock_build_navigation_links, post_helper_config, reddit, ): """ Test the preparation of the post body. 1. Prepare post without thumbnail. 2. Prepare post with thumbnail. """ mock_build_navigation_links.return_value = "links" posts = [ Post( name="e{0:02}".format(post), submit_at=datetime(2018, 1, 6, 17, 0, 0), subreddit="anime", title="Slow Start - Episode {0} Discussion".format(post), body_template="$navigation_links|1:$e01|2:$e02|3:$e03", navigation_scheduled="-", navigation_submitted="$link", navigation_current="*", ) for post in range(1, 4) ] posts[0].submission_id = "cute_id" schedule = Schedule(subreddit="anime", posts=posts) post_helper = PostHelper(post_helper_config, reddit) post_helper.prepare_post(posts[1], schedule) assert posts[1].body_md == "links|1:/cute_id|2:*|3:-" assert posts[0].body_md is None assert mock_prepare_thumbnail.call_count == 0 post_helper.prepare_post(posts[0], schedule, prepare_thumbnail=True) assert posts[0].body_md == "links|1:*|2:-|3:-" assert mock_prepare_thumbnail.call_count == 1
def test_submit_post_with_thumbnail( mock_reddit, mock_reddit_helper, mock_sleep, mock_update_post, reddit_cutifier_config, post: Post, ): """ Test submitting a post with thumbnail. Set the :attr:`Post.body_rtjson` (empty by default). Check that :meth:`RedditHelper.submit_post_rtjson()` is called with the correct arguments. Check that :meth:`Reddit.subreddit()` is not called (used only for posts without thumbnail). Check the delay before updating the post. Check that :meth:`RedditCutifier.update_post()` is called. """ post.body_rtjson = [{"c": [{"t": "Slow Start"}]}] reddit_cutifier = RedditCutifier(reddit_cutifier_config) submit_post_rtjson = mock_reddit_helper.return_value.submit_post_rtjson mock_update_post.return_value.permalink = "slow_start_link" assert reddit_cutifier.submit_post(post).permalink == "slow_start_link" assert submit_post_rtjson.call_args == call( subreddit=post.subreddit, title=post.title, body_rtjson=post.body_rtjson, flair_id=post.flair_id, ) assert not mock_reddit.return_value.subreddit.called assert mock_sleep.call_args == call(2000 / 1000) assert mock_update_post.called
def prepare_post( self, post: Post, schedule: Schedule, prepare_thumbnail=False, ) -> None: """ Prepare the post body based on the post body template. Substitute placeholders using the attributes of the provided `Schedule` instance. Call :meth:`prepare_thumbnail()` if :attr:`Post.submit_with_thumbnail` is `True`. """ mapping = {} for schedule_post in schedule.posts: if schedule_post.name == post.name: navigation_text = schedule_post.navigation_current elif schedule_post.submission_id: navigation_text = re.sub( r"\$link", "/{0}".format(schedule_post.submission_id), schedule_post.navigation_submitted, ) else: navigation_text = schedule_post.navigation_scheduled mapping[schedule_post.name] = navigation_text mapping[self. navigation_links["placeholder"]] = self.build_navigation_links( post, schedule.posts) post.body_md = Template(post.body_template).safe_substitute(mapping, ) if prepare_thumbnail and post.submit_with_thumbnail: self.prepare_thumbnail(post)
def test_update_post( mock_reddit, reddit_cutifier_config, post: Post, submission, capsys, ): """ Test updating a post. 1. Test a successful edit. 2. Test updating without providing both `submission` parameter and the :attr:`Post.submission_id`. 3. Previous test with the `submission_id` provided. 4. Test handling of an exception raised by `PRAW`. """ reddit_cutifier = RedditCutifier(reddit_cutifier_config) reddit_cutifier.update_post(post, submission) assert submission.edit.call_args == call(post.body_md) with pytest.raises(RuntimeError): reddit_cutifier.update_post(post) post.submission_id = "cute_id" reddit_cutifier.update_post(post) assert mock_reddit.return_value.submission.call_args == call("cute_id") submission.edit.side_effect = PrawcoreException reddit_cutifier.update_post(post, submission) captured = capsys.readouterr() assert "Failed to update the post" in captured.err
def test_update_posts( mock_reddit, mock_update_post, reddit_cutifier_config, ): """Test updating multiple posts.""" posts = [ Post( name="episode_{0}".format(post_id), submit_at=datetime(2018, 1, 6, 17, 0, 0), subreddit="anime", title="Slow Start - Episode {0} Discussion".format(post_id), body_template="*Slow Start*, Episode {0}".format(post_id), ) for post_id in range(1, 4) ] reddit_cutifier = RedditCutifier(reddit_cutifier_config) reddit_cutifier.update_posts(posts) expected_calls = [call(post) for post in posts] assert list(mock_update_post.call_args_list) == expected_calls