Exemple #1
0
    def setUp(self):
        self.app = Holocron(
            conf={
                'site': {
                    'title': 'MyTestSite',
                    'author': 'Tester',
                    'url': 'http://www.mytest.com/',
                },
                'encoding': {
                    'output': 'my-enc',
                },
                'paths': {
                    'output': 'path/to/output',
                },
                'ext': {
                    'enabled': [],
                    'feed': {
                        'save_as': 'myfeed.xml',
                        'posts_number': 3,
                    },
                },
            })
        self.feed = Feed(self.app)

        self.date_early = datetime(2012, 2, 2)
        self.date_moderate = datetime(2013, 4, 1)
        self.date_late = datetime(2014, 6, 12)

        self.date_early_updated = datetime(2012, 12, 6)
        self.date_moderate_updated = datetime(2013, 12, 6)
        self.date_late_updated = datetime(2014, 12, 6)

        self.post_early = mock.Mock(spec=Post,
                                    published=self.date_early,
                                    updated_local=self.date_early_updated,
                                    abs_url='http://www.post_early.com',
                                    title='MyEarlyPost')

        self.post_moderate = mock.Mock(
            spec=Post,
            published=self.date_moderate,
            updated_local=self.date_moderate_updated,
            abs_url='http://www.post_moderate.com')

        self.post_late = mock.Mock(spec=Post,
                                   published=self.date_late,
                                   updated_local=self.date_late_updated,
                                   url='www.post_late.com',
                                   abs_url='http://www.post_late.com',
                                   title='MyTestPost')

        self.late_id = '<id>http://www.post_late.com</id>'
        self.moderate_id = '<id>http://www.post_moderate.com</id>'
        self.early_id = '<id>http://www.post_early.com</id>'

        self.page = mock.Mock(spec=Page, url='www.page.com')
        self.static = mock.Mock(spec=Static, url='www.image.com')

        self.open_fn = 'holocron.ext.feed.open'
    def setUp(self):
        self.app = Holocron(conf={
            'site': {
                'title': 'MyTestSite',
                'author': 'Tester',
                'url': 'http://www.mytest.com/',
            },

            'encoding': {
                'output': 'my-enc',
            },

            'paths': {
                'output': 'path/to/output',
            },

            'ext': {
                'enabled': [],
                'feed': {
                    'save_as': 'myfeed.xml',
                    'posts_number': 3,
                },
            },
        })
        self.feed = Feed(self.app)

        self.date_early = datetime(2012, 2, 2)
        self.date_moderate = datetime(2013, 4, 1)
        self.date_late = datetime(2014, 6, 12)

        self.date_early_updated = datetime(2012, 12, 6)
        self.date_moderate_updated = datetime(2013, 12, 6)
        self.date_late_updated = datetime(2014, 12, 6)

        self.post_early = mock.Mock(
            spec=Post,
            published=self.date_early,
            updated_local=self.date_early_updated,
            abs_url='http://www.post_early.com',
            title='MyEarlyPost')

        self.post_moderate = mock.Mock(
            spec=Post,
            published=self.date_moderate,
            updated_local=self.date_moderate_updated,
            abs_url='http://www.post_moderate.com')

        self.post_late = mock.Mock(
            spec=Post,
            published=self.date_late,
            updated_local=self.date_late_updated,
            url='www.post_late.com',
            abs_url='http://www.post_late.com',
            title='MyTestPost')

        self.late_id = '<id>http://www.post_late.com</id>'
        self.moderate_id = '<id>http://www.post_moderate.com</id>'
        self.early_id = '<id>http://www.post_early.com</id>'

        self.page = mock.Mock(spec=Page, url='www.page.com')
        self.static = mock.Mock(spec=Static, url='www.image.com')

        self.open_fn = 'holocron.ext.feed.open'
class TestFeedGenerator(HolocronTestCase):
    """
    Test feed generator.
    """

    def setUp(self):
        self.app = Holocron(conf={
            'site': {
                'title': 'MyTestSite',
                'author': 'Tester',
                'url': 'http://www.mytest.com/',
            },

            'encoding': {
                'output': 'my-enc',
            },

            'paths': {
                'output': 'path/to/output',
            },

            'ext': {
                'enabled': [],
                'feed': {
                    'save_as': 'myfeed.xml',
                    'posts_number': 3,
                },
            },
        })
        self.feed = Feed(self.app)

        self.date_early = datetime(2012, 2, 2)
        self.date_moderate = datetime(2013, 4, 1)
        self.date_late = datetime(2014, 6, 12)

        self.date_early_updated = datetime(2012, 12, 6)
        self.date_moderate_updated = datetime(2013, 12, 6)
        self.date_late_updated = datetime(2014, 12, 6)

        self.post_early = mock.Mock(
            spec=Post,
            published=self.date_early,
            updated_local=self.date_early_updated,
            abs_url='http://www.post_early.com',
            title='MyEarlyPost')

        self.post_moderate = mock.Mock(
            spec=Post,
            published=self.date_moderate,
            updated_local=self.date_moderate_updated,
            abs_url='http://www.post_moderate.com')

        self.post_late = mock.Mock(
            spec=Post,
            published=self.date_late,
            updated_local=self.date_late_updated,
            url='www.post_late.com',
            abs_url='http://www.post_late.com',
            title='MyTestPost')

        self.late_id = '<id>http://www.post_late.com</id>'
        self.moderate_id = '<id>http://www.post_moderate.com</id>'
        self.early_id = '<id>http://www.post_early.com</id>'

        self.page = mock.Mock(spec=Page, url='www.page.com')
        self.static = mock.Mock(spec=Static, url='www.image.com')

        self.open_fn = 'holocron.ext.feed.open'

    @mock.patch('holocron.ext.feed.mkdir', mock.Mock())
    def _get_content(self, documents):
        """
        This helper method mocks the open function and returns the content
        passed as input to write function.
        """
        with mock.patch(self.open_fn, mock.mock_open(), create=True) as mopen:
            self.feed.generate(documents)

            content, = mopen().write.call_args[0]
            return content

    def _xml_to_dict(self, xml):
        """
        Generates and returns python dict from an xml string passed as input.
        """
        parsed = minidom.parseString(xml)
        root = parsed.documentElement

        #: use this to sort DOM Elements from DOM Text containing \n and spaces
        def is_element(n):
            return n.nodeType == n.ELEMENT_NODE

        #: use this to parse <link> which contain attributes instead of values
        def is_attribute(n):
            return len(n.attributes) != 0

        #: use this to distinguish feed common elements (link, title) from post
        def has_child(n):
            return len(n.childNodes) < 2

        urls = [url for url in filter(is_element, root.childNodes)]

        entries = {}
        url_data = {}
        #: use to store dictionnaries with post data
        posts = []

        #: links in feed differ by attribute (rel or alt), use this attribute
        #: as a key to avoid dicts with same key
        key = '{link}.{fmt}'.format

        for url in urls:
            if has_child(url):
                if is_attribute(url):
                    link = key(link=url.nodeName, fmt=url.getAttribute('rel'))
                    entries[link] = url.getAttribute('href')
                else:
                    entries[url.nodeName] = url.firstChild.nodeValue
            else:
                for attr in filter(is_element, url.childNodes):
                    if is_attribute(attr):
                        url_data[attr.nodeName] = attr.getAttribute('href')
                    else:
                        url_data[attr.nodeName] = attr.firstChild.nodeValue

                posts.append(url_data)
                entries[url.nodeName] = posts

        content = {root.nodeName: entries}

        return content

    @mock.patch('holocron.ext.feed.mkdir', mock.Mock())
    def test_feed_filename_and_enc(self):
        """
        Feed function has to save feed xml file to a proper location and with
        proper filename. All settings are fetched from the configuration file.
        """
        with mock.patch(self.open_fn, mock.mock_open(), create=True) as mopen:
            self.feed.generate([])

            self.assertEqual(
                mopen.call_args[0][0], 'path/to/output/myfeed.xml')
            self.assertEqual(
                mopen.call_args[1]['encoding'], 'my-enc')

    def test_feed_encoding_attr(self):
        """
        The feed.xml has to have an XML tag with right encoding.
        """
        output = self._get_content([])

        self.assertIn('encoding="my-enc"', output)

    def test_feed_template(self):
        """
        Test that feed writes correct values to an xml template.
        """
        content = self._get_content([])
        content = self._xml_to_dict(content)

        feed = content['feed']

        self.assertEqual('MyTestSite', feed['title'])
        self.assertEqual('http://www.mytest.com/', feed['id'])
        self.assertEqual('http://www.mytest.com/myfeed.xml', feed['link.self'])
        self.assertEqual('http://www.mytest.com/', feed['link.alternate'])

    def test_feed_empty(self):
        """
        Feed runned on an empty list of documents has to create an xml file, no
        posts should be listed there.
        """
        content = self._get_content([])
        content = self._xml_to_dict(content)

        self.assertNotIn('entry', content['feed'])

    def test_feed_with_posts(self):
        """
        Feed function has to f*****g work.
        """
        # here we require only one post to test its content correctness
        # we test posts in other test suites
        self.feed._conf['posts_number'] = 1

        content = self._get_content([self.post_early, self.post_late])
        content = self._xml_to_dict(content)

        self.assertIn('entry', content['feed'])
        self.assertEqual(len(content['feed']['entry']), 1)

        feed = content['feed']['entry'][0]

        self.assertEqual('http://www.post_late.com', feed['link'])
        self.assertEqual(self.date_late_updated.isoformat(), feed['updated'])

        self.assertEqual(self.date_late.isoformat(), feed['published'])

        self.assertEqual('http://www.post_late.com', feed['id'])
        self.assertEqual('MyTestPost', feed['title'])

    def test_posts_in_front_order(self):
        """
        Tests posts ordering. Feed must display older posts first.
        """
        posts = [self.post_early, self.post_moderate, self.post_late]
        content = self._get_content(posts)

        #: test that the latest post comes first
        self.assertIn(self.late_id, content)

        #: delete information about the latest post
        post_position = content.index(self.late_id) + len(self.late_id)
        content = content[post_position:]

        self.assertIn(self.moderate_id, content)

        #: another strim to delete next post
        post_position = content.index(self.moderate_id) + len(self.moderate_id)
        content = content[post_position:]

        self.assertIn(self.early_id, content)

    def test_posts_in_reverse_order(self):
        """
        Tests posts ordering. Feed must display older posts first.
        """
        posts = [self.post_late, self.post_moderate, self.post_early]
        content = self._get_content(posts)

        #: test that the latest post comes first
        self.assertIn(self.late_id, content)

        #: delete information about the latest post
        post_position = content.index(self.late_id) + len(self.late_id)
        content = content[post_position:]

        self.assertIn(self.moderate_id, content)

        #: another strim to delete next post
        post_position = content.index(self.moderate_id) + len(self.moderate_id)
        content = content[post_position:]

        self.assertIn(self.early_id, content)

    def test_mixed_documents(self):
        """
        Test that feed generator sorts out post documents out of other types.
        """
        documents = [self.page, self.post_late, self.static]
        content = self._get_content(documents)

        self.assertNotIn('www.page.com', content)
        self.assertNotIn('www.image.com', content)
        self.assertIn('www.post_late.com', content)

    @mock.patch('holocron.content.os.mkdir', mock.Mock())
    @mock.patch('holocron.content.os.path.getmtime')
    @mock.patch('holocron.content.os.path.getctime')
    def test_feed_link_in_html_header(self, _, __):
        """
        Test that html pages have the link to feed.
        """
        # since we're interested in rendered page, let's register
        # a fake converter for that purpose
        self.app.add_converter(FakeConverter())

        open_fn = 'holocron.content.open'
        with mock.patch(open_fn, mock.mock_open(read_data=''), create=True):
            page = Page('filename.fake', self.app)

        with mock.patch(open_fn, mock.mock_open(), create=True) as mopen:
            page.build()
            content = mopen().write.call_args[0][0]

        err = 'could not find link to feed in html header'
        self.assertIn(
            '<link rel="alternate" type="application/atom+xml" '
            'href="http://www.mytest.com/myfeed.xml" title="MyTestSite">',
            content, err)
Exemple #4
0
class TestFeedGenerator(HolocronTestCase):
    """
    Test feed generator.
    """
    def setUp(self):
        self.app = Holocron(
            conf={
                'site': {
                    'title': 'MyTestSite',
                    'author': 'Tester',
                    'url': 'http://www.mytest.com/',
                },
                'encoding': {
                    'output': 'my-enc',
                },
                'paths': {
                    'output': 'path/to/output',
                },
                'ext': {
                    'enabled': [],
                    'feed': {
                        'save_as': 'myfeed.xml',
                        'posts_number': 3,
                    },
                },
            })
        self.feed = Feed(self.app)

        self.date_early = datetime(2012, 2, 2)
        self.date_moderate = datetime(2013, 4, 1)
        self.date_late = datetime(2014, 6, 12)

        self.date_early_updated = datetime(2012, 12, 6)
        self.date_moderate_updated = datetime(2013, 12, 6)
        self.date_late_updated = datetime(2014, 12, 6)

        self.post_early = mock.Mock(spec=Post,
                                    published=self.date_early,
                                    updated_local=self.date_early_updated,
                                    abs_url='http://www.post_early.com',
                                    title='MyEarlyPost')

        self.post_moderate = mock.Mock(
            spec=Post,
            published=self.date_moderate,
            updated_local=self.date_moderate_updated,
            abs_url='http://www.post_moderate.com')

        self.post_late = mock.Mock(spec=Post,
                                   published=self.date_late,
                                   updated_local=self.date_late_updated,
                                   url='www.post_late.com',
                                   abs_url='http://www.post_late.com',
                                   title='MyTestPost')

        self.late_id = '<id>http://www.post_late.com</id>'
        self.moderate_id = '<id>http://www.post_moderate.com</id>'
        self.early_id = '<id>http://www.post_early.com</id>'

        self.page = mock.Mock(spec=Page, url='www.page.com')
        self.static = mock.Mock(spec=Static, url='www.image.com')

        self.open_fn = 'holocron.ext.feed.open'

    @mock.patch('holocron.ext.feed.mkdir', mock.Mock())
    def _get_content(self, documents):
        """
        This helper method mocks the open function and returns the content
        passed as input to write function.
        """
        with mock.patch(self.open_fn, mock.mock_open(), create=True) as mopen:
            self.feed.generate(documents)

            content, = mopen().write.call_args[0]
            return content

    def _xml_to_dict(self, xml):
        """
        Generates and returns python dict from an xml string passed as input.
        """
        parsed = minidom.parseString(xml)
        root = parsed.documentElement

        #: use this to sort DOM Elements from DOM Text containing \n and spaces
        def is_element(n):
            return n.nodeType == n.ELEMENT_NODE

        #: use this to parse <link> which contain attributes instead of values
        def is_attribute(n):
            return len(n.attributes) != 0

        #: use this to distinguish feed common elements (link, title) from post
        def has_child(n):
            return len(n.childNodes) < 2

        urls = [url for url in filter(is_element, root.childNodes)]

        entries = {}
        url_data = {}
        #: use to store dictionnaries with post data
        posts = []

        #: links in feed differ by attribute (rel or alt), use this attribute
        #: as a key to avoid dicts with same key
        key = '{link}.{fmt}'.format

        for url in urls:
            if has_child(url):
                if is_attribute(url):
                    link = key(link=url.nodeName, fmt=url.getAttribute('rel'))
                    entries[link] = url.getAttribute('href')
                else:
                    entries[url.nodeName] = url.firstChild.nodeValue
            else:
                for attr in filter(is_element, url.childNodes):
                    if is_attribute(attr):
                        url_data[attr.nodeName] = attr.getAttribute('href')
                    else:
                        url_data[attr.nodeName] = attr.firstChild.nodeValue

                posts.append(url_data)
                entries[url.nodeName] = posts

        content = {root.nodeName: entries}

        return content

    @mock.patch('holocron.ext.feed.mkdir', mock.Mock())
    def test_feed_filename_and_enc(self):
        """
        Feed function has to save feed xml file to a proper location and with
        proper filename. All settings are fetched from the configuration file.
        """
        with mock.patch(self.open_fn, mock.mock_open(), create=True) as mopen:
            self.feed.generate([])

            self.assertEqual(mopen.call_args[0][0],
                             'path/to/output/myfeed.xml')
            self.assertEqual(mopen.call_args[1]['encoding'], 'my-enc')

    def test_feed_encoding_attr(self):
        """
        The feed.xml has to have an XML tag with right encoding.
        """
        output = self._get_content([])

        self.assertIn('encoding="my-enc"', output)

    def test_feed_template(self):
        """
        Test that feed writes correct values to an xml template.
        """
        content = self._get_content([])
        content = self._xml_to_dict(content)

        feed = content['feed']

        self.assertEqual('MyTestSite', feed['title'])
        self.assertEqual('http://www.mytest.com/', feed['id'])
        self.assertEqual('http://www.mytest.com/myfeed.xml', feed['link.self'])
        self.assertEqual('http://www.mytest.com/', feed['link.alternate'])

    def test_feed_empty(self):
        """
        Feed runned on an empty list of documents has to create an xml file, no
        posts should be listed there.
        """
        content = self._get_content([])
        content = self._xml_to_dict(content)

        self.assertNotIn('entry', content['feed'])

    def test_feed_with_posts(self):
        """
        Feed function has to f*****g work.
        """
        # here we require only one post to test its content correctness
        # we test posts in other test suites
        self.feed._conf['posts_number'] = 1

        content = self._get_content([self.post_early, self.post_late])
        content = self._xml_to_dict(content)

        self.assertIn('entry', content['feed'])
        self.assertEqual(len(content['feed']['entry']), 1)

        feed = content['feed']['entry'][0]

        self.assertEqual('http://www.post_late.com', feed['link'])
        self.assertEqual(self.date_late_updated.isoformat(), feed['updated'])

        self.assertEqual(self.date_late.isoformat(), feed['published'])

        self.assertEqual('http://www.post_late.com', feed['id'])
        self.assertEqual('MyTestPost', feed['title'])

    def test_posts_in_front_order(self):
        """
        Tests posts ordering. Feed must display older posts first.
        """
        posts = [self.post_early, self.post_moderate, self.post_late]
        content = self._get_content(posts)

        #: test that the latest post comes first
        self.assertIn(self.late_id, content)

        #: delete information about the latest post
        post_position = content.index(self.late_id) + len(self.late_id)
        content = content[post_position:]

        self.assertIn(self.moderate_id, content)

        #: another strim to delete next post
        post_position = content.index(self.moderate_id) + len(self.moderate_id)
        content = content[post_position:]

        self.assertIn(self.early_id, content)

    def test_posts_in_reverse_order(self):
        """
        Tests posts ordering. Feed must display older posts first.
        """
        posts = [self.post_late, self.post_moderate, self.post_early]
        content = self._get_content(posts)

        #: test that the latest post comes first
        self.assertIn(self.late_id, content)

        #: delete information about the latest post
        post_position = content.index(self.late_id) + len(self.late_id)
        content = content[post_position:]

        self.assertIn(self.moderate_id, content)

        #: another strim to delete next post
        post_position = content.index(self.moderate_id) + len(self.moderate_id)
        content = content[post_position:]

        self.assertIn(self.early_id, content)

    def test_mixed_documents(self):
        """
        Test that feed generator sorts out post documents out of other types.
        """
        documents = [self.page, self.post_late, self.static]
        content = self._get_content(documents)

        self.assertNotIn('www.page.com', content)
        self.assertNotIn('www.image.com', content)
        self.assertIn('www.post_late.com', content)

    @mock.patch('holocron.content.os.mkdir', mock.Mock())
    @mock.patch('holocron.content.os.path.getmtime')
    @mock.patch('holocron.content.os.path.getctime')
    def test_feed_link_in_html_header(self, _, __):
        """
        Test that html pages have the link to feed.
        """
        # since we're interested in rendered page, let's register
        # a fake converter for that purpose
        self.app.add_converter(FakeConverter())

        open_fn = 'holocron.content.open'
        with mock.patch(open_fn, mock.mock_open(read_data=''), create=True):
            page = Page('filename.fake', self.app)

        with mock.patch(open_fn, mock.mock_open(), create=True) as mopen:
            page.build()
            content = mopen().write.call_args[0][0]

        err = 'could not find link to feed in html header'
        self.assertIn(
            '<link rel="alternate" type="application/atom+xml" '
            'href="http://www.mytest.com/myfeed.xml" title="MyTestSite">',
            content, err)