Commits

Andy Mikhailenko committed af51f36

Improved validation. Added some tests (one of them is marked as broken and should be addressed later).

Comments (0)

Files changed (2)

monk/validation.py

             continue
         elif typespec is None:
             # any value is acceptable
+            #------
+            # FIXME if the value was expected to be a nested dict instance, we
+            #   still get here because walk_dict yields None as value for
+            #   nested items. This should be fixed, see tests:
+            #   test_validation:TestDocumentStructureValidation.test_bad_types_FIXME
             continue
         elif isinstance(typespec, list) and value:
             # nested list
             if not typespec:
                 # empty by default
                 continue
+            if not isinstance(value, list):
+                key = '.'.join(keys)
+                raise TypeError('{key}: expected {typespec.__name__}, got '
+                                '{valtype.__name__} {value!r}'.format(
+                                    key=key, typespec=list,
+                                    valtype=type(value), value=value))
             item_spec = typespec[0]
             for item in value:
                 if item_spec == dict or isinstance(item, dict):

unittests/test_validation.py

         validate_structure({'foo': {'bar': [{'baz': [unicode]}]}}, {}, skip_missing=True)
         '''
 
-    def test_bad_types(self):
-        pass
-
     def test_malformed_lists(self):
         pass
 
     #---
 
+    def test_bad_types(self):
+        with pytest.raises(TypeError) as excinfo:
+            validate_structure({'a': int}, {'a': 'bad'})
+        assert "a: expected int, got str 'bad'" in str(excinfo)
+
+        with pytest.raises(TypeError) as excinfo:
+            validate_structure({'a': [int]}, {'a': 'bad'})
+        assert "a: expected list, got str 'bad'" in str(excinfo)
+
+        with pytest.raises(TypeError) as excinfo:
+            validate_structure({'a': [int]}, {'a': ['bad']})
+        assert "a: expected int, got str 'bad'" in str(excinfo)
+
+        with pytest.raises(TypeError) as excinfo:
+            validate_structure({'a': {'b': int}}, {'a': {'b': 'bad'}})
+        assert "a.b: expected int, got str 'bad'" in str(excinfo)
+
+        with pytest.raises(TypeError) as excinfo:
+            validate_structure({'a': [{'b': [int]}]}, {'a': [{'b': ['bad']}]})
+        assert "a: b: expected int, got str 'bad'" in str(excinfo)
+
+    @pytest.mark.xfail
+    def test_bad_types_FIXME(self):
+        with pytest.raises(TypeError) as excinfo:
+            validate_structure({'a': {'b': int}}, {'a': 'bad'})
+        assert "a: expected dict, got str 'bad'" in str(excinfo)
+
+
     def test_empty(self):
         validate_structure({'a': unicode}, {'a': None})
         validate_structure({'a': list}, {'a': None})
             validate_structure({}, {'x': 123})
         with pytest.raises(UnknownKey):
             validate_structure({'a': unicode}, {'a': u'A', 'x': 123})
-        with pytest.raises(TypeError) as excinfo:
-            validate_structure({'a': [{'b': [int]}]}, {'a': [{'b': ['bad']}]})
-        assert "a: b: expected int, got str 'bad'" in str(excinfo)
 
     def test_bool(self):
         validate_structure({'a': bool}, {'a': None})