diff --git a/.coveragerc b/.coveragerc index e69de29..78c3951 100644 --- a/.coveragerc +++ b/.coveragerc @@ -0,0 +1,2 @@ +[run] +omit = dankbot/_version.py diff --git a/circle.yml b/circle.yml index a5962b4..d2a1709 100644 --- a/circle.yml +++ b/circle.yml @@ -4,9 +4,9 @@ machine: general: artifacts: - - .tox/tox/junit.xml - - .tox/tox/flake8.txt - - .tox/coverage.xml + - .tox/py3/junit.xml + - .tox/lint/flake8.txt + - coverage.xml dependencies: override: @@ -20,8 +20,8 @@ test: post: - coveralls - mkdir -p $CIRCLE_TEST_REPORTS/dankbot - - cp .tox/tox/junit.xml $CIRCLE_TEST_REPORTS/dankbot - - cp .tox/tox/flake8.txt $CIRCLE_TEST_REPORTS/dankbot + - cp .tox/py3/junit.xml $CIRCLE_TEST_REPORTS/dankbot + - cp .tox/lint/flake8.txt $CIRCLE_TEST_REPORTS/dankbot - cp -R htmlcov $CIRCLE_TEST_REPORTS/dankbot - cp coverage.xml $CIRCLE_TEST_REPORTS/dankbot diff --git a/dankbot/cli.py b/dankbot/cli.py index e3731be..d02fb44 100644 --- a/dankbot/cli.py +++ b/dankbot/cli.py @@ -1,4 +1,4 @@ -from __future__ import print_function + import sys import logging @@ -55,7 +55,3 @@ def main(): DankBot(config, logger).find_and_post_memes() except Exception: # pylint: disable=W0703 logger.exception("Caught exception:") - - -if __name__ == "__main__": - main() diff --git a/dankbot/dankbot.py b/dankbot/dankbot.py index 66d0d24..1d7e239 100644 --- a/dankbot/dankbot.py +++ b/dankbot/dankbot.py @@ -20,7 +20,7 @@ def __init__(self, config, logger): # pylint: disable=too-many-instance-attributes self.slack_token = config['slack']['token'] - self.channel = config['slack']['channel'] + self.slack_channel = config['slack']['channel'] self.database = config['mysql']['database'] self.username = config['mysql']['username'] @@ -95,7 +95,7 @@ def get_memes(self): self.logger.debug("Collecting memes from subreddit: {0}".format(sub)) try: subreddit_memes = self._get_memes_from_subreddit(r_client, sub) - except HTTPException: + except HTTPException: # pragma: no cover log = "API failed to get memes for subreddit: {0}" self.logger.exception(log.format(sub)) continue @@ -116,7 +116,7 @@ def get_memes(self): def _get_memes_from_subreddit(client, subreddit): return client.get_subreddit(subreddit).get_hot() - def in_collection(self, meme): + def in_collection(self, meme): # pragma no cover ''' Checks to see if the supplied meme is already in the collection of known memes @@ -143,7 +143,7 @@ def in_collection(self, meme): return True if resp else False - def add_to_collection(self, meme): + def add_to_collection(self, meme): # pragma no cover ''' Adds a meme to the collection ''' @@ -176,7 +176,6 @@ def post_to_slack(self, memes): slack = Slacker(self.slack_token) for meme in memes: - try: message = meme.format_for_slack() except UndigestedError: @@ -185,7 +184,7 @@ def post_to_slack(self, memes): message = "from {0}: {1}".format(meme.source, meme.link) - resp = slack.chat.post_message(self.channel, message, as_user=True) + resp = slack.chat.post_message(self.slack_channel, message, as_user=True) if resp.successful: self.add_to_collection(meme) diff --git a/setup.cfg b/setup.cfg index 28782f4..76c9ce1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,4 +1,4 @@ -[pytest] +[tool:pytest] testpaths = tests [versioneer] @@ -13,4 +13,4 @@ count = True statistics = True max-complexity = 10 max-line-length = 100 -exclude = .git,__pycache__,old,build,dist,versioneer.py,dankbot/_version.py +exclude = .git,versioneer.py,dankbot/_version.py diff --git a/tests/test_cli.py b/tests/test_cli.py new file mode 100644 index 0000000..83b7389 --- /dev/null +++ b/tests/test_cli.py @@ -0,0 +1,30 @@ +from configparser import ConfigParser +from unittest.mock import patch + +from dankbot import cli + + +@patch('dankbot.cli.logging.getLogger') +@patch('dankbot.cli.RotatingFileHandler') +@patch('dankbot.cli.DankBot') +def test_main(dankbot, _, gl_mock): + gl_mock.return_value = gl_mock + dankbot.return_value = dankbot + + cli.main() + + assert dankbot.called + assert isinstance(dankbot.call_args[0][0], ConfigParser) + assert dankbot.call_args[0][1], gl_mock + assert dankbot.find_and_post_memes.called + + +@patch('dankbot.cli.configure_logger') +@patch('dankbot.cli.DankBot') +def test_main_exception(dankbot, logger_mock): + logger_mock.return_value = logger_mock + dankbot.return_value = ValueError("Mock exception") + + cli.main() + + assert logger_mock.exception.called diff --git a/tests/test_dankbot.py b/tests/test_dankbot.py new file mode 100644 index 0000000..4ccbceb --- /dev/null +++ b/tests/test_dankbot.py @@ -0,0 +1,213 @@ +import logging +from unittest import mock +from unittest.mock import patch, call +from configparser import ConfigParser + +import pytest + +from dankbot.dankbot import DankBot +from dankbot.memes import ImgurMeme + +MOCK_TOKEN = "mock_token" +MOCK_CHANNEL = "mock_slack_channel" + +MOCK_DB = "mock_db" +MOCK_UN = "mock_username" +MOCK_PW = "mock_password" + +INCLUDE_NSFW = False +MAX_MEMES = 5 + +SUB_1 = "dankmemes" +SUB_2 = "fishpost" +SUB_3 = "memes" +SUBREDDITS = ", ".join([SUB_1, SUB_2, SUB_3]) + +IMGUR_CLIENT_ID = "mock imgur client id" +IMGUR_CLIENT_SECRET = "mock imgur secret" + +DANK_MEME_URL = "http://dank.meme.url" +IMGUR_MEME_URL = "http://imgur.com/mockhash" + + +class MockMeme(object): + def __init__(self, over_18, url): + self.over_18 = False + self.url = url + + +@pytest.fixture(scope="function") +def slack(): + with patch('dankbot.dankbot.Slacker') as slack: + slack.return_value = slack + slack.chat = slack + slack.post_message.return_value = slack + slack.successful = True + + yield slack + + +@pytest.fixture(scope="function") +def praw(dank_meme): + with patch('dankbot.dankbot.praw') as praw: + praw.Reddit.return_value = praw + praw.get_subreddit.return_value = praw + praw.get_hot.return_value = [dank_meme, ] + + yield praw + + +@pytest.fixture(scope="function") +def dank_meme(): + return MockMeme(False, DANK_MEME_URL) + + +@pytest.fixture(scope="function") +def imgur_meme(): + return MockMeme(False, IMGUR_MEME_URL) + + +@pytest.fixture(scope="function") +def config(): + ''' Returns a mock configuration object + ''' + config_dict = { + 'slack': { + 'channel': MOCK_CHANNEL, + 'token': MOCK_TOKEN, + }, + 'mysql': { + 'database': MOCK_DB, + 'username': MOCK_UN, + 'password': MOCK_PW, + }, + 'misc': { + 'include_nsfw': INCLUDE_NSFW, + 'max_memes': MAX_MEMES + }, + 'reddit': { + 'subreddits': SUBREDDITS + }, + 'imgur': { + 'client_id': IMGUR_CLIENT_ID, + 'client_secret': IMGUR_CLIENT_SECRET + } + } + + config_ = ConfigParser() + config_.read_dict(config_dict) + + return config_ + + +@pytest.fixture(scope="function") +def logger(): + ''' Returns a mock logger + ''' + return mock.create_autospec(logging.Logger) + + +def test___init__(config, logger): + dankbot = DankBot(config, logger) + + assert dankbot.slack_token == MOCK_TOKEN + assert dankbot.slack_channel == MOCK_CHANNEL + assert dankbot.database == MOCK_DB + assert dankbot.username == MOCK_UN + assert dankbot.password == MOCK_PW + assert dankbot.include_nsfw == INCLUDE_NSFW + assert dankbot.max_memes == MAX_MEMES + assert SUB_1 in dankbot.subreddits + assert SUB_2 in dankbot.subreddits + assert SUB_3 in dankbot.subreddits + assert dankbot.logger == logger + + assert ImgurMeme.client_id == IMGUR_CLIENT_ID + assert ImgurMeme.client_secret == IMGUR_CLIENT_SECRET + + +@patch.object(DankBot, 'add_to_collection') +@patch.object(DankBot, 'in_collection') +def test_find_and_post(ic, atc, praw, slack, config, logger): + ic.return_value = False + + dankbot = DankBot(config, logger) + dankbot.subreddits = dankbot.subreddits[:1] + + resp = dankbot.find_and_post_memes() + + assert slack.post_message.called + assert resp is True + assert atc.called + + message = "from {0}: {1}".format(SUB_1, DANK_MEME_URL) + assert slack.post_message.call_args == call(MOCK_CHANNEL, message, as_user=True) + + +@patch.object(DankBot, 'in_collection') +def test_find_and_post_in_collection(ic, praw, config, logger): + ic.return_value = True + + dankbot = DankBot(config, logger) + dankbot.subreddits = dankbot.subreddits[:1] + + resp = dankbot.find_and_post_memes() + + assert resp is False + + +@patch.object(DankBot, 'in_collection') +def test_no_nsfw_and_18plus(ic, praw, config, logger): + ic.return_value = False + praw.get_hot.return_value[0].over_18 = True + + dankbot = DankBot(config, logger) + dankbot.subreddits = dankbot.subreddits[:1] + + resp = dankbot.find_and_post_memes() + + assert resp is False + + +@patch.object(DankBot, 'add_to_collection') +@patch.object(DankBot, 'in_collection') +def test_yes_nsfw_and_18plus(ic, atc, slack, praw, config, logger): + ic.return_value = False + praw.get_hot.return_value[0].over_18 = True + + dankbot = DankBot(config, logger) + dankbot.include_nsfw = True + dankbot.subreddits = dankbot.subreddits[:1] + + resp = dankbot.find_and_post_memes() + + assert slack.post_message.called + assert resp is True + assert atc.called + + message = "from {0}: {1}".format(SUB_1, DANK_MEME_URL) + assert slack.post_message.call_args == call(MOCK_CHANNEL, message, as_user=True) + + +@patch('dankbot.dankbot.praw') +@patch('dankbot.memes.ImgurClient') +@patch.object(DankBot, 'add_to_collection') +@patch.object(DankBot, 'in_collection') +def test_post_imgur_meme(ic, atc, imgur, praw, slack, config, logger, imgur_meme): + ic.return_value = False + imgur.return_value = Exception("Preventing connection") + praw.Reddit.return_value = praw + praw.get_subreddit.return_value = praw + praw.get_hot.return_value = [imgur_meme, ] + + dankbot = DankBot(config, logger) + dankbot.subreddits = dankbot.subreddits[:1] + + resp = dankbot.find_and_post_memes() + + assert slack.post_message.called + assert resp is True + assert atc.called + + message = "from {0}: {1}".format(SUB_1, IMGUR_MEME_URL) + assert slack.post_message.call_args == call(MOCK_CHANNEL, message, as_user=True) diff --git a/tox.ini b/tox.ini index b29ca76..d56d872 100644 --- a/tox.ini +++ b/tox.ini @@ -1,22 +1,18 @@ [tox] skipdist = True -envlist = test +envlist = py3,lint [testenv] -basepython = python3.5 -envdir = {toxworkdir}/tox +commands = + py.test -s --cov dankbot --cov-report term-missing --cov-report html --cov-report xml --junitxml={envdir}/junit.xml tests [] deps = pytest>=2.6.4 pytest-cov>=1.8.1 - pytest-sugar - flake8 - isort - tox-pyenv - -[testenv:test] +[testenv:lint] +deps = + flake8==2.4.0 commands = - py.test --cov dankbot --cov-report term-missing --cov-report html --cov-report xml --junitxml={envdir}/junit.xml [] flake8 --output-file={envdir}/flake8.txt setup.py dankbot tests [testenv:env]