From c1f08c40db6bece986cd04bf670443655d85f7cd Mon Sep 17 00:00:00 2001 From: Christophe de Vienne Date: Thu, 28 Mar 2013 23:30:06 +0100 Subject: [PATCH] Create some (incomplete) tests for the cornice adapter and fix it --- tests/test_cornice.py | 79 +++++++++++++++++++++++++++++++++++++++++++ tox-tmpl.ini | 15 ++++++-- tox.ini | 14 +++++++- wsme/rest/xml.py | 7 ++-- wsmeext/cornice.py | 52 ++++++++++++++-------------- 5 files changed, 133 insertions(+), 34 deletions(-) create mode 100644 tests/test_cornice.py diff --git a/tests/test_cornice.py b/tests/test_cornice.py new file mode 100644 index 0000000..19c93f1 --- /dev/null +++ b/tests/test_cornice.py @@ -0,0 +1,79 @@ +import unittest +import json + +import webtest + +from cornice import Service +from pyramid.config import Configurator + +from wsme.types import text, Base +from wsmeext.cornice import signature + + +class User(Base): + id = int + name = text + +users = Service(name='users', path='/users') + + +@users.get() +@signature([User]) +def users_get(): + return [User(id=1, name='first')] + + +@users.post() +@signature(User, body=User) +def users_create(data): + data.id = 2 + return data + + +def make_app(): + config = Configurator() + config.include("cornice") + config.include("wsmeext.cornice") + config.scan("test_cornice") + return config.make_wsgi_app() + + +class WSMECorniceTestCase(unittest.TestCase): + def setUp(self): + self.app = webtest.TestApp(make_app()) + + def test_get_json_list(self): + resp = self.app.get('/users') + self.assertEquals( + resp.body, + '[{"id": 1, "name": "first"}]' + ) + + def test_get_xml_list(self): + resp = self.app.get('/users', headers={"Accept": "text/xml"}) + self.assertEquals( + resp.body, + '1first' + ) + + def test_post_json_data(self): + data = json.dumps({"name": "new"}) + resp = self.app.post( + '/users', data, + headers={"Content-Type": "application/json"} + ) + self.assertEquals( + resp.body, + '{"id": 2, "name": "new"}' + ) + + def test_post_xml_data(self): + data = 'new' + resp = self.app.post( + '/users', data, + headers={"Content-Type": "text/xml"} + ) + self.assertEquals( + resp.body, + '2new' + ) diff --git a/tox-tmpl.ini b/tox-tmpl.ini index c348e73..4a8627e 100644 --- a/tox-tmpl.ini +++ b/tox-tmpl.ini @@ -1,6 +1,6 @@ # content of: tox.ini , put in same dir as setup.py [tox] -envlist = py27,py27-nolxml,py32,py32-nolxml,pypy,py25-simplejson,sphinxext,tg11,tg15,pecan,flask,coverage +envlist = py27,py27-nolxml,py32,py32-nolxml,pypy,py25-simplejson,sphinxext,tg11,tg15,pecan,flask,cornice,coverage [common] testtools= @@ -187,7 +187,18 @@ deps= commands= {envbindir}/nosetests tests/test_flask.py --with-xunit --xunit-file nosetests-{envname}.xml --verbose --with-coverage --cover-package wsme {posargs} {envbindir}/coverage xml -o coverage-{envname}.xml wsme/*.py wsmeext/flask.py - + +[testenv:cornice] +basepython=python2.7 +deps= + d2to1 + nose + webtest + coverage + cornice +commands= + {envbindir}/nosetests tests/test_cornice.py --with-xunit --xunit-file nosetests-{envname}.xml --verbose --with-coverage --cover-package wsmeext {posargs} + {envbindir}/coverage xml -o coverage-{envname}.xml wsme/*.py wsmeext/cornice.py [testenv:coverage] basepython=python diff --git a/tox.ini b/tox.ini index fe9b46e..6c5b99b 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py27,py27-nolxml,py32,py32-nolxml,pypy,py25-simplejson,sphinxext,tg11,tg15,pecan,flask,coverage +envlist = py27,py27-nolxml,py32,py32-nolxml,pypy,py25-simplejson,sphinxext,tg11,tg15,pecan,flask,cornice,coverage [common] testtools = @@ -105,6 +105,18 @@ commands = {envbindir}/nosetests tests/test_flask.py --with-xunit --xunit-file nosetests-{envname}.xml --verbose --with-coverage --cover-package wsme {posargs} {envbindir}/coverage xml -o coverage-{envname}.xml wsme/*.py wsmeext/flask.py +[testenv:cornice] +basepython = python2.7 +deps = + d2to1 + nose + webtest + coverage + cornice +commands = + {envbindir}/nosetests tests/test_cornice.py --with-xunit --xunit-file nosetests-{envname}.xml --verbose --with-coverage --cover-package wsmeext {posargs} + {envbindir}/coverage xml -o coverage-{envname}.xml wsme/*.py wsmeext/cornice.py + [testenv:coverage] basepython = python deps = diff --git a/wsme/rest/xml.py b/wsme/rest/xml.py index 2d1b585..01ffaed 100644 --- a/wsme/rest/xml.py +++ b/wsme/rest/xml.py @@ -61,7 +61,7 @@ def toxml(datatype, key, value): else: if wsme.types.isusertype(datatype): return toxml(datatype.basetype, - key, datatype.tobasetype(value)) + key, datatype.tobasetype(value)) elif wsme.types.iscomplex(datatype): for attrdef in datatype._wsme_attributes: attrvalue = getattr(value, attrdef.key) @@ -95,8 +95,7 @@ def fromxml(datatype, element): if element.get('nil', False): return None if wsme.types.isusertype(datatype): - return datatype.frombasetype( - fromxml(datatype.basetype, element)) + return datatype.frombasetype(fromxml(datatype.basetype, element)) if wsme.types.iscomplex(datatype): obj = datatype() for attrdef in datatype._wsme_attributes: @@ -243,7 +242,7 @@ def parse(s, datatypes, bodyarg): tree = et.fromstring(s) if bodyarg: name = list(datatypes.keys())[0] - return fromxml(datatypes[name], tree) + return {name: fromxml(datatypes[name], tree)} else: kw = {} extra_args = [] diff --git a/wsmeext/cornice.py b/wsmeext/cornice.py index 16e4efd..e03ed5e 100644 --- a/wsmeext/cornice.py +++ b/wsmeext/cornice.py @@ -15,17 +15,13 @@ And use it:: return Message(text='Hello %s' % who) """ from __future__ import absolute_import -import json - -import xml.etree.ElementTree as et import wsme -import wsme.protocols -from wsme.protocols import restjson -from wsme.protocols import restxml +from wsme.rest import json as restjson +from wsme.rest import xml as restxml import functools -from wsme.protocols.commons import ( +from wsme.rest.args import ( args_from_params, args_from_body, combine_args ) @@ -37,11 +33,7 @@ class WSMEJsonRenderer(object): def __call__(self, data, context): response = context['request'].response response.content_type = 'application/json' - data = restjson.tojson( - data['datatype'], - data['result'] - ) - return json.dumps(data) + return restjson.encode_result(data['result'], data['datatype']) class WSMEXmlRenderer(object): @@ -51,34 +43,40 @@ class WSMEXmlRenderer(object): def __call__(self, data, context): response = context['request'].response response.content_type = 'text/xml' - data = restxml.toxml( - data['datatype'], - 'result', - data['result'] - ) - return et.tostring(data) + return restxml.encode_result(data['result'], data['datatype']) + + +def get_outputformat(request): + df = None + if 'Accept' in request.headers: + if 'application/json' in request.headers['Accept']: + df = 'json' + elif 'text/xml' in request.headers['Accept']: + df = 'xml' + if df is None and 'Content-Type' in request.headers: + if 'application/json' in request.headers['Content-Type']: + df = 'json' + elif 'text/xml' in request.headers['Content-Type']: + df = 'xml' + return df if df else 'json' def signature(*args, **kwargs): - sig = wsme.sig(*args, **kwargs) + sig = wsme.signature(*args, **kwargs) def decorate(f): sig(f) funcdef = wsme.api.FunctionDefinition.get(f) + funcdef.resolve_types(wsme.types.registry) @functools.wraps(f) def callfunction(request): args, kwargs = combine_args( funcdef, - args_from_params(funcdef, request.params), - args_from_body(funcdef, request.body, request.content_type) + (args_from_params(funcdef, request.params), + args_from_body(funcdef, request.body, request.content_type)) ) - if 'application/json' in request.headers['Accept']: - request.override_renderer = 'wsmejson' - elif 'text/xml' in request.headers['Accept']: - request.override_renderer = 'wsmexml' - else: - request.override_renderer = 'wsmejson' + request.override_renderer = 'wsme' + get_outputformat(request) return { 'datatype': funcdef.return_type, 'result': f(*args, **kwargs)