diff --git a/.gitignore b/.gitignore index 8b3ffe1..ed4f90e 100644 --- a/.gitignore +++ b/.gitignore @@ -32,4 +32,6 @@ nosetests.xml # Idea .idea +# System +.DS_Store diff --git a/falcon/api.py b/falcon/api.py index 22e41e1..33d07a8 100644 --- a/falcon/api.py +++ b/falcon/api.py @@ -12,18 +12,21 @@ class Api: def __call__(self, environ, start_response): # PERF: Use literal constructor for dicts - ctx, req, resp = {}, {}, {} + # PERF: Don't use multi-assignment + ctx = {} + req = {} + resp = {} # TODO: What other things does req need? req['path'] = path = environ['PATH_INFO'] - # TODO + # TODO # ctx.update(global_ctx_for_route) - # PERF: Use try...except blocks when errors are rare (otherwise use in) + # PERF: Use try...except blocks when the key usually exists try: - # TODO: Figure out a way to use codegen to make a state machine, + # TODO: Figure out a way to use codegen to make a state machine, # may have to in order to support URI templates. handler = self.routes[path] except KeyError: @@ -42,7 +45,7 @@ class Api: pass # PERF: Can't predict ratio of empty body to nonempty, so use - # "in" which is a good all-around performer. + # "in" which is a good all-around performer. return [resp['body']] if 'body' in resp else [] def add_route(self, uri_template, handler): diff --git a/test/helpers.py b/test/helpers.py index e93612e..e56da75 100644 --- a/test/helpers.py +++ b/test/helpers.py @@ -1,9 +1,14 @@ +import inspect +import testtools + +import falcon + class StartResponseMock: def __init__(self): self._called = 0 self.status = None self.headers = None - + def __call__(self, status, headers): self._called += 1 self.status = status @@ -12,6 +17,20 @@ class StartResponseMock: def call_count(self): return self._called +class TestSuite(testtools.TestCase): + + def setUp(self): + super(TestSuite, self).setUp() + self.api = falcon.Api() + self.srmock = StartResponseMock() + self.test_route = '/' + self.getUniqueString() + + prepare = getattr(self, 'prepare', None) + if hasattr(prepare, '__call__'): + prepare() + + def _simulate_request(self, path): + self.api(create_environ(path), self.srmock) def create_environ(path='/', query_string=''): return { diff --git a/test/test_headers.py b/test/test_headers.py index ea537b0..5b6985d 100644 --- a/test/test_headers.py +++ b/test/test_headers.py @@ -23,21 +23,11 @@ class RequestHandler: resp['status'] = falcon.HTTP_200 resp['body'] = self.sample_body -class TestHeaders(testtools.TestCase): - - # TODO: Figure out a way to DRY this statement - maybe via subclassing - def setUp(self): - super(TestHeaders, self).setUp() - self.api = falcon.Api() - self.srmock = helpers.StartResponseMock() - self.test_route = '/' + self.getUniqueString() +class TestHeaders(helpers.TestSuite): + def prepare(self): self.on_hello = RequestHandler() self.api.add_route(self.test_route, self.on_hello) - def test_auto_headers(self): - # TODO: Figure out a way to DRY this statement - self.api(helpers.create_environ(self.test_route), self.srmock) - - pass \ No newline at end of file + self._simulate_request(self.test_route) diff --git a/test/test_hello.py b/test/test_hello.py index 4d12e83..4178d87 100644 --- a/test/test_hello.py +++ b/test/test_hello.py @@ -20,28 +20,22 @@ class HelloRequestHandler: resp['body'] = self.sample_body -class TestHelloWorld(testtools.TestCase): - - def setUp(self): - super(TestHelloWorld, self).setUp() - self.api = falcon.Api() - self.start_resp = helpers.StartResponseMock() - self.test_route = '/hello' +class TestHelloWorld(helpers.TestSuite): + def prepare(self): self.on_hello = HelloRequestHandler() self.api.add_route(self.test_route, self.on_hello) def test_hello_route_negative(self): bogus_route = self.test_route + 'x' - self.api(helpers.create_environ(bogus_route), self.start_resp) + self._simulate_request(bogus_route) # Ensure the request was NOT routed to on_hello self.assertFalse(self.on_hello.called) - self.assertThat(self.start_resp.status, Equals(falcon.HTTP_404)) + self.assertThat(self.srmock.status, Equals(falcon.HTTP_404)) def test_hello_route(self): - # Simulate a request to the attached route - self.api(helpers.create_environ(self.test_route), self.start_resp) + self._simulate_request(self.test_route) resp = self.on_hello.resp self.assertTrue('status' in resp) @@ -49,20 +43,3 @@ class TestHelloWorld(testtools.TestCase): self.assertTrue('body' in resp) self.assertThat(resp['body'], Equals(self.on_hello.sample_body)) - - # TODO: Test compiling routes, throwing on invalid routes (such as missing initial forward slash or non-ascii) - # TODO: Test setting the body to a stream, rather than a string (and content-length set to chunked?) - # TODO: Test custom error handlers - customizing error document at least - # TODO: Test async middleware ala rproxy - # TODO: Test setting different routes for different verbs - # TODO: Test throwing an exception from within a handler - # TODO: Test neglecting to set a body - # TODO: Test neglecting to set a status - # TODO: Test passing bad arguments to add_route - # TODO: Test other kinds of routes - empty, root, multiple levels - # TODO: Test URI-template parsing (precompile) - # TODO: Test passing a shared dict to each mock call (e.g., db connections, config) - # ...and that it is passed to the request handler correctly - # TODO: Test pre/post filters - # TODO: Test error handling with standard response (for all error classes?) - pass diff --git a/test/todo.md b/test/todo.md new file mode 100644 index 0000000..bcea073 --- /dev/null +++ b/test/todo.md @@ -0,0 +1,17 @@ + +* Test sending/receiving various status codes +* Test compiling routes, throwing on invalid routes (such as missing initial forward slash or non-ascii) +* Test setting the body to a stream, rather than a string (and content-length set to chunked?) +* Test custom error handlers - customizing error document at least +* Test async middleware ala rproxy +* Test setting different routes for different verbs +* Test throwing an exception from within a handler +* Test neglecting to set a body +* Test neglecting to set a status +* Test passing bad arguments to add_route +* Test other kinds of routes - empty, root, multiple levels +* Test URI-template parsing (precompile) +* Test passing a shared dict to each mock call (e.g., db connections, config)...and that it is passed to the request handler correctly +* Test pre/post filters +* Test error handling with standard response (for all error classes?) +