Commits

Jean-Tiare Le Bigot  committed 91900fe

add comparison operators and tests needed for scan

  • Participants
  • Parent commits 797d6ff

Comments (0)

Files changed (2)

File ddbmock/database/comparison.py

     if typename == 'N':
         return Decimal(value)
     if typename == 'NS':
-        return set(map(value, Decimal))
+        return set(map(Decimal, value))
 
 def _parse_elem(elem):
     typename, value = elem.items()[0]
 
 #Types restrictions are performed by the input validators
 
-TYPEMSG2 = "Types {} and {} are not copatible in test {}"
-TYPEMSG3 = "Types {}, {} and {} are not copatible in test {}"
+
+# Common comparison operators
+
+TYPEMSG1 = "Types '{}' is not compatible in test {}"
+TYPEMSG2 = "Types '{}' and '{}' are not compatible in test {}"
+TYPEMSG3 = "Types '{}', '{} and '{}' are not compatible in test {}"
 
 def eq(target, rule):
+    if target is None: return False
     itype, ivalue = _parse_elem(target)
     rtype, rvalue = _parse_elem(rule)
     if itype != rtype: raise TypeError(TYPEMSG2.format(itype, rtype, __name__))
     return ivalue == rvalue
 
 def le(target, rule):
+    if target is None: return False
     itype, ivalue = _parse_elem(target)
     rtype, rvalue = _parse_elem(rule)
     if itype != rtype: raise TypeError(TYPEMSG2.format(itype, rtype, __name__))
     return ivalue <= rvalue
 
 def lt(target, rule):
+    if target is None: return False
     itype, ivalue = _parse_elem(target)
     rtype, rvalue = _parse_elem(rule)
     if itype != rtype: raise TypeError(TYPEMSG2.format(itype, rtype, __name__))
     return ivalue < rvalue
 
 def ge(target, rule):
+    if target is None: return False
     itype, ivalue = _parse_elem(target)
     rtype, rvalue = _parse_elem(rule)
     if itype != rtype: raise TypeError(TYPEMSG2.format(itype, rtype, __name__))
     return ivalue >= rvalue
 
 def gt(target, rule):
+    if target is None: return False
     itype, ivalue = _parse_elem(target)
     rtype, rvalue = _parse_elem(rule)
     if itype != rtype: raise TypeError(TYPEMSG2.format(itype, rtype, __name__))
     return ivalue > rvalue
 
 def begins_with(target, rule):
+    if target is None: return False
     itype, ivalue = _parse_elem(target)
     rtype, rvalue = _parse_elem(rule)
     if itype != rtype: raise TypeError(TYPEMSG2.format(itype, rtype, __name__))
     return ivalue.startswith(rvalue)
 
 def between(target, rule1, rule2):
+    if target is None: return False
     itype, ivalue = _parse_elem(target)
     rtype1, rvalue1 = _parse_elem(rule1)
     rtype2, rvalue2 = _parse_elem(rule2)
-    if not (itype == rtype2 == rtype2):
+    if not (itype == rtype1 == rtype2):
         raise TypeError(TYPEMSG3.format(itype, rtype1, rtype2, __name__))
     return ivalue >= rvalue1 and ivalue <= rvalue2
+
+# Comparisons specific to Scan
+
+def null(target):
+    return target is None
+
+def not_null(target):
+    return not null(target)
+
+def contains(target, rule):
+    if target is None: return False
+    itype, ivalue = _parse_elem(target)
+    rtype, rvalue = _parse_elem(rule)
+
+    if itype[0] != rtype or itype == u"N":
+        raise TypeError(TYPEMSG2.format(itype, rtype, __name__))
+    return rvalue in ivalue
+
+def not_contains(target, rule):
+    return not contains(target, rule)
+
+def in_test(target, *rules):
+    if target is None: return False
+    itype, ivalue = _parse_elem(target)
+
+    if len(itype) != 1:
+        raise TypeError(TYPEMSG1.format(itype, __name__))
+
+    return target in rules
+
+# workaround to use a function with a built-in name
+import sys
+setattr(sys.modules[__name__], 'in', in_test)

File ddbmock/tests/unit/test_item_field_comparison.py

 
         self.assertTrue(eq({u'S': u'waldo'}, {u'S': u'waldo'}))
         self.assertFalse(eq({u'S': u'waldo'}, {u'S': u'on-time'}))
+        self.assertFalse(eq(None, {u'S': u'on-time'}))
 
     def test_le(self):
         from ddbmock.database.comparison import le
         self.assertTrue(le({u'N': u'42'}, {u'N': u'42.001'}))
         self.assertFalse(le({u'N': u'42'}, {u'N': u'7'}))
 
+        self.assertFalse(le(None, {u'N': u'7'}))
+
     def test_lt(self):
         from ddbmock.database.comparison import lt
 
         self.assertTrue(lt({u'N': u'42'}, {u'N': u'42.001'}))
         self.assertFalse(lt({u'N': u'42'}, {u'N': u'7'}))
 
+        self.assertFalse(lt(None, {u'N': u'7'}))
+
     def test_ge(self):
         from ddbmock.database.comparison import ge
 
         self.assertFalse(ge({u'N': u'42'}, {u'N': u'42.001'}))
         self.assertTrue(ge({u'N': u'42'}, {u'N': u'7'}))
 
+        self.assertFalse(ge(None, {u'N': u'7'}))
+
     def test_gt(self):
         from ddbmock.database.comparison import gt
 
         self.assertFalse(gt({u'N': u'42'}, {u'N': u'42.001'}))
         self.assertTrue(gt({u'N': u'42'}, {u'N': u'7'}))
 
+        self.assertFalse(gt(None, {u'N': u'7'}))
+
     def test_begins_with(self):
         from ddbmock.database.comparison import begins_with
 
         self.assertTrue(begins_with({u'S': u'waldo'}, {u'S': u'wal'}))
         self.assertFalse(begins_with({u'S': u'waldo'}, {u'S': u'waldo-mario'}))
 
+        self.assertFalse(begins_with(None, {u'S': u'waldo-mario'}))
+
     def test_between(self):
         from ddbmock.database.comparison import between
 
         self.assertTrue(between({u'N': u'42'}, {u'N': u'42'}, {u'N': u'42'}))
         self.assertFalse(between({u'N': u'42'}, {u'N': u'43'}, {u'N': u'123'}))
 
+        self.assertFalse(between(None, {u'N': u'43'}, {u'N': u'123'}))
+
+    def test_null(self):
+        from ddbmock.database.comparison import null
+
+        self.assertTrue(null(None))
+        self.assertFalse(null([]))
+        self.assertFalse(null({}))
+        self.assertFalse(null(False))
+        self.assertFalse(null({u'N': u'42'}))
+
+    def test_not_null(self):
+        from ddbmock.database.comparison import not_null
+
+        self.assertFalse(not_null(None))
+        self.assertTrue(not_null([]))
+        self.assertTrue(not_null({}))
+        self.assertTrue(not_null(False))
+        self.assertTrue(not_null({u'N': u'42'}))
+
+    def test_contains(self):
+        from ddbmock.database.comparison import contains
+
+        self.assertTrue(contains({u'S': u'waldo'}, {u'S': u'al'}))
+        self.assertFalse(contains({u'S': u'waldo'}, {u'S': u'alee'}))
+        self.assertTrue(contains({u'SS': [u'waldo']}, {u'S': u'waldo'}))
+        self.assertFalse(contains({u'SS': [u'waldo']}, {u'S': u'al'}))
+        self.assertTrue(contains({u'NS': [u'123']}, {u'N': u'123'}))
+        self.assertFalse(contains({u'NS': [u'123']}, {u'N': u'12'}))
+
+    def test_not_contains(self):
+        from ddbmock.database.comparison import not_contains
+
+        self.assertFalse(not_contains({u'S': u'waldo'}, {u'S': u'al'}))
+        self.assertTrue(not_contains({u'S': u'waldo'}, {u'S': u'alee'}))
+        self.assertFalse(not_contains({u'SS': [u'waldo']}, {u'S': u'waldo'}))
+        self.assertTrue(not_contains({u'SS': [u'waldo']}, {u'S': u'al'}))
+        self.assertFalse(not_contains({u'NS': [u'123']}, {u'N': u'123'}))
+        self.assertTrue(not_contains({u'NS': [u'123']}, {u'N': u'12'}))
+
+    def test_in(self):
+        import ddbmock.database.comparison as c
+
+        # This module is intended to be used reflexively, so... normal behovior
+        test_in = getattr(c, 'in')
+
+        pool = [
+            {u'S': u'waldo'},
+            {u'N': u'123'},
+        ]
+
+        self.assertTrue(test_in({u'S': u'waldo'}, *pool))
+        self.assertTrue(test_in({u'N': u'123'}, *pool))
+        self.assertFalse(test_in({u'S': u'wal'}, *pool))
+        self.assertFalse(test_in({u'N': u'12'}, *pool))
+
     def test_type_mismatch(self):
         from ddbmock.database.comparison import (
-            between, begins_with, eq, lt, le, gt, ge)
+            between, begins_with, eq, lt, le, gt, ge, contains)
 
         target = {u'S': u'waldo'}
         rule = {u'N': u'123'}
         self.assertRaises(TypeError, gt, target, rule)
         self.assertRaises(TypeError, ge, target, rule)
         self.assertRaises(TypeError, begins_with, target, rule)
-        self.assertRaises(TypeError, between, target, rule, rule)
+        self.assertRaises(TypeError, between, target, rule, rule)
+        self.assertRaises(TypeError, contains, target, rule)
+        self.assertRaises(TypeError, contains, rule, rule) # can not use contains on number