Start on fixing tests

This commit is contained in:
Andrew Godwin 2017-11-25 13:39:46 -08:00
parent 017797c05b
commit 22aa56e196
7 changed files with 273 additions and 227 deletions

View File

@ -20,6 +20,8 @@ class CommandLineInterface(object):
description = "Django HTTP/WebSocket server"
server_class = Server
def __init__(self):
self.parser = argparse.ArgumentParser(
description=self.description,
@ -124,12 +126,6 @@ class CommandLineInterface(object):
default=False,
action='store_true',
)
self.parser.add_argument(
'--threads',
help='Number of threads to run the application in',
type=int,
default=2,
)
self.parser.add_argument(
'application',
help='The application to dispatch to as path.to.module:instance.path',
@ -197,7 +193,7 @@ class CommandLineInterface(object):
'Starting server at %s' %
(', '.join(endpoints), )
)
self.server = Server(
self.server = self.server_class(
application=application,
endpoints=endpoints,
http_timeout=args.http_timeout,

View File

@ -1,3 +0,0 @@
# coding=utf-8
channel_layer = {}

View File

@ -1,200 +0,0 @@
# coding: utf8
from __future__ import unicode_literals
import logging
from unittest import TestCase
from six import string_types
from ..cli import CommandLineInterface
from ..server import Server, build_endpoint_description_strings
# this is the callable that will be tested here
build = build_endpoint_description_strings
class TestEndpointDescriptions(TestCase):
def testBasics(self):
self.assertEqual(build(), [], msg="Empty list returned when no kwargs given")
def testTcpPortBindings(self):
self.assertEqual(
build(port=1234, host='example.com'),
['tcp:port=1234:interface=example.com']
)
self.assertEqual(
build(port=8000, host='127.0.0.1'),
['tcp:port=8000:interface=127.0.0.1']
)
self.assertEqual(
build(port=8000, host='[200a::1]'),
['tcp:port=8000:interface=200a\:\:1']
)
self.assertEqual(
build(port=8000, host='200a::1'),
['tcp:port=8000:interface=200a\:\:1']
)
# incomplete port/host kwargs raise errors
self.assertRaises(
ValueError,
build, port=123
)
self.assertRaises(
ValueError,
build, host='example.com'
)
def testUnixSocketBinding(self):
self.assertEqual(
build(unix_socket='/tmp/daphne.sock'),
['unix:/tmp/daphne.sock']
)
def testFileDescriptorBinding(self):
self.assertEqual(
build(file_descriptor=5),
['fd:fileno=5']
)
def testMultipleEnpoints(self):
self.assertEqual(
sorted(
build(
file_descriptor=123,
unix_socket='/tmp/daphne.sock',
port=8080,
host='10.0.0.1'
)
),
sorted([
'tcp:port=8080:interface=10.0.0.1',
'unix:/tmp/daphne.sock',
'fd:fileno=123'
])
)
class TestCLIInterface(TestCase):
# construct a string that will be accepted as the channel_layer argument
_import_channel_layer_string = 'daphne.tests.asgi:channel_layer'
def setUp(self):
logging.disable(logging.CRITICAL)
# patch out the servers run method
self._default_server_run = Server.run
Server.run = lambda x: x
def tearDown(self):
logging.disable(logging.NOTSET)
# restore the original server run method
Server.run = self._default_server_run
def build_cli(self, cli_args=''):
# split the string and append the channel_layer positional argument
if isinstance(cli_args, string_types):
cli_args = cli_args.split()
args = cli_args + [self._import_channel_layer_string]
cli = CommandLineInterface()
cli.run(args)
return cli
def get_endpoints(self, cli_args=''):
cli = self.build_cli(cli_args=cli_args)
return cli.server.endpoints
def checkCLI(self, args='', endpoints=None, msg='Expected endpoints do not match.'):
endpoints = endpoints or []
cli = self.build_cli(cli_args=args)
generated_endpoints = sorted(cli.server.endpoints)
endpoints.sort()
self.assertEqual(
generated_endpoints,
endpoints,
msg=msg
)
def testCLIBasics(self):
self.checkCLI(
'',
['tcp:port=8000:interface=127.0.0.1']
)
self.checkCLI(
'-p 123',
['tcp:port=123:interface=127.0.0.1']
)
self.checkCLI(
'-b 10.0.0.1',
['tcp:port=8000:interface=10.0.0.1']
)
self.checkCLI(
'-b 200a::1',
['tcp:port=8000:interface=200a\:\:1']
)
self.checkCLI(
'-b [200a::1]',
['tcp:port=8000:interface=200a\:\:1']
)
self.checkCLI(
'-p 8080 -b example.com',
['tcp:port=8080:interface=example.com']
)
def testCLIEndpointCreation(self):
self.checkCLI(
'-p 8080 -u /tmp/daphne.sock',
[
'tcp:port=8080:interface=127.0.0.1',
'unix:/tmp/daphne.sock',
],
'Default binding host patched in when only port given'
)
self.checkCLI(
'-b example.com -u /tmp/daphne.sock',
[
'tcp:port=8000:interface=example.com',
'unix:/tmp/daphne.sock',
],
'Default port patched in when missing.'
)
self.checkCLI(
'-u /tmp/daphne.sock --fd 5',
[
'fd:fileno=5',
'unix:/tmp/daphne.sock'
],
'File descriptor and unix socket bound, TCP ignored.'
)
def testMixedCLIEndpointCreation(self):
self.checkCLI(
'-p 8080 -e unix:/tmp/daphne.sock',
[
'tcp:port=8080:interface=127.0.0.1',
'unix:/tmp/daphne.sock'
],
'Mix host/port args with endpoint args'
)
self.checkCLI(
'-p 8080 -e tcp:port=8080:interface=127.0.0.1',
[
'tcp:port=8080:interface=127.0.0.1',
] * 2,
'Do not try to de-duplicate endpoint description strings.'
'This would fail when running the server.'
)
def testCustomEndpoints(self):
self.checkCLI(
'-e imap:',
['imap:']
)

View File

@ -4,8 +4,7 @@ Contains a test case class to allow verifying ASGI messages
from __future__ import unicode_literals
from collections import defaultdict
import six
from six.moves.urllib import parse
from urllib import parse
import socket
import unittest
@ -33,19 +32,19 @@ class ASGITestCaseBase(unittest.TestCase):
self.assertEqual(set(), present_keys - required_keys - optional_keys)
def assert_valid_reply_channel(self, reply_channel):
self.assertIsInstance(reply_channel, six.text_type)
self.assertIsInstance(reply_channel, str)
# The reply channel is decided by the server.
self.assertTrue(reply_channel.startswith('test!'))
def assert_valid_path(self, path, request_path):
self.assertIsInstance(path, six.text_type)
self.assertIsInstance(path, str)
self.assertEqual(path, request_path)
# Assert that it's already url decoded
self.assertEqual(path, parse.unquote(path))
def assert_valid_address_and_port(self, host):
address, port = host
self.assertIsInstance(address, six.text_type)
self.assertIsInstance(address, str)
self.assert_is_ip_address(address)
self.assertIsInstance(port, int)
@ -74,17 +73,17 @@ class ASGIHTTPTestCase(ASGITestCaseBase):
self.assert_valid_path(channel_message['path'], request_path)
http_version = channel_message['http_version']
self.assertIsInstance(http_version, six.text_type)
self.assertIsInstance(http_version, str)
self.assertIn(http_version, ['1.0', '1.1', '1.2'])
method = channel_message['method']
self.assertIsInstance(method, six.text_type)
self.assertIsInstance(method, str)
self.assertTrue(method.isupper())
self.assertEqual(channel_message['method'], request_method)
query_string = channel_message['query_string']
# Assert that query_string is a byte string and still url encoded
self.assertIsInstance(query_string, six.binary_type)
self.assertIsInstance(query_string, bytes)
self.assertEqual(query_string, parse.urlencode(request_params or []).encode('ascii'))
# Ordering of header names is not important, but the order of values for a header
@ -107,22 +106,22 @@ class ASGIHTTPTestCase(ASGITestCaseBase):
scheme = channel_message.get('scheme')
if scheme is not None:
self.assertIsInstance(scheme, six.text_type)
self.assertIsInstance(scheme, str)
self.assertTrue(scheme) # May not be empty
root_path = channel_message.get('root_path')
if root_path is not None:
self.assertIsInstance(root_path, six.text_type)
self.assertIsInstance(root_path, str)
body = channel_message.get('body')
# Ensure we test for presence of 'body' if a request body was given
if request_body is not None or body is not None:
self.assertIsInstance(body, six.binary_type)
self.assertIsInstance(body, str)
self.assertEqual(body, (request_body or '').encode('ascii'))
body_channel = channel_message.get('body_channel')
if body_channel is not None:
self.assertIsInstance(body_channel, six.text_type)
self.assertIsInstance(body_channel, str)
self.assertIn('?', body_channel)
client = channel_message.get('client')
@ -137,7 +136,7 @@ class ASGIHTTPTestCase(ASGITestCaseBase):
self.assertTrue(message)
self.assertTrue(response.startswith(b'HTTP'))
status_code_bytes = six.text_type(message['status']).encode('ascii')
status_code_bytes = str(message['status']).encode('ascii')
self.assertIn(status_code_bytes, response)
if 'content' in message:

View File

@ -1,2 +1,14 @@
[bdist_wheel]
universal=1
[tool:pytest]
addopts = tests/
[yapf]
based_on_style = pep8
column_limit = 120
join_multiple_lines = false
split_arguments_when_comma_terminated = true
split_before_expression_after_opening_paren = true
split_before_first_argument = true
split_penalty_after_opening_bracket = -10

View File

@ -23,13 +23,18 @@ setup(
packages=find_packages() + ['twisted.plugins'],
include_package_data=True,
install_requires=[
'asgiref~=1.1',
'asgiref~=2.0',
'twisted>=17.5',
'autobahn>=0.18',
],
extras_require={
'tests': ['hypothesis', 'tox']
},
setup_requires=[
'pytest-runner',
],
tests_require=[
'hypothesis',
'tox',
'pytest',
],
entry_points={'console_scripts': [
'daphne = daphne.cli:CommandLineInterface.entrypoint',
]},

237
tests/test_cli.py Normal file
View File

@ -0,0 +1,237 @@
# coding: utf8
import logging
from unittest import TestCase
from daphne.cli import CommandLineInterface
from daphne.endpoints import build_endpoint_description_strings as build
class TestEndpointDescriptions(TestCase):
"""
Tests that the endpoint parsing/generation works as intended.
"""
def testBasics(self):
self.assertEqual(build(), [], msg='Empty list returned when no kwargs given')
def testTcpPortBindings(self):
self.assertEqual(
build(port=1234, host='example.com'),
['tcp:port=1234:interface=example.com']
)
self.assertEqual(
build(port=8000, host='127.0.0.1'),
['tcp:port=8000:interface=127.0.0.1']
)
self.assertEqual(
build(port=8000, host='[200a::1]'),
[r'tcp:port=8000:interface=200a\:\:1']
)
self.assertEqual(
build(port=8000, host='200a::1'),
[r'tcp:port=8000:interface=200a\:\:1']
)
# incomplete port/host kwargs raise errors
self.assertRaises(
ValueError,
build, port=123
)
self.assertRaises(
ValueError,
build, host='example.com'
)
def testUnixSocketBinding(self):
self.assertEqual(
build(unix_socket='/tmp/daphne.sock'),
['unix:/tmp/daphne.sock']
)
def testFileDescriptorBinding(self):
self.assertEqual(
build(file_descriptor=5),
['fd:fileno=5']
)
def testMultipleEnpoints(self):
self.assertEqual(
sorted(
build(
file_descriptor=123,
unix_socket='/tmp/daphne.sock',
port=8080,
host='10.0.0.1'
)
),
sorted([
'tcp:port=8080:interface=10.0.0.1',
'unix:/tmp/daphne.sock',
'fd:fileno=123'
])
)
class TestCLIInterface(TestCase):
"""
Tests the overall CLI class.
"""
class TestedCLI(CommandLineInterface):
"""
CommandLineInterface subclass that we used for testing (has a fake
server subclass).
"""
class TestedServer:
"""
Mock server object for testing.
"""
def __init__(self, **kwargs):
self.init_kwargs = kwargs
def run(self):
pass
server_class = TestedServer
def setUp(self):
logging.disable(logging.CRITICAL)
def tearDown(self):
logging.disable(logging.NOTSET)
def assertCLI(self, args, server_kwargs):
"""
Asserts that the CLI class passes the right args to the server class.
Passes in a fake application automatically.
"""
cli = self.TestedCLI()
cli.run(args + ['daphne:__version__']) # We just pass something importable as app
# Check the server got all arguments as intended
for key, value in server_kwargs.items():
# Get the value and sort it if it's a list (for endpoint checking)
actual_value = cli.server.init_kwargs.get(key)
if isinstance(actual_value, list):
actual_value.sort()
# Check values
self.assertEqual(
value,
actual_value,
'Wrong value for server kwarg %s: %r != %r' % (
key,
value,
actual_value,
),
)
def testCLIBasics(self):
"""
Tests basic endpoint generation.
"""
self.assertCLI(
[],
{
'endpoints': ['tcp:port=8000:interface=127.0.0.1'],
},
)
self.assertCLI(
['-p', '123'],
{
'endpoints': ['tcp:port=123:interface=127.0.0.1'],
},
)
self.assertCLI(
['-b', '10.0.0.1'],
{
'endpoints': ['tcp:port=8000:interface=10.0.0.1'],
},
)
self.assertCLI(
['-b', '200a::1'],
{
'endpoints': [r'tcp:port=8000:interface=200a\:\:1'],
},
)
self.assertCLI(
['-b', '[200a::1]'],
{
'endpoints': [r'tcp:port=8000:interface=200a\:\:1'],
},
)
self.assertCLI(
['-p', '8080', '-b', 'example.com'],
{
'endpoints': ['tcp:port=8080:interface=example.com'],
},
)
def testUnixSockets(self):
self.assertCLI(
['-p', '8080', '-u', '/tmp/daphne.sock'],
{
'endpoints': [
'tcp:port=8080:interface=127.0.0.1',
'unix:/tmp/daphne.sock',
],
},
)
self.assertCLI(
['-b', 'example.com', '-u', '/tmp/daphne.sock'],
{
'endpoints': [
'tcp:port=8000:interface=example.com',
'unix:/tmp/daphne.sock',
],
},
)
self.assertCLI(
['-u', '/tmp/daphne.sock', '--fd', '5'],
{
'endpoints': [
'fd:fileno=5',
'unix:/tmp/daphne.sock'
],
},
)
def testMixedCLIEndpointCreation(self):
"""
Tests mixing the shortcut options with the endpoint string options.
"""
self.assertCLI(
['-p', '8080', '-e', 'unix:/tmp/daphne.sock'],
{
'endpoints': [
'tcp:port=8080:interface=127.0.0.1',
'unix:/tmp/daphne.sock'
],
},
)
self.assertCLI(
['-p', '8080', '-e', 'tcp:port=8080:interface=127.0.0.1'],
{
'endpoints': [
'tcp:port=8080:interface=127.0.0.1',
'tcp:port=8080:interface=127.0.0.1',
],
},
)
def testCustomEndpoints(self):
"""
Tests entirely custom endpoints
"""
self.assertCLI(
['-e', 'imap:'],
{
'endpoints': [
'imap:',
],
},
)