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 ...@@ -8,7 +8,6 @@ from corepost.enums import Http
from twisted.internet import defer from twisted.internet import defer
app = CorePost() app = CorePost()
app.isLeaf = True
@app.route("/",Http.GET) @app.route("/",Http.GET)
@defer.inlineCallbacks @defer.inlineCallbacks
...@@ -21,16 +20,25 @@ def root(request,**kwargs): ...@@ -21,16 +20,25 @@ def root(request,**kwargs):
def test(request,**kwargs): def test(request,**kwargs):
return "%s" % kwargs return "%s" % kwargs
@app.route("/test/<int:jacek>/yo/<someid>",Http.GET) @app.route("/test/<int:numericid>/resource/<stringid>",Http.GET)
def test_get_resources(request,jacek,someid,**kwargs): def test_get_resources(request,numericid,stringid,**kwargs):
return "%s - %s" % (jacek,someid) return "%s - %s" % (numericid,stringid)
@app.route("/test",Http.POST) @app.route("/post",(Http.POST,Http.PUT))
def test_post(request,**kwargs): def test_post(request,**kwargs):
return "%s" % kwargs return "%s" % kwargs
if __name__ == '__main__': @app.route("/put",(Http.POST,Http.PUT))
# hookup submodule def test_put(request,**kwargs):
#from submodule_test import submodule return "%s" % kwargs
app.putChild("submodule", submodule)
@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() app.run()
\ No newline at end of file
...@@ -3,19 +3,33 @@ Common Freshen BDD steps ...@@ -3,19 +3,33 @@ Common Freshen BDD steps
@author: jacekf @author: jacekf
''' '''
import httplib2 from multiprocessing import Process
from freshen import * #@UnusedWildImport 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 / AFTER
################################## ##################################
@Before @Before
def setup(): def setup(slc):
scc.processes = [] scc.processes = []
scc.http_headers = {}
@After @After
def cleanup(): def cleanup(slc):
# shut down processes # shut down processes
for process in scc.processes: for process in scc.processes:
process.terminate() process.terminate()
...@@ -24,40 +38,60 @@ def cleanup(): ...@@ -24,40 +38,60 @@ def cleanup():
# GIVEN # GIVEN
################################## ##################################
@Given("'(.+)' is running") @Given(r"^'(.+)' is running\s*$")
def given_process_is_running(processname): 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
################################## ##################################
@When("^as user '(.+):(.+)' I (GET|DELETE) '(.+)'\s*$") @When(r"^as user '(.+):(.+)' I (GET|DELETE) '(.+)'\s*$")
def when_as_user_i_send_get_delete_to_url(user,passord,method,url): def when_as_user_i_send_get_delete_to_url(user,password,method,url):
pass 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): def when_as_user_i_send_post_put_to_url(user,password,method,url,params):
pass h = httplib2.Http()
h.follow_redirects = False
@When("^as user '(.+):(.+)' I (POST|PUT) '(.+)' (XML|JSON)\s*$") h.add_credentials(user, password)
def when_as_user_i_send_post_put_xml_json_to_url(payload,user,passord,method,url): scc.http_headers['Content-type'] = 'application/x-www-form-urlencoded'
pass 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
################################## ##################################
@Then("^I expect HTTP code \d+\s*$") @Then(r"^I expect HTTP code (\d+)\s*$")
def expect_http_code(code): 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*$") @Then(r"^I expect content contains\s*$")
def expect_content(conntent): def expect_content_multiline(content):
pass 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*$") @Then(r"^I expect '([^']*)' header matches '([^']*)'\s*$")
def expect_header_contains(conntent): def then_check_http_header_matches(header,regex):
pass 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 ...@@ -7,6 +7,7 @@ import re, copy
from twisted.internet import reactor, defer from twisted.internet import reactor, defer
from twisted.web.resource import Resource from twisted.web.resource import Resource
from twisted.web.server import Site, NOT_DONE_YET from twisted.web.server import Site, NOT_DONE_YET
from twisted.web.http import parse_qs
from collections import defaultdict from collections import defaultdict
from enums import MediaType from enums import MediaType
from corepost.enums import Http from corepost.enums import Http
...@@ -159,11 +160,17 @@ class CorePost(Resource): ...@@ -159,11 +160,17 @@ class CorePost(Resource):
#actual call #actual call
if urlrouter != None and pathargs != None: if urlrouter != None and pathargs != None:
allargs = copy.deepcopy(pathargs) 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 #merge form args
for arg in request.args.keys(): for arg in requestargs.keys():
# maintain first instance of an argument always # maintain first instance of an argument always
if arg not in allargs: 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 # if POST/PUT, check if we need to automatically parse JSON
# TODO # TODO
...@@ -178,9 +185,6 @@ class CorePost(Resource): ...@@ -178,9 +185,6 @@ class CorePost(Resource):
return val return val
else: else:
# could be part of a submodule
return self.__renderError(request,404,"URL '%s' not found\n" % request.path) return self.__renderError(request,404,"URL '%s' not found\n" % request.path)
def __renderError(self,request,code,message): 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