from __future__ import print_function import unittest from cgi import FieldStorage, MiniFieldStorage from roundup.cgi.templating import * from .test_actions import MockNull, true import pytest from .pytest_patcher import mark_class try: from docutils.core import publish_parts as ReStructuredText skip_rst = lambda func, *args, **kwargs: func except ImportError: ReStructuredText = None skip_rst = mark_class(pytest.mark.skip( reason='ReStructuredText not available')) try: from StructuredText.StructuredText import HTML as StructuredText skip_stext = lambda func, *args, **kwargs: func except ImportError: try: # older version import StructuredText skip_stext = lambda func, *args, **kwargs: func except ImportError: StructuredText = None skip_stext = mark_class(pytest.mark.skip( reason='StructuredText not available')) try: skip_markdown = lambda func, *args, **kwargs: func from markdown2 import markdown except ImportError: try: from markdown import markdown except ImportError: markdown = None skip_markdown = mark_class(pytest.mark.skip( reason='markdown not available')) from roundup.anypy.strings import u2s, s2u class MockDatabase(MockNull): def getclass(self, name): return self.classes[name] # setup for csrf testing of otks database api storage = {} def set(self, key, **props): MockDatabase.storage[key] = {} MockDatabase.storage[key].update(props) def get(self, key, field, default=None): if key not in MockDatabase.storage: return default return MockDatabase.storage[key][field] def exists(self,key): return key in MockDatabase.storage def getOTKManager(self): return MockDatabase() class TemplatingTestCase(unittest.TestCase): def setUp(self): self.form = FieldStorage() self.client = MockNull() self.client.db = db = MockDatabase() db.security.hasPermission = lambda *args, **kw: True self.client.form = self.form # add client props for testing anti_csrf_nonce self.client.session_api = MockNull(_sid="1234567890") self.client.db.getuid = lambda : 10 self.client.db.config = {'WEB_CSRF_TOKEN_LIFETIME': 10 } class HTMLDatabaseTestCase(TemplatingTestCase): def test_HTMLDatabase___getitem__(self): db = HTMLDatabase(self.client) self.assertTrue(isinstance(db['issue'], HTMLClass)) # following assertions are invalid # since roundup/cgi/templating.py r1.173. # HTMLItem is function, not class, # but HTMLUserClass and HTMLUser are passed on. # these classes are no more. they have ceased to be. #self.assertTrue(isinstance(db['user'], HTMLUserClass)) #self.assertTrue(isinstance(db['issue1'], HTMLItem)) #self.assertTrue(isinstance(db['user1'], HTMLUser)) def test_HTMLDatabase___getattr__(self): db = HTMLDatabase(self.client) self.assertTrue(isinstance(db.issue, HTMLClass)) # see comment in test_HTMLDatabase___getitem__ #self.assertTrue(isinstance(db.user, HTMLUserClass)) #self.assertTrue(isinstance(db.issue1, HTMLItem)) #self.assertTrue(isinstance(db.user1, HTMLUser)) def test_HTMLDatabase_classes(self): db = HTMLDatabase(self.client) db._db.classes = {'issue':MockNull(), 'user': MockNull()} db.classes() class FunctionsTestCase(TemplatingTestCase): def test_lookupIds(self): db = HTMLDatabase(self.client) def lookup(key): if key == 'ok': return '1' if key == 'fail': raise KeyError('fail') return key db._db.classes = {'issue': MockNull(lookup=lookup)} prop = MockNull(classname='issue') self.assertEqual(lookupIds(db._db, prop, ['1','2']), ['1','2']) self.assertEqual(lookupIds(db._db, prop, ['ok','2']), ['1','2']) self.assertEqual(lookupIds(db._db, prop, ['ok', 'fail'], 1), ['1', 'fail']) self.assertEqual(lookupIds(db._db, prop, ['ok', 'fail']), ['1']) def test_lookupKeys(self): db = HTMLDatabase(self.client) def get(entry, key): return {'1': 'green', '2': 'eggs'}.get(entry, entry) shrubbery = MockNull(get=get) db._db.classes = {'shrubbery': shrubbery} self.assertEqual(lookupKeys(shrubbery, 'spam', ['1','2']), ['green', 'eggs']) self.assertEqual(lookupKeys(shrubbery, 'spam', ['ok','2']), ['ok', 'eggs']) class HTMLClassTestCase(TemplatingTestCase) : def test_link(self): """Make sure lookup of a Link property works even in the presence of multiple values in the form.""" def lookup(key) : self.assertEqual(key, key.strip()) return "Status%s"%key self.form.list.append(MiniFieldStorage("status", "1")) self.form.list.append(MiniFieldStorage("status", "2")) status = hyperdb.Link("status") self.client.db.classes = dict \ ( issue = MockNull(getprops = lambda : dict(status = status)) , status = MockNull(get = lambda id, name : id, lookup = lookup) ) cls = HTMLClass(self.client, "issue") cls["status"] def test_multilink(self): """`lookup` of an item will fail if leading or trailing whitespace has not been stripped. """ def lookup(key) : self.assertEqual(key, key.strip()) return "User%s"%key self.form.list.append(MiniFieldStorage("nosy", "1, 2")) nosy = hyperdb.Multilink("user") self.client.db.classes = dict \ ( issue = MockNull(getprops = lambda : dict(nosy = nosy)) , user = MockNull(get = lambda id, name : id, lookup = lookup) ) cls = HTMLClass(self.client, "issue") cls["nosy"] def test_anti_csrf_nonce(self): '''call the csrf creation function and do basic length test Store the data in a mock db with the same api as the otk db. Make sure nonce is 64 chars long. Lookup the nonce in db and retrieve data. Verify that the nonce lifetime is correct (within 1 second of 1 week - lifetime), the uid is correct (1), the dummy sid is correct. Consider three cases: * create nonce via module function setting lifetime * create nonce via TemplatingUtils method setting lifetime * create nonce via module function with default lifetime ''' # the value below is number of seconds in a week. week_seconds = 604800 otks=self.client.db.getOTKManager() for test in [ 'module', 'template', 'default_time' ]: print("Testing:", test) if test == 'module': # test the module function nonce1 = anti_csrf_nonce(self.client, lifetime=1) # lifetime * 60 is the offset greater_than = week_seconds - 1 * 60 elif test == 'template': # call the function through the TemplatingUtils class cls = TemplatingUtils(self.client) nonce1 = cls.anti_csrf_nonce(lifetime=5) greater_than = week_seconds - 5 * 60 elif test == 'default_time': # use the module function but with no lifetime nonce1 = anti_csrf_nonce(self.client) # see above for web nonce lifetime. greater_than = week_seconds - 10 * 60 self.assertEqual(len(nonce1), 64) uid = otks.get(nonce1, 'uid', default=None) sid = otks.get(nonce1, 'sid', default=None) timestamp = otks.get(nonce1, '__timestamp', default=None) self.assertEqual(uid, 10) self.assertEqual(sid, self.client.session_api._sid) now = time.time() print("now, timestamp, greater, difference", now, timestamp, greater_than, now - timestamp) # lower bound of the difference is above. Upper bound # of difference is run time between time.time() in # the call to anti_csrf_nonce and the time.time() call # that assigns ts above. I declare that difference # to be less than 1 second for this to pass. self.assertEqual(True, greater_than <= now - timestamp < (greater_than + 1) ) def test_string_url_quote(self): ''' test that urlquote quotes the string ''' p = StringHTMLProperty(self.client, 'test', '1', None, 'test', 'test string< foo@bar') self.assertEqual(p.url_quote(), 'test%20string%3C%20foo%40bar') def test_string_email(self): ''' test that email obscures the email ''' p = StringHTMLProperty(self.client, 'test', '1', None, 'test', 'rouilj@foo.example.com') self.assertEqual(p.email(), 'rouilj at foo example ...') def test_string_plain_or_hyperlinked(self): ''' test that email obscures the email ''' p = StringHTMLProperty(self.client, 'test', '1', None, 'test', 'A string with rouilj@example.com embedded < html') self.assertEqual(p.plain(), 'A string with rouilj@example.com embedded < html') self.assertEqual(p.plain(escape=1), 'A string <b> with rouilj@example.com embedded < html</b>') self.assertEqual(p.plain(hyperlink=1), 'A string <b> with rouilj@example.com embedded < html</b>') self.assertEqual(p.plain(escape=1, hyperlink=1), 'A string <b> with rouilj@example.com embedded < html</b>') self.assertEqual(p.hyperlinked(), 'A string <b> with rouilj@example.com embedded < html</b>') @skip_markdown def test_string_markdown_installed(self): pass # just so we have a record of a skipped test def test_string_markdown(self): p = StringHTMLProperty(self.client, 'test', '1', None, 'test', u2s(u'A string http://localhost with cmeerw@example.com *embedded* \u00df')) if markdown: self.assertEqual(p.markdown().strip(), u2s(u'
A string http://localhost with cmeerw@example.com embedded \u00df
')) else: self.assertEqual(p.markdown(), u2s(u'A string http://localhost with cmeerw@example.com *embedded* \u00df')) @skip_rst def test_string_rst_installed(self): pass # just so we have a record of a skipped test def test_string_rst(self): p = StringHTMLProperty(self.client, 'test', '1', None, 'test', u2s(u'A string with cmeerw@example.com *embedded* \u00df')) if ReStructuredText: self.assertEqual(p.rst(), u2s(u'A string with cmeerw@example.com embedded \u00df
\nA string with cmeerw@example.com embedded \u00df
\n')) else: self.assertEqual(p.stext(), u2s(u'A string with cmeerw@example.com *embedded* \u00df')) def test_string_field(self): p = StringHTMLProperty(self.client, 'test', '1', None, 'test', 'A string with rouilj@example.com embedded < html') self.assertEqual(p.field(), '') def test_string_multiline(self): p = StringHTMLProperty(self.client, 'test', '1', None, 'test', 'A string with rouilj@example.com embedded < html') self.assertEqual(p.multiline(), '') self.assertEqual(p.multiline(rows=300, cols=100, **{'class':'css_class'}), '') def test_url_match(self): '''Test the URL regular expression in StringHTMLProperty. ''' def t(s, nothing=False, **groups): m = StringHTMLProperty.hyper_re.search(s) if nothing: if m: self.assertEqual(m, None, '%r matched (%r)'%(s, m.groupdict())) return else: self.assertNotEqual(m, None, '%r did not match'%s) d = m.groupdict() for g in groups: self.assertEqual(d[g], groups[g], '%s %r != %r in %r'%(g, d[g], groups[g], s)) #t('123.321.123.321', 'url') t('http://localhost/', url='http://localhost/') t('http://roundup.net/', url='http://roundup.net/') t('http://richard@localhost/', url='http://richard@localhost/') t('http://richard:sekrit@localhost/', url='http://richard:sekrit@localhost/') t('