Hello community,

here is the log from the commit of package python-WSME for openSUSE:Factory 
checked in at 2015-09-11 09:04:29
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-WSME (Old)
 and      /work/SRC/openSUSE:Factory/.python-WSME.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-WSME"

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-WSME/python-WSME.changes  2015-06-03 
08:49:19.000000000 +0200
+++ /work/SRC/openSUSE:Factory/.python-WSME.new/python-WSME.changes     
2015-09-11 09:04:40.000000000 +0200
@@ -1,0 +2,11 @@
+Wed Sep  9 07:12:25 UTC 2015 - tbecht...@suse.com
+
+- update to 0.8.0:
+  * Return 400, if the query string is not a dict
+  * rest: return 415 when content-type is invalid
+  * json: raise ValueError invalid list or dict
+  * Fixes exception path with the datatype is a Object
+  * Update README formatting for release tools
+  * Set up dependencies for cross-tests
+
+-------------------------------------------------------------------

Old:
----
  WSME-0.7.0.tar.gz

New:
----
  WSME-0.8.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-WSME.spec ++++++
--- /var/tmp/diff_new_pack.wCdf2E/_old  2015-09-11 09:04:41.000000000 +0200
+++ /var/tmp/diff_new_pack.wCdf2E/_new  2015-09-11 09:04:41.000000000 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           python-WSME
-Version:        0.7.0
+Version:        0.8.0
 Release:        0
 Summary:        Web Services Made Easy
 License:        MIT

++++++ WSME-0.7.0.tar.gz -> WSME-0.8.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/WSME-0.7.0/AUTHORS new/WSME-0.8.0/AUTHORS
--- old/WSME-0.7.0/AUTHORS      2015-06-01 15:19:45.000000000 +0200
+++ new/WSME-0.8.0/AUTHORS      2015-08-25 17:05:53.000000000 +0200
@@ -21,6 +21,7 @@
 Louis Taylor <lo...@kragniz.eu>
 Lucas Alvares Gomes <lucasago...@gmail.com>
 Mehdi Abaakouk <mehdi.abaak...@enovance.com>
+Mehdi Abaakouk <sil...@sileht.net>
 Michael Krotscheck <krotsch...@gmail.com>
 Ryan Petrello <li...@ryanpetrello.com>
 Sascha Peilicke <speili...@suse.com>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/WSME-0.7.0/ChangeLog new/WSME-0.8.0/ChangeLog
--- old/WSME-0.7.0/ChangeLog    2015-06-01 15:19:45.000000000 +0200
+++ new/WSME-0.8.0/ChangeLog    2015-08-25 17:05:53.000000000 +0200
@@ -1,6 +1,16 @@
 CHANGES
 =======
 
+0.8.0
+-----
+
+* Return 400, if the query string is not a dict
+* rest: return 415 when content-type is invalid
+* json: raise ValueError invalid list or dict
+* Fixes exception path with the datatype is a Object
+* Update README formatting for release tools
+* Set up dependencies for cross-tests
+
 0.7.0
 -----
 
@@ -9,6 +19,7 @@
 * Add pytz as a dependency
 * Fix wrong reference to status argument in the docs
 * Added timezone support to parse_isodatetime
+* Complex types should check unexpected attributes
 * Replace deprecated assertEquals with assertEqual
 * Update changes doc
 * Ensure UserType objects are converted to basetype
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/WSME-0.7.0/PKG-INFO new/WSME-0.8.0/PKG-INFO
--- old/WSME-0.7.0/PKG-INFO     2015-06-01 15:19:45.000000000 +0200
+++ new/WSME-0.8.0/PKG-INFO     2015-08-25 17:05:53.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: WSME
-Version: 0.7.0
+Version: 0.8.0
 Summary: Simplify the writing of REST APIs, and extend them with additional 
protocols.
 Home-page: UNKNOWN
 Author: "Christophe de Vienne"
@@ -108,9 +108,10 @@
         Contribute
         ~~~~~~~~~~
         
-        :Report issues: `WSME issue tracker`_
-        :Source code: git clone https://github.com/stackforge/wsme/
-        :Gerrit: https://review.openstack.org/#/q/project:stackforge/wsme,n,z/
+        * Documentation: http://packages.python.org/WSME/
+        * Source: http://git.openstack.org/cgit/stackforge/wsme
+        * Bugs: https://bugs.launchpad.net/wsme/+bugs
+        * Code review: 
https://review.openstack.org/#/q/project:stackforge/wsme,n,z
         
         .. _Changelog: http://packages.python.org/WSME/changes.html
         .. _python-wsme mailinglist: http://groups.google.com/group/python-wsme
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/WSME-0.7.0/README.rst new/WSME-0.8.0/README.rst
--- old/WSME-0.7.0/README.rst   2015-06-01 15:19:37.000000000 +0200
+++ new/WSME-0.8.0/README.rst   2015-08-25 17:05:33.000000000 +0200
@@ -100,9 +100,10 @@
 Contribute
 ~~~~~~~~~~
 
-:Report issues: `WSME issue tracker`_
-:Source code: git clone https://github.com/stackforge/wsme/
-:Gerrit: https://review.openstack.org/#/q/project:stackforge/wsme,n,z/
+* Documentation: http://packages.python.org/WSME/
+* Source: http://git.openstack.org/cgit/stackforge/wsme
+* Bugs: https://bugs.launchpad.net/wsme/+bugs
+* Code review: https://review.openstack.org/#/q/project:stackforge/wsme,n,z
 
 .. _Changelog: http://packages.python.org/WSME/changes.html
 .. _python-wsme mailinglist: http://groups.google.com/group/python-wsme
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/WSME-0.7.0/WSME.egg-info/PKG-INFO 
new/WSME-0.8.0/WSME.egg-info/PKG-INFO
--- old/WSME-0.7.0/WSME.egg-info/PKG-INFO       2015-06-01 15:19:45.000000000 
+0200
+++ new/WSME-0.8.0/WSME.egg-info/PKG-INFO       2015-08-25 17:05:53.000000000 
+0200
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: WSME
-Version: 0.7.0
+Version: 0.8.0
 Summary: Simplify the writing of REST APIs, and extend them with additional 
protocols.
 Home-page: UNKNOWN
 Author: "Christophe de Vienne"
@@ -108,9 +108,10 @@
         Contribute
         ~~~~~~~~~~
         
-        :Report issues: `WSME issue tracker`_
-        :Source code: git clone https://github.com/stackforge/wsme/
-        :Gerrit: https://review.openstack.org/#/q/project:stackforge/wsme,n,z/
+        * Documentation: http://packages.python.org/WSME/
+        * Source: http://git.openstack.org/cgit/stackforge/wsme
+        * Bugs: https://bugs.launchpad.net/wsme/+bugs
+        * Code review: 
https://review.openstack.org/#/q/project:stackforge/wsme,n,z
         
         .. _Changelog: http://packages.python.org/WSME/changes.html
         .. _python-wsme mailinglist: http://groups.google.com/group/python-wsme
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/WSME-0.7.0/WSME.egg-info/pbr.json 
new/WSME-0.8.0/WSME.egg-info/pbr.json
--- old/WSME-0.7.0/WSME.egg-info/pbr.json       2015-06-01 15:19:45.000000000 
+0200
+++ new/WSME-0.8.0/WSME.egg-info/pbr.json       2015-08-25 17:05:53.000000000 
+0200
@@ -1 +1 @@
-{"git_version": "43e125d", "is_release": true}
\ No newline at end of file
+{"git_version": "1dc4421", "is_release": true}
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/WSME-0.7.0/setup.cfg new/WSME-0.8.0/setup.cfg
--- old/WSME-0.7.0/setup.cfg    2015-06-01 15:19:45.000000000 +0200
+++ new/WSME-0.8.0/setup.cfg    2015-08-25 17:05:53.000000000 +0200
@@ -42,6 +42,6 @@
 
 [egg_info]
 tag_date = 0
-tag_build = 
 tag_svn_revision = 0
+tag_build = 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/WSME-0.7.0/tox-tmpl.ini new/WSME-0.8.0/tox-tmpl.ini
--- old/WSME-0.7.0/tox-tmpl.ini 2015-06-01 15:19:37.000000000 +0200
+++ new/WSME-0.8.0/tox-tmpl.ini 2015-08-25 17:05:33.000000000 +0200
@@ -126,5 +126,7 @@
 [testenv:venv]
 commands = {posargs}
 usedevelop=True
-deps=
-    pbr
+deps =
+       pbr
+    oslo.config
+    oslotest
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/WSME-0.7.0/tox.ini new/WSME-0.8.0/tox.ini
--- old/WSME-0.7.0/tox.ini      2015-06-01 15:19:37.000000000 +0200
+++ new/WSME-0.8.0/tox.ini      2015-08-25 17:05:33.000000000 +0200
@@ -81,6 +81,8 @@
 usedevelop = True
 deps = 
        pbr
+       oslo.config
+       oslotest
 
 [testenv:py27-sa5-lxml-json]
 commands = 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/WSME-0.7.0/wsme/exc.py new/WSME-0.8.0/wsme/exc.py
--- old/WSME-0.7.0/wsme/exc.py  2015-06-01 15:19:37.000000000 +0200
+++ new/WSME-0.8.0/wsme/exc.py  2015-08-25 17:05:33.000000000 +0200
@@ -62,3 +62,31 @@
     @property
     def faultstring(self):
         return _(six.u("Unknown function name: %s")) % (self.name)
+
+
+class UnknownAttribute(ClientSideError):
+    def __init__(self, fieldname, attributes, msg=''):
+        self.fieldname = fieldname
+        self.attributes = attributes
+        self.msg = msg
+        super(UnknownAttribute, self).__init__(self.msg)
+
+    @property
+    def faultstring(self):
+        error = _("Unknown attribute for argument %(argn)s: %(attrs)s")
+        if len(self.attributes) > 1:
+            error = _("Unknown attributes for argument %(argn)s: %(attrs)s")
+        str_attrs = ", ".join(self.attributes)
+        return error % {'argn': self.fieldname, 'attrs': str_attrs}
+
+    def add_fieldname(self, name):
+        """Add a fieldname to concatenate the full name.
+
+        Add a fieldname so that the whole hierarchy is displayed. Successive
+        calls to this method will prepend ``name`` to the hierarchy of names.
+        """
+        if self.fieldname is not None:
+            self.fieldname = "{}.{}".format(name, self.fieldname)
+        else:
+            self.fieldname = name
+        super(UnknownAttribute, self).__init__(self.msg)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/WSME-0.7.0/wsme/rest/args.py 
new/WSME-0.8.0/wsme/rest/args.py
--- old/WSME-0.7.0/wsme/rest/args.py    2015-06-01 15:19:37.000000000 +0200
+++ new/WSME-0.8.0/wsme/rest/args.py    2015-08-25 17:05:33.000000000 +0200
@@ -181,8 +181,10 @@
         except Exception:
             if isinstance(argdef.datatype, UserType):
                 datatype_name = argdef.datatype.name
-            else:
+            elif isinstance(argdef.datatype, type):
                 datatype_name = argdef.datatype.__name__
+            else:
+                datatype_name = argdef.datatype.__class__.__name__
             raise InvalidInput(
                 argdef.name,
                 arg,
@@ -231,7 +233,8 @@
     elif mimetype in restxml.accept_content_types:
         dataformat = restxml
     else:
-        raise ValueError("Unknown mimetype: %s" % mimetype)
+        raise ClientSideError("Unknown mimetype: %s" % mimetype,
+                              status_code=415)
 
     try:
         kw = dataformat.parse(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/WSME-0.7.0/wsme/rest/json.py 
new/WSME-0.8.0/wsme/rest/json.py
--- old/WSME-0.7.0/wsme/rest/json.py    2015-06-01 15:19:37.000000000 +0200
+++ new/WSME-0.8.0/wsme/rest/json.py    2015-08-25 17:05:33.000000000 +0200
@@ -1,6 +1,4 @@
-"""
-REST+Json protocol implementation.
-"""
+"""REST+Json protocol implementation."""
 from __future__ import absolute_import
 import datetime
 import decimal
@@ -9,10 +7,10 @@
 
 from simplegeneric import generic
 
-from wsme.types import Unset
+import wsme.exc
 import wsme.types
+from wsme.types import Unset
 import wsme.utils
-from wsme.exc import ClientSideError, UnknownArgument, InvalidInput
 
 
 try:
@@ -116,8 +114,7 @@
 
 @generic
 def fromjson(datatype, value):
-    """
-    A generic converter from json base types to python datatype.
+    """A generic converter from json base types to python datatype.
 
     If a non-complex user specific type is to be used in the api,
     a specific fromjson should be added::
@@ -135,16 +132,31 @@
         return None
     if wsme.types.iscomplex(datatype):
         obj = datatype()
-        for attrdef in wsme.types.list_attributes(datatype):
+        attributes = wsme.types.list_attributes(datatype)
+
+        # Here we check that all the attributes in the value are also defined
+        # in our type definition, otherwise we raise an Error.
+        v_keys = set(value.keys())
+        a_keys = set(adef.name for adef in attributes)
+        if not v_keys <= a_keys:
+            raise wsme.exc.UnknownAttribute(None, v_keys - a_keys)
+
+        for attrdef in attributes:
             if attrdef.name in value:
-                val_fromjson = fromjson(attrdef.datatype, value[attrdef.name])
+                try:
+                    val_fromjson = fromjson(attrdef.datatype,
+                                            value[attrdef.name])
+                except wsme.exc.UnknownAttribute as e:
+                    e.add_fieldname(attrdef.name)
+                    raise
                 if getattr(attrdef, 'readonly', False):
-                    raise InvalidInput(attrdef.name, val_fromjson,
-                                       "Cannot set read only field.")
+                    raise wsme.exc.InvalidInput(attrdef.name, val_fromjson,
+                                                "Cannot set read only field.")
                 setattr(obj, attrdef.key, val_fromjson)
             elif attrdef.mandatory:
-                raise InvalidInput(attrdef.name, None,
-                                   "Mandatory field missing.")
+                raise wsme.exc.InvalidInput(attrdef.name, None,
+                                            "Mandatory field missing.")
+
         return wsme.types.validate_value(datatype, obj)
     elif wsme.types.isusertype(datatype):
         value = datatype.frombasetype(
@@ -156,6 +168,8 @@
 def array_fromjson(datatype, value):
     if value is None:
         return None
+    if not isinstance(value, list):
+        raise ValueError("Value not a valid list: %s" % value)
     return [fromjson(datatype.item_type, item) for item in value]
 
 
@@ -163,6 +177,8 @@
 def dict_fromjson(datatype, value):
     if value is None:
         return None
+    if not isinstance(value, dict):
+        raise ValueError("Value not a valid dict: %s" % value)
     return dict((
         (fromjson(datatype.key_type, item[0]),
             fromjson(datatype.value_type, item[1]))
@@ -243,16 +259,23 @@
     try:
         jdata = jload(s)
     except ValueError:
-        raise ClientSideError("Request is not in valid JSON format")
+        raise wsme.exc.ClientSideError("Request is not in valid JSON format")
     if bodyarg:
         argname = list(datatypes.keys())[0]
         try:
             kw = {argname: fromjson(datatypes[argname], jdata)}
         except ValueError as e:
-            raise InvalidInput(argname, jdata, e.args[0])
+            raise wsme.exc.InvalidInput(argname, jdata, e.args[0])
+        except wsme.exc.UnknownAttribute as e:
+            # We only know the fieldname at this level, not in the
+            # called function. We fill in this information here.
+            e.add_fieldname(argname)
+            raise
     else:
         kw = {}
         extra_args = []
+        if not isinstance(jdata, dict):
+            raise wsme.exc.ClientSideError("Request must be a JSON dict")
         for key in jdata:
             if key not in datatypes:
                 extra_args.append(key)
@@ -260,9 +283,14 @@
                 try:
                     kw[key] = fromjson(datatypes[key], jdata[key])
                 except ValueError as e:
-                    raise InvalidInput(key, jdata[key], e.args[0])
+                    raise wsme.exc.InvalidInput(key, jdata[key], e.args[0])
+                except wsme.exc.UnknownAttribute as e:
+                    # We only know the fieldname at this level, not in the
+                    # called function. We fill in this information here.
+                    e.add_fieldname(key)
+                    raise
         if extra_args:
-            raise UnknownArgument(', '.join(extra_args))
+            raise wsme.exc.UnknownArgument(', '.join(extra_args))
     return kw
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/WSME-0.7.0/wsme/root.py new/WSME-0.8.0/wsme/root.py
--- old/WSME-0.7.0/wsme/root.py 2015-06-01 15:19:37.000000000 +0200
+++ new/WSME-0.8.0/wsme/root.py 2015-08-25 17:05:33.000000000 +0200
@@ -203,6 +203,7 @@
             infos = wsme.api.format_exception(sys.exc_info(), self._debug)
             if isinstance(e, ClientSideError):
                 request.client_errorcount += 1
+                request.client_last_status_code = e.code
             else:
                 request.server_errorcount += 1
             return protocol.encode_error(context, infos)
@@ -266,6 +267,7 @@
 
         request.calls = []
         request.client_errorcount = 0
+        request.client_last_status_code = None
         request.server_errorcount = 0
 
         try:
@@ -290,7 +292,9 @@
                 if hasattr(protocol, 'get_response_status'):
                     res.status = protocol.get_response_status(request)
                 else:
-                    if request.client_errorcount:
+                    if request.client_errorcount == 1:
+                        res.status = request.client_last_status_code
+                    elif request.client_errorcount:
                         res.status = 400
                     elif request.server_errorcount:
                         res.status = 500
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/WSME-0.7.0/wsme/tests/test_protocols_commons.py 
new/WSME-0.8.0/wsme/tests/test_protocols_commons.py
--- old/WSME-0.7.0/wsme/tests/test_protocols_commons.py 2015-06-01 
15:19:37.000000000 +0200
+++ new/WSME-0.8.0/wsme/tests/test_protocols_commons.py 2015-08-25 
17:05:33.000000000 +0200
@@ -7,7 +7,11 @@
 from wsme.rest.args import from_param, from_params, args_from_args
 from wsme.exc import InvalidInput
 
-from wsme.types import UserType, Unset, ArrayType, DictType
+from wsme.types import UserType, Unset, ArrayType, DictType, Base
+
+
+class MyBaseType(Base):
+    test = str
 
 
 class MyUserType(UserType):
@@ -89,6 +93,17 @@
         else:
             self.fail('Should have thrown an InvalidInput')
 
+    def test_args_from_args_array_type(self):
+        fake_type = ArrayType(MyBaseType)
+        fd = FunctionDefinition(FunctionDefinition)
+        fd.arguments.append(FunctionArgument('fake-arg', fake_type, True, []))
+        try:
+            args_from_args(fd, [['invalid-argument']], {})
+        except InvalidInput as e:
+            assert ArrayType.__name__ in str(e)
+        else:
+            self.fail('Should have thrown an InvalidInput')
+
 
 class ArgTypeConversion(unittest.TestCase):
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/WSME-0.7.0/wsme/tests/test_restjson.py 
new/WSME-0.8.0/wsme/tests/test_restjson.py
--- old/WSME-0.7.0/wsme/tests/test_restjson.py  2015-06-01 15:19:37.000000000 
+0200
+++ new/WSME-0.8.0/wsme/tests/test_restjson.py  2015-08-25 17:05:33.000000000 
+0200
@@ -11,7 +11,8 @@
 
 from wsme.rest.json import fromjson, tojson, parse
 from wsme.utils import parse_isodatetime, parse_isotime, parse_isodate
-from wsme.types import isarray, isdict, isusertype, register_type, UserType
+from wsme.types import isarray, isdict, isusertype, register_type
+from wsme.types import UserType, ArrayType, DictType
 from wsme.rest import expose, validate
 from wsme.exc import ClientSideError, InvalidInput
 
@@ -100,6 +101,10 @@
     name = wsme.types.text
 
 
+class NestedObj(wsme.types.Base):
+    o = Obj
+
+
 class CRUDResult(object):
     data = Obj
     message = wsme.types.text
@@ -249,6 +254,15 @@
         print(r)
         assert json.loads(r.text) == 2
 
+    def test_invalid_content_type_body(self):
+        r = self.app.post('/argtypes/setint.json', '{"value": 2}',
+                          headers={"Content-Type": "application/invalid"},
+                          expect_errors=True)
+        print(r)
+        assert r.status_int == 415
+        assert json.loads(r.text)['faultstring'] == \
+            "Unknown mimetype: application/invalid"
+
     def test_invalid_json_body(self):
         r = self.app.post('/argtypes/setint.json', '{"value": 2',
                           headers={"Content-Type": "application/json"},
@@ -325,6 +339,36 @@
         j = parse('{"a": "2011-01-01"}', {'a': datetime.date}, False)
         assert isinstance(j['a'], datetime.date)
 
+    def test_invalid_root_dict_fromjson(self):
+        try:
+            parse('["invalid"]', {'a': ArrayType(str)}, False)
+            assert False
+        except Exception as e:
+            assert isinstance(e, ClientSideError)
+            assert e.msg == "Request must be a JSON dict"
+
+    def test_invalid_list_fromjson(self):
+        jlist = "invalid"
+        try:
+            parse('{"a": "%s"}' % jlist, {'a': ArrayType(str)}, False)
+            assert False
+        except Exception as e:
+            assert isinstance(e, InvalidInput)
+            assert e.fieldname == 'a'
+            assert e.value == jlist
+            assert e.msg == "Value not a valid list: %s" % jlist
+
+    def test_invalid_dict_fromjson(self):
+        jdict = "invalid"
+        try:
+            parse('{"a": "%s"}' % jdict, {'a': DictType(str, str)}, False)
+            assert False
+        except Exception as e:
+            assert isinstance(e, InvalidInput)
+            assert e.fieldname == 'a'
+            assert e.value == jdict
+            assert e.msg == "Value not a valid dict: %s" % jdict
+
     def test_invalid_date_fromjson(self):
         jdate = "2015-01-invalid"
         try:
@@ -476,6 +520,37 @@
                     "invalid literal for int() with base 10: '%s'" % value
                 )
 
+    def test_parse_unexpected_attribute(self):
+        o = {
+            "id": "1",
+            "name": "test",
+            "other": "unknown",
+            "other2": "still unknown",
+        }
+        for ba in True, False:
+            jd = o if ba else {"o": o}
+            try:
+                parse(json.dumps(jd), {'o': Obj}, ba)
+                raise AssertionError("Object should not parse correcty.")
+            except wsme.exc.UnknownAttribute as e:
+                self.assertEqual(e.attributes, set(['other', 'other2']))
+
+    def test_parse_unexpected_nested_attribute(self):
+        no = {
+            "o": {
+                "id": "1",
+                "name": "test",
+                "other": "unknown",
+            },
+        }
+        for ba in False, True:
+            jd = no if ba else {"no": no}
+            try:
+                parse(json.dumps(jd), {'no': NestedObj}, ba)
+            except wsme.exc.UnknownAttribute as e:
+                self.assertEqual(e.attributes, set(['other']))
+                self.assertEqual(e.fieldname, "no.o")
+
     def test_nest_result(self):
         self.root.protocols[0].nest_result = True
         r = self.app.get('/returntypes/getint.json')
@@ -659,6 +734,33 @@
         assert result['data']['name'] == u("test")
         assert result['message'] == "read"
 
+    def test_unexpected_extra_arg(self):
+        headers = {
+            'Content-Type': 'application/json',
+        }
+        data = {"id": 1, "name": "test"}
+        content = json.dumps({"data": data, "other": "unexpected"})
+        res = self.app.put(
+            '/crud',
+            content,
+            headers=headers,
+            expect_errors=True)
+        self.assertEqual(res.status_int, 400)
+
+    def test_unexpected_extra_attribute(self):
+        """Expect a failure if we send an unexpected object attribute."""
+        headers = {
+            'Content-Type': 'application/json',
+        }
+        data = {"id": 1, "name": "test", "other": "unexpected"}
+        content = json.dumps({"data": data})
+        res = self.app.put(
+            '/crud',
+            content,
+            headers=headers,
+            expect_errors=True)
+        self.assertEqual(res.status_int, 400)
+
     def test_body_arg(self):
         headers = {
             'Content-Type': 'application/json',


Reply via email to