mirror of
https://github.com/django/daphne.git
synced 2025-04-20 08:42:18 +03:00
Test runserver (#214)
* Add tests for runserver and runworker management commands * Fix flake8 and isort errors * Refactor mocking, add comments to tests * rm unneeded vargs
This commit is contained in:
parent
5eb3bf848c
commit
4a09cec2d4
|
@ -1,6 +1,6 @@
|
|||
[run]
|
||||
branch = True
|
||||
source = channels, django.http.response
|
||||
source = channels
|
||||
omit = channels/tests/*
|
||||
|
||||
[report]
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
import logging
|
||||
|
||||
handler = logging.StreamHandler()
|
||||
|
||||
|
||||
def setup_logger(name, verbosity=1):
|
||||
"""
|
||||
Basic logger for runserver etc.
|
||||
"""
|
||||
|
||||
formatter = logging.Formatter(fmt='%(asctime)s - %(levelname)s - %(module)s - %(message)s')
|
||||
formatter = logging.Formatter(
|
||||
fmt='%(asctime)s - %(levelname)s - %(module)s - %(message)s')
|
||||
|
||||
handler = logging.StreamHandler()
|
||||
handler.setFormatter(formatter)
|
||||
|
||||
# Set up main logger
|
||||
|
@ -22,7 +24,8 @@ def setup_logger(name, verbosity=1):
|
|||
for module in ["daphne.ws_protocol", "daphne.http_protocol"]:
|
||||
daphne_logger = logging.getLogger(module)
|
||||
daphne_logger.addHandler(handler)
|
||||
daphne_logger.setLevel(logging.DEBUG if verbosity > 1 else logging.INFO)
|
||||
daphne_logger.setLevel(
|
||||
logging.DEBUG if verbosity > 1 else logging.INFO)
|
||||
|
||||
logger.propagate = False
|
||||
return logger
|
||||
|
|
|
@ -2,6 +2,7 @@ import datetime
|
|||
import sys
|
||||
import threading
|
||||
|
||||
from daphne.server import Server
|
||||
from django.conf import settings
|
||||
from django.core.management.commands.runserver import \
|
||||
Command as RunserverCommand
|
||||
|
@ -72,7 +73,6 @@ class Command(RunserverCommand):
|
|||
# actually a subthread under the autoreloader.
|
||||
self.logger.debug("Daphne running, listening on %s:%s", self.addr, self.port)
|
||||
try:
|
||||
from daphne.server import Server
|
||||
Server(
|
||||
channel_layer=self.channel_layer,
|
||||
host=self.addr,
|
||||
|
|
|
@ -5,8 +5,8 @@ from django.core.management import BaseCommand, CommandError
|
|||
|
||||
from channels import DEFAULT_CHANNEL_LAYER, channel_layers
|
||||
from channels.log import setup_logger
|
||||
from channels.worker import Worker
|
||||
from channels.staticfiles import StaticFilesConsumer
|
||||
from channels.worker import Worker
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
SECRET_KEY = 'cat'
|
||||
|
||||
INSTALLED_APPS = (
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.admin',
|
||||
'channels',
|
||||
)
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
|
@ -11,6 +19,10 @@ CHANNEL_LAYERS = {
|
|||
'BACKEND': 'asgiref.inmemory.ChannelLayer',
|
||||
'ROUTING': [],
|
||||
},
|
||||
'fake_channel': {
|
||||
'BACKEND': 'channels.tests.test_management.FakeChannelLayer',
|
||||
'ROUTING': [],
|
||||
}
|
||||
}
|
||||
|
||||
MIDDLEWARE_CLASSES = []
|
||||
|
|
158
channels/tests/test_management.py
Normal file
158
channels/tests/test_management.py
Normal file
|
@ -0,0 +1,158 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
import logging
|
||||
|
||||
from asgiref.inmemory import ChannelLayer
|
||||
from django.core.management import CommandError, call_command
|
||||
from channels.staticfiles import StaticFilesConsumer
|
||||
from django.test import TestCase, mock
|
||||
from six import StringIO
|
||||
|
||||
from channels.management.commands import runserver
|
||||
|
||||
|
||||
class FakeChannelLayer(ChannelLayer):
|
||||
'''
|
||||
Dummy class to bypass the 'inmemory' string check.
|
||||
'''
|
||||
pass
|
||||
|
||||
|
||||
@mock.patch('channels.management.commands.runworker.Worker')
|
||||
class RunWorkerTests(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
import channels.log
|
||||
self.stream = StringIO()
|
||||
channels.log.handler = logging.StreamHandler(self.stream)
|
||||
|
||||
def test_runworker_no_local_only(self, mock_worker):
|
||||
"""
|
||||
Runworker should fail with the default "inmemory" worker.
|
||||
"""
|
||||
with self.assertRaises(CommandError):
|
||||
call_command('runworker')
|
||||
|
||||
def test_debug(self, mock_worker):
|
||||
"""
|
||||
Test that the StaticFilesConsumer is used in debug mode.
|
||||
"""
|
||||
with self.settings(DEBUG=True, STATIC_URL='/static/'):
|
||||
# Use 'fake_channel' that bypasses the 'inmemory' check
|
||||
call_command('runworker', '--layer', 'fake_channel')
|
||||
mock_worker.assert_called_with(
|
||||
only_channels=None, exclude_channels=None, callback=None, channel_layer=mock.ANY)
|
||||
|
||||
channel_layer = mock_worker.call_args[1]['channel_layer']
|
||||
static_consumer = channel_layer.router.root.routing[0].consumer
|
||||
self.assertIsInstance(static_consumer, StaticFilesConsumer)
|
||||
|
||||
def test_runworker(self, mock_worker):
|
||||
# Use 'fake_channel' that bypasses the 'inmemory' check
|
||||
call_command('runworker', '--layer', 'fake_channel')
|
||||
mock_worker.assert_called_with(callback=None,
|
||||
only_channels=None,
|
||||
channel_layer=mock.ANY,
|
||||
exclude_channels=None)
|
||||
|
||||
def test_runworker_verbose(self, mocked_worker):
|
||||
# Use 'fake_channel' that bypasses the 'inmemory' check
|
||||
call_command('runworker', '--layer',
|
||||
'fake_channel', '--verbosity', '2')
|
||||
|
||||
# Verify the callback is set
|
||||
mocked_worker.assert_called_with(callback=mock.ANY,
|
||||
only_channels=None,
|
||||
channel_layer=mock.ANY,
|
||||
exclude_channels=None)
|
||||
|
||||
|
||||
class RunServerTests(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
import channels.log
|
||||
self.stream = StringIO()
|
||||
# Capture the logging of the channels moduel to match against the
|
||||
# output.
|
||||
channels.log.handler = logging.StreamHandler(self.stream)
|
||||
|
||||
@mock.patch('channels.management.commands.runserver.sys.stdout', new_callable=StringIO)
|
||||
@mock.patch('channels.management.commands.runserver.Server')
|
||||
@mock.patch('channels.management.commands.runworker.Worker')
|
||||
def test_runserver_basic(self, mocked_worker, mocked_server, mock_stdout):
|
||||
# Django's autoreload util uses threads and this is not needed
|
||||
# in the test envirionment.
|
||||
# See:
|
||||
# https://github.com/django/django/blob/master/django/core/management/commands/runserver.py#L105
|
||||
call_command('runserver', '--noreload')
|
||||
mocked_server.assert_called_with(port=8000, signal_handlers=True, http_timeout=60,
|
||||
host='127.0.0.1', action_logger=mock.ANY, channel_layer=mock.ANY)
|
||||
|
||||
@mock.patch('channels.management.commands.runserver.sys.stdout', new_callable=StringIO)
|
||||
@mock.patch('channels.management.commands.runserver.Server')
|
||||
@mock.patch('channels.management.commands.runworker.Worker')
|
||||
def test_runserver_debug(self, mocked_worker, mocked_server, mock_stdout):
|
||||
"""
|
||||
Test that the server runs with `DEBUG=True`.
|
||||
"""
|
||||
# Debug requires the static url is set.
|
||||
with self.settings(DEBUG=True, STATIC_URL='/static/'):
|
||||
call_command('runserver', '--noreload')
|
||||
mocked_server.assert_called_with(port=8000, signal_handlers=True, http_timeout=60,
|
||||
host='127.0.0.1', action_logger=mock.ANY, channel_layer=mock.ANY)
|
||||
|
||||
call_command('runserver', '--noreload', 'localhost:8001')
|
||||
mocked_server.assert_called_with(port=8001, signal_handlers=True, http_timeout=60,
|
||||
host='localhost', action_logger=mock.ANY, channel_layer=mock.ANY)
|
||||
|
||||
self.assertFalse(mocked_worker.called,
|
||||
"The worker should not be called with '--noworker'")
|
||||
|
||||
@mock.patch('channels.management.commands.runserver.sys.stdout', new_callable=StringIO)
|
||||
@mock.patch('channels.management.commands.runserver.Server')
|
||||
@mock.patch('channels.management.commands.runworker.Worker')
|
||||
def test_runserver_noworker(self, mocked_worker, mocked_server, mock_stdout):
|
||||
'''
|
||||
Test that the Worker is not called when using the `--noworker` parameter.
|
||||
'''
|
||||
call_command('runserver', '--noreload', '--noworker')
|
||||
mocked_server.assert_called_with(port=8000, signal_handlers=True, http_timeout=60,
|
||||
host='127.0.0.1', action_logger=mock.ANY, channel_layer=mock.ANY)
|
||||
self.assertFalse(mocked_worker.called,
|
||||
"The worker should not be called with '--noworker'")
|
||||
|
||||
@mock.patch('channels.management.commands.runserver.sys.stderr', new_callable=StringIO)
|
||||
def test_log_action(self, mocked_stderr):
|
||||
cmd = runserver.Command()
|
||||
test_actions = [
|
||||
(100, 'http', 'complete',
|
||||
'HTTP GET /a-path/ 100 [0.12, a-client]'),
|
||||
(200, 'http', 'complete',
|
||||
'HTTP GET /a-path/ 200 [0.12, a-client]'),
|
||||
(300, 'http', 'complete',
|
||||
'HTTP GET /a-path/ 300 [0.12, a-client]'),
|
||||
(304, 'http', 'complete',
|
||||
'HTTP GET /a-path/ 304 [0.12, a-client]'),
|
||||
(400, 'http', 'complete',
|
||||
'HTTP GET /a-path/ 400 [0.12, a-client]'),
|
||||
(404, 'http', 'complete',
|
||||
'HTTP GET /a-path/ 404 [0.12, a-client]'),
|
||||
(500, 'http', 'complete',
|
||||
'HTTP GET /a-path/ 500 [0.12, a-client]'),
|
||||
(None, 'websocket', 'connected',
|
||||
'WebSocket CONNECT /a-path/ [a-client]'),
|
||||
(None, 'websocket', 'disconnected',
|
||||
'WebSocket DISCONNECT /a-path/ [a-client]'),
|
||||
(None, 'websocket', 'something', ''), # This shouldn't happen
|
||||
]
|
||||
|
||||
for status_code, protocol, action, output in test_actions:
|
||||
details = {'status': status_code,
|
||||
'method': 'GET',
|
||||
'path': '/a-path/',
|
||||
'time_taken': 0.12345,
|
||||
'client': 'a-client'}
|
||||
cmd.log_action(protocol, action, details)
|
||||
self.assertIn(output, mocked_stderr.getvalue())
|
||||
# Clear previous output
|
||||
mocked_stderr.truncate(0)
|
Loading…
Reference in New Issue
Block a user