import re import httplib import cStringIO as StringIO import mimetools from turbogears import testutil, validate import cherrypy import cElementTree as et from tgwebservices.controllers import WebServicesRoot, WebServicesController, \ wsexpose, wsvalidate from tgwebservices import soap from tgwebservices.tests.fixtures import * from tgwebservices import runtime def run_soap(method, params=""): request = """ <%(method)s> %(params)s """ % dict(method=method, params=params) testutil.create_request("/soap/", rfile=StringIO.StringIO(request), method="POST", headers={"Content-Length" : str(len(request)), "Content-Type" : "application/soap+xml; charset=utf-8"}) output = cherrypy.response.body[0] return output def get_wsdl(): testutil.create_request("/soap/api.wsdl") output = cherrypy.response.body[0] print output return output def test_basic_call(): cherrypy.root = MyService("http://foo.bar.baz/") output = run_soap("times2", "5") print output root = et.fromstring(output) result_elements = list(root.findall(".//{http://foo.bar.baz/soap/types}result")) assert len(result_elements) == 1, "Should only have one result" elem = result_elements[0] print "result:", elem assert elem.attrib["{http://www.w3.org/2001/XMLSchema-instance}type"] == \ "xsd:int", "Expected result to be labeled an int" assert elem.text == "10", "Expected result to be 10" def test_wsdl_with_ints(): cherrypy.root = MyService("http://foo.bar.baz/") testutil.create_request("/soap/api.wsdl") output = cherrypy.response.body[0] print output assert cherrypy.response.headers["Content-Type"] == "text/xml;charset=utf-8" root = et.fromstring(output) schema_elements = root.findall(".//{http://www.w3.org/2001/XMLSchema}element") found = False for elem in schema_elements: print "FE:", elem if "name" in elem.attrib and elem.attrib["name"] == "times2Response": found = True interior_elem = elem[0][0][0] print "response value: %s (%s)" % (interior_elem, interior_elem.attrib) assert elem[0][0][0].attrib["type"] == "xsd:int" assert found, "Should have found a definition for times2Response" def test_wsdl_with_complex(): cherrypy.root = ComplexService("http://foo.bar.baz/") testutil.create_request("/soap/api.wsdl") output = cherrypy.response.body[0] print output root = et.fromstring(output) schema_elements = root.findall( ".//{http://www.w3.org/2001/XMLSchema}element") found = False for elem in schema_elements: if "name" in elem.attrib and elem.attrib["name"] == \ "getfancyResponse": found = True interior_elem = elem[0][0][0] print "response value: %s (%s)" % (interior_elem, interior_elem.attrib) assert elem[0][0][0].attrib["type"] == "types:FancyValue" complex_types = root.findall( ".//{http://www.w3.org/2001/XMLSchema}complexType") assert found, "Should have found a definition for fancyResponse" found = False found_array = False for elem in complex_types: if "name" in elem.attrib and elem.attrib["name"] == \ "FancyValue": found = True objattributes = elem[0].getchildren() found_name = False found_age = False found_computed = False for attr in objattributes: if attr.attrib["name"] == "name": assert attr.attrib["type"] == "xsd:string" found_name = True elif attr.attrib["name"] == "age": assert attr.attrib["type"] == "xsd:int" found_age = True elif attr.attrib["name"] == "computed": assert attr.attrib["type"] == "xsd:string" found_computed = True else: assert False, "Found a %s element which shouldn't "\ "be there" % (attr.attrib["name"]) assert found_name, "Did not find name element" assert found_age, "Did not find age element" assert found_computed, "Did not find computed element" elif "name" in elem.attrib and elem.attrib["name"] == \ "FoodItem_Array": found_array = True assert found, "Should have found a delcaration for FancyValue" assert found_array, "Should have found the FoodItem_Array" def test_soap_array(): cherrypy.root = ComplexService("http://foo.bar.baz/") testutil.create_request("/soap/api.wsdl") output = cherrypy.response.body[0] print output root = et.fromstring(output) schema_elements = root.findall( ".//{http://www.w3.org/2001/XMLSchema}complexType") found = False stringarray_found = False for elem in schema_elements: if "name" in elem.attrib and elem.attrib["name"] == \ "FancyValue_Array": found = True interior_elem = elem[0] assert interior_elem.tag == \ "{http://www.w3.org/2001/XMLSchema}sequence", \ "Expected to find a sequence in FancyValue_Array" interior_elem = interior_elem[0] assert interior_elem.tag == \ "{http://www.w3.org/2001/XMLSchema}element", \ "Expected to find element in FancyValue_Array" print interior_elem.attrib assert interior_elem.attrib["maxOccurs"] == \ "unbounded", "expected unbounded array" assert interior_elem.attrib["nillable"] == \ "true", "Array should be nillable" assert interior_elem.attrib["type"] == \ "types:FancyValue", \ "Should have found array type declaration of FancyValue" if "name" in elem.attrib and elem.attrib["name"] == \ "String_Array": stringarray_found = True interior_elem = elem[0][0] assert interior_elem.tag == \ "{http://www.w3.org/2001/XMLSchema}element", \ "Expected to find element in String_Array" print interior_elem.attrib assert interior_elem.attrib["type"] == \ "xsd:string", \ "Should have found string type for String_Array" assert found, "Expected to find the FancyValiue_Array type" assert stringarray_found, "Expected to find the String_Array type" output = run_soap("somestrings") root = et.fromstring(output) print "SOAP somestrings call output:", output result_elements = list(root.findall(".//{http://foo.bar.baz/soap/types}result")) assert len(result_elements) == 1 items = result_elements[0].getchildren() assert len(items) == 3, "Expected three items in the array" assert items[0].text == "A" assert items[1].text == "B" assert items[2].text == "C" def test_nested_complex(): cherrypy.root = ComplexService("http://foo.bar.baz/") testutil.create_request("/soap/api.wsdl") output = cherrypy.response.body[0] print "WSDL:\n\n" print output root = et.fromstring(output) schema_elements = root.findall( ".//{http://www.w3.org/2001/XMLSchema}complexType") found = False for elem in schema_elements: if "name" in elem.attrib and elem.attrib["name"] == "FoodOrder": found = True elements = elem.getchildren()[0].getchildren() assert len(elements) == 2 foundarray = False for e in elements: if e.attrib["type"] == "types:FoodItem_Array": foundarray = True assert foundarray, "Should have found array of FoodItems" assert found, "Should have found FoodOrder type" output = run_soap("getorder") print "\n\nSOAP:\n\n" print output assert '' in output root = et.fromstring(output) persontags = root.findall(".//{http://foo.bar.baz/soap/types}person") assert len(persontags) > 0 assert FoodItem._type_dependents == 1 class LocalObject(object): name = "" class LocalController(WebServicesRoot): @wsexpose(LocalObject) def getobj(self): return LocalObject() def test_complex_types_stay_in_one_controller(): cherrypy.root = LocalController("http://foo.bar.baz") testutil.create_request("/soap/api.wsdl") output = cherrypy.response.body[0] print "WSDL:\n\n" print output root = et.fromstring(output) schema_elements = root.findall( ".//{http://www.w3.org/2001/XMLSchema}complexType") for elem in schema_elements: assert not ("name" in elem.attrib and elem.attrib["name"] == "FoodOrder"), \ "Complex type definitions should be limited to controllers that use them" def test_nested_controllers(): cherrypy.root = OuterService("http://foo.bar.baz") testutil.create_request("/soap/api.wsdl") output = cherrypy.response.body[0] print "WSDL:\n\n" print output root = et.fromstring(output) schema_elements = root.findall( ".//{http://www.w3.org/2001/XMLSchema}complexType") found = False for elem in schema_elements: if "name" in elem.attrib and elem.attrib["name"] == \ "FancyValue": found = True assert found, "Should have found FancyValue from the nested controller" operations = set(["ultimatequestion", "innerGetFancy", "innerFooGetFancy", "anotherGetFancy"]) porttype = root.findall(".//{http://schemas.xmlsoap.org/wsdl/}portType")[0] op_elements = porttype.findall( ".//{http://schemas.xmlsoap.org/wsdl/}operation") for elem in op_elements: name = elem.attrib["name"] assert name in operations, "Found unexpected op %s in WSDL" % (name) operations.remove(name) assert not operations, "Did not find these ops: %s" % (operations) def test_call_to_nested_controller(): cherrypy.root = OuterService("http://foo.bar.baz") output = run_soap("innerFooGetFancy") print output assert """42Hello!Mr. Bean""" in output def test_documentation_strings(): cherrypy.root = MyService("http://foo.bar.baz") testutil.create_request("/soap/api.wsdl") output = cherrypy.response.body[0] print "WSDL:\n\n" print output root = et.fromstring(output) porttype = root.findall(".//{http://schemas.xmlsoap.org/wsdl/}portType")[0] operation = porttype.getchildren()[0] assert operation.attrib["name"] == "times2" docelems = operation.findall( ".//{http://schemas.xmlsoap.org/wsdl/}documentation") assert len(docelems) == 1 assert docelems[0].text == "Multiplies value by two." def test_fault_for_invalid_input(): cherrypy.root = MyService("http://foo.bar.baz") output = run_soap("times2", """whaleblubber""") print cherrypy.response.status print output assert cherrypy.response.status == "500 Invalid Input" assert output == """Clientwhaleblubber value for the 'value' parameter is not a valid int""" def test_fault_for_exception(): cherrypy.root = MyService("http://foo.bar.baz") output = run_soap("twentyover", """0""") print cherrypy.response.status assert cherrypy.response.status == "500 Internal Server Error" print output assert "SOAP method: twentyover" in output assert "foo.bar.baz/soap/" in output, "Raw request should be in the fault" assert "return 20 / value" in output, "Traceback should be in fault" assert "ZeroDivisionError" in output, "error should be in fault" def test_boolean_type(): elem = soap.soap_value("test", bool, True) output = str(elem) print output assert output == 'true' def test_big_number(): elem = soap.soap_value("test", int, 3063485326L) output = str(elem) assert output == '3063485326' def test_empty_list(): cherrypy.root = ComplexService("http://foo.bar.baz") output = run_soap("getempty") print output assert "fault" not in output assert '' in output def test_too_many_arguments(): output = run_soap("somestrings", "value") print output root = et.fromstring(output) fc = root.findall(".//{http://schemas.xmlsoap.org/soap/envelope/}faultcode") assert len(fc) == 1 fc = fc[0] print "Fault code: ", fc.text assert fc.text == "Client" def test_bogus_function_gives_client_fault(): output = run_soap("nonexistentfunction") print output root = et.fromstring(output) fc = root.findall(".//{http://schemas.xmlsoap.org/soap/envelope/}faultcode") assert len(fc) == 1 fc = fc[0] print "Fault code: ", fc.text assert fc.text == "Client" def test_subclass_complex_types_should_appear_in_wsdl(): output = get_wsdl() root = et.fromstring(output) schema_elements = root.findall( ".//{http://www.w3.org/2001/XMLSchema}complexType") foundperson = False for elem in schema_elements: if "name" in elem.attrib and elem.attrib["name"] == "SubFood": for child in elem.getchildren()[0].getchildren(): if child.attrib["name"] == "person": foundperson = True print "SubFood person type is %s" % child.attrib["type"] assert child.attrib["type"] == "types:FancyValue", \ "SubFood person should be a FancyValue" assert foundperson, "Should have found the person attribute on SubFood" def test_unsigned_integer(): output = get_wsdl() root = et.fromstring(output) schema_elements = root.findall( ".//{http://www.w3.org/2001/XMLSchema}complexType") founditem = False for elem in schema_elements: if "name" in elem.attrib and elem.attrib["name"] == "FoodItem": for child in elem.getchildren()[0].getchildren(): if child.attrib["name"] == "quantity": founditem = True print "FoodItem quantity type is %s" % child.attrib["type"] assert child.attrib["type"] == "xsd:unsignedInt" assert founditem, "Should have found FoodItem's quantity field" elem = soap.soap_value("quantity", runtime.unsigned, 5) assert str(elem) == '5' class NamespacedRoot(WebServicesRoot): @wsexpose(str) def world(self): return "Hello!" def test_explicit_namespace(): cherrypy.root = NamespacedRoot("https://foo.bar.baz/", "http://foo.bar/service/URI/") output = get_wsdl() print output assert 'targetNamespace="http://foo.bar/service/URI/"' in output assert 'xmlns:types="http://foo.bar/service/URI/types"' in output class OptionalParams(WebServicesRoot): @wsexpose(int) @wsvalidate(int, int, int) def addem(self, num1, num2=2, num3=3): return num1+num2+num3 def test_optional(): cherrypy.root = OptionalParams("https://foo.bar.baz") output = get_wsdl() print output root = et.fromstring(output) schema_elements = root.findall( ".//{http://www.w3.org/2001/XMLSchema}element") for elem in schema_elements: if elem.attrib.get("name", None) == "addem": complextype = elem.getchildren()[0] sequence = complextype.getchildren()[0] num1 = sequence.getchildren()[0] assert num1.attrib["name"] == "num1" assert "minOccurs" not in num1.attrib num2 = sequence.getchildren()[1] assert num2.attrib["name"] == "num2" assert num2.attrib["minOccurs"] == "0" num3 = sequence.getchildren()[2] assert num3.attrib["name"] == "num3" assert num3.attrib["minOccurs"] == "0" class StringRoot(WebServicesRoot): @wsexpose(str) @wsvalidate(str) def say_hello(self, name): assert name is not None assert name != "None" return "Hi, %s" % name def test_empty_parameter_is_empty_string(): cherrypy.root = StringRoot("http://foo.bar.baz/") output = run_soap("say_hello", """""") print output assert "fault" not in output class Person(object): name = str age = int class ComplexInput(WebServicesRoot): @wsexpose() @wsvalidate(Person) def savePerson(self, p): self.person = p def test_complex_input_simple_object(): ci = ComplexInput("http://foo.bar.baz/") cherrypy.root = ci output = run_soap("savePerson", """

George 280

""") print output assert isinstance(ci.person, Person)