Skip to content

Instantly share code, notes, and snippets.

@maiksprenger
Created February 6, 2017 13:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save maiksprenger/3e4fd1a5aefe4e53fa48868f222e07f5 to your computer and use it in GitHub Desktop.
Save maiksprenger/3e4fd1a5aefe4e53fa48868f222e07f5 to your computer and use it in GitHub Desktop.
from urllib.parse import urlencode
from hypothesis import strategies
HTTP_METHODS = ['OPTIONS', 'GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'TRACE', 'CONNECT']
letters = strategies.characters(whitelist_categories=('Lu', 'Ll', 'Lt', 'Lm', 'Lo', 'Nl'))
def http_method():
return strategies.sampled_from(HTTP_METHODS)
def http_path():
"""
Returns a URL path. It only ensures that it starts with a slash.
It's too broad because it may return any sequence of unicode characters, but it
suffices for our needs.
"""
return strategies.text(min_size=0, max_size=255, alphabet=letters).map(lambda s: '/' + s)
def reject_bidi_violations(host):
try:
host.encode('idna')
except UnicodeError:
return False
else:
return True
def _domain():
return strategies.text(alphabet=letters, min_size=1, average_size=6, max_size=63).filter(reject_bidi_violations)
def http_host():
"""
Returns an string of a domain name, IDNA-encoded.
"""
return strategies.lists(_domain(), min_size=2, average_size=2).map(lambda s: ('.'.join(s)).encode('idna'))
def query_param():
return strategies.text(alphabet=letters, min_size=1, average_size=10, max_size=255)
def query_params():
"""
Returns a list of two-tuples, ready for encoding with urlencode.
It ensures that the total urlencoded query string is not longer than 1500 characters. We're aiming
for a total length of the URL below 2083 characters.
"""
return strategies.lists(
strategies.tuples(query_param(), query_param()), min_size=0, average_size=20).\
filter(lambda x: len(urlencode(x)) < 1500)
@given(
request_method=http_strategies.http_method(),
request_host=http_strategies.http_host(),
request_path=http_strategies.http_path(),
request_params=http_strategies.query_params())
def test_basic_request(self, request_method, request_host, request_path, request_params):
request = self.build_request(request_method, request_path, host=request_host, params=request_params)
message = self.message_for_request(request)
assert message
assert 'reply_channel' in message
reply_channel = message['reply_channel']
assert isinstance(reply_channel, six.text_type)
assert reply_channel.startswith('http.response!')
assert 'http_version' in message
http_version = message['http_version']
assert isinstance(http_version, six.text_type)
assert http_version in ['1.0', '1.1', '1.2']
assert 'method' in message
method = message['method']
assert isinstance(method, six.text_type)
assert method.isupper()
assert message['method'] == request_method
# Optional
scheme = message.get('scheme')
if scheme:
assert isinstance(scheme, six.text_type)
assert scheme in ['http', 'https']
assert 'path' in message
path = message['path']
assert isinstance(path, six.text_type)
assert path == request_path
# Test that the URL path is already decoded. That is easily tested if the original
# path does not contain percent signs, but I can't think of a way to test it when they're
# present.
if '%' not in request_path:
self.assertEqual(path, unquote(path))
assert 'query_string' in message
query_string = message['query_string']
# Assert that query_string is a byte string and still url encoded
assert isinstance(query_string, six.binary_type)
assert query_string == urlencode(request_params).encode('ascii')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment