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 - tbechtold@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
Lucas Alvares Gomes
Mehdi Abaakouk
+Mehdi Abaakouk
Michael Krotscheck
Ryan Petrello
Sascha Peilicke
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',