Skip to content

Commit

Permalink
Merge 9f0dff1 into 7ec6058
Browse files Browse the repository at this point in the history
  • Loading branch information
dpnova committed Sep 3, 2014
2 parents 7ec6058 + 9f0dff1 commit b093929
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 11 deletions.
2 changes: 1 addition & 1 deletion cyclone/escape.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
assert hasattr(json, "loads") and hasattr(json, "dumps")
_json_decode = json.loads
_json_encode = json.dumps
except Exception:
except Exception: # pragma: nocover
try:
import simplejson
_json_decode = lambda s: simplejson.loads(_unicode(s))
Expand Down
30 changes: 28 additions & 2 deletions cyclone/testing/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,29 @@
# under the License.

from cyclone.httpserver import HTTPRequest, HTTPConnection
from cyclone.web import decode_signed_value
from cyclone.httputil import HTTPHeaders
import urllib
from twisted.test import proto_helpers
from twisted.internet.defer import inlineCallbacks, returnValue
from Cookie import SimpleCookie

class DecodingSimpleCookie(SimpleCookie):
def __init__(self, app, *args, **kwargs):
self.app = app

def get_secure_cookie(self, name, value=None, max_age_days=31):
if value is None:
value = self[name].value
return decode_signed_value(
self.app.settings["cookie_secret"],
name, value, max_age_days=max_age_days)


class Client(object):
def __init__(self, app):
self.app = app
self.cookies = DecodingSimpleCookie(self.app)

def get(self, uri, params=None, version="HTTP/1.0", headers=None,
body=None, remote_ip=None, protocol=None, host=None,
Expand Down Expand Up @@ -79,7 +94,11 @@ def request(self, method, uri, *args, **kwargs):
connection.xheaders = False
kwargs['connection'] = connection
connection.factory = self.app

cookie_value = self.cookies.output(header="")
if cookie_value.strip():
if kwargs['headers'] is None:
kwargs['headers'] = {}
kwargs['headers']['Cookie'] = cookie_value.strip()
request = HTTPRequest(method, uri, *args, **kwargs)
connection.connectionMade()
connection._request = request
Expand All @@ -88,9 +107,16 @@ def request(self, method, uri, *args, **kwargs):
handler = self.app(request)

def setup_response():
headers = HTTPHeaders()
for line in handler._generate_headers().split("\r\n"):
if line.startswith("HTTP") or not line.strip():
continue
headers.parse_line(line)
for cookie in headers.get_list("Set-Cookie"):
self.cookies.load(cookie)
response_body = connection.transport.io.getvalue()
handler.content = response_body.split("\r\n\r\n", 1)[1]
handler.headers = handler._headers
handler.headers = headers

if handler._finished:
setup_response()
Expand Down
16 changes: 14 additions & 2 deletions cyclone/testing/testcase.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@

class CycloneTestCase(unittest.TestCase, object):
client_impl = Client
app_builder = None

def __init__(self, app_builder, *args, **kwargs):
def __init__(self, *args, **kwargs):
"""
Create a test case for a cyclone app.
Expand All @@ -32,6 +33,17 @@ def __init__(self, app_builder, *args, **kwargs):
that returns you application instead of just declaring it in a file
somewhere.
"""
app_builder = None
if "app_builder" in kwargs:
app_builder = kwargs.pop("app_builder")
if not app_builder and not self.app_builder:
raise ValueError(
"You need to either pass an app_builder named param to "
"__init__ or set the app_builder attribute on your test case. "
"it should be a callable that returns an app instance for "
"your app. The Application class for your project may work."
)
super(CycloneTestCase, self).__init__(*args, **kwargs)
self._app = app_builder()
builder = app_builder or self.app_builder
self._app = builder()
self.client = self.client_impl(self._app)
65 changes: 65 additions & 0 deletions cyclone/tests/test_db.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#
# Copyright 2014 David Novakovic
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

from twisted.trial import unittest
from cyclone.sqlite import InlineSQLite
from mock import Mock


class InlineSQLiteTest(unittest.TestCase):
def setUp(self):
self.isq = InlineSQLite()
self.isq.runOperation(
"create table nothing (val1 string, val2 string)")
self.isq.runOperation('insert into nothing values ("a", "b")')

def test_init(self):
self.assertTrue(hasattr(self.isq, "autoCommit"))
self.assertTrue(hasattr(self.isq, "conn"))
self.assertTrue(hasattr(self.isq, "curs"))

def test_runQuery(self):
self.isq.curs = Mock()
self.isq.curs.__iter__ = Mock(return_value=iter([1, 2, 3]))
res = self.isq.runQuery("a query")
self.assertEqual(res, [1, 2, 3])

def test_runOperation(self):
self.isq.runOperation('insert into nothing values ("c", "d")')
res = self.isq.runQuery("select count(*) from nothing")
self.assertEqual(res[0][0], 2)

def test_runOperationMany(self):
self.isq.runOperationMany(
'insert into nothing values (?, ?)',
[["a", "b"], ["c", "d"]]
)
res = self.isq.runQuery("select count(*) from nothing")
self.assertEqual(res[0][0], 3)

def test_commit(self):
self.isq.conn = Mock()
self.isq.commit()
self.isq.conn.commit.assert_called_with()

def test_rollback(self):
self.isq.conn = Mock()
self.isq.rollback()
self.isq.conn.rollback.assert_called_with()

def test_close(self):
self.isq.conn = Mock()
self.isq.close()
self.isq.conn.close.assert_called_with()
2 changes: 1 addition & 1 deletion cyclone/tests/test_httpserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ def test_request_time_empty_finish(self):
self.assertTrue(self.req.request_time() < 0.01)

def test_request_time(self):
self.assertTrue(self.req.request_time() <0.01)
self.assertTrue(self.req.request_time() < 0.01)

def test_repr(self):
"""
Expand Down
29 changes: 26 additions & 3 deletions cyclone/tests/test_testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,28 @@ def do_something(self):
self.finish()


class CookieTestHandler(RequestHandler):
def get(self):
self.set_secure_cookie("test_cookie", "test_value")
self.finish()

def post(self):
value = self.get_secure_cookie("test_cookie")
self.finish(value)


def mock_app_builder():
return Application([
(r'/testing/', TestHandler),
(r'/deferred_testing/', DeferredTestHandler)
])
(r'/deferred_testing/', DeferredTestHandler),
(r'/cookie_testing/', CookieTestHandler),
], cookie_secret="insecure")


class TestTestCase(unittest.TestCase):
def test_create(self):
case = CycloneTestCase(mock_app_builder)
self.assertRaises(ValueError, CycloneTestCase, mock_app_builder)
case = CycloneTestCase(app_builder=mock_app_builder)
self.assertTrue(case._app)
self.assertTrue(case.client)

Expand Down Expand Up @@ -113,3 +125,14 @@ def test_get_deferred_request(self):
response = yield self.client.get("/deferred_testing/")
self.assertEqual(response.content, "Something...done!")
self.assertTrue(len(response.headers) > 3)

@inlineCallbacks
def test_cookies(self):
response = yield self.client.get("/cookie_testing/")
self.assertEqual(
self.client.cookies.get_secure_cookie("test_cookie"),
"test_value"
)

response = yield self.client.post("/cookie_testing/")
self.assertEqual(response.content, "test_value")
58 changes: 57 additions & 1 deletion cyclone/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@
from cyclone.escape import json_encode, json_decode
from cyclone.escape import squeeze, url_escape, url_unescape
from cyclone.escape import utf8, to_unicode, to_basestring
from cyclone.escape import recursive_unicode, linkify
from cyclone.escape import recursive_unicode, linkify, _convert_entity
from cyclone.util import _emit, ObjectDict, import_object
from mock import Mock
import datetime


class EscapeTest(unittest.TestCase):
Expand Down Expand Up @@ -100,3 +103,56 @@ def test_linkify(self):
"alongurlrighthere"
"/a/long/url/right/here",
shorten=True, require_protocol=True, extra_params=lambda x: "x=y")

def test_convert_entity(self):
"""
A bit hacky for now. Gets things convered though.
"""
m = Mock()
_convert_entity(m)
m.group.return_value = "#"
_convert_entity(m)


class UtilsTest(unittest.TestCase):
def test_emit(self):
m = Mock()
t = datetime.time()
_emit(m, {
"message": "some message",
"time": t
})
m.formatTime.assert_called_with(t)

def test_emit_empty(self):
m = Mock()
t = datetime.time()
failure = Mock()
failure.getTraceback.return_value = ""
_emit(m, {
"message": "",
"time": t,
"isError": "",
"failure": failure
})
self.assertEqual(m.formatTime.call_count, 0)

def test_object_dict(self):
od = ObjectDict()
self.assertRaises(AttributeError, getattr, od, "something")
od["foo"] = "bar"
self.assertEqual(od['foo'], "bar")
self.assertEqual(od.foo, "bar")
od.rah = "meow"
self.assertEqual(od['rah'], "meow")

def test_import_object(self):
import os.path
other_os = import_object("os.path")
self.assertIs(os.path, other_os)

def test_import_object_fail(self):
self.assertRaises(ImportError, import_object, "meowkittens.something")

def test_import_object_fail_no_method(self):
self.assertRaises(ImportError, import_object, "os.something")
2 changes: 1 addition & 1 deletion cyclone/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,6 @@ def import_object(name):
basestring_type = basestring


def doctests():
def doctests(): # pragma: no cover
import doctest
return doctest.DocTestSuite()

0 comments on commit b093929

Please sign in to comment.