Tests: style.

This commit is contained in:
Andrei Zeliankou
2021-04-05 14:03:05 +01:00
parent 46d8567dd7
commit 6c97a1a069
67 changed files with 689 additions and 614 deletions

View File

@@ -6,7 +6,6 @@ import platform
import re import re
import shutil import shutil
import signal import signal
import socket
import stat import stat
import subprocess import subprocess
import sys import sys
@@ -15,11 +14,12 @@ import time
from multiprocessing import Process from multiprocessing import Process
import pytest import pytest
from unit.check.go import check_go from unit.check.go import check_go
from unit.check.isolation import check_isolation from unit.check.isolation import check_isolation
from unit.check.node import check_node from unit.check.node import check_node
from unit.check.tls import check_openssl
from unit.check.regex import check_regex from unit.check.regex import check_regex
from unit.check.tls import check_openssl
from unit.http import TestHTTP from unit.http import TestHTTP
from unit.option import option from unit.option import option
from unit.utils import public_dir from unit.utils import public_dir
@@ -85,6 +85,7 @@ _fds_check = {
} }
http = TestHTTP() http = TestHTTP()
def pytest_configure(config): def pytest_configure(config):
option.config = config.option option.config = config.option
@@ -115,9 +116,11 @@ def pytest_configure(config):
def pytest_generate_tests(metafunc): def pytest_generate_tests(metafunc):
cls = metafunc.cls cls = metafunc.cls
if (not hasattr(cls, 'application_type') if (
not hasattr(cls, 'application_type')
or cls.application_type == None or cls.application_type == None
or cls.application_type == 'external'): or cls.application_type == 'external'
):
return return
type = cls.application_type type = cls.application_type
@@ -216,6 +219,7 @@ def pytest_sessionstart(session):
elif option.save_log: elif option.save_log:
open(unit_instance['temp_dir'] + '/' + unit_log_copy, 'w').close() open(unit_instance['temp_dir'] + '/' + unit_log_copy, 'w').close()
@pytest.hookimpl(tryfirst=True, hookwrapper=True) @pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call): def pytest_runtest_makereport(item, call):
# execute all other hooks to obtain the report object # execute all other hooks to obtain the report object
@@ -320,7 +324,9 @@ def run(request):
public_dir(path) public_dir(path)
if os.path.isfile(path) or stat.S_ISSOCK(os.stat(path).st_mode): if os.path.isfile(path) or stat.S_ISSOCK(
os.stat(path).st_mode
):
os.remove(path) os.remove(path)
else: else:
shutil.rmtree(path) shutil.rmtree(path)
@@ -384,6 +390,7 @@ def run(request):
_check_alerts(log=log) _check_alerts(log=log)
def unit_run(): def unit_run():
global unit_instance global unit_instance
@@ -482,7 +489,6 @@ def unit_stop():
return 'Could not terminate unit' return 'Could not terminate unit'
def _check_alerts(path=None, log=None): def _check_alerts(path=None, log=None):
if path is None: if path is None:
path = unit_instance['log'] path = unit_instance['log']
@@ -554,24 +560,21 @@ def _clear_conf(sock, log=None):
return return
try: try:
certs = json.loads(http.get( certs = json.loads(
url='/certificates', http.get(url='/certificates', sock_type='unix', addr=sock,)['body']
sock_type='unix', ).keys()
addr=sock,
)['body']).keys()
except json.JSONDecodeError: except json.JSONDecodeError:
pytest.fail('Can\'t parse certificates list.') pytest.fail('Can\'t parse certificates list.')
for cert in certs: for cert in certs:
resp = http.delete( resp = http.delete(
url='/certificates/' + cert, url='/certificates/' + cert, sock_type='unix', addr=sock,
sock_type='unix',
addr=sock,
)['body'] )['body']
check_success(resp) check_success(resp)
def _count_fds(pid): def _count_fds(pid):
procfile = '/proc/%s/fd' % pid procfile = '/proc/%s/fd' % pid
if os.path.isdir(procfile): if os.path.isdir(procfile):
@@ -606,6 +609,7 @@ def run_process(target, *args):
_processes.append(process) _processes.append(process)
def stop_processes(): def stop_processes():
if not _processes: if not _processes:
return return
@@ -657,18 +661,22 @@ def skip_fds_check():
def temp_dir(request): def temp_dir(request):
return unit_instance['temp_dir'] return unit_instance['temp_dir']
@pytest.fixture @pytest.fixture
def is_unsafe(request): def is_unsafe(request):
return request.config.getoption("--unsafe") return request.config.getoption("--unsafe")
@pytest.fixture @pytest.fixture
def is_su(request): def is_su(request):
return os.geteuid() == 0 return os.geteuid() == 0
@pytest.fixture @pytest.fixture
def unit_pid(request): def unit_pid(request):
return unit_instance['process'].pid return unit_instance['process'].pid
def pytest_sessionfinish(session): def pytest_sessionfinish(session):
if not option.restart and option.save_log: if not option.restart and option.save_log:
print('Path to unit.log:\n' + unit_instance['log'] + '\n') print('Path to unit.log:\n' + unit_instance['log'] + '\n')

View File

@@ -1,5 +1,6 @@
import atexit import atexit
def application(environ, start_response): def application(environ, start_response):
def at_exit(): def at_exit():
environ['wsgi.errors'].write('At exit called.\n') environ['wsgi.errors'].write('At exit called.\n')

View File

@@ -1,5 +1,6 @@
import io import io
def application(env, start_response): def application(env, start_response):
start_response('200', [('Content-Length', '10')]) start_response('200', [('Content-Length', '10')])
f = io.BytesIO(b'0123456789') f = io.BytesIO(b'0123456789')

View File

@@ -2,6 +2,7 @@ def application(env, start_response):
start_response('204', [('Content-Length', '0')]) start_response('204', [('Content-Length', '0')])
return [] return []
def app(env, start_response): def app(env, start_response):
start_response('200', [('Content-Length', '0')]) start_response('200', [('Content-Length', '0')])
return [] return []

View File

@@ -1,5 +1,6 @@
import atexit import atexit
class application: class application:
def __init__(self, environ, start_response): def __init__(self, environ, start_response):
self.environ = environ self.environ = environ
@@ -11,13 +12,14 @@ class application:
content_length = int(self.environ.get('CONTENT_LENGTH', 0)) content_length = int(self.environ.get('CONTENT_LENGTH', 0))
body = bytes(self.environ['wsgi.input'].read(content_length)) body = bytes(self.environ['wsgi.input'].read(content_length))
self.start('200', [ self.start(
'200',
[
('Content-Type', self.environ.get('CONTENT_TYPE')), ('Content-Type', self.environ.get('CONTENT_TYPE')),
('Content-Length', str(len(body))) ('Content-Length', str(len(body))),
]) ],
)
yield body yield body
def _atexit(self): def _atexit(self):
self.start('200', [ self.start('200', [('Content-Length', '0')])
('Content-Length', '0')
])

View File

@@ -1,7 +1,10 @@
def application(environ, start_response): def application(environ, start_response):
start_response('200', [ start_response(
'200',
[
('Content-Length', '0'), ('Content-Length', '0'),
('Custom-Header', environ.get('HTTP_CUSTOM_HEADER')) ('Custom-Header', environ.get('HTTP_CUSTOM_HEADER')),
]) ],
)
return [] return []

View File

@@ -1,5 +1,6 @@
import asyncio import asyncio
async def application(scope, receive, send): async def application(scope, receive, send):
assert scope['type'] == 'http' assert scope['type'] == 'http'
@@ -28,13 +29,13 @@ async def application(scope, receive, send):
loop.call_later(n, future.set_result, None) loop.call_later(n, future.set_result, None)
await future await future
await send({ await send(
{
'type': 'http.response.start', 'type': 'http.response.start',
'status': 200, 'status': 200,
'headers': [ 'headers': [(b'content-length', str(len(body)).encode()),],
(b'content-length', str(len(body)).encode()), }
] )
})
if not body: if not body:
await sleep(delay) await sleep(delay)
@@ -42,10 +43,12 @@ async def application(scope, receive, send):
step = int(len(body) / parts) step = int(len(body) / parts)
for i in range(0, len(body), step): for i in range(0, len(body), step):
await send({ await send(
{
'type': 'http.response.body', 'type': 'http.response.body',
'body': body[i : i + step], 'body': body[i : i + step],
'more_body': True, 'more_body': True,
}) }
)
await sleep(delay) await sleep(delay)

View File

@@ -1,10 +1,10 @@
async def application(scope, receive, send): async def application(scope, receive, send):
assert scope['type'] == 'http' assert scope['type'] == 'http'
await send({ await send(
{
'type': 'http.response.start', 'type': 'http.response.start',
'status': 200, 'status': 200,
'headers': [ 'headers': [(b'content-length', b'0')],
(b'content-length', b'0'), }
] )
})

View File

@@ -1,5 +1,6 @@
import os import os
def application(env, start_response): def application(env, start_response):
body = '' body = ''
vars = env.get('HTTP_X_VARIABLES').split(',') vars = env.get('HTTP_X_VARIABLES').split(',')

View File

@@ -2,8 +2,7 @@ def application(environ, start_response):
h = (k for k, v in environ.items() if k.startswith('HTTP_')) h = (k for k, v in environ.items() if k.startswith('HTTP_'))
start_response('200', [ start_response(
('Content-Length', '0'), '200', [('Content-Length', '0'), ('All-Headers', ','.join(h))]
('All-Headers', ','.join(h)) )
])
return [] return []

View File

@@ -1,7 +1,10 @@
def application(env, start_response): def application(env, start_response):
start_response('200', [ start_response(
'200',
[
('Content-Length', '0'), ('Content-Length', '0'),
('X-Server-Name', env.get('SERVER_NAME')), ('X-Server-Name', env.get('SERVER_NAME')),
('X-Http-Host', str(env.get('HTTP_HOST'))) ('X-Http-Host', str(env.get('HTTP_HOST'))),
]) ],
)
return [] return []

View File

@@ -8,7 +8,9 @@ class application:
def __iter__(self): def __iter__(self):
self.__i = 0 self.__i = 0
self._skip_level = int(self.environ.get('HTTP_X_SKIP', 0)) self._skip_level = int(self.environ.get('HTTP_X_SKIP', 0))
self._not_skip_close = int(self.environ.get('HTTP_X_NOT_SKIP_CLOSE', 0)) self._not_skip_close = int(
self.environ.get('HTTP_X_NOT_SKIP_CLOSE', 0)
)
self._is_chunked = self.environ.get('HTTP_X_CHUNKED') self._is_chunked = self.environ.get('HTTP_X_CHUNKED')
headers = [(('Content-Length', '10'))] headers = [(('Content-Length', '10'))]

View File

@@ -3,11 +3,12 @@ def application(scope):
return app_http return app_http
async def app_http(receive, send): async def app_http(receive, send):
await send({ await send(
{
'type': 'http.response.start', 'type': 'http.response.start',
'status': 200, 'status': 200,
'headers': [ 'headers': [(b'content-length', b'0'),],
(b'content-length', b'0'), }
] )
})

View File

@@ -7,11 +7,12 @@ def application(scope, receive=None, send=None):
else: else:
return app_http(receive, send) return app_http(receive, send)
async def app_http(receive, send): async def app_http(receive, send):
await send({ await send(
{
'type': 'http.response.start', 'type': 'http.response.start',
'status': 200, 'status': 200,
'headers': [ 'headers': [(b'content-length', b'0'),],
(b'content-length', b'0'), }
] )
})

View File

@@ -8,15 +8,12 @@ async def application(scope, receive, send):
if not m.get('more_body', False): if not m.get('more_body', False):
break break
await send({ await send(
{
'type': 'http.response.start', 'type': 'http.response.start',
'status': 200, 'status': 200,
'headers': [ 'headers': [(b'content-length', str(len(body)).encode())],
(b'content-length', str(len(body)).encode()), }
] )
})
await send({ await send({'type': 'http.response.body', 'body': body})
'type': 'http.response.body',
'body': body,
})

View File

@@ -3,7 +3,5 @@ def application(environ, start_response):
content_length = int(environ.get('CONTENT_LENGTH', 0)) content_length = int(environ.get('CONTENT_LENGTH', 0))
body = bytes(environ['wsgi.input'].read(content_length)) body = bytes(environ['wsgi.input'].read(content_length))
start_response('200', [ start_response('200', [('Content-Length', str(len(body)))])
('Content-Length', str(len(body)))
])
return [body] return [body]

View File

@@ -1,6 +1,7 @@
import os import os
import sys import sys
def application(environ, start_response): def application(environ, start_response):
body = os.pathsep.join(sys.path).encode() body = os.pathsep.join(sys.path).encode()

View File

@@ -1,11 +1,13 @@
async def application(scope, receive, send): async def application(scope, receive, send):
assert scope['type'] == 'http' assert scope['type'] == 'http'
await send({ await send(
{
'type': 'http.response.start', 'type': 'http.response.start',
'status': 200, 'status': 200,
'headers': [ 'headers': [
(b'content-length', b'0'), (b'content-length', b'0'),
(b'query-string', scope['query_string']), (b'query-string', scope['query_string']),
] ],
}) }
)

View File

@@ -1,7 +1,10 @@
def application(environ, start_response): def application(environ, start_response):
start_response('200', [ start_response(
'200',
[
('Content-Length', '0'), ('Content-Length', '0'),
('Query-String', environ.get('QUERY_STRING')) ('Query-String', environ.get('QUERY_STRING')),
]) ],
)
return [] return []

View File

@@ -1,11 +1,13 @@
async def application(scope, receive, send): async def application(scope, receive, send):
assert scope['type'] == 'http' assert scope['type'] == 'http'
await send({ await send(
{
'type': 'http.response.start', 'type': 'http.response.start',
'status': 200, 'status': 200,
'headers': [ 'headers': [
(b'content-length', b'0'), (b'content-length', b'0'),
(b'server-port', str(scope['server'][1]).encode()), (b'server-port', str(scope['server'][1]).encode()),
] ],
}) }
)

View File

@@ -1,7 +1,7 @@
def application(environ, start_response): def application(environ, start_response):
start_response('200', [ start_response(
('Content-Length', '0'), '200',
('Server-Port', environ.get('SERVER_PORT')) [('Content-Length', '0'), ('Server-Port', environ.get('SERVER_PORT'))],
]) )
return [] return []

View File

@@ -33,10 +33,10 @@ async def application(scope, receive, send):
Foo(Foo.num).start() Foo(Foo.num).start()
Foo.num += 10 Foo.num += 10
await send({ await send(
{
'type': 'http.response.start', 'type': 'http.response.start',
'status': 200, 'status': 200,
'headers': [ 'headers': [(b'content-length', b'0')],
(b'content-length', b'0'), }
] )
})

View File

@@ -2,6 +2,7 @@ import asyncio
import time import time
import threading import threading
async def application(scope, receive, send): async def application(scope, receive, send):
assert scope['type'] == 'http' assert scope['type'] == 'http'
@@ -17,11 +18,13 @@ async def application(scope, receive, send):
time.sleep(delay) time.sleep(delay)
await send({ await send(
{
'type': 'http.response.start', 'type': 'http.response.start',
'status': 200, 'status': 200,
'headers': [ 'headers': [
(b'content-length', b'0'), (b'content-length', b'0'),
(b'x-thread', str(threading.currentThread().ident).encode()), (b'x-thread', str(threading.currentThread().ident).encode()),
] ],
}) }
)

View File

@@ -1,15 +1,19 @@
import time import time
import threading import threading
def application(environ, start_response): def application(environ, start_response):
delay = float(environ.get('HTTP_X_DELAY', 0)) delay = float(environ.get('HTTP_X_DELAY', 0))
time.sleep(delay) time.sleep(delay)
start_response('200', [ start_response(
'200',
[
('Content-Length', '0'), ('Content-Length', '0'),
('Wsgi-Multithread', str(environ['wsgi.multithread'])), ('Wsgi-Multithread', str(environ['wsgi.multithread'])),
('X-Thread', str(threading.currentThread().ident)) ('X-Thread', str(threading.currentThread().ident)),
]) ],
)
return [] return []

View File

@@ -1,6 +1,7 @@
from tempfile import TemporaryFile from tempfile import TemporaryFile
import os, cgi import os, cgi
def read(environ): def read(environ):
length = int(environ.get('CONTENT_LENGTH', 0)) length = int(environ.get('CONTENT_LENGTH', 0))
@@ -11,6 +12,7 @@ def read(environ):
environ['wsgi.input'] = body environ['wsgi.input'] = body
return body return body
def application(environ, start_response): def application(environ, start_response):
file = read(environ) file = read(environ)
@@ -19,9 +21,9 @@ def application(environ, start_response):
filename = form['file'].filename filename = form['file'].filename
data = filename.encode() + form['file'].file.read() data = filename.encode() + form['file'].file.read()
start_response('200 OK', [ start_response(
('Content-Type', 'text/plain'), '200 OK',
('Content-Length', str(len(data))), [('Content-Type', 'text/plain'), ('Content-Length', str(len(data)))],
]) )
return data return data

View File

@@ -1,18 +1,19 @@
import json import json
import os import os
def application(environ, start_response): def application(environ, start_response):
uid = os.geteuid() uid = os.geteuid()
gid = os.getegid() gid = os.getegid()
out = json.dumps({ out = json.dumps({'UID': uid, 'GID': gid,}).encode('utf-8')
'UID': uid,
'GID': gid,
}).encode('utf-8')
start_response('200 OK', [ start_response(
'200 OK',
[
('Content-Length', str(len(out))), ('Content-Length', str(len(out))),
('Content-Type', 'application/json') ('Content-Type', 'application/json'),
]) ],
)
return [out] return [out]

View File

@@ -17,7 +17,8 @@ async def application(scope, receive, send):
res.append(h[1]) res.append(h[1])
return b', '.join(res) return b', '.join(res)
await send({ await send(
{
'type': 'http.response.start', 'type': 'http.response.start',
'status': 200, 'status': 200,
'headers': [ 'headers': [
@@ -31,10 +32,8 @@ async def application(scope, receive, send):
(b'asgi-spec-version', scope['asgi']['spec_version'].encode()), (b'asgi-spec-version', scope['asgi']['spec_version'].encode()),
(b'scheme', scope['scheme'].encode()), (b'scheme', scope['scheme'].encode()),
(b'custom-header', get_header(b'custom-header')), (b'custom-header', get_header(b'custom-header')),
] ],
}) }
)
await send({ await send({'type': 'http.response.body', 'body': body})
'type': 'http.response.body',
'body': body,
})

View File

@@ -3,7 +3,9 @@ def application(environ, start_response):
content_length = int(environ.get('CONTENT_LENGTH', 0)) content_length = int(environ.get('CONTENT_LENGTH', 0))
body = bytes(environ['wsgi.input'].read(content_length)) body = bytes(environ['wsgi.input'].read(content_length))
start_response('200', [ start_response(
'200',
[
('Content-Type', environ.get('CONTENT_TYPE')), ('Content-Type', environ.get('CONTENT_TYPE')),
('Content-Length', str(len(body))), ('Content-Length', str(len(body))),
('Request-Method', environ.get('REQUEST_METHOD')), ('Request-Method', environ.get('REQUEST_METHOD')),
@@ -16,6 +18,7 @@ def application(environ, start_response):
('Wsgi-Url-Scheme', environ['wsgi.url_scheme']), ('Wsgi-Url-Scheme', environ['wsgi.url_scheme']),
('Wsgi-Multithread', str(environ['wsgi.multithread'])), ('Wsgi-Multithread', str(environ['wsgi.multithread'])),
('Wsgi-Multiprocess', str(environ['wsgi.multiprocess'])), ('Wsgi-Multiprocess', str(environ['wsgi.multiprocess'])),
('Wsgi-Run-Once', str(environ['wsgi.run_once'])) ('Wsgi-Run-Once', str(environ['wsgi.run_once'])),
]) ],
)
return [body] return [body]

View File

@@ -3,16 +3,16 @@ async def application(scope, receive, send):
while True: while True:
m = await receive() m = await receive()
if m['type'] == 'websocket.connect': if m['type'] == 'websocket.connect':
await send({ await send({'type': 'websocket.accept'})
'type': 'websocket.accept',
})
if m['type'] == 'websocket.receive': if m['type'] == 'websocket.receive':
await send({ await send(
{
'type': 'websocket.send', 'type': 'websocket.send',
'bytes': m.get('bytes', None), 'bytes': m.get('bytes', None),
'text': m.get('text', None), 'text': m.get('text', None),
}) }
)
if m['type'] == 'websocket.disconnect': if m['type'] == 'websocket.disconnect':
break; break

View File

@@ -6,20 +6,24 @@ async def application(scope, receive, send):
if m['type'] == 'websocket.connect': if m['type'] == 'websocket.connect':
subprotocols = scope['subprotocols'] subprotocols = scope['subprotocols']
await send({ await send(
{
'type': 'websocket.accept', 'type': 'websocket.accept',
'headers': [ 'headers': [
(b'x-subprotocols', str(subprotocols).encode()), (b'x-subprotocols', str(subprotocols).encode()),
], ],
'subprotocol': subprotocols[0], 'subprotocol': subprotocols[0],
}) }
)
if m['type'] == 'websocket.receive': if m['type'] == 'websocket.receive':
await send({ await send(
{
'type': 'websocket.send', 'type': 'websocket.send',
'bytes': m.get('bytes', None), 'bytes': m.get('bytes', None),
'text': m.get('text', None), 'text': m.get('text', None),
}) }
)
if m['type'] == 'websocket.disconnect': if m['type'] == 'websocket.disconnect':
break; break

View File

@@ -1,6 +1,7 @@
import time import time
import pytest import pytest
from unit.applications.lang.python import TestApplicationPython from unit.applications.lang.python import TestApplicationPython
from unit.option import option from unit.option import option

View File

@@ -3,13 +3,15 @@ import time
from distutils.version import LooseVersion from distutils.version import LooseVersion
import pytest import pytest
from unit.applications.lang.python import TestApplicationPython from unit.applications.lang.python import TestApplicationPython
from unit.option import option from unit.option import option
class TestASGIApplication(TestApplicationPython): class TestASGIApplication(TestApplicationPython):
prerequisites = {'modules': {'python': prerequisites = {
lambda v: LooseVersion(v) >= LooseVersion('3.5')}} 'modules': {'python': lambda v: LooseVersion(v) >= LooseVersion('3.5')}
}
load_module = 'asgi' load_module = 'asgi'
def findall(self, pattern): def findall(self, pattern):
@@ -31,7 +33,8 @@ Content-Type: text/html
Connection: close Connection: close
custom-header: BLAH custom-header: BLAH
%s""" % (len(body), body.encode()), %s"""
% (len(body), body.encode()),
raw=True, raw=True,
) )
@@ -145,7 +148,7 @@ custom-header: BLAH
assert 'success' in self.conf( assert 'success' in self.conf(
'{"http":{"max_body_size": ' + str(max_body_size) + ' }}', '{"http":{"max_body_size": ' + str(max_body_size) + ' }}',
'settings' 'settings',
) )
assert self.get()['status'] == 200, 'init' assert self.get()['status'] == 200, 'init'

View File

@@ -2,6 +2,7 @@ import os
from distutils.version import LooseVersion from distutils.version import LooseVersion
import pytest import pytest
from conftest import unit_stop from conftest import unit_stop
from unit.applications.lang.python import TestApplicationPython from unit.applications.lang.python import TestApplicationPython
from unit.option import option from unit.option import option

View File

@@ -3,14 +3,16 @@ import time
from distutils.version import LooseVersion from distutils.version import LooseVersion
import pytest import pytest
from unit.applications.lang.python import TestApplicationPython from unit.applications.lang.python import TestApplicationPython
from unit.applications.websockets import TestApplicationWebsocket from unit.applications.websockets import TestApplicationWebsocket
from unit.option import option from unit.option import option
class TestASGIWebsockets(TestApplicationPython): class TestASGIWebsockets(TestApplicationPython):
prerequisites = {'modules': {'python': prerequisites = {
lambda v: LooseVersion(v) >= LooseVersion('3.5')}} 'modules': {'python': lambda v: LooseVersion(v) >= LooseVersion('3.5')}
}
load_module = 'asgi' load_module = 'asgi'
ws = TestApplicationWebsocket() ws = TestApplicationWebsocket()
@@ -74,7 +76,9 @@ class TestASGIWebsockets(TestApplicationPython):
sock.close() sock.close()
assert resp['status'] == 101, 'status' assert resp['status'] == 101, 'status'
assert resp['headers']['x-subprotocols'] == "('chat', 'phone', 'video')", 'subprotocols' assert (
resp['headers']['x-subprotocols'] == "('chat', 'phone', 'video')"
), 'subprotocols'
assert resp['headers']['sec-websocket-protocol'] == 'chat', 'key' assert resp['headers']['sec-websocket-protocol'] == 'chat', 'key'
def test_asgi_websockets_mirror(self): def test_asgi_websockets_mirror(self):
@@ -231,7 +235,7 @@ class TestASGIWebsockets(TestApplicationPython):
@pytest.mark.skip('not yet') @pytest.mark.skip('not yet')
def test_asgi_websockets_handshake_upgrade_absent( def test_asgi_websockets_handshake_upgrade_absent(
self self,
): # FAIL https://tools.ietf.org/html/rfc6455#section-4.2.1 ): # FAIL https://tools.ietf.org/html/rfc6455#section-4.2.1
self.load('websockets/mirror') self.load('websockets/mirror')
@@ -324,7 +328,9 @@ class TestASGIWebsockets(TestApplicationPython):
}, },
) )
assert resp['status'] == 400, 'key double' # FAIL https://tools.ietf.org/html/rfc6455#section-11.3.1 assert (
resp['status'] == 400
), 'key double' # FAIL https://tools.ietf.org/html/rfc6455#section-11.3.1
def test_asgi_websockets_handshake_method_invalid(self): def test_asgi_websockets_handshake_method_invalid(self):
self.load('websockets/mirror') self.load('websockets/mirror')
@@ -935,7 +941,9 @@ class TestASGIWebsockets(TestApplicationPython):
frame = self.ws.frame_read(sock) frame = self.ws.frame_read(sock)
if frame['opcode'] == self.ws.OP_TEXT: if frame['opcode'] == self.ws.OP_TEXT:
self.check_frame(frame, True, self.ws.OP_TEXT, 'fragment1fragment2') self.check_frame(
frame, True, self.ws.OP_TEXT, 'fragment1fragment2'
)
frame = None frame = None
self.check_close(sock, 1002, frame=frame) self.check_close(sock, 1002, frame=frame)

View File

@@ -1,4 +1,5 @@
import pytest import pytest
from unit.control import TestControl from unit.control import TestControl

View File

@@ -129,11 +129,9 @@ class TestGoApplication(TestApplicationGo):
def test_go_application_command_line_arguments_type(self): def test_go_application_command_line_arguments_type(self):
self.load('command_line_arguments') self.load('command_line_arguments')
assert 'error' in \ assert 'error' in self.conf(
self.conf(
'' "a b c", 'applications/command_line_arguments/arguments' '' "a b c", 'applications/command_line_arguments/arguments'
), \ ), 'arguments type'
'arguments type'
def test_go_application_command_line_arguments_0(self): def test_go_application_command_line_arguments_0(self):
self.load('command_line_arguments') self.load('command_line_arguments')

View File

@@ -3,10 +3,12 @@ import os
import pwd import pwd
import pytest import pytest
from unit.applications.lang.go import TestApplicationGo from unit.applications.lang.go import TestApplicationGo
from unit.option import option from unit.option import option
from unit.utils import getns from unit.utils import getns
class TestGoIsolation(TestApplicationGo): class TestGoIsolation(TestApplicationGo):
prerequisites = {'modules': {'go': 'any'}, 'features': ['isolation']} prerequisites = {'modules': {'go': 'any'}, 'features': ['isolation']}
@@ -279,7 +281,7 @@ class TestGoIsolation(TestApplicationGo):
isolation['namespaces'] = { isolation['namespaces'] = {
'mount': True, 'mount': True,
'credential': True, 'credential': True,
'pid': True 'pid': True,
} }
self.load('ns_inspect', isolation=isolation) self.load('ns_inspect', isolation=isolation)
@@ -337,12 +339,10 @@ class TestGoIsolation(TestApplicationGo):
isolation['namespaces'] = { isolation['namespaces'] = {
'mount': True, 'mount': True,
'credential': True, 'credential': True,
'pid': True 'pid': True,
} }
isolation['automount'] = { isolation['automount'] = {'tmpfs': False}
'tmpfs': False
}
self.load('ns_inspect', isolation=isolation) self.load('ns_inspect', isolation=isolation)
@@ -352,9 +352,7 @@ class TestGoIsolation(TestApplicationGo):
"/ /tmp" not in obj['Mounts'] and "tmpfs" not in obj['Mounts'] "/ /tmp" not in obj['Mounts'] and "tmpfs" not in obj['Mounts']
), 'app has no /tmp mounted' ), 'app has no /tmp mounted'
isolation['automount'] = { isolation['automount'] = {'tmpfs': True}
'tmpfs': True
}
self.load('ns_inspect', isolation=isolation) self.load('ns_inspect', isolation=isolation)

View File

@@ -1,6 +1,7 @@
import os import os
import pytest import pytest
from unit.applications.lang.go import TestApplicationGo from unit.applications.lang.go import TestApplicationGo

View File

@@ -1,4 +1,5 @@
import pytest import pytest
from unit.applications.lang.python import TestApplicationPython from unit.applications.lang.python import TestApplicationPython

View File

@@ -7,6 +7,7 @@ from unit.applications.lang.java import TestApplicationJava
from unit.option import option from unit.option import option
from unit.utils import public_dir from unit.utils import public_dir
class TestJavaApplication(TestApplicationJava): class TestJavaApplication(TestApplicationJava):
prerequisites = {'modules': {'java': 'all'}} prerequisites = {'modules': {'java': 'all'}}

View File

@@ -2,6 +2,7 @@ import os
import subprocess import subprocess
import pytest import pytest
from unit.applications.lang.java import TestApplicationJava from unit.applications.lang.java import TestApplicationJava
from unit.option import option from unit.option import option

View File

@@ -2,6 +2,7 @@ import struct
import time import time
import pytest import pytest
from unit.applications.lang.java import TestApplicationJava from unit.applications.lang.java import TestApplicationJava
from unit.applications.websockets import TestApplicationWebsocket from unit.applications.websockets import TestApplicationWebsocket
from unit.option import option from unit.option import option
@@ -163,7 +164,7 @@ class TestJavaWebsockets(TestApplicationJava):
@pytest.mark.skip('not yet') @pytest.mark.skip('not yet')
def test_java_websockets_handshake_upgrade_absent( def test_java_websockets_handshake_upgrade_absent(
self self,
): # FAIL https://tools.ietf.org/html/rfc6455#section-4.2.1 ): # FAIL https://tools.ietf.org/html/rfc6455#section-4.2.1
self.load('websockets_mirror') self.load('websockets_mirror')
@@ -256,7 +257,9 @@ class TestJavaWebsockets(TestApplicationJava):
}, },
) )
assert resp['status'] == 400, 'key double' # FAIL https://tools.ietf.org/html/rfc6455#section-11.3.1 assert (
resp['status'] == 400
), 'key double' # FAIL https://tools.ietf.org/html/rfc6455#section-11.3.1
def test_java_websockets_handshake_method_invalid(self): def test_java_websockets_handshake_method_invalid(self):
self.load('websockets_mirror') self.load('websockets_mirror')
@@ -867,7 +870,9 @@ class TestJavaWebsockets(TestApplicationJava):
frame = self.ws.frame_read(sock) frame = self.ws.frame_read(sock)
if frame['opcode'] == self.ws.OP_TEXT: if frame['opcode'] == self.ws.OP_TEXT:
self.check_frame(frame, True, self.ws.OP_TEXT, 'fragment1fragment2') self.check_frame(
frame, True, self.ws.OP_TEXT, 'fragment1fragment2'
)
frame = None frame = None
self.check_close(sock, 1002, frame=frame) self.check_close(sock, 1002, frame=frame)

View File

@@ -1,6 +1,7 @@
import re import re
import pytest import pytest
from unit.applications.lang.node import TestApplicationNode from unit.applications.lang.node import TestApplicationNode
from unit.utils import waitforfiles from unit.utils import waitforfiles
@@ -205,7 +206,9 @@ class TestNodeApplication(TestApplicationNode):
def test_node_application_status_message(self): def test_node_application_status_message(self):
self.load('status_message') self.load('status_message')
assert re.search(r'200 blah', self.get(raw_resp=True)), 'status message' assert re.search(
r'200 blah', self.get(raw_resp=True)
), 'status message'
def test_node_application_get_header_type(self): def test_node_application_get_header_type(self):
self.load('get_header_type') self.load('get_header_type')

View File

@@ -2,6 +2,7 @@ import struct
import time import time
import pytest import pytest
from unit.applications.lang.node import TestApplicationNode from unit.applications.lang.node import TestApplicationNode
from unit.applications.websockets import TestApplicationWebsocket from unit.applications.websockets import TestApplicationWebsocket
from unit.option import option from unit.option import option
@@ -182,7 +183,7 @@ class TestNodeWebsockets(TestApplicationNode):
@pytest.mark.skip('not yet') @pytest.mark.skip('not yet')
def test_node_websockets_handshake_upgrade_absent( def test_node_websockets_handshake_upgrade_absent(
self self,
): # FAIL https://tools.ietf.org/html/rfc6455#section-4.2.1 ): # FAIL https://tools.ietf.org/html/rfc6455#section-4.2.1
self.load('websockets/mirror') self.load('websockets/mirror')
@@ -275,7 +276,9 @@ class TestNodeWebsockets(TestApplicationNode):
}, },
) )
assert resp['status'] == 400, 'key double' # FAIL https://tools.ietf.org/html/rfc6455#section-11.3.1 assert (
resp['status'] == 400
), 'key double' # FAIL https://tools.ietf.org/html/rfc6455#section-11.3.1
def test_node_websockets_handshake_method_invalid(self): def test_node_websockets_handshake_method_invalid(self):
self.load('websockets/mirror') self.load('websockets/mirror')
@@ -886,7 +889,9 @@ class TestNodeWebsockets(TestApplicationNode):
frame = self.ws.frame_read(sock) frame = self.ws.frame_read(sock)
if frame['opcode'] == self.ws.OP_TEXT: if frame['opcode'] == self.ws.OP_TEXT:
self.check_frame(frame, True, self.ws.OP_TEXT, 'fragment1fragment2') self.check_frame(
frame, True, self.ws.OP_TEXT, 'fragment1fragment2'
)
frame = None frame = None
self.check_close(sock, 1002, frame=frame) self.check_close(sock, 1002, frame=frame)

View File

@@ -1,6 +1,7 @@
import re import re
import pytest import pytest
from unit.applications.lang.perl import TestApplicationPerl from unit.applications.lang.perl import TestApplicationPerl

View File

@@ -5,9 +5,11 @@ import time
from subprocess import call from subprocess import call
import pytest import pytest
from unit.applications.lang.php import TestApplicationPHP from unit.applications.lang.php import TestApplicationPHP
from unit.option import option from unit.option import option
class TestPHPApplication(TestApplicationPHP): class TestPHPApplication(TestApplicationPHP):
prerequisites = {'modules': {'php': 'all'}} prerequisites = {'modules': {'php': 'all'}}
@@ -428,11 +430,13 @@ class TestPHPApplication(TestApplicationPHP):
self.load('auth') self.load('auth')
def check_auth(auth): def check_auth(auth):
resp = self.get(headers={ resp = self.get(
headers={
'Host': 'localhost', 'Host': 'localhost',
'Authorization': auth, 'Authorization': auth,
'Connection': 'close', 'Connection': 'close',
}) }
)
assert resp['status'] == 200, 'status' assert resp['status'] == 200, 'status'
assert resp['headers']['X-Digest'] == 'not set', 'Digest' assert resp['headers']['X-Digest'] == 'not set', 'Digest'

View File

@@ -1,4 +1,5 @@
import pytest import pytest
from unit.applications.lang.php import TestApplicationPHP from unit.applications.lang.php import TestApplicationPHP
from unit.option import option from unit.option import option
@@ -28,7 +29,7 @@ class TestPHPIsolation(TestApplicationPHP):
isolation['namespaces'] = { isolation['namespaces'] = {
'mount': True, 'mount': True,
'credential': True, 'credential': True,
'pid': True 'pid': True,
} }
self.load('phpinfo', isolation=isolation) self.load('phpinfo', isolation=isolation)
@@ -64,7 +65,7 @@ class TestPHPIsolation(TestApplicationPHP):
isolation['namespaces'] = { isolation['namespaces'] = {
'mount': True, 'mount': True,
'credential': True, 'credential': True,
'pid': True 'pid': True,
} }
self.load('list-extensions', isolation=isolation) self.load('list-extensions', isolation=isolation)

View File

@@ -3,6 +3,7 @@ import socket
import time import time
import pytest import pytest
from conftest import run_process from conftest import run_process
from unit.applications.lang.python import TestApplicationPython from unit.applications.lang.python import TestApplicationPython
from unit.option import option from unit.option import option
@@ -464,9 +465,9 @@ Content-Length: 10
def test_proxy_invalid(self): def test_proxy_invalid(self):
def check_proxy(proxy): def check_proxy(proxy):
assert 'error' in \ assert 'error' in self.conf(
self.conf([{"action": {"proxy": proxy}}], 'routes'), \ [{"action": {"proxy": proxy}}], 'routes'
'proxy invalid' ), 'proxy invalid'
check_proxy('blah') check_proxy('blah')
check_proxy('/blah') check_proxy('/blah')
@@ -502,7 +503,8 @@ Content-Length: 10
"type": "python", "type": "python",
"processes": {"spare": 0}, "processes": {"spare": 0},
"path": option.test_dir + "/python/mirror", "path": option.test_dir + "/python/mirror",
"working_directory": option.test_dir + "/python/mirror", "working_directory": option.test_dir
+ "/python/mirror",
"module": "wsgi", "module": "wsgi",
}, },
}, },

View File

@@ -5,6 +5,7 @@ import re
import time import time
import pytest import pytest
from unit.applications.lang.python import TestApplicationPython from unit.applications.lang.python import TestApplicationPython
from unit.option import option from unit.option import option
@@ -31,7 +32,8 @@ Content-Type: text/html
Connection: close Connection: close
custom-header: BLAH custom-header: BLAH
%s""" % (len(body), body.encode()), %s"""
% (len(body), body.encode()),
raw=True, raw=True,
) )
@@ -816,7 +818,11 @@ last line: 987654321
assert ['/new', *sys_path] == get_path(), 'check path update' assert ['/new', *sys_path] == get_path(), 'check path update'
set_path('["/blah1", "/blah2"]') set_path('["/blah1", "/blah2"]')
assert ['/blah1', '/blah2', *sys_path] == get_path(), 'check path array' assert [
'/blah1',
'/blah2',
*sys_path,
] == get_path(), 'check path array'
def test_python_application_path_invalid(self): def test_python_application_path_invalid(self):
self.load('path') self.load('path')

View File

@@ -1,5 +1,5 @@
import pytest import pytest
from unit.applications.lang.python import TestApplicationPython from unit.applications.lang.python import TestApplicationPython
from unit.option import option from unit.option import option
from unit.utils import findmnt from unit.utils import findmnt
@@ -32,7 +32,7 @@ class TestPythonIsolation(TestApplicationPython):
isolation['namespaces'] = { isolation['namespaces'] = {
'mount': True, 'mount': True,
'credential': True, 'credential': True,
'pid': True 'pid': True,
} }
self.load('ns_inspect', isolation=isolation) self.load('ns_inspect', isolation=isolation)
@@ -43,8 +43,7 @@ class TestPythonIsolation(TestApplicationPython):
), 'temp_dir does not exists in rootfs' ), 'temp_dir does not exists in rootfs'
assert ( assert (
self.getjson(url='/?path=/proc/self')['body']['FileExists'] self.getjson(url='/?path=/proc/self')['body']['FileExists'] == True
== True
), 'no /proc/self' ), 'no /proc/self'
assert ( assert (
@@ -66,15 +65,12 @@ class TestPythonIsolation(TestApplicationPython):
if not is_su: if not is_su:
pytest.skip('requires root') pytest.skip('requires root')
isolation = { isolation = {'rootfs': temp_dir, 'automount': {'language_deps': False}}
'rootfs': temp_dir,
'automount': {'language_deps': False}
}
self.load('empty', isolation=isolation) self.load('empty', isolation=isolation)
assert findmnt().find(temp_dir) == -1 assert findmnt().find(temp_dir) == -1
assert (self.get()['status'] != 200), 'disabled language_deps' assert self.get()['status'] != 200, 'disabled language_deps'
assert findmnt().find(temp_dir) == -1 assert findmnt().find(temp_dir) == -1
isolation['automount']['language_deps'] = True isolation['automount']['language_deps'] = True
@@ -82,7 +78,7 @@ class TestPythonIsolation(TestApplicationPython):
self.load('empty', isolation=isolation) self.load('empty', isolation=isolation)
assert findmnt().find(temp_dir) == -1 assert findmnt().find(temp_dir) == -1
assert (self.get()['status'] == 200), 'enabled language_deps' assert self.get()['status'] == 200, 'enabled language_deps'
assert waitformount(temp_dir), 'language_deps mount' assert waitformount(temp_dir), 'language_deps mount'
self.conf({"listeners": {}, "applications": {}}) self.conf({"listeners": {}, "applications": {}})
@@ -90,8 +86,6 @@ class TestPythonIsolation(TestApplicationPython):
assert waitforunmount(temp_dir), 'language_deps unmount' assert waitforunmount(temp_dir), 'language_deps unmount'
def test_python_isolation_procfs(self, is_su, temp_dir): def test_python_isolation_procfs(self, is_su, temp_dir):
isolation_features = option.available['features']['isolation'].keys()
if not is_su: if not is_su:
pytest.skip('requires root') pytest.skip('requires root')

View File

@@ -1,4 +1,5 @@
import pytest import pytest
from unit.applications.lang.python import TestApplicationPython from unit.applications.lang.python import TestApplicationPython
@@ -21,8 +22,7 @@ class TestPythonIsolation(TestApplicationPython):
), 'temp_dir does not exists in rootfs' ), 'temp_dir does not exists in rootfs'
assert ( assert (
self.getjson(url='/?path=/proc/self')['body']['FileExists'] self.getjson(url='/?path=/proc/self')['body']['FileExists'] == True
== True
), 'no /proc/self' ), 'no /proc/self'
assert ( assert (

View File

@@ -3,6 +3,7 @@ import subprocess
import time import time
import pytest import pytest
from unit.applications.lang.python import TestApplicationPython from unit.applications.lang.python import TestApplicationPython
from unit.option import option from unit.option import option

View File

@@ -76,9 +76,10 @@ class TestRespawn(TestApplicationPython):
self.kill_pids(pid) self.kill_pids(pid)
skip_alert(r'process %s exited on signal 9' % pid) skip_alert(r'process %s exited on signal 9' % pid)
assert self.wait_for_process( assert (
self.PATTERN_CONTROLLER, unit_pid self.wait_for_process(self.PATTERN_CONTROLLER, unit_pid)
) is not None is not None
)
assert self.get()['status'] == 200 assert self.get()['status'] == 200

View File

@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import pytest import pytest
from unit.applications.proto import TestApplicationProto from unit.applications.proto import TestApplicationProto
from unit.option import option from unit.option import option
@@ -336,8 +337,7 @@ class TestRouting(TestApplicationProto):
"type": "python", "type": "python",
"processes": {"spare": 0}, "processes": {"spare": 0},
"path": option.test_dir + '/python/empty', "path": option.test_dir + '/python/empty',
"working_directory": option.test_dir "working_directory": option.test_dir + '/python/empty',
+ '/python/empty',
"module": "wsgi", "module": "wsgi",
} }
}, },
@@ -495,8 +495,7 @@ class TestRouting(TestApplicationProto):
'routes/0/action', 'routes/0/action',
), 'proxy pass' ), 'proxy pass'
assert 'error' in self.conf( assert 'error' in self.conf(
{"share": temp_dir, "pass": "applications/app"}, {"share": temp_dir, "pass": "applications/app"}, 'routes/0/action',
'routes/0/action',
), 'share pass' ), 'share pass'
def test_routes_rules_two(self): def test_routes_rules_two(self):

View File

@@ -2,6 +2,7 @@ import re
import subprocess import subprocess
import pytest import pytest
from unit.applications.lang.ruby import TestApplicationRuby from unit.applications.lang.ruby import TestApplicationRuby
@@ -208,7 +209,6 @@ class TestRubyApplication(TestApplicationRuby):
self.get() self.get()
assert ( assert (
self.wait_for_record(r'\[error\].+1234567890') is not None self.wait_for_record(r'\[error\].+1234567890') is not None
), 'errors write int' ), 'errors write int'
@@ -228,9 +228,13 @@ class TestRubyApplication(TestApplicationRuby):
self.load('encoding') self.load('encoding')
try: try:
locales = subprocess.check_output( locales = (
subprocess.check_output(
['locale', '-a'], stderr=subprocess.STDOUT, ['locale', '-a'], stderr=subprocess.STDOUT,
).decode().split('\n') )
.decode()
.split('\n')
)
except (FileNotFoundError, subprocess.CalledProcessError): except (FileNotFoundError, subprocess.CalledProcessError):
pytest.skip('require locale') pytest.skip('require locale')

View File

@@ -2,6 +2,7 @@ import os
import shutil import shutil
import pytest import pytest
from unit.applications.lang.ruby import TestApplicationRuby from unit.applications.lang.ruby import TestApplicationRuby
from unit.option import option from unit.option import option

View File

@@ -3,6 +3,7 @@ import socket
import time import time
import pytest import pytest
from unit.applications.lang.python import TestApplicationPython from unit.applications.lang.python import TestApplicationPython

View File

@@ -1,6 +1,7 @@
import os import os
import pytest import pytest
from unit.applications.proto import TestApplicationProto from unit.applications.proto import TestApplicationProto
from unit.option import option from unit.option import option
@@ -81,10 +82,7 @@ class TestStatic(TestApplicationProto):
def test_fallback_share(self, temp_dir): def test_fallback_share(self, temp_dir):
self.action_update( self.action_update(
{ {"share": "/blah", "fallback": {"share": temp_dir + "/assets"},}
"share": "/blah",
"fallback": {"share": temp_dir + "/assets"},
}
) )
resp = self.get() resp = self.get()

View File

@@ -2,6 +2,7 @@ import os
import socket import socket
import pytest import pytest
from unit.applications.proto import TestApplicationProto from unit.applications.proto import TestApplicationProto
from unit.option import option from unit.option import option
from unit.utils import waitforfiles from unit.utils import waitforfiles
@@ -85,8 +86,7 @@ class TestStatic(TestApplicationProto):
def test_static_space_in_name(self, temp_dir): def test_static_space_in_name(self, temp_dir):
os.rename( os.rename(
temp_dir + '/assets/dir/file', temp_dir + '/assets/dir/file', temp_dir + '/assets/dir/fi le',
temp_dir + '/assets/dir/fi le',
) )
assert waitforfiles(temp_dir + '/assets/dir/fi le') assert waitforfiles(temp_dir + '/assets/dir/fi le')
assert self.get(url='/dir/fi le')['body'] == 'blah', 'file name' assert self.get(url='/dir/fi le')['body'] == 'blah', 'file name'
@@ -95,9 +95,7 @@ class TestStatic(TestApplicationProto):
assert waitforfiles(temp_dir + '/assets/di r/fi le') assert waitforfiles(temp_dir + '/assets/di r/fi le')
assert self.get(url='/di r/fi le')['body'] == 'blah', 'dir name' assert self.get(url='/di r/fi le')['body'] == 'blah', 'dir name'
os.rename( os.rename(temp_dir + '/assets/di r', temp_dir + '/assets/ di r ')
temp_dir + '/assets/di r', temp_dir + '/assets/ di r '
)
assert waitforfiles(temp_dir + '/assets/ di r /fi le') assert waitforfiles(temp_dir + '/assets/ di r /fi le')
assert ( assert (
self.get(url='/ di r /fi le')['body'] == 'blah' self.get(url='/ di r /fi le')['body'] == 'blah'
@@ -150,8 +148,7 @@ class TestStatic(TestApplicationProto):
), 'file name 2' ), 'file name 2'
os.rename( os.rename(
temp_dir + '/assets/ di r ', temp_dir + '/assets/ di r ', temp_dir + '/assets/ди ректория',
temp_dir + '/assets/ди ректория',
) )
assert waitforfiles(temp_dir + '/assets/ди ректория/фа йл') assert waitforfiles(temp_dir + '/assets/ди ректория/фа йл')
assert ( assert (

View File

@@ -4,6 +4,7 @@ import ssl
import subprocess import subprocess
import pytest import pytest
from unit.applications.tls import TestApplicationTLS from unit.applications.tls import TestApplicationTLS
from unit.option import option from unit.option import option
@@ -22,7 +23,7 @@ class TestTLS(TestApplicationTLS):
assert 'success' in self.conf( assert 'success' in self.conf(
{ {
"pass": "applications/" + application, "pass": "applications/" + application,
"tls": {"certificate": cert} "tls": {"certificate": cert},
}, },
'listeners/*:' + str(port), 'listeners/*:' + str(port),
) )

View File

@@ -1,7 +1,8 @@
import subprocess
import ssl import ssl
import subprocess
import pytest import pytest
from unit.applications.tls import TestApplicationTLS from unit.applications.tls import TestApplicationTLS
from unit.option import option from unit.option import option
@@ -23,10 +24,7 @@ class TestTLSSNI(TestApplicationTLS):
def add_tls(self, cert='default'): def add_tls(self, cert='default'):
assert 'success' in self.conf( assert 'success' in self.conf(
{ {"pass": "routes", "tls": {"certificate": cert}},
"pass": "routes",
"tls": {"certificate": cert}
},
'listeners/*:7080', 'listeners/*:7080',
) )
@@ -153,10 +151,7 @@ basicConstraints = critical,CA:TRUE"""
def test_tls_sni(self): def test_tls_sni(self):
bundles = { bundles = {
"default": { "default": {"subj": "default", "alt_names": ["default"]},
"subj": "default",
"alt_names": ["default"],
},
"localhost.com": { "localhost.com": {
"subj": "localhost.com", "subj": "localhost.com",
"alt_names": ["alt1.localhost.com"], "alt_names": ["alt1.localhost.com"],
@@ -205,10 +200,7 @@ basicConstraints = critical,CA:TRUE"""
def test_tls_sni_wildcard(self): def test_tls_sni_wildcard(self):
bundles = { bundles = {
"localhost.com": { "localhost.com": {"subj": "localhost.com", "alt_names": []},
"subj": "localhost.com",
"alt_names": [],
},
"example.com": { "example.com": {
"subj": "example.com", "subj": "example.com",
"alt_names": ["*.example.com", "*.alt.example.com"], "alt_names": ["*.example.com", "*.alt.example.com"],
@@ -248,11 +240,7 @@ basicConstraints = critical,CA:TRUE"""
self.check_cert('example', bundles['localhost']['subj']) self.check_cert('example', bundles['localhost']['subj'])
def test_tls_sni_empty_cn(self): def test_tls_sni_empty_cn(self):
bundles = { bundles = {"localhost": {"alt_names": ["alt.localhost.com"]}}
"localhost": {
"alt_names": ["alt.localhost.com"],
}
}
self.config_bundles(bundles) self.config_bundles(bundles)
self.add_tls(["localhost"]) self.add_tls(["localhost"])
@@ -266,7 +254,9 @@ basicConstraints = critical,CA:TRUE"""
) )
assert resp['status'] == 200 assert resp['status'] == 200
assert sock.getpeercert()['subjectAltName'][0][1] == 'alt.localhost.com' assert (
sock.getpeercert()['subjectAltName'][0][1] == 'alt.localhost.com'
)
def test_tls_sni_invalid(self): def test_tls_sni_invalid(self):
self.config_bundles({"localhost": {"subj": "subj1", "alt_names": ''}}) self.config_bundles({"localhost": {"subj": "subj1", "alt_names": ''}})

View File

@@ -92,16 +92,8 @@ class TestVariables(TestApplicationProto):
"*:7080": {"pass": "upstreams$uri"}, "*:7080": {"pass": "upstreams$uri"},
"*:7081": {"pass": "routes/one"}, "*:7081": {"pass": "routes/one"},
}, },
"upstreams": { "upstreams": {"1": {"servers": {"127.0.0.1:7081": {}}}},
"1": { "routes": {"one": [{"action": {"return": 200}}]},
"servers": {
"127.0.0.1:7081": {},
},
},
},
"routes": {
"one": [{"action": {"return": 200}}],
},
}, },
), 'upstreams initial configuration' ), 'upstreams initial configuration'

View File

@@ -21,10 +21,14 @@ class TestApplicationTLS(TestApplicationProto):
'req', 'req',
'-x509', '-x509',
'-new', '-new',
'-subj', '/CN=' + name + '/', '-subj',
'-config', option.temp_dir + '/openssl.conf', '/CN=' + name + '/',
'-out', option.temp_dir + '/' + name + '.crt', '-config',
'-keyout', option.temp_dir + '/' + name + '.key', option.temp_dir + '/openssl.conf',
'-out',
option.temp_dir + '/' + name + '.crt',
'-keyout',
option.temp_dir + '/' + name + '.key',
], ],
stderr=subprocess.STDOUT, stderr=subprocess.STDOUT,
) )
@@ -80,7 +84,9 @@ class TestApplicationTLS(TestApplicationProto):
[ myca_req_extensions ] [ myca_req_extensions ]
subjectAltName = @alt_names subjectAltName = @alt_names
{a_names}""".format(a_names=a_names) {a_names}""".format(
a_names=a_names
)
with open(conf_path, 'w') as f: with open(conf_path, 'w') as f:
f.write( f.write(
@@ -90,7 +96,9 @@ encrypt_key = no
distinguished_name = req_distinguished_name distinguished_name = req_distinguished_name
{a_sec} {a_sec}
[ req_distinguished_name ]""".format(a_sec=a_sec if alt_names else "") [ req_distinguished_name ]""".format(
a_sec=a_sec if alt_names else ""
)
) )
def load(self, script, name=None): def load(self, script, name=None):

View File

@@ -43,11 +43,7 @@ class TestApplicationWebsocket(TestApplicationProto):
'Sec-WebSocket-Version': 13, 'Sec-WebSocket-Version': 13,
} }
_, sock = self.get( _, sock = self.get(headers=headers, no_recv=True, start=True,)
headers=headers,
no_recv=True,
start=True,
)
resp = '' resp = ''
while True: while True:
@@ -57,7 +53,7 @@ class TestApplicationWebsocket(TestApplicationProto):
resp += sock.recv(4096).decode() resp += sock.recv(4096).decode()
if (resp.startswith('HTTP/') and '\r\n\r\n' in resp): if resp.startswith('HTTP/') and '\r\n\r\n' in resp:
resp = self._resp_to_dict(resp) resp = self._resp_to_dict(resp)
break break
@@ -90,8 +86,8 @@ class TestApplicationWebsocket(TestApplicationProto):
frame = {} frame = {}
head1, = struct.unpack('!B', recv_bytes(sock, 1)) (head1,) = struct.unpack('!B', recv_bytes(sock, 1))
head2, = struct.unpack('!B', recv_bytes(sock, 1)) (head2,) = struct.unpack('!B', recv_bytes(sock, 1))
frame['fin'] = bool(head1 & 0b10000000) frame['fin'] = bool(head1 & 0b10000000)
frame['rsv1'] = bool(head1 & 0b01000000) frame['rsv1'] = bool(head1 & 0b01000000)
@@ -103,10 +99,10 @@ class TestApplicationWebsocket(TestApplicationProto):
length = head2 & 0b01111111 length = head2 & 0b01111111
if length == 126: if length == 126:
data = recv_bytes(sock, 2) data = recv_bytes(sock, 2)
length, = struct.unpack('!H', data) (length,) = struct.unpack('!H', data)
elif length == 127: elif length == 127:
data = recv_bytes(sock, 8) data = recv_bytes(sock, 8)
length, = struct.unpack('!Q', data) (length,) = struct.unpack('!Q', data)
if frame['mask']: if frame['mask']:
mask_bits = recv_bytes(sock, 4) mask_bits = recv_bytes(sock, 4)
@@ -121,7 +117,7 @@ class TestApplicationWebsocket(TestApplicationProto):
if frame['opcode'] == self.OP_CLOSE: if frame['opcode'] == self.OP_CLOSE:
if length >= 2: if length >= 2:
code, = struct.unpack('!H', data[:2]) (code,) = struct.unpack('!H', data[:2])
reason = data[2:].decode('utf-8') reason = data[2:].decode('utf-8')
if not (code in self.CLOSE_CODES or 3000 <= code < 5000): if not (code in self.CLOSE_CODES or 3000 <= code < 5000):
pytest.fail('Invalid status code') pytest.fail('Invalid status code')

View File

@@ -12,6 +12,7 @@ from unit.utils import getns
allns = ['pid', 'mnt', 'ipc', 'uts', 'cgroup', 'net'] allns = ['pid', 'mnt', 'ipc', 'uts', 'cgroup', 'net']
http = TestHTTP() http = TestHTTP()
def check_isolation(): def check_isolation():
test_conf = {"namespaces": {"credential": True}} test_conf = {"namespaces": {"credential": True}}
available = option.available available = option.available
@@ -117,8 +118,7 @@ def check_isolation():
"body_empty": { "body_empty": {
"type": "perl", "type": "perl",
"processes": {"spare": 0}, "processes": {"spare": 0},
"working_directory": option.test_dir "working_directory": option.test_dir + "/perl/body_empty",
+ "/perl/body_empty",
"script": option.test_dir + "/perl/body_empty/psgi.pl", "script": option.test_dir + "/perl/body_empty/psgi.pl",
"isolation": {"namespaces": {"credential": True}}, "isolation": {"namespaces": {"credential": True}},
} }

View File

@@ -10,15 +10,16 @@ import pytest
from unit.option import option from unit.option import option
class TestHTTP(): class TestHTTP:
def http(self, start_str, **kwargs): def http(self, start_str, **kwargs):
sock_type = kwargs.get('sock_type', 'ipv4') sock_type = kwargs.get('sock_type', 'ipv4')
port = kwargs.get('port', 7080) port = kwargs.get('port', 7080)
url = kwargs.get('url', '/') url = kwargs.get('url', '/')
http = 'HTTP/1.0' if 'http_10' in kwargs else 'HTTP/1.1' http = 'HTTP/1.0' if 'http_10' in kwargs else 'HTTP/1.1'
headers = kwargs.get('headers', headers = kwargs.get(
{'Host': 'localhost', 'Connection': 'close'}) 'headers', {'Host': 'localhost', 'Connection': 'close'}
)
body = kwargs.get('body', b'') body = kwargs.get('body', b'')
crlf = '\r\n' crlf = '\r\n'
@@ -305,8 +306,9 @@ class TestHTTP():
return body, content_type return body, content_type
def form_url_encode(self, fields): def form_url_encode(self, fields):
data = "&".join("%s=%s" % (name, value) data = "&".join(
for name, value in fields.items()).encode() "%s=%s" % (name, value) for name, value in fields.items()
).encode()
return data, 'application/x-www-form-urlencoded' return data, 'application/x-www-form-urlencoded'
def multipart_encode(self, fields): def multipart_encode(self, fields):
@@ -326,7 +328,9 @@ class TestHTTP():
datatype = value['type'] datatype = value['type']
if not isinstance(value['data'], io.IOBase): if not isinstance(value['data'], io.IOBase):
pytest.fail('multipart encoding of file requires a stream.') pytest.fail(
'multipart encoding of file requires a stream.'
)
data = value['data'].read() data = value['data'].read()
@@ -336,9 +340,10 @@ class TestHTTP():
else: else:
pytest.fail('multipart requires a string or stream data') pytest.fail('multipart requires a string or stream data')
body += ( body += ("--%s\r\nContent-Disposition: form-data; name=\"%s\"") % (
"--%s\r\nContent-Disposition: form-data; name=\"%s\"" boundary,
) % (boundary, field) field,
)
if filename != '': if filename != '':
body += "; filename=\"%s\"" % filename body += "; filename=\"%s\"" % filename

View File

@@ -1,4 +1,4 @@
class Options(): class Options:
_options = { _options = {
'skip_alerts': [], 'skip_alerts': [],
'skip_sanitizer': False, 'skip_sanitizer': False,
@@ -13,4 +13,5 @@ class Options():
raise AttributeError raise AttributeError
option = Options() option = Options()