From 5cbf381a2c875a1d3a799662b7531053d90324a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonel=20C=C3=A2mara?= Date: Sun, 27 Mar 2016 14:23:24 +0100 Subject: [PATCH 1/2] More test coverage for validators.py Fixed a bug in IS_EMAIL throwing exceptions when asked to validate anything other than a string which was problematic for ANY_OF Fixed a bug in ANY_OF.formatter where it was trying to format with a validator that didn't validate --- gluon/tests/test_validators.py | 32 ++++++++++++++++++++++++++++++++ gluon/validators.py | 9 +++++++-- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/gluon/tests/test_validators.py b/gluon/tests/test_validators.py index 5005cbfd..cc925065 100644 --- a/gluon/tests/test_validators.py +++ b/gluon/tests/test_validators.py @@ -160,6 +160,14 @@ class TestValidators(unittest.TestCase): self.assertEqual(rtn, ({u'a': 100}, None)) rtn = IS_JSON()('spam1234') self.assertEqual(rtn, ('spam1234', 'Invalid json')) + rtn = IS_JSON(native_json=True)('{"a": 100}') + self.assertEqual(rtn, ('{"a": 100}', None)) + rtn = IS_JSON().formatter(None) + self.assertEqual(rtn, None) + rtn = IS_JSON().formatter({'a': 100}) + self.assertEqual(rtn, '{"a": 100}') + rtn = IS_JSON(native_json=True).formatter({'a': 100}) + self.assertEqual(rtn, {'a': 100}) def test_IS_IN_SET(self): rtn = IS_IN_SET(['max', 'john'])('max') @@ -174,6 +182,10 @@ class TestValidators(unittest.TestCase): self.assertEqual(rtn, ('id1', None)) rtn = IS_IN_SET({'id1':'first label', 'id2':'second label'})('id1') self.assertEqual(rtn, ('id1', None)) + rtn = IS_IN_SET(['id1', 'id2'], error_message='oops', multiple=True)(None) + self.assertEqual(rtn, ([], None)) + rtn = IS_IN_SET(['id1', 'id2'], error_message='oops', multiple=(1,2))(None) + self.assertEqual(rtn, ([], 'oops')) import itertools rtn = IS_IN_SET(itertools.chain(['1','3','5'],['2','4','6']))('1') self.assertEqual(rtn, ('1', None)) @@ -181,6 +193,10 @@ class TestValidators(unittest.TestCase): self.assertEqual(rtn, ('id1', None)) rtn = IS_IN_SET([('id1','first label'), ('id2','second label')]).options(zero=False) self.assertEqual(rtn, [('id1', 'first label'), ('id2', 'second label')]) + rtn = IS_IN_SET(['id1', 'id2']).options(zero=False) + self.assertEqual(rtn, [('id1', 'id1'), ('id2', 'id2')]) + rtn = IS_IN_SET(['id2', 'id1'], sort=True).options(zero=False) + self.assertEqual(rtn, [('id1', 'id1'), ('id2', 'id2')]) def test_IS_IN_DB(self): from gluon.dal import DAL, Field @@ -190,6 +206,10 @@ class TestValidators(unittest.TestCase): costanza_id = db.person.insert(name='costanza') rtn = IS_IN_DB(db, 'person.id', '%(name)s')(george_id) self.assertEqual(rtn, (george_id, None)) + rtn = IS_IN_DB(db, db.person, '%(name)s')(george_id) + self.assertEqual(rtn, (george_id, None)) + rtn = IS_IN_DB(db(db.person.id > 0), db.person, '%(name)s')(george_id) + self.assertEqual(rtn, (george_id, None)) rtn = IS_IN_DB(db, 'person.id', '%(name)s', error_message='oops')(george_id+costanza_id) self.assertEqual(rtn, (george_id+costanza_id, 'oops')) rtn = IS_IN_DB(db, db.person.id, '%(name)s')(george_id) @@ -200,6 +220,10 @@ class TestValidators(unittest.TestCase): self.assertEqual(rtn, ([george_id,costanza_id], None)) rtn = IS_IN_DB(db, 'person.id', '%(name)s', multiple=True, delimiter=',')('%d,%d' % (george_id, costanza_id)) self.assertEqual(rtn, ( ('%d,%d' % (george_id, costanza_id)).split(','), None)) + rtn = IS_IN_DB(db, 'person.id', '%(name)s', multiple=(1,3), delimiter=',')('%d,%d' % (george_id, costanza_id)) + self.assertEqual(rtn, ( ('%d,%d' % (george_id, costanza_id)).split(','), None)) + rtn = IS_IN_DB(db, 'person.id', '%(name)s', multiple=(1,2), delimiter=',', error_message='oops')('%d,%d' % (george_id, costanza_id)) + self.assertEqual(rtn, ( ('%d,%d' % (george_id, costanza_id)).split(','), 'oops')) rtn = IS_IN_DB(db, db.person.id, '%(name)s', error_message='oops').options(zero=False) self.assertEqual(sorted(rtn), [('%d' % george_id, 'george'), ('%d' % costanza_id, 'costanza')]) db.person.drop() @@ -211,8 +235,14 @@ class TestValidators(unittest.TestCase): db.person.insert(name='george') rtn = IS_NOT_IN_DB(db, 'person.name', error_message='oops')('george') self.assertEqual(rtn, ('george', 'oops')) + rtn = IS_NOT_IN_DB(db, 'person.name', error_message='oops', allowed_override=['george'])('george') + self.assertEqual(rtn, ('george', None)) + rtn = IS_NOT_IN_DB(db, 'person.name', error_message='oops')(' ') + self.assertEqual(rtn, (' ', 'oops')) rtn = IS_NOT_IN_DB(db, 'person.name')('jerry') self.assertEqual(rtn, ('jerry', None)) + rtn = IS_NOT_IN_DB(db, 'person.name')(u'jerry') + self.assertEqual(rtn, ('jerry', None)) db.person.drop() def test_IS_INT_IN_RANGE(self): @@ -668,6 +698,8 @@ class TestValidators(unittest.TestCase): self.assertEqual(rtn, ('a@b.co', None)) rtn = ANY_OF([IS_DATE(),IS_EMAIL()])('1982-12-14') self.assertEqual(rtn, (datetime.date(1982, 12, 14), None)) + rtn = ANY_OF([IS_DATE(format='%m/%d/%Y'),IS_EMAIL()]).formatter(datetime.date(1834, 12, 14)) + self.assertEqual(rtn, '12/14/1834') def test_IS_EMPTY_OR(self): rtn = IS_EMPTY_OR(IS_EMAIL())('abc@def.com') diff --git a/gluon/validators.py b/gluon/validators.py index 08bd3ae5..84367d08 100644 --- a/gluon/validators.py +++ b/gluon/validators.py @@ -1200,7 +1200,12 @@ class IS_EMAIL(Validator): self.error_message = error_message def __call__(self, value): - match = self.regex.match(value) + try: + match = self.regex.match(value) + except TypeError: + # Value may not be a string where we can look for matches. + # Example: we're calling ANY_OF formatter and IS_EMAIL is asked to validate a date. + match = None if match: domain = value.split('@')[1] if (not self.banned or not self.banned.match(domain)) \ @@ -2608,7 +2613,7 @@ class ANY_OF(Validator): # Use the formatter of the first subvalidator # that validates the value and has a formatter for validator in self.subs: - if hasattr(validator, 'formatter') and validator(value)[1] != None: + if hasattr(validator, 'formatter') and validator(value)[1] is None: return validator.formatter(value) From 5c292640ba07b789fbeedfd99565ab4a97111c70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonel=20C=C3=A2mara?= Date: Mon, 28 Mar 2016 14:47:58 +0100 Subject: [PATCH 2/2] Complete coverage for IS_IN_SET Removed unreachable code in IS_IN_SET if failures and self.theset: if self.multiple and (value is None or value == '') It's impossible to have *failures* and have value be None or '' at the same time --- gluon/tests/test_validators.py | 4 ++++ gluon/validators.py | 2 -- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/gluon/tests/test_validators.py b/gluon/tests/test_validators.py index cc925065..76c8c6c0 100644 --- a/gluon/tests/test_validators.py +++ b/gluon/tests/test_validators.py @@ -184,6 +184,10 @@ class TestValidators(unittest.TestCase): self.assertEqual(rtn, ('id1', None)) rtn = IS_IN_SET(['id1', 'id2'], error_message='oops', multiple=True)(None) self.assertEqual(rtn, ([], None)) + rtn = IS_IN_SET(['id1', 'id2'], error_message='oops', multiple=True)('') + self.assertEqual(rtn, ([], None)) + rtn = IS_IN_SET(['id1', 'id2'], error_message='oops', multiple=True)('id1') + self.assertEqual(rtn, (['id1'], None)) rtn = IS_IN_SET(['id1', 'id2'], error_message='oops', multiple=(1,2))(None) self.assertEqual(rtn, ([], 'oops')) import itertools diff --git a/gluon/validators.py b/gluon/validators.py index 84367d08..050c3a9c 100644 --- a/gluon/validators.py +++ b/gluon/validators.py @@ -471,8 +471,6 @@ class IS_IN_SET(Validator): thestrset = [str(x) for x in self.theset] failures = [x for x in values if not str(x) in thestrset] if failures and self.theset: - if self.multiple and (value is None or value == ''): - return ([], None) return (value, translate(self.error_message)) if self.multiple: if isinstance(self.multiple, (tuple, list)) and \