from unittest.mock import Mock, patch

from django.test import TestCase

from bs4 import BeautifulSoup

from newsreader.news.collection.exceptions import (
    StreamDeniedException,
    StreamForbiddenException,
    StreamNotFoundException,
    StreamParseException,
    StreamTimeOutException,
)
from newsreader.news.collection.favicon import FaviconCollector
from newsreader.news.collection.tests.factories import CollectionRuleFactory

from .mocks import feed_mock, website_mock


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

        self.patched_feed_client = patch(
            "newsreader.news.collection.favicon.FeedClient.__enter__"
        )
        self.mocked_feed_client = self.patched_feed_client.start()

        self.patched_website_read = patch(
            "newsreader.news.collection.favicon.WebsiteStream.read"
        )
        self.mocked_website_read = self.patched_website_read.start()

    def tearDown(self):
        patch.stopall()

    def test_simple(self):
        rule = CollectionRuleFactory(succeeded=True, error=None)

        self.mocked_feed_client.return_value = [(feed_mock, Mock(rule=rule))]
        self.mocked_website_read.return_value = (website_mock, Mock(rule=rule))

        collector = FaviconCollector()
        collector.collect()

        rule.refresh_from_db()

        self.assertEquals(rule.succeeded, True)
        self.assertEquals(rule.error, None)
        self.assertEquals(rule.website_url, "https://www.bbc.co.uk/news/")
        self.assertEquals(rule.favicon, "https://www.bbc.co.uk/news/favicon.ico")

    def test_empty_stream(self):
        rule = CollectionRuleFactory(succeeded=True, error=None)

        self.mocked_feed_client.return_value = [(feed_mock, Mock(rule=rule))]
        self.mocked_website_read.return_value = (
            BeautifulSoup("", "lxml"),
            Mock(rule=rule),
        )

        collector = FaviconCollector()
        collector.collect()

        rule.refresh_from_db()

        self.assertEquals(rule.succeeded, True)
        self.assertEquals(rule.error, None)
        self.assertEquals(rule.website_url, "https://www.bbc.co.uk/news/")
        self.assertEquals(rule.favicon, None)

    def test_not_found(self):
        rule = CollectionRuleFactory(succeeded=True, error=None)

        self.mocked_feed_client.return_value = [(feed_mock, Mock(rule=rule))]
        self.mocked_website_read.side_effect = StreamNotFoundException

        collector = FaviconCollector()
        collector.collect()

        rule.refresh_from_db()

        self.assertEquals(rule.succeeded, True)
        self.assertEquals(rule.error, None)
        self.assertEquals(rule.website_url, "https://www.bbc.co.uk/news/")
        self.assertEquals(rule.favicon, None)

    def test_denied(self):
        rule = CollectionRuleFactory(succeeded=True, error=None)

        self.mocked_feed_client.return_value = [(feed_mock, Mock(rule=rule))]
        self.mocked_website_read.side_effect = StreamDeniedException

        collector = FaviconCollector()
        collector.collect()

        rule.refresh_from_db()

        self.assertEquals(rule.succeeded, True)
        self.assertEquals(rule.error, None)
        self.assertEquals(rule.website_url, "https://www.bbc.co.uk/news/")
        self.assertEquals(rule.favicon, None)

    def test_forbidden(self):
        rule = CollectionRuleFactory(succeeded=True, error=None)

        self.mocked_feed_client.return_value = [(feed_mock, Mock(rule=rule))]
        self.mocked_website_read.side_effect = StreamForbiddenException

        collector = FaviconCollector()
        collector.collect()

        rule.refresh_from_db()

        self.assertEquals(rule.succeeded, True)
        self.assertEquals(rule.error, None)
        self.assertEquals(rule.website_url, "https://www.bbc.co.uk/news/")
        self.assertEquals(rule.favicon, None)

    def test_timed_out(self):
        rule = CollectionRuleFactory(succeeded=True, error=None)

        self.mocked_feed_client.return_value = [(feed_mock, Mock(rule=rule))]
        self.mocked_website_read.side_effect = StreamTimeOutException

        collector = FaviconCollector()
        collector.collect()

        rule.refresh_from_db()

        self.assertEquals(rule.succeeded, True)
        self.assertEquals(rule.error, None)
        self.assertEquals(rule.website_url, "https://www.bbc.co.uk/news/")
        self.assertEquals(rule.favicon, None)

    def test_wrong_stream_content_type(self):
        rule = CollectionRuleFactory(succeeded=True, error=None)

        self.mocked_feed_client.return_value = [(feed_mock, Mock(rule=rule))]
        self.mocked_website_read.side_effect = StreamParseException

        collector = FaviconCollector()
        collector.collect()

        rule.refresh_from_db()

        self.assertEquals(rule.succeeded, True)
        self.assertEquals(rule.error, None)
        self.assertEquals(rule.website_url, "https://www.bbc.co.uk/news/")
        self.assertEquals(rule.favicon, None)
