from json.decoder import JSONDecodeError
from unittest.mock import patch
from uuid import uuid4

from django.test import TestCase

from newsreader.accounts.tests.factories import UserFactory
from newsreader.news.collection.exceptions import (
    StreamDeniedException,
    StreamException,
    StreamForbiddenException,
    StreamNotFoundException,
    StreamParseException,
    StreamTimeOutException,
)
from newsreader.news.collection.reddit import RedditStream
from newsreader.news.collection.tests.factories import SubredditFactory
from newsreader.news.collection.tests.reddit.stream.mocks import simple_mock


class RedditStreamTestCase(TestCase):
    def setUp(self):
        self.maxDiff = None

        self.patched_fetch = patch("newsreader.news.collection.reddit.fetch")
        self.mocked_fetch = self.patched_fetch.start()

    def tearDown(self):
        patch.stopall()

    def test_simple_stream(self):
        self.mocked_fetch.return_value.json.return_value = simple_mock

        access_token = str(uuid4())
        user = UserFactory(reddit_access_token=access_token)

        subreddit = SubredditFactory(user=user)
        stream = RedditStream(subreddit)

        data, stream = stream.read()

        self.assertEquals(data, simple_mock)
        self.assertEquals(stream, stream)
        self.mocked_fetch.assert_called_once_with(
            subreddit.url, headers={"Authorization": f"bearer {access_token}"}
        )

    def test_stream_raises_exception(self):
        self.mocked_fetch.side_effect = StreamException

        access_token = str(uuid4())
        user = UserFactory(reddit_access_token=access_token)

        subreddit = SubredditFactory(user=user)
        stream = RedditStream(subreddit)

        with self.assertRaises(StreamException):
            stream.read()

        self.mocked_fetch.assert_called_once_with(
            subreddit.url, headers={"Authorization": f"bearer {access_token}"}
        )

    def test_stream_raises_denied_exception(self):
        self.mocked_fetch.side_effect = StreamDeniedException

        access_token = str(uuid4())
        user = UserFactory(reddit_access_token=access_token)

        subreddit = SubredditFactory(user=user)
        stream = RedditStream(subreddit)

        with self.assertRaises(StreamDeniedException):
            stream.read()

        self.mocked_fetch.assert_called_once_with(
            subreddit.url, headers={"Authorization": f"bearer {access_token}"}
        )

    def test_stream_raises_not_found_exception(self):
        self.mocked_fetch.side_effect = StreamNotFoundException

        access_token = str(uuid4())
        user = UserFactory(reddit_access_token=access_token)

        subreddit = SubredditFactory(user=user)
        stream = RedditStream(subreddit)

        with self.assertRaises(StreamNotFoundException):
            stream.read()

        self.mocked_fetch.assert_called_once_with(
            subreddit.url, headers={"Authorization": f"bearer {access_token}"}
        )

    def test_stream_raises_time_out_exception(self):
        self.mocked_fetch.side_effect = StreamTimeOutException

        access_token = str(uuid4())
        user = UserFactory(reddit_access_token=access_token)

        subreddit = SubredditFactory(user=user)
        stream = RedditStream(subreddit)

        with self.assertRaises(StreamTimeOutException):
            stream.read()

        self.mocked_fetch.assert_called_once_with(
            subreddit.url, headers={"Authorization": f"bearer {access_token}"}
        )

    def test_stream_raises_forbidden_exception(self):
        self.mocked_fetch.side_effect = StreamForbiddenException

        access_token = str(uuid4())
        user = UserFactory(reddit_access_token=access_token)

        subreddit = SubredditFactory(user=user)
        stream = RedditStream(subreddit)

        with self.assertRaises(StreamForbiddenException):
            stream.read()

        self.mocked_fetch.assert_called_once_with(
            subreddit.url, headers={"Authorization": f"bearer {access_token}"}
        )

    def test_stream_raises_parse_exception(self):
        self.mocked_fetch.return_value.json.side_effect = JSONDecodeError(
            "No json found", "{}", 5
        )

        access_token = str(uuid4())
        user = UserFactory(reddit_access_token=access_token)

        subreddit = SubredditFactory(user=user)
        stream = RedditStream(subreddit)

        with self.assertRaises(StreamParseException):
            stream.read()

        self.mocked_fetch.assert_called_once_with(
            subreddit.url, headers={"Authorization": f"bearer {access_token}"}
        )
