from unittest.mock import Mock, patch

from django.test import TestCase

from requests.exceptions import ConnectionError as RequestConnectionError
from requests.exceptions import HTTPError, RequestException, SSLError, TooManyRedirects

from newsreader.news.collection.exceptions import (
    StreamConnectionException,
    StreamDeniedException,
    StreamException,
    StreamForbiddenException,
    StreamNotFoundException,
    StreamTimeOutException,
    StreamTooManyException,
)
from newsreader.news.collection.utils import fetch, post


class HelperFunctionTestCase:
    def test_simple(self):
        self.mocked_method.return_value = Mock(status_code=200, content="content")

        url = "https://www.bbc.co.uk/news"
        response = self.method(url)

        self.assertEquals(response.content, "content")

    def test_raises_not_found(self):
        self.mocked_method.return_value = Mock(status_code=404)

        url = "https://www.bbc.co.uk/news"

        with self.assertRaises(StreamNotFoundException):
            self.method(url)

    def test_raises_denied(self):
        self.mocked_method.return_value = Mock(status_code=401)

        url = "https://www.bbc.co.uk/news"

        with self.assertRaises(StreamDeniedException):
            self.method(url)

    def test_raises_forbidden(self):
        self.mocked_method.return_value = Mock(status_code=403)

        url = "https://www.bbc.co.uk/news"

        with self.assertRaises(StreamForbiddenException):
            self.method(url)

    def test_raises_timed_out(self):
        self.mocked_method.return_value = Mock(status_code=408)

        url = "https://www.bbc.co.uk/news"

        with self.assertRaises(StreamTimeOutException):
            self.method(url)

    def test_raises_stream_error_on_ssl_error(self):
        self.mocked_method.side_effect = SSLError

        url = "https://www.bbc.co.uk/news"

        with self.assertRaises(StreamException):
            self.method(url)

    def test_raises_stream_error_on_connection_error(self):
        self.mocked_method.side_effect = RequestConnectionError

        url = "https://www.bbc.co.uk/news"

        with self.assertRaises(StreamConnectionException):
            self.method(url)

    def test_raises_stream_error_on_http_error(self):
        self.mocked_method.side_effect = HTTPError

        url = "https://www.bbc.co.uk/news"

        with self.assertRaises(StreamException):
            self.method(url)

    def test_raises_stream_error_on_request_exception(self):
        self.mocked_method.side_effect = RequestException

        url = "https://www.bbc.co.uk/news"

        with self.assertRaises(StreamException):
            self.method(url)

    def test_raises_stream_error_on_too_many_redirects(self):
        self.mocked_method.side_effect = TooManyRedirects

        url = "https://www.bbc.co.uk/news"

        with self.assertRaises(StreamException):
            self.method(url)

    def test_raises_stream_error_on_too_many_requests(self):
        self.mocked_method.return_value = Mock(status_code=429)

        url = "https://www.bbc.co.uk/news"

        with self.assertRaises(StreamTooManyException):
            self.method(url)


class FetchTestCase(HelperFunctionTestCase, TestCase):
    def setUp(self):
        self.patch = patch("newsreader.news.collection.utils.requests.get")
        self.mocked_method = self.patch.start()

        self.method = fetch


class PostTestCase(HelperFunctionTestCase, TestCase):
    def setUp(self):
        self.patch = patch("newsreader.news.collection.utils.requests.post")
        self.mocked_method = self.patch.start()

        self.method = post
