test_comments.py :  » Blog » PyBlosxom » pyblosxom-1.5-rc1 » plugins » tests » comments » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Blog » PyBlosxom 
PyBlosxom » pyblosxom 1.5 rc1 » plugins » tests » comments » test_comments.py
"""
Tests for the comments plugin,
contrib/plugins/comments/plugins/comments.py.
"""

__author__ = 'Ryan Barrett <pyblosxom@ryanb.org>'
__url__ = 'http://pyblosxom.sourceforge.net/wiki/index.php/Framework_for_testing_plugins'

from plugins.tests.test_base import PluginTest
from plugins.comments.plugins import comments

import cgi, cPickle, os, cStringIO, sys

class TestComments(PluginTest):
    """Test class for the comments plugin.
    """
    def setUp(self):
        PluginTest.setUp(self, comments)

        # add comment templates
        self.renderer.flavour.update({'comment-story': 'comment-story',
                                      'comment': 'comment'})

        # inject the frozen time module
        comments.time = self.frozen_time

        # populate with default config vars
        comments.cb_start(self.args)

    def tearDown(self):
        PluginTest.tearDown(self)

    def comment_path(self):
        """Returns the comment path that would currently be created."""
        filename = '%s-%0.1f.%s' % (self.entry_name, self.timestamp,
                                    self.config['comment_draft_ext'])
        return os.path.join(self.config['comment_dir'], filename)

    def comment(self, title='title', author='author', body='body', url=None,
                email=None, ipaddress=None, encoding=None, preview=None,
                **kwargs):
        """Posts a comment with the given contents."""
        # set the encoding in the config. it should default to utf-8
        if encoding:
            self.config['blog_encoding'] = encoding

        # build up the form data and post the comment
        args = [(arg, vars()[arg])
                for arg in ('title', 'author', 'body', 'url', 'email', 'preview')
                if vars()[arg] is not None]
        self.set_form_data(dict(args))
        comments.cb_prepare(self.args)

    def check_comment_file(self, title='title', author='author', body='body',
                           url='', email='', encoding=None, ipaddress='',
                           expected_title=None, expected_author=None,
                           expected_body=None, expected_url=None, preview=None,
                           delete_datadir=True):
        """Posts a comment and checks its contents on disk."""
        self.comment(title, author, body, url, email, ipaddress, encoding,
                     preview)

        if encoding is None:
            encoding = 'utf-8'
        if expected_title is None:
            expected_title = title
        if expected_author is None:
            expected_author = author
        if expected_body is None:
            expected_body = body
        if expected_url is None:
            expected_url = url

        # check the files in the comments directory
        files = os.listdir(self.config['comment_dir'])
        self.assert_(os.path.basename(self.comment_path()) in files)
        self.assert_(comments.LATEST_PICKLE_FILE in files)

        # check the coment file's contents
        expected_lines = [
            '<?xml version="1.0" encoding="%s"?>\n' % encoding,
            '<item>\n',
            '<description>%s</description>\n' % cgi.escape(expected_body),
            '<pubDate>%0.1f</pubDate>\n' % self.timestamp,
            '<author>%s</author>\n' % cgi.escape(expected_author),
            '<title>%s</title>\n' % cgi.escape(expected_title),
            '<source></source>\n',
            '<link>%s</link>\n' % cgi.escape(expected_url),
            '<w3cdate>%s</w3cdate>\n' % self.timestamp_w3c,
            '<date>%s</date>\n' % self.timestamp_date,
            '<ipaddress>%s</ipaddress>\n' % ipaddress,
            '</item>\n',
            ]
        if email:
            expected_lines.insert(-1, '<email>%s</email>\n' % email)

        file = open(self.comment_path())
        actual_lines = file.readlines()
        file.close()

        expected_lines.sort()
        actual_lines.sort()
        for expected, actual in zip(expected_lines, actual_lines):
            self.assertEquals(expected, actual)

        if delete_datadir:
            self.delete_datadir()

    def check_comment_output(self, expected, delete_datadir=True, **kwargs):
        """Posts a comment and checks its rendered output.

        Note that this deletes the datadir before it posts the
        comment!
        """
        self.data['display_comment_default'] = True

        self.comment(**kwargs)
        comments.cb_story(self.args)
        self.args['template'] = ''
        comments.cb_story_end(self.args)
        self.assertEquals(expected, self.args['template'])

        if delete_datadir:
            self.delete_datadir()

    def test_cb_start(self):
        """cb_start() should set defaults for some config variables."""
        self.config = self.config_base
        comments.cb_start(self.args)

        self.assertEquals(os.path.join(self.datadir, 'comments'),
                          self.config['comment_dir'])
        self.assertEquals('cmt', self.config['comment_ext'])
        self.assertEquals('cmt', self.config['comment_draft_ext'])
        self.assertEquals(0, self.config['comment_nofollow'])

    def test_verify_installation(self):
        """verify_installation should check the comment dir and smtp
        config."""
        # comment_dir must exist
        assert not os.path.exists('/not/a/directory')
        self.config['comment_dir'] = '/not/a/directory'
        self.assertEquals(0, comments.verify_installation(self.request))
        del self.config['comment_dir']

        # either all smtp config variables must be defined, or none
        smtp_vars = ['comment_smtp_server', 'comment_smtp_from',
                     'comment_smtp_to']
        for smtp_var in smtp_vars:
            [self.config.pop(var, '') for var in smtp_vars]
            self.config[smtp_var] = 'abc'
            self.assertEquals(0, comments.verify_installation(self.request))

        del self.config[smtp_vars[-1]]

        self.assertEquals(1, comments.verify_installation(self.request))

    def test_cb_handle(self):
        """cb_handle() should intercept requests for /comments.js."""
        self.assertEquals(None, comments.cb_handle(self.args))

        self.request.add_http({'PATH_INFO': '/not_comments.js'})
        self.assertEquals(None, comments.cb_handle(self.args))

        self.request.add_http({'PATH_INFO': '/comments.js'})
        self.assertEquals(True, comments.cb_handle(self.args))

        response = self.request.get_response()
        self.assertEquals('text/javascript',
                          response.get_headers()['Content-Type'])

        out = cStringIO.StringIO()
        response.send_body(out)
        self.assert_(out.getvalue().startswith(
            '/* AJAX comment support for pyblosxom'))

    def test_cb_prepare_showcomments(self):
        """cb_prepare() should set display_comment_default to show
        comments."""
        # default is to not show comments
        del self.data['bl_type']
        comments.cb_prepare(self.args)
        self.assertEquals(False, self.data['display_comment_default'])

        # show them if the bl_type config var is 'file'
        self.data['bl_type'] = 'db'
        comments.cb_prepare(self.args)
        self.assertEquals(False, self.data['display_comment_default'])

        self.data['bl_type'] = 'file'
        comments.cb_prepare(self.args)
        self.assertEquals(True, self.data['display_comment_default'])

        # or if the query string has showcomments=yes
        del self.data['bl_type']
        self.request.add_http({'QUERY_STRING': 'x=yes&showcomments=no7&y=no'})
        comments.cb_prepare(self.args)
        self.assertEquals(False, self.data['display_comment_default'])

        self.request.add_http({'QUERY_STRING': 'x=yes&showcomments=yes&y=no'})
        comments.cb_prepare(self.args)
        self.assertEquals(True, self.data['display_comment_default'])

    def test_cb_prepare_new_comment(self):
        """A new comment should be packaged in XML and stored in a new file."""
        self.check_comment_file(title='title', author='author', body='body')

        # url is optional. try setting it.
        self.check_comment_file(url='http://home/')

        # previewed comments shouldn't be stored
        self.comment(preview='yes')
        self.assert_(not os.path.exists(self.comment_path()))

    def test_cb_prepare_encoding(self):
        """If the blog_encoding config var is set, it should be used."""
        self.check_comment_file(encoding='us-ascii')

    def test_cb_prepare_massage_link(self):
        """User-provided URLs should be scrubbed and linkified if
        necessary."""
        # html control characters should be stripped
        self.check_comment_file(url='<script arg=\'val"ue"\'>',
                                expected_url='http://script arg=value')

        # http:// should only be added if there isn't already a protocol
        self.check_comment_file(url='xmpp:me@jabber.org')

    def test_cb_prepare_nofollow(self):
        """Nofollow support should add rel="nofollow" to links in the
        body."""
        body = '<a href="/dest">x</a>'

        # default is off
        self.assert_(self.config['comment_nofollow'] == False)
        self.check_comment_file(body=body)

        # turned on
        self.config['comment_nofollow'] = True
        nofollow_body = '<a rel="nofollow" href="/dest">x</a>'
        self.check_comment_file(body=body, expected_body=nofollow_body)

    def test_cb_prepare_email(self):
        """User-provided URLs should be scrubbed and linkified if necessary."""
        self.check_comment_file(email='a@b.c')

    def test_cb_prepare_ipaddress(self):
        """If provided, IP address should be recorded."""
        ipaddress = '12.34.56.78'
        self.request.add_http({'REMOTE_ADDR': ipaddress})
        self.check_comment_file(ipaddress=ipaddress)

    def test_cb_reject(self):
        """Comments should be filtered with cb_comment_reject()."""
        # try rejecting the comment, with and without a message
        for return_value, msg in ((True, 'Comment rejected.'),
                                  ((True, 'bad!'), 'bad!')
                                  ):
            self.inject_callback('comment_reject', lambda: return_value)
            self.comment()
            self.assert_(not os.path.exists(self.comment_path()))
            self.assertEquals(True, self.data['rejected'])
            self.assertEquals(msg, self.data['comment_message'])

        del self.data['rejected']

        # try accepting the comment, with and without a message
        for return_value in [False, (False, 'ok')]:
            self.inject_callback('comment_reject', lambda: return_value)
            self.check_comment_file()

            self.assert_('rejected' not in self.data)
            self.assertEquals('Comment submitted.  Thanks!',
                              self.data['comment_message'])

    def test_cb_prepare_latest_pickle(self):
        """The "latest" file should contain the last comment's timestamp."""
        self.comment()
        latest_path = os.path.join(self.config['comment_dir'],
                                   comments.LATEST_PICKLE_FILE)
        timestamp = cPickle.load(open(latest_path))
        self.assertEquals(self.timestamp, timestamp)

    def test_cb_prepare_draft(self):
        """For draft support, comment_draft_ext should override comment_ext."""
        self.config['comment_draft_ext'] = 'draft'
        self.check_comment_file()

    def test_cb_head(self):
        """cb_head() should expand template variables in single-entry lists."""
        template = self.args['template']

        # only expand if we have a comment-head template
        self.assert_('comment-head' not in self.renderer.flavour)
        self.assertEquals(template, comments.cb_head(self.args))
        self.assert_(not self.entry.has_key('title'))

        # don't expand if we're displaying more than one entry
        self.renderer.flavour['comment-head'] = ''
        self.renderer.set_content([self.entry, self.entry])
        self.assertEquals(template, comments.cb_head(self.args))
        self.assert_(not self.entry.has_key('title'))

        # we have comment-head and only one entry. expand!
        class MockEntry(dict):
            """Intercepts __getitem__ and records the key."""
            def __getitem__(self, key):
                self.key = key

        mock_entry = MockEntry()

        self.renderer.set_content([self.entry])
        self.args['entry'] = {'entry_list': [mock_entry]}
        self.assertEquals(template, comments.cb_head(self.args))
        self.assertEquals('title', mock_entry.key)

    def test_cb_renderer(self):
        """cb_renderer() should return an AjaxRenderer for ajax
        requests."""
        self.assert_(not isinstance(comments.cb_renderer(self.args),
                                    comments.AjaxRenderer))

        self.set_form_data({'ajax': 'true'})
        self.assert_(isinstance(comments.cb_renderer(self.args),
                                comments.AjaxRenderer))

    def test_ajax_renderer(self):
        """AjaxRenderer should only output previewed and posted
        comments."""
        def should_output(template_name):
            renderer = comments.AjaxRenderer(self.request, self.data)
            return renderer._should_output(self.entry,
                                           template_name)

        # a comment preview
        self.set_form_data({'ajax': 'preview'})
        self.assertEquals(True, should_output('comment-preview'))
        self.assertEquals(False, should_output('story'))

        # a comment that was just posted
        self.set_form_data({'ajax': 'post'})
        self.assertEquals(False, should_output('story'))

        self.entry['cmt_time'] = self.timestamp
        self.assert_('cmt_time' not in self.data)
        self.assertEquals(False, should_output('comment'))

        self.data['cmt_time'] = self.timestamp
        self.assertEquals(True, should_output('comment'))

    def test_num_comments(self):
        """cb_story() should count the number of comments."""
        self.data['display_comment_default'] = True

        def check_num_comments(expected):
            if self.entry.has_key("num_comments"):
                self.entry["num_comments"] = None
            comments.cb_story(self.args)
            self.assertEquals(expected, self.entry['num_comments'])

        check_num_comments(0)
        self.comment()
        check_num_comments(1)

        self.frozen_time.timestamp += 1
        self.comment()
        check_num_comments(2)

    def test_when_to_render_comments(self):
        """cb_story[_end]() should only render comment templates when
        appropriate.
        """
        def check_no_comments(expected):
            args_template = self.args['template']
            comments.cb_story(self.args)
            self.assertEquals(expected, self.args['template'])
            comments.cb_story_end(self.args)
            self.assertEquals(expected, self.args['template'])
            self.args['template'] = args_template

        # with no comment-story template, there's nothing
        del self.renderer.flavour['comment-story']
        self.renderer.set_content([self.entry])
        check_no_comments('template starts:')

        # with a comment-story template, we show the template
        # once
        self.renderer.flavour['comment-story'] = 'comment-story'
        self.renderer.set_content([self.entry, self.entry])
        check_no_comments('template starts:comment-story')

        self.renderer.set_content([self.entry])
        self.data['display_comment_default'] = False
        check_no_comments('template starts:comment-story')

        self.data['display_comment_default'] = True
        self.entry['nocomments'] = True
        check_no_comments('template starts:')

    def test_cb_story_comment_story_template(self):
        """Check that cb_story() appends the comment-story template.."""
        self.data['display_comment_default'] = True
        self.assert_(self.renderer.flavour['comment-story'] == 'comment-story')
        comments.cb_story(self.args)
        self.assertEquals('template starts:comment-story',
                          self.args['template'])

    def test_cb_story_end_renders_comments(self):
        self.comment()

        # """Check that cb_story_end() renders comments."""
        self.data['display_comment_default'] = True

        # no comments. check both cb_story_end's return value and
        # args['template'].
        self.args['template'] = 'foo'
        self.assertEquals('foo', comments.cb_story_end(self.args))
        self.assertEquals('foo', self.args['template'])

        # one comment
        self.renderer.flavour['comment'] = '$cmt_time '
        expected = '%s ' % self.timestamp
        self.check_comment_output(expected, delete_datadir=False)

        # two comments
        self.frozen_time.timestamp += 1
        expected += '%s ' % self.frozen_time.timestamp
        self.check_comment_output(expected)

    def test_template_variables(self):
        """Check the comment template variables."""
        self.data['display_comment_default'] = True

        # these will sent in the form data
        args = {
            'body': 'body=with"chars',
            'author': 'author',
            'title': 'title',
            'url': 'http://snarfed.org/',
            'email': 'pyblosxom@ryanb.org',
            }

        # these will be used as template variables, prefixed with $cmt_
        vars = dict(args)

        for old, new in [('body', 'description'), ('url', 'link')]:
            vars[new] = vars[old]
            del vars[old]

        vars.update({
            # these are generated by pyblosxom
            'time': str(self.timestamp),
            'w3cdate': self.timestamp_w3c,
            'date': self.timestamp_date,
            'pubDate': self.timestamp_asc,
            'description': 'body=with"chars',
            })

        # these depends on the fact that dict.keys() and dict.values() return
        # items in the same order. so far, python guarantees this.
        def make_template():
            return '\n'.join('$cmt_%s' % name for name in vars.keys())

        def make_expected():
            return '\n'.join(vars.values())

        # a normal comment
        self.renderer.flavour['comment'] = 'comment:\n' + make_template()
        self.check_comment_output('comment:\n' + make_expected(), **args)

        # a previewed comment
        self.renderer.flavour['comment-preview'] = 'preview:\n' + make_template()
        args['preview'] = 'yes'
        self.check_comment_output('preview:\n' + make_expected(), **args)

        # a rejected comment
        del args['preview']
        self.inject_callback('comment_reject', lambda: (True, 'foo'))
        vars['description'] = '<span class="error">foo</span>'
        self.renderer.flavour['comment'] = 'comment:\n' + make_template()
        self.check_comment_output('comment:\n' + make_expected(), **args)

    def test_optionally_linked_author(self):
        """Test the cmt_optionally_linked_author template variable."""
        self.renderer.flavour['comment'] = '$cmt_optionally_linked_author'

        self.assert_(self.config['comment_nofollow'] == False)
        self.check_comment_output('me', author='me', url='')
        self.check_comment_output('<a href="http://home">me</a>',
                                  author='me', url='home')

        self.config['comment_nofollow'] = True
        self.check_comment_output('me', author='me', url='')
        self.check_comment_output('<a rel="nofollow" href="http://home">me</a>',
                                  author='me', url='home')
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.