wsme/wsmeext/tests/test_sqlalchemy_controllers.py
Chris Dent 8710dabb65 Improve Accept and Content-Type handling
Originally, if WSME received an Accept or Content-Type header that
was not aligned with what it was prepared to handle it would error
out with a 500 status code. This is not good behavior for a web
service.

In the process of trying to fix this it was discovered that the
content-negotiation code within WSME (the code that, in part,
looks for a suitable protocol handler for a request) and tests of
that code are incorrect, violating expected HTTP behaviors. GET
requests are passing Content-Type headers to declare the desired
type of representation in the response. This is what Accept is for.

Unfortunately the server-side code was perfectly willing to accept
this behavior. These changes correct that.

Closes-Bug: 1419110
Change-Id: I2b5c0075611490c047b27b1b43b0505fc5534b3b
2015-02-18 14:11:35 +00:00

224 lines
6.4 KiB
Python

import datetime
try:
import json
except ImportError:
import simplejson as json
from webtest import TestApp
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, Unicode, Date, ForeignKey
from sqlalchemy.orm import relation
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, scoped_session
from wsme import WSRoot
import wsme.types
from wsmeext.sqlalchemy.types import generate_types
from wsmeext.sqlalchemy.controllers import CRUDController
from six import u
engine = create_engine('sqlite:///')
DBSession = scoped_session(sessionmaker(autocommit=False, autoflush=False,
bind=engine))
DBBase = declarative_base()
registry = wsme.types.Registry()
class DBPerson(DBBase):
__tablename__ = 'person'
id = Column(Integer, primary_key=True)
name = Column(Unicode(50))
birthdate = Column(Date)
addresses = relation('DBAddress')
class DBAddress(DBBase):
__tablename__ = 'address'
id = Column(Integer, primary_key=True)
_person_id = Column('person_id', ForeignKey(DBPerson.id))
street = Column(Unicode(50))
city = Column(Unicode(50))
person = relation(DBPerson)
globals().update(
generate_types(DBPerson, DBAddress, makename=lambda s: s[2:],
registry=registry))
class PersonController(CRUDController):
__saclass__ = DBPerson
__dbsession__ = DBSession
__registry__ = registry
class AddressController(CRUDController):
__saclass__ = DBAddress
__dbsession__ = DBSession
__registry__ = registry
class Root(WSRoot):
__registry__ = registry
person = PersonController()
address = AddressController()
class TestCRUDController():
def setUp(self):
DBBase.metadata.create_all(DBSession.bind)
self.root = Root()
self.root.getapi()
self.root.addprotocol('restjson')
self.app = TestApp(self.root.wsgiapp())
def tearDown(self):
DBBase.metadata.drop_all(DBSession.bind)
def test_create(self):
data = dict(data=dict(
name=u('Pierre-Joseph'),
birthdate=u('1809-01-15')
))
r = self.app.post('/person/create', json.dumps(data),
headers={'Content-Type': 'application/json'})
r = json.loads(r.text)
print(r)
assert r['name'] == u('Pierre-Joseph')
assert r['birthdate'] == u('1809-01-15')
def test_PUT(self):
data = dict(data=dict(
name=u('Pierre-Joseph'),
birthdate=u('1809-01-15')
))
r = self.app.put('/person', json.dumps(data),
headers={'Content-Type': 'application/json'})
r = json.loads(r.text)
print(r)
assert r['name'] == u('Pierre-Joseph')
assert r['birthdate'] == u('1809-01-15')
def test_read(self):
p = DBPerson(
name=u('Pierre-Joseph'),
birthdate=datetime.date(1809, 1, 15))
DBSession.add(p)
DBSession.flush()
pid = p.id
r = self.app.post('/person/read', '{"ref": {"id": %s}}' % pid,
headers={'Content-Type': 'application/json'})
r = json.loads(r.text)
print(r)
assert r['name'] == u('Pierre-Joseph')
assert r['birthdate'] == u('1809-01-15')
def test_GET(self):
p = DBPerson(
name=u('Pierre-Joseph'),
birthdate=datetime.date(1809, 1, 15))
DBSession.add(p)
DBSession.flush()
pid = p.id
r = self.app.get('/person?ref.id=%s' % pid,
headers={'Accept': 'application/json'})
r = json.loads(r.text)
print(r)
assert r['name'] == u('Pierre-Joseph')
assert r['birthdate'] == u('1809-01-15')
def test_GET_bad_accept(self):
p = DBPerson(
name=u('Pierre-Joseph'),
birthdate=datetime.date(1809, 1, 15))
DBSession.add(p)
DBSession.flush()
pid = p.id
r = self.app.get('/person?ref.id=%s' % pid,
headers={'Accept': 'text/plain'},
status=406)
assert r.text == ("Unacceptable Accept type: text/plain not in "
"['application/json', 'text/javascript', "
"'application/javascript', 'text/xml']")
def test_update(self):
p = DBPerson(
name=u('Pierre-Joseph'),
birthdate=datetime.date(1809, 1, 15))
DBSession.add(p)
DBSession.flush()
pid = p.id
data = {
"id": pid,
"name": u('Pierre-Joseph Proudon')
}
r = self.app.post('/person/update', json.dumps(dict(data=data)),
headers={'Content-Type': 'application/json'})
r = json.loads(r.text)
print(r)
assert r['name'] == u('Pierre-Joseph Proudon')
assert r['birthdate'] == u('1809-01-15')
def test_POST(self):
p = DBPerson(
name=u('Pierre-Joseph'),
birthdate=datetime.date(1809, 1, 15))
DBSession.add(p)
DBSession.flush()
pid = p.id
data = {
"id": pid,
"name": u('Pierre-Joseph Proudon')
}
r = self.app.post('/person', json.dumps(dict(data=data)),
headers={'Content-Type': 'application/json'})
r = json.loads(r.text)
print(r)
assert r['name'] == u('Pierre-Joseph Proudon')
assert r['birthdate'] == u('1809-01-15')
def test_delete(self):
p = DBPerson(
name=u('Pierre-Joseph'),
birthdate=datetime.date(1809, 1, 15))
DBSession.add(p)
DBSession.flush()
pid = p.id
r = self.app.post('/person/delete', json.dumps(
dict(ref=dict(id=pid))),
headers={
'Content-Type': 'application/json'
})
print(r)
assert DBSession.query(DBPerson).get(pid) is None
def test_DELETE(self):
p = DBPerson(
name=u('Pierre-Joseph'),
birthdate=datetime.date(1809, 1, 15))
DBSession.add(p)
DBSession.flush()
pid = p.id
r = self.app.delete('/person?ref.id=%s' % pid,
headers={'Content-Type': 'application/json'})
print(r)
assert DBSession.query(DBPerson).get(pid) is None
def test_nothing(self):
pass