Commit a08b5e25 authored by Jacek Furmankiewicz's avatar Jacek Furmankiewicz

BDDs + fix for bizarre Twisted bug that was not parsing form params on PUT

parent 1b68675b
Using step definitions from: '../steps'
Feature: URL routing
CorePost should be able to
correctly route requests
depending on how the Resource instances
were registered
@single @single_get
Scenario: Single resource - GET
Given 'home_resource' is running
When as user 'None:None' I GET 'http://127.0.0.1:8080'
Then I expect HTTP code 200
And I expect content contains '{}'
When as user 'None:None' I GET 'http://127.0.0.1:8080/?test=value'
Then I expect HTTP code 200
And I expect content contains '{'test': 'value'}'
When as user 'None:None' I GET 'http://127.0.0.1:8080/test?query=test'
Then I expect HTTP code 200
And I expect content contains '{'query': 'test'}'
When as user 'None:None' I GET 'http://127.0.0.1:8080/test/23/resource/someid'
Then I expect HTTP code 200
And I expect content contains '23 - someid'
@single @single_post
Scenario: Single resource - POST
Given 'home_resource' is running
When as user 'None:None' I POST 'http://127.0.0.1:8080/post' with 'test=value&test2=value2'
Then I expect HTTP code 200
And I expect content contains '{'test': 'value', 'test2': 'value2'}'
@single @single_put
Scenario: Single resource - PUT
Given 'home_resource' is running
When as user 'None:None' I PUT 'http://127.0.0.1:8080/put' with 'test=value&test2=value2'
Then I expect HTTP code 200
And I expect content contains '{'test': 'value', 'test2': 'value2'}'
@single @single_delete
Scenario: Single resource - DELETE
Given 'home_resource' is running
When as user 'None:None' I DELETE 'http://127.0.0.1:8080/delete'
Then I expect HTTP code 200
@single @single_post @single_put
Scenario: Single resource - multiple methods at same URL
Given 'home_resource' is running
When as user 'None:None' I POST 'http://127.0.0.1:8080/postput' with 'test=value&test2=value2'
Then I expect HTTP code 200
And I expect content contains '{'test': 'value', 'test2': 'value2'}'
When as user 'None:None' I PUT 'http://127.0.0.1:8080/postput' with 'test=value&test2=value2'
Then I expect HTTP code 200
And I expect content contains '{'test': 'value', 'test2': 'value2'}'
\ No newline at end of file
......@@ -8,7 +8,6 @@ from corepost.enums import Http
from twisted.internet import defer
app = CorePost()
app.isLeaf = True
@app.route("/",Http.GET)
@defer.inlineCallbacks
......@@ -21,16 +20,25 @@ def root(request,**kwargs):
def test(request,**kwargs):
return "%s" % kwargs
@app.route("/test/<int:jacek>/yo/<someid>",Http.GET)
def test_get_resources(request,jacek,someid,**kwargs):
return "%s - %s" % (jacek,someid)
@app.route("/test/<int:numericid>/resource/<stringid>",Http.GET)
def test_get_resources(request,numericid,stringid,**kwargs):
return "%s - %s" % (numericid,stringid)
@app.route("/test",Http.POST)
@app.route("/post",(Http.POST,Http.PUT))
def test_post(request,**kwargs):
return "%s" % kwargs
if __name__ == '__main__':
# hookup submodule
#from submodule_test import submodule
app.putChild("submodule", submodule)
@app.route("/put",(Http.POST,Http.PUT))
def test_put(request,**kwargs):
return "%s" % kwargs
@app.route("/postput",(Http.POST,Http.PUT))
def test_postput(request,**kwargs):
return "%s" % kwargs
@app.route("/delete",Http.DELETE)
def test_delete(request,**kwargs):
return "%s" % kwargs
def run_app_home():
app.run()
\ No newline at end of file
......@@ -3,19 +3,33 @@ Common Freshen BDD steps
@author: jacekf
'''
import httplib2
from freshen import * #@UnusedWildImport
from multiprocessing import Process
import httplib2, json, re, time
from freshen import Before, After, Given, When, Then, scc, assert_equals, assert_true #@UnresolvedImport
from urllib import urlencode
from corepost.test.home_resource import run_app_home
apps = {'home_resource' : run_app_home}
def as_dict(parameters):
dict_val = {}
for pair in parameters.split('&') :
params = pair.split('=', 1)
if (params[0] != None) and (len(params) == 2):
dict_val[params[0]] = params[1]
return dict_val
##################################
# BEFORE / AFTER
##################################
@Before
def setup():
def setup(slc):
scc.processes = []
scc.http_headers = {}
@After
def cleanup():
def cleanup(slc):
# shut down processes
for process in scc.processes:
process.terminate()
......@@ -24,40 +38,60 @@ def cleanup():
# GIVEN
##################################
@Given("'(.+)' is running")
@Given(r"^'(.+)' is running\s*$")
def given_process_is_running(processname):
pass
process = Process(target=apps[processname])
process.daemon = True
process.start()
scc.processes.append(process)
time.sleep(0.25) # let it start up
##################################
# WHEN
##################################
@When("^as user '(.+):(.+)' I (GET|DELETE) '(.+)'\s*$")
def when_as_user_i_send_get_delete_to_url(user,passord,method,url):
pass
@When(r"^as user '(.+):(.+)' I (GET|DELETE) '(.+)'\s*$")
def when_as_user_i_send_get_delete_to_url(user,password,method,url):
h = httplib2.Http()
h.follow_redirects = False
h.add_credentials(user, password)
scc.response, scc.content = h.request(url, method)
@When("^as user '(.+):(.+)' I (POST|PUT) '(.+)' with '(.+)'\s*$")
@When(r"^as user '(.+):(.+)' I (POST|PUT) '(.+)' with '(.+)'\s*$")
def when_as_user_i_send_post_put_to_url(user,password,method,url,params):
pass
@When("^as user '(.+):(.+)' I (POST|PUT) '(.+)' (XML|JSON)\s*$")
def when_as_user_i_send_post_put_xml_json_to_url(payload,user,passord,method,url):
pass
h = httplib2.Http()
h.follow_redirects = False
h.add_credentials(user, password)
scc.http_headers['Content-type'] = 'application/x-www-form-urlencoded'
scc.response, scc.content = h.request(url, method, urlencode(as_dict(params)), headers = scc.http_headers)
@When(r"^as user '(.+):(.+)' I (POST|PUT) '(.+)' with (XML|JSON)\s*$")
def when_as_user_i_send_post_put_xml_json_to_url(payload,user,password,method,url,request_type):
h = httplib2.Http()
h.follow_redirects = False
h.add_credentials(user, password)
scc.http_headers['Content-type'] = 'application/json' if request_type == "JSON" else 'text/xml'
scc.response, scc.content = h.request(url, method, payload, headers = scc.http_headers)
##################################
# THEN
##################################
@Then("^I expect HTTP code \d+\s*$")
@Then(r"^I expect HTTP code (\d+)\s*$")
def expect_http_code(code):
pass
assert_equals(int(code),int(scc.response.status), msg="%s != %s\n%s\n%s" % (code,scc.response.status,scc.response,scc.content))
@Then(r"^I expect content contains '(.+)'\s*$")
def expect_content(content):
assert_true(scc.content.find(content) >= 0,"Did not find:\n%s\nin content:\n%s" % (content,scc.content))
@Then("^I expect content contains\s*$")
def expect_content(conntent):
pass
@Then(r"^I expect content contains\s*$")
def expect_content_multiline(content):
assert_true(scc.content.find(content) >= 0,"Did not find:\n%s\nin content:\n%s" % (content,scc.content))
@Then("^I expect header '(.+)' contains '(.+)'\s*$")
def expect_header_contains(conntent):
pass
@Then(r"^I expect '([^']*)' header matches '([^']*)'\s*$")
def then_check_http_header_matches(header,regex):
assert_true(re.search(regex,scc.response[header.lower()], re.X | re.I) != None,
"the regex %s does not match the response\n%s" % (regex, scc.response[header.lower()]))
......@@ -7,6 +7,7 @@ import re, copy
from twisted.internet import reactor, defer
from twisted.web.resource import Resource
from twisted.web.server import Site, NOT_DONE_YET
from twisted.web.http import parse_qs
from collections import defaultdict
from enums import MediaType
from corepost.enums import Http
......@@ -159,11 +160,17 @@ class CorePost(Resource):
#actual call
if urlrouter != None and pathargs != None:
allargs = copy.deepcopy(pathargs)
# handler for weird Twisted logic where PUT does not get form params
# see: http://twistedmatrix.com/pipermail/twisted-web/2007-March/003338.html
requestargs = request.args
if request.method == Http.PUT:
requestargs = parse_qs(request.content.read(), 1)
#merge form args
for arg in request.args.keys():
for arg in requestargs.keys():
# maintain first instance of an argument always
if arg not in allargs:
allargs[arg] = request.args[arg][0]
allargs[arg] = requestargs[arg][0]
# if POST/PUT, check if we need to automatically parse JSON
# TODO
......@@ -178,9 +185,6 @@ class CorePost(Resource):
return val
else:
# could be part of a submodule
return self.__renderError(request,404,"URL '%s' not found\n" % request.path)
def __renderError(self,request,code,message):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment