Tests: migrated to the pytest.

This commit is contained in:
Andrei Zeliankou
2020-09-16 21:31:15 +01:00
parent 77ecb6ab49
commit d5e9159340
55 changed files with 4717 additions and 6262 deletions

View File

@@ -299,7 +299,7 @@ test: unit modules
test -h debuild/unit-$(VERSION)/debian/build-unit/build/$${soname} || \ test -h debuild/unit-$(VERSION)/debian/build-unit/build/$${soname} || \
ln -fs `pwd`/$${so} debuild/unit-$(VERSION)/debian/build-unit/build/$${soname} ; \ ln -fs `pwd`/$${so} debuild/unit-$(VERSION)/debian/build-unit/build/$${soname} ; \
done ; \ done ; \
( cd debuild/unit-$(VERSION)/debian/build-unit && ./test/run.py ) ; \ ( cd debuild/unit-$(VERSION)/debian/build-unit && env python3 -m pytest ) ; \
} }
test-debug: unit modules test-debug: unit modules
@@ -310,7 +310,7 @@ test-debug: unit modules
test -h debuild/unit-$(VERSION)/debian/build-unit-debug/build/$${soname} || \ test -h debuild/unit-$(VERSION)/debian/build-unit-debug/build/$${soname} || \
ln -fs `pwd`/$${so} debuild/unit-$(VERSION)/debian/build-unit-debug/build/$${soname} ; \ ln -fs `pwd`/$${so} debuild/unit-$(VERSION)/debian/build-unit-debug/build/$${soname} ; \
done ; \ done ; \
( cd debuild/unit-$(VERSION)/debian/build-unit-debug && ./test/run.py ) ; \ ( cd debuild/unit-$(VERSION)/debian/build-unit-debug && env python3 -m pytest ) ; \
} }
clean: clean:

View File

@@ -274,7 +274,7 @@ test: unit modules
test -h rpmbuild/BUILD/unit-$(VERSION)/build-nodebug/$${soname} || \ test -h rpmbuild/BUILD/unit-$(VERSION)/build-nodebug/$${soname} || \
ln -fs `pwd`/$${so} rpmbuild/BUILD/unit-$(VERSION)/build-nodebug/$${soname} ; \ ln -fs `pwd`/$${so} rpmbuild/BUILD/unit-$(VERSION)/build-nodebug/$${soname} ; \
done ; \ done ; \
( cd rpmbuild/BUILD/unit-$(VERSION) && rm -f build && ln -s build-nodebug build && ./test/run.py ) ; \ ( cd rpmbuild/BUILD/unit-$(VERSION) && rm -f build && ln -s build-nodebug build && env python3 -m pytest ) ; \
} }
test-debug: unit modules test-debug: unit modules
@@ -285,7 +285,7 @@ test-debug: unit modules
test -h rpmbuild/BUILD/unit-$(VERSION)/build-debug/$${soname} || \ test -h rpmbuild/BUILD/unit-$(VERSION)/build-debug/$${soname} || \
ln -fs `pwd`/$${so} rpmbuild/BUILD/unit-$(VERSION)/build-debug/$${soname} ; \ ln -fs `pwd`/$${so} rpmbuild/BUILD/unit-$(VERSION)/build-debug/$${soname} ; \
done ; \ done ; \
( cd rpmbuild/BUILD/unit-$(VERSION) && rm -f build && ln -s build-debug build && ./test/run.py ) ; \ ( cd rpmbuild/BUILD/unit-$(VERSION) && rm -f build && ln -s build-debug build && env python3 -m pytest ) ; \
} }
clean: clean:

299
test/conftest.py Normal file
View File

@@ -0,0 +1,299 @@
import fcntl
import os
import platform
import pytest
import signal
import stat
import subprocess
import sys
import re
import tempfile
import time
def pytest_addoption(parser):
parser.addoption(
"--detailed",
default=False,
action="store_true",
help="Detailed output for tests",
)
parser.addoption(
"--print_log",
default=False,
action="store_true",
help="Print unit.log to stdout in case of errors",
)
parser.addoption(
"--save_log",
default=False,
action="store_true",
help="Save unit.log after the test execution",
)
parser.addoption(
"--unsafe",
default=False,
action="store_true",
help="Run unsafe tests",
)
unit_instance = {}
option = None
def pytest_configure(config):
global option
option = config.option
option.generated_tests = {}
option.current_dir = os.path.abspath(
os.path.join(os.path.dirname(__file__), os.pardir)
)
option.test_dir = option.current_dir + '/test'
option.architecture = platform.architecture()[0]
option.system = platform.system()
# set stdout to non-blocking
if option.detailed or option.print_log:
fcntl.fcntl(sys.stdout.fileno(), fcntl.F_SETFL, 0)
def pytest_generate_tests(metafunc):
cls = metafunc.cls
if not hasattr(cls, 'application_type'):
return
type = cls.application_type
# take available module from option and generate tests for each version
for module in cls.prerequisites['modules']:
if module in option.available['modules']:
prereq_version = cls.prerequisites['modules'][module]
available_versions = option.available['modules'][module]
if prereq_version == 'all':
metafunc.fixturenames.append('tmp_ct')
metafunc.parametrize('tmp_ct', range(len(available_versions)))
for i in range(len(available_versions)):
version = available_versions[i]
option.generated_tests[
metafunc.function.__name__ + '[{}]'.format(i)
] = (type + ' ' + version)
elif prereq_version == 'any':
option.generated_tests[metafunc.function.__name__] = (
type + ' ' + available_versions[0]
)
else:
for version in available_versions:
if version.startswith(prereq_version):
option.generated_tests[metafunc.function.__name__] = (
type + ' ' + version
)
def pytest_sessionstart(session):
option.available = {'modules': {}, 'features': {}}
unit = unit_run()
# read unit.log
for i in range(50):
with open(unit['temp_dir'] + '/unit.log', 'r') as f:
log = f.read()
m = re.search('controller started', log)
if m is None:
time.sleep(0.1)
else:
break
if m is None:
_print_log()
exit("Unit is writing log too long")
# discover available modules from unit.log
for module in re.findall(r'module: ([a-zA-Z]+) (.*) ".*"$', log, re.M):
if module[0] not in option.available['modules']:
option.available['modules'][module[0]] = [module[1]]
else:
option.available['modules'][module[0]].append(module[1])
unit_stop()
def setup_method(self):
option.skip_alerts = [
r'read signalfd\(4\) failed',
r'sendmsg.+failed',
r'recvmsg.+failed',
]
option.skip_sanitizer = False
def unit_run():
global unit_instance
build_dir = option.current_dir + '/build'
unitd = build_dir + '/unitd'
if not os.path.isfile(unitd):
exit('Could not find unit')
temp_dir = tempfile.mkdtemp(prefix='unit-test-')
public_dir(temp_dir)
if oct(stat.S_IMODE(os.stat(build_dir).st_mode)) != '0o777':
public_dir(build_dir)
os.mkdir(temp_dir + '/state')
with open(temp_dir + '/unit.log', 'w') as log:
unit_instance['process'] = subprocess.Popen(
[
unitd,
'--no-daemon',
'--modules',
build_dir,
'--state',
temp_dir + '/state',
'--pid',
temp_dir + '/unit.pid',
'--log',
temp_dir + '/unit.log',
'--control',
'unix:' + temp_dir + '/control.unit.sock',
'--tmp',
temp_dir,
],
stderr=log,
)
if not waitforfiles(temp_dir + '/control.unit.sock'):
_print_log()
exit('Could not start unit')
# dumb (TODO: remove)
option.skip_alerts = [
r'read signalfd\(4\) failed',
r'sendmsg.+failed',
r'recvmsg.+failed',
]
option.skip_sanitizer = False
unit_instance['temp_dir'] = temp_dir
unit_instance['log'] = temp_dir + '/unit.log'
unit_instance['control_sock'] = temp_dir + '/control.unit.sock'
unit_instance['unitd'] = unitd
return unit_instance
def unit_stop():
p = unit_instance['process']
if p.poll() is not None:
return
p.send_signal(signal.SIGQUIT)
try:
retcode = p.wait(15)
if retcode:
return 'Child process terminated with code ' + str(retcode)
except:
p.kill()
return 'Could not terminate unit'
def public_dir(path):
os.chmod(path, 0o777)
for root, dirs, files in os.walk(path):
for d in dirs:
os.chmod(os.path.join(root, d), 0o777)
for f in files:
os.chmod(os.path.join(root, f), 0o777)
def waitforfiles(*files):
for i in range(50):
wait = False
ret = False
for f in files:
if not os.path.exists(f):
wait = True
break
if wait:
time.sleep(0.1)
else:
ret = True
break
return ret
def skip_alert(*alerts):
option.skip_alerts.extend(alerts)
def _check_alerts(log):
found = False
alerts = re.findall(r'.+\[alert\].+', log)
if alerts:
print('All alerts/sanitizer errors found in log:')
[print(alert) for alert in alerts]
found = True
if option.skip_alerts:
for skip in option.skip_alerts:
alerts = [al for al in alerts if re.search(skip, al) is None]
if alerts:
_print_log(log)
assert not alerts, 'alert(s)'
if not option.skip_sanitizer:
sanitizer_errors = re.findall('.+Sanitizer.+', log)
if sanitizer_errors:
_print_log(log)
assert not sanitizer_errors, 'sanitizer error(s)'
if found:
print('skipped.')
def _print_log(data=None):
unit_log = unit_instance['log']
print('Path to unit.log:\n' + unit_log + '\n')
if option.print_log:
os.set_blocking(sys.stdout.fileno(), True)
sys.stdout.flush()
if data is None:
with open(unit_log, 'r', encoding='utf-8', errors='ignore') as f:
shutil.copyfileobj(f, sys.stdout)
else:
sys.stdout.write(data)
@pytest.fixture
def is_unsafe(request):
return request.config.getoption("--unsafe")
@pytest.fixture
def is_su(request):
return os.geteuid() == 0
def pytest_sessionfinish(session):
unit_stop()

View File

@@ -1,19 +0,0 @@
#!/usr/bin/env python3
import os
import sys
import unittest
if __name__ == '__main__':
loader = unittest.TestLoader()
suite = unittest.TestSuite()
this_dir = os.path.dirname(__file__)
tests = loader.discover(start_dir=this_dir)
suite.addTests(tests)
runner = unittest.TextTestRunner(stream=sys.stdout, verbosity=3)
result = runner.run(suite)
ret = not (len(result.failures) == len(result.errors) == 0)
sys.exit(ret)

View File

@@ -1,5 +1,5 @@
import pytest
import time import time
import unittest
from unit.applications.lang.python import TestApplicationPython from unit.applications.lang.python import TestApplicationPython
@@ -10,11 +10,9 @@ class TestAccessLog(TestApplicationPython):
def load(self, script): def load(self, script):
super().load(script) super().load(script)
self.assertIn( assert 'success' in self.conf(
'success', '"' + self.temp_dir + '/access.log"', 'access_log'
self.conf('"' + self.testdir + '/access.log"', 'access_log'), ), 'access_log configure'
'access_log configure',
)
def wait_for_record(self, pattern, name='access.log'): def wait_for_record(self, pattern, name='access.log'):
return super().wait_for_record(pattern, name) return super().wait_for_record(pattern, name)
@@ -22,7 +20,7 @@ class TestAccessLog(TestApplicationPython):
def test_access_log_keepalive(self): def test_access_log_keepalive(self):
self.load('mirror') self.load('mirror')
self.assertEqual(self.get()['status'], 200, 'init') assert self.get()['status'] == 200, 'init'
(resp, sock) = self.post( (resp, sock) = self.post(
headers={ headers={
@@ -35,9 +33,9 @@ class TestAccessLog(TestApplicationPython):
read_timeout=1, read_timeout=1,
) )
self.assertIsNotNone( assert (
self.wait_for_record(r'"POST / HTTP/1.1" 200 5'), 'keepalive 1' self.wait_for_record(r'"POST / HTTP/1.1" 200 5') is not None
) ), 'keepalive 1'
resp = self.post( resp = self.post(
headers={ headers={
@@ -51,9 +49,9 @@ class TestAccessLog(TestApplicationPython):
self.stop() self.stop()
self.assertIsNotNone( assert (
self.wait_for_record(r'"POST / HTTP/1.1" 200 10'), 'keepalive 2' self.wait_for_record(r'"POST / HTTP/1.1" 200 10') is not None
) ), 'keepalive 2'
def test_access_log_pipeline(self): def test_access_log_pipeline(self):
self.load('empty') self.load('empty')
@@ -79,18 +77,18 @@ Connection: close
self.stop() self.stop()
self.assertIsNotNone( assert (
self.wait_for_record(r'"GET / HTTP/1.1" 200 0 "Referer-1" "-"'), self.wait_for_record(r'"GET / HTTP/1.1" 200 0 "Referer-1" "-"')
'pipeline 1', is not None
) ), 'pipeline 1'
self.assertIsNotNone( assert (
self.wait_for_record(r'"GET / HTTP/1.1" 200 0 "Referer-2" "-"'), self.wait_for_record(r'"GET / HTTP/1.1" 200 0 "Referer-2" "-"')
'pipeline 2', is not None
) ), 'pipeline 2'
self.assertIsNotNone( assert (
self.wait_for_record(r'"GET / HTTP/1.1" 200 0 "Referer-3" "-"'), self.wait_for_record(r'"GET / HTTP/1.1" 200 0 "Referer-3" "-"')
'pipeline 3', is not None
) ), 'pipeline 3'
def test_access_log_ipv6(self): def test_access_log_ipv6(self):
self.load('empty') self.load('empty')
@@ -101,17 +99,17 @@ Connection: close
self.stop() self.stop()
self.assertIsNotNone( assert (
self.wait_for_record( self.wait_for_record(
r'::1 - - \[.+\] "GET / HTTP/1.1" 200 0 "-" "-"' r'::1 - - \[.+\] "GET / HTTP/1.1" 200 0 "-" "-"'
),
'ipv6',
) )
is not None
), 'ipv6'
def test_access_log_unix(self): def test_access_log_unix(self):
self.load('empty') self.load('empty')
addr = self.testdir + '/sock' addr = self.temp_dir + '/sock'
self.conf( self.conf(
{"unix:" + addr: {"pass": "applications/empty"}}, 'listeners' {"unix:" + addr: {"pass": "applications/empty"}}, 'listeners'
@@ -121,12 +119,12 @@ Connection: close
self.stop() self.stop()
self.assertIsNotNone( assert (
self.wait_for_record( self.wait_for_record(
r'unix: - - \[.+\] "GET / HTTP/1.1" 200 0 "-" "-"' r'unix: - - \[.+\] "GET / HTTP/1.1" 200 0 "-" "-"'
),
'unix',
) )
is not None
), 'unix'
def test_access_log_referer(self): def test_access_log_referer(self):
self.load('empty') self.load('empty')
@@ -141,12 +139,10 @@ Connection: close
self.stop() self.stop()
self.assertIsNotNone( assert (
self.wait_for_record( self.wait_for_record(r'"GET / HTTP/1.1" 200 0 "referer-value" "-"')
r'"GET / HTTP/1.1" 200 0 "referer-value" "-"' is not None
), ), 'referer'
'referer',
)
def test_access_log_user_agent(self): def test_access_log_user_agent(self):
self.load('empty') self.load('empty')
@@ -161,12 +157,12 @@ Connection: close
self.stop() self.stop()
self.assertIsNotNone( assert (
self.wait_for_record( self.wait_for_record(
r'"GET / HTTP/1.1" 200 0 "-" "user-agent-value"' r'"GET / HTTP/1.1" 200 0 "-" "user-agent-value"'
),
'user agent',
) )
is not None
), 'user agent'
def test_access_log_http10(self): def test_access_log_http10(self):
self.load('empty') self.load('empty')
@@ -175,14 +171,14 @@ Connection: close
self.stop() self.stop()
self.assertIsNotNone( assert (
self.wait_for_record(r'"GET / HTTP/1.0" 200 0 "-" "-"'), 'http 1.0' self.wait_for_record(r'"GET / HTTP/1.0" 200 0 "-" "-"') is not None
) ), 'http 1.0'
def test_access_log_partial(self): def test_access_log_partial(self):
self.load('empty') self.load('empty')
self.assertEqual(self.post()['status'], 200, 'init') assert self.post()['status'] == 200, 'init'
resp = self.http(b"""GE""", raw=True, read_timeout=1) resp = self.http(b"""GE""", raw=True, read_timeout=1)
@@ -190,27 +186,27 @@ Connection: close
self.stop() self.stop()
self.assertIsNotNone( assert (
self.wait_for_record(r'"GE" 400 0 "-" "-"'), 'partial' self.wait_for_record(r'"GE" 400 0 "-" "-"') is not None
) ), 'partial'
def test_access_log_partial_2(self): def test_access_log_partial_2(self):
self.load('empty') self.load('empty')
self.assertEqual(self.post()['status'], 200, 'init') assert self.post()['status'] == 200, 'init'
self.http(b"""GET /\n""", raw=True) self.http(b"""GET /\n""", raw=True)
self.stop() self.stop()
self.assertIsNotNone( assert (
self.wait_for_record(r'"GET /" 400 \d+ "-" "-"'), 'partial 2' self.wait_for_record(r'"GET /" 400 \d+ "-" "-"') is not None
) ), 'partial 2'
def test_access_log_partial_3(self): def test_access_log_partial_3(self):
self.load('empty') self.load('empty')
self.assertEqual(self.post()['status'], 200, 'init') assert self.post()['status'] == 200, 'init'
resp = self.http(b"""GET / HTTP/1.1""", raw=True, read_timeout=1) resp = self.http(b"""GET / HTTP/1.1""", raw=True, read_timeout=1)
@@ -218,14 +214,14 @@ Connection: close
self.stop() self.stop()
self.assertIsNotNone( assert (
self.wait_for_record(r'"GET /" 400 0 "-" "-"'), 'partial 3' self.wait_for_record(r'"GET /" 400 0 "-" "-"') is not None
) ), 'partial 3'
def test_access_log_partial_4(self): def test_access_log_partial_4(self):
self.load('empty') self.load('empty')
self.assertEqual(self.post()['status'], 200, 'init') assert self.post()['status'] == 200, 'init'
resp = self.http(b"""GET / HTTP/1.1\n""", raw=True, read_timeout=1) resp = self.http(b"""GET / HTTP/1.1\n""", raw=True, read_timeout=1)
@@ -233,25 +229,24 @@ Connection: close
self.stop() self.stop()
self.assertIsNotNone( assert (
self.wait_for_record(r'"GET / HTTP/1.1" 400 0 "-" "-"'), self.wait_for_record(r'"GET / HTTP/1.1" 400 0 "-" "-"') is not None
'partial 4', ), 'partial 4'
)
@unittest.skip('not yet') @pytest.mark.skip('not yet')
def test_access_log_partial_5(self): def test_access_log_partial_5(self):
self.load('empty') self.load('empty')
self.assertEqual(self.post()['status'], 200, 'init') assert self.post()['status'] == 200, 'init'
self.get(headers={'Connection': 'close'}) self.get(headers={'Connection': 'close'})
self.stop() self.stop()
self.assertIsNotNone( assert (
self.wait_for_record(r'"GET / HTTP/1.1" 400 \d+ "-" "-"'), self.wait_for_record(r'"GET / HTTP/1.1" 400 \d+ "-" "-"')
'partial 5', is not None
) ), 'partial 5'
def test_access_log_get_parameters(self): def test_access_log_get_parameters(self):
self.load('empty') self.load('empty')
@@ -260,12 +255,12 @@ Connection: close
self.stop() self.stop()
self.assertIsNotNone( assert (
self.wait_for_record( self.wait_for_record(
r'"GET /\?blah&var=val HTTP/1.1" 200 0 "-" "-"' r'"GET /\?blah&var=val HTTP/1.1" 200 0 "-" "-"'
),
'get parameters',
) )
is not None
), 'get parameters'
def test_access_log_delete(self): def test_access_log_delete(self):
self.load('empty') self.load('empty')
@@ -276,25 +271,20 @@ Connection: close
self.stop() self.stop()
self.assertIsNone( assert self.search_in_log(r'/delete', 'access.log') is None, 'delete'
self.search_in_log(r'/delete', 'access.log'), 'delete'
)
def test_access_log_change(self): def test_access_log_change(self):
self.load('empty') self.load('empty')
self.get() self.get()
self.conf('"' + self.testdir + '/new.log"', 'access_log') self.conf('"' + self.temp_dir + '/new.log"', 'access_log')
self.get() self.get()
self.stop() self.stop()
self.assertIsNotNone( assert (
self.wait_for_record(r'"GET / HTTP/1.1" 200 0 "-" "-"', 'new.log'), self.wait_for_record(r'"GET / HTTP/1.1" 200 0 "-" "-"', 'new.log')
'change', is not None
) ), 'change'
if __name__ == '__main__':
TestAccessLog.main()

View File

@@ -1,21 +1,20 @@
import unittest import pytest
from unit.control import TestControl from unit.control import TestControl
from conftest import skip_alert
class TestConfiguration(TestControl): class TestConfiguration(TestControl):
prerequisites = {'modules': {'python': 'any'}} prerequisites = {'modules': {'python': 'any'}}
def test_json_empty(self): def test_json_empty(self):
self.assertIn('error', self.conf(''), 'empty') assert 'error' in self.conf(''), 'empty'
def test_json_leading_zero(self): def test_json_leading_zero(self):
self.assertIn('error', self.conf('00'), 'leading zero') assert 'error' in self.conf('00'), 'leading zero'
def test_json_unicode(self): def test_json_unicode(self):
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
b""" b"""
{ {
"ap\u0070": { "ap\u0070": {
@@ -27,27 +26,19 @@ class TestConfiguration(TestControl):
} }
""", """,
'applications', 'applications',
), ), 'unicode'
'unicode',
)
self.assertDictEqual( assert self.conf_get('applications') == {
self.conf_get('applications'),
{
"app": { "app": {
"type": "python", "type": "python",
"processes": {"spare": 0}, "processes": {"spare": 0},
"path": "/app", "path": "/app",
"module": "wsgi", "module": "wsgi",
} }
}, }, 'unicode get'
'unicode get',
)
def test_json_unicode_2(self): def test_json_unicode_2(self):
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{ {
"приложение": { "приложение": {
"type": "python", "type": "python",
@@ -57,18 +48,12 @@ class TestConfiguration(TestControl):
} }
}, },
'applications', 'applications',
), ), 'unicode 2'
'unicode 2',
)
self.assertIn( assert 'приложение' in self.conf_get('applications'), 'unicode 2 get'
'приложение', self.conf_get('applications'), 'unicode 2 get'
)
def test_json_unicode_number(self): def test_json_unicode_number(self):
self.assertIn( assert 'error' in self.conf(
'error',
self.conf(
b""" b"""
{ {
"app": { "app": {
@@ -80,14 +65,10 @@ class TestConfiguration(TestControl):
} }
""", """,
'applications', 'applications',
), ), 'unicode number'
'unicode number',
)
def test_json_utf8_bom(self): def test_json_utf8_bom(self):
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
b"""\xEF\xBB\xBF b"""\xEF\xBB\xBF
{ {
"app": { "app": {
@@ -99,14 +80,10 @@ class TestConfiguration(TestControl):
} }
""", """,
'applications', 'applications',
), ), 'UTF-8 BOM'
'UTF-8 BOM',
)
def test_json_comment_single_line(self): def test_json_comment_single_line(self):
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
b""" b"""
// this is bridge // this is bridge
{ {
@@ -122,14 +99,10 @@ class TestConfiguration(TestControl):
// end of json \xEF\t // end of json \xEF\t
""", """,
'applications', 'applications',
), ), 'single line comments'
'single line comments',
)
def test_json_comment_multi_line(self): def test_json_comment_multi_line(self):
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
b""" b"""
/* this is bridge */ /* this is bridge */
{ {
@@ -149,39 +122,29 @@ class TestConfiguration(TestControl):
/* end of json \xEF\t\b */ /* end of json \xEF\t\b */
""", """,
'applications', 'applications',
), ), 'multi line comments'
'multi line comments',
)
def test_json_comment_invalid(self): def test_json_comment_invalid(self):
self.assertIn('error', self.conf(b'/{}', 'applications'), 'slash') assert 'error' in self.conf(b'/{}', 'applications'), 'slash'
self.assertIn('error', self.conf(b'//{}', 'applications'), 'comment') assert 'error' in self.conf(b'//{}', 'applications'), 'comment'
self.assertIn('error', self.conf(b'{} /', 'applications'), 'slash end') assert 'error' in self.conf(b'{} /', 'applications'), 'slash end'
self.assertIn( assert 'error' in self.conf(b'/*{}', 'applications'), 'slash star'
'error', self.conf(b'/*{}', 'applications'), 'slash star' assert 'error' in self.conf(b'{} /*', 'applications'), 'slash star end'
)
self.assertIn(
'error', self.conf(b'{} /*', 'applications'), 'slash star end'
)
def test_applications_open_brace(self): def test_applications_open_brace(self):
self.assertIn('error', self.conf('{', 'applications'), 'open brace') assert 'error' in self.conf('{', 'applications'), 'open brace'
def test_applications_string(self): def test_applications_string(self):
self.assertIn('error', self.conf('"{}"', 'applications'), 'string') assert 'error' in self.conf('"{}"', 'applications'), 'string'
@unittest.skip('not yet, unsafe') @pytest.mark.skip('not yet, unsafe')
def test_applications_type_only(self): def test_applications_type_only(self):
self.assertIn( assert 'error' in self.conf(
'error', {"app": {"type": "python"}}, 'applications'
self.conf({"app": {"type": "python"}}, 'applications'), ), 'type only'
'type only',
)
def test_applications_miss_quote(self): def test_applications_miss_quote(self):
self.assertIn( assert 'error' in self.conf(
'error',
self.conf(
""" """
{ {
app": { app": {
@@ -193,14 +156,10 @@ class TestConfiguration(TestControl):
} }
""", """,
'applications', 'applications',
), ), 'miss quote'
'miss quote',
)
def test_applications_miss_colon(self): def test_applications_miss_colon(self):
self.assertIn( assert 'error' in self.conf(
'error',
self.conf(
""" """
{ {
"app" { "app" {
@@ -212,14 +171,10 @@ class TestConfiguration(TestControl):
} }
""", """,
'applications', 'applications',
), ), 'miss colon'
'miss colon',
)
def test_applications_miss_comma(self): def test_applications_miss_comma(self):
self.assertIn( assert 'error' in self.conf(
'error',
self.conf(
""" """
{ {
"app": { "app": {
@@ -231,19 +186,15 @@ class TestConfiguration(TestControl):
} }
""", """,
'applications', 'applications',
), ), 'miss comma'
'miss comma',
)
def test_applications_skip_spaces(self): def test_applications_skip_spaces(self):
self.assertIn( assert 'success' in self.conf(
'success', self.conf(b'{ \n\r\t}', 'applications'), 'skip spaces' b'{ \n\r\t}', 'applications'
) ), 'skip spaces'
def test_applications_relative_path(self): def test_applications_relative_path(self):
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{ {
"app": { "app": {
"type": "python", "type": "python",
@@ -253,27 +204,21 @@ class TestConfiguration(TestControl):
} }
}, },
'applications', 'applications',
), ), 'relative path'
'relative path',
)
@unittest.skip('not yet, unsafe') @pytest.mark.skip('not yet, unsafe')
def test_listeners_empty(self): def test_listeners_empty(self):
self.assertIn( assert 'error' in self.conf(
'error', self.conf({"*:7080": {}}, 'listeners'), 'listener empty' {"*:7080": {}}, 'listeners'
) ), 'listener empty'
def test_listeners_no_app(self): def test_listeners_no_app(self):
self.assertIn( assert 'error' in self.conf(
'error', {"*:7080": {"pass": "applications/app"}}, 'listeners'
self.conf({"*:7080": {"pass": "applications/app"}}, 'listeners'), ), 'listeners no app'
'listeners no app',
)
def test_listeners_wildcard(self): def test_listeners_wildcard(self):
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{ {
"listeners": {"*:7080": {"pass": "applications/app"}}, "listeners": {"*:7080": {"pass": "applications/app"}},
"applications": { "applications": {
@@ -285,14 +230,10 @@ class TestConfiguration(TestControl):
} }
}, },
} }
), ), 'listeners wildcard'
'listeners wildcard',
)
def test_listeners_explicit(self): def test_listeners_explicit(self):
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{ {
"listeners": {"127.0.0.1:7080": {"pass": "applications/app"}}, "listeners": {"127.0.0.1:7080": {"pass": "applications/app"}},
"applications": { "applications": {
@@ -304,14 +245,10 @@ class TestConfiguration(TestControl):
} }
}, },
} }
), ), 'explicit'
'explicit',
)
def test_listeners_explicit_ipv6(self): def test_listeners_explicit_ipv6(self):
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{ {
"listeners": {"[::1]:7080": {"pass": "applications/app"}}, "listeners": {"[::1]:7080": {"pass": "applications/app"}},
"applications": { "applications": {
@@ -323,15 +260,11 @@ class TestConfiguration(TestControl):
} }
}, },
} }
), ), 'explicit ipv6'
'explicit ipv6',
)
@unittest.skip('not yet, unsafe') @pytest.mark.skip('not yet, unsafe')
def test_listeners_no_port(self): def test_listeners_no_port(self):
self.assertIn( assert 'error' in self.conf(
'error',
self.conf(
{ {
"listeners": {"127.0.0.1": {"pass": "applications/app"}}, "listeners": {"127.0.0.1": {"pass": "applications/app"}},
"applications": { "applications": {
@@ -343,16 +276,12 @@ class TestConfiguration(TestControl):
} }
}, },
} }
), ), 'no port'
'no port',
)
def test_json_application_name_large(self): def test_json_application_name_large(self):
name = "X" * 1024 * 1024 name = "X" * 1024 * 1024
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{ {
"listeners": {"*:7080": {"pass": "applications/" + name}}, "listeners": {"*:7080": {"pass": "applications/" + name}},
"applications": { "applications": {
@@ -364,10 +293,9 @@ class TestConfiguration(TestControl):
} }
}, },
} }
),
) )
@unittest.skip('not yet') @pytest.mark.skip('not yet')
def test_json_application_many(self): def test_json_application_many(self):
apps = 999 apps = 999
@@ -388,7 +316,7 @@ class TestConfiguration(TestControl):
}, },
} }
self.assertIn('success', self.conf(conf)) assert 'success' in self.conf(conf)
def test_json_application_many2(self): def test_json_application_many2(self):
conf = { conf = {
@@ -407,22 +335,14 @@ class TestConfiguration(TestControl):
"listeners": {"*:7080": {"pass": "applications/app-1"}}, "listeners": {"*:7080": {"pass": "applications/app-1"}},
} }
self.assertIn('success', self.conf(conf)) assert 'success' in self.conf(conf)
def test_unprivileged_user_error(self): def test_unprivileged_user_error(self, is_su):
self.skip_alerts.extend( skip_alert(r'cannot set user "root"', r'failed to apply new conf')
[ if is_su:
r'cannot set user "root"', pytest.skip('unprivileged tests')
r'failed to apply new conf',
]
)
if self.is_su:
print('unprivileged tests, skip this')
raise unittest.SkipTest()
self.assertIn( assert 'error' in self.conf(
'error',
self.conf(
{ {
"app": { "app": {
"type": "external", "type": "external",
@@ -432,10 +352,4 @@ class TestConfiguration(TestControl):
} }
}, },
'applications', 'applications',
), ), 'setting user'
'setting user',
)
if __name__ == '__main__':
TestConfiguration.main()

View File

@@ -1,4 +1,5 @@
from unit.applications.lang.go import TestApplicationGo from unit.applications.lang.go import TestApplicationGo
import re
class TestGoApplication(TestApplicationGo): class TestGoApplication(TestApplicationGo):
@@ -19,22 +20,18 @@ class TestGoApplication(TestApplicationGo):
body=body, body=body,
) )
self.assertEqual(resp['status'], 200, 'status') assert resp['status'] == 200, 'status'
headers = resp['headers'] headers = resp['headers']
header_server = headers.pop('Server') header_server = headers.pop('Server')
self.assertRegex(header_server, r'Unit/[\d\.]+', 'server header') assert re.search(r'Unit/[\d\.]+', header_server), 'server header'
date = headers.pop('Date') date = headers.pop('Date')
self.assertEqual(date[-4:], ' GMT', 'date header timezone') assert date[-4:] == ' GMT', 'date header timezone'
self.assertLess( assert (
abs(self.date_to_sec_epoch(date) - self.sec_epoch()), abs(self.date_to_sec_epoch(date) - self.sec_epoch()) < 5
5, ), 'date header'
'date header',
)
self.assertDictEqual( assert headers == {
headers,
{
'Content-Length': str(len(body)), 'Content-Length': str(len(body)),
'Content-Type': 'text/html', 'Content-Type': 'text/html',
'Request-Method': 'POST', 'Request-Method': 'POST',
@@ -45,18 +42,16 @@ class TestGoApplication(TestApplicationGo):
'Server-Protocol-Minor': '1', 'Server-Protocol-Minor': '1',
'Custom-Header': 'blah', 'Custom-Header': 'blah',
'Connection': 'close', 'Connection': 'close',
}, }, 'headers'
'headers', assert resp['body'] == body, 'body'
)
self.assertEqual(resp['body'], body, 'body')
def test_go_application_get_variables(self): def test_go_application_get_variables(self):
self.load('get_variables') self.load('get_variables')
resp = self.get(url='/?var1=val1&var2=&var3') resp = self.get(url='/?var1=val1&var2=&var3')
self.assertEqual(resp['headers']['X-Var-1'], 'val1', 'GET variables') assert resp['headers']['X-Var-1'] == 'val1', 'GET variables'
self.assertEqual(resp['headers']['X-Var-2'], '', 'GET variables 2') assert resp['headers']['X-Var-2'] == '', 'GET variables 2'
self.assertEqual(resp['headers']['X-Var-3'], '', 'GET variables 3') assert resp['headers']['X-Var-3'] == '', 'GET variables 3'
def test_go_application_post_variables(self): def test_go_application_post_variables(self):
self.load('post_variables') self.load('post_variables')
@@ -70,24 +65,24 @@ class TestGoApplication(TestApplicationGo):
body='var1=val1&var2=&var3', body='var1=val1&var2=&var3',
) )
self.assertEqual(resp['headers']['X-Var-1'], 'val1', 'POST variables') assert resp['headers']['X-Var-1'] == 'val1', 'POST variables'
self.assertEqual(resp['headers']['X-Var-2'], '', 'POST variables 2') assert resp['headers']['X-Var-2'] == '', 'POST variables 2'
self.assertEqual(resp['headers']['X-Var-3'], '', 'POST variables 3') assert resp['headers']['X-Var-3'] == '', 'POST variables 3'
def test_go_application_404(self): def test_go_application_404(self):
self.load('404') self.load('404')
resp = self.get() resp = self.get()
self.assertEqual(resp['status'], 404, '404 status') assert resp['status'] == 404, '404 status'
self.assertRegex( assert re.search(
resp['body'], r'<title>404 Not Found</title>', '404 body' r'<title>404 Not Found</title>', resp['body']
) ), '404 body'
def test_go_keepalive_body(self): def test_go_keepalive_body(self):
self.load('mirror') self.load('mirror')
self.assertEqual(self.get()['status'], 200, 'init') assert self.get()['status'] == 200, 'init'
body = '0123456789' * 500 body = '0123456789' * 500
(resp, sock) = self.post( (resp, sock) = self.post(
@@ -101,7 +96,7 @@ class TestGoApplication(TestApplicationGo):
read_timeout=1, read_timeout=1,
) )
self.assertEqual(resp['body'], body, 'keep-alive 1') assert resp['body'] == body, 'keep-alive 1'
body = '0123456789' body = '0123456789'
resp = self.post( resp = self.post(
@@ -114,7 +109,7 @@ class TestGoApplication(TestApplicationGo):
body=body, body=body,
) )
self.assertEqual(resp['body'], body, 'keep-alive 2') assert resp['body'] == body, 'keep-alive 2'
def test_go_application_cookies(self): def test_go_application_cookies(self):
self.load('cookies') self.load('cookies')
@@ -127,28 +122,24 @@ class TestGoApplication(TestApplicationGo):
} }
) )
self.assertEqual(resp['headers']['X-Cookie-1'], 'val1', 'cookie 1') assert resp['headers']['X-Cookie-1'] == 'val1', 'cookie 1'
self.assertEqual(resp['headers']['X-Cookie-2'], 'val2', 'cookie 2') assert resp['headers']['X-Cookie-2'] == 'val2', 'cookie 2'
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')
self.assertIn( assert 'error' in \
'error',
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')
self.assertEqual( assert self.get()['headers']['X-Arg-0'] == self.conf_get(
self.get()['headers']['X-Arg-0'], 'applications/command_line_arguments/executable'
self.conf_get('applications/command_line_arguments/executable'), ), 'argument 0'
'argument 0',
)
def test_go_application_command_line_arguments(self): def test_go_application_command_line_arguments(self):
self.load('command_line_arguments') self.load('command_line_arguments')
@@ -162,9 +153,9 @@ class TestGoApplication(TestApplicationGo):
'applications/command_line_arguments/arguments', 'applications/command_line_arguments/arguments',
) )
self.assertEqual( assert (
self.get()['body'], arg1 + ',' + arg2 + ',' + arg3, 'arguments' self.get()['body'] == arg1 + ',' + arg2 + ',' + arg3
) ), 'arguments'
def test_go_application_command_line_arguments_change(self): def test_go_application_command_line_arguments_change(self):
self.load('command_line_arguments') self.load('command_line_arguments')
@@ -173,18 +164,14 @@ class TestGoApplication(TestApplicationGo):
self.conf('["0", "a", "$", ""]', args_path) self.conf('["0", "a", "$", ""]', args_path)
self.assertEqual(self.get()['body'], '0,a,$,', 'arguments') assert self.get()['body'] == '0,a,$,', 'arguments'
self.conf('["-1", "b", "%"]', args_path) self.conf('["-1", "b", "%"]', args_path)
self.assertEqual(self.get()['body'], '-1,b,%', 'arguments change') assert self.get()['body'] == '-1,b,%', 'arguments change'
self.conf('[]', args_path) self.conf('[]', args_path)
self.assertEqual( assert (
self.get()['headers']['Content-Length'], '0', 'arguments empty' self.get()['headers']['Content-Length'] == '0'
) ), 'arguments empty'
if __name__ == '__main__':
TestGoApplication.main()

View File

@@ -1,21 +1,21 @@
import grp import grp
import os
import pwd import pwd
import unittest import pytest
from unit.applications.lang.go import TestApplicationGo from unit.applications.lang.go import TestApplicationGo
from unit.feature.isolation import TestFeatureIsolation from unit.feature.isolation import TestFeatureIsolation
class TestGoIsolation(TestApplicationGo): class TestGoIsolation(TestApplicationGo):
prerequisites = {'modules': {'go': 'any'}, 'features': ['isolation']} prerequisites = {'modules': {'go': 'any'}, 'features': ['isolation']}
isolation = TestFeatureIsolation() isolation = TestFeatureIsolation()
@classmethod @classmethod
def setUpClass(cls, complete_check=True): def setup_class(cls, complete_check=True):
unit = super().setUpClass(complete_check=False) unit = super().setup_class(complete_check=False)
TestFeatureIsolation().check(cls.available, unit.testdir) TestFeatureIsolation().check(cls.available, unit.temp_dir)
return unit if not complete_check else unit.complete() return unit if not complete_check else unit.complete()
@@ -41,24 +41,20 @@ class TestGoIsolation(TestApplicationGo):
for ns, ns_value in self.available['features']['isolation'].items(): for ns, ns_value in self.available['features']['isolation'].items():
if ns.upper() in obj['NS']: if ns.upper() in obj['NS']:
self.assertEqual( assert obj['NS'][ns.upper()] == ns_value, '%s match' % ns
obj['NS'][ns.upper()], ns_value, '%s match' % ns
)
def test_isolation_unpriv_user(self): def test_isolation_unpriv_user(self, is_su):
if not self.isolation_key('unprivileged_userns_clone'): if not self.isolation_key('unprivileged_userns_clone'):
print('unprivileged clone is not available') pytest.skip('unprivileged clone is not available')
raise unittest.SkipTest()
if self.is_su: if is_su:
print('privileged tests, skip this') pytest.skip('privileged tests, skip this')
raise unittest.SkipTest()
self.load('ns_inspect') self.load('ns_inspect')
obj = self.getjson()['body'] obj = self.getjson()['body']
self.assertEqual(obj['UID'], self.uid, 'uid match') assert obj['UID'] == os.geteuid(), 'uid match'
self.assertEqual(obj['GID'], self.gid, 'gid match') assert obj['GID'] == os.getegid(), 'gid match'
self.load('ns_inspect', isolation={'namespaces': {'credential': True}}) self.load('ns_inspect', isolation={'namespaces': {'credential': True}})
@@ -67,8 +63,8 @@ class TestGoIsolation(TestApplicationGo):
nobody_uid, nogroup_gid, nogroup = self.unpriv_creds() nobody_uid, nogroup_gid, nogroup = self.unpriv_creds()
# unprivileged unit map itself to nobody in the container by default # unprivileged unit map itself to nobody in the container by default
self.assertEqual(obj['UID'], nobody_uid, 'uid of nobody') assert obj['UID'] == nobody_uid, 'uid of nobody'
self.assertEqual(obj['GID'], nogroup_gid, 'gid of %s' % nogroup) assert obj['GID'] == nogroup_gid, 'gid of %s' % nogroup
self.load( self.load(
'ns_inspect', 'ns_inspect',
@@ -78,8 +74,8 @@ class TestGoIsolation(TestApplicationGo):
obj = self.getjson()['body'] obj = self.getjson()['body']
self.assertEqual(obj['UID'], 0, 'uid match user=root') assert obj['UID'] == 0, 'uid match user=root'
self.assertEqual(obj['GID'], 0, 'gid match user=root') assert obj['GID'] == 0, 'gid match user=root'
self.load( self.load(
'ns_inspect', 'ns_inspect',
@@ -90,10 +86,8 @@ class TestGoIsolation(TestApplicationGo):
obj = self.getjson()['body'] obj = self.getjson()['body']
self.assertEqual(obj['UID'], 0, 'uid match user=root group=nogroup') assert obj['UID'] == 0, 'uid match user=root group=nogroup'
self.assertEqual( assert obj['GID'] == nogroup_gid, 'gid match user=root group=nogroup'
obj['GID'], nogroup_gid, 'gid match user=root group=nogroup'
)
self.load( self.load(
'ns_inspect', 'ns_inspect',
@@ -101,20 +95,19 @@ class TestGoIsolation(TestApplicationGo):
group='root', group='root',
isolation={ isolation={
'namespaces': {'credential': True}, 'namespaces': {'credential': True},
'uidmap': [{'container': 0, 'host': self.uid, 'size': 1}], 'uidmap': [{'container': 0, 'host': os.geteuid(), 'size': 1}],
'gidmap': [{'container': 0, 'host': self.gid, 'size': 1}], 'gidmap': [{'container': 0, 'host': os.getegid(), 'size': 1}],
}, },
) )
obj = self.getjson()['body'] obj = self.getjson()['body']
self.assertEqual(obj['UID'], 0, 'uid match uidmap') assert obj['UID'] == 0, 'uid match uidmap'
self.assertEqual(obj['GID'], 0, 'gid match gidmap') assert obj['GID'] == 0, 'gid match gidmap'
def test_isolation_priv_user(self): def test_isolation_priv_user(self, is_su):
if not self.is_su: if not is_su:
print('unprivileged tests, skip this') pytest.skip('unprivileged tests, skip this')
raise unittest.SkipTest()
self.load('ns_inspect') self.load('ns_inspect')
@@ -122,16 +115,16 @@ class TestGoIsolation(TestApplicationGo):
obj = self.getjson()['body'] obj = self.getjson()['body']
self.assertEqual(obj['UID'], nobody_uid, 'uid match') assert obj['UID'] == nobody_uid, 'uid match'
self.assertEqual(obj['GID'], nogroup_gid, 'gid match') assert obj['GID'] == nogroup_gid, 'gid match'
self.load('ns_inspect', isolation={'namespaces': {'credential': True}}) self.load('ns_inspect', isolation={'namespaces': {'credential': True}})
obj = self.getjson()['body'] obj = self.getjson()['body']
# privileged unit map app creds in the container by default # privileged unit map app creds in the container by default
self.assertEqual(obj['UID'], nobody_uid, 'uid nobody') assert obj['UID'] == nobody_uid, 'uid nobody'
self.assertEqual(obj['GID'], nogroup_gid, 'gid nobody') assert obj['GID'] == nogroup_gid, 'gid nobody'
self.load( self.load(
'ns_inspect', 'ns_inspect',
@@ -141,8 +134,8 @@ class TestGoIsolation(TestApplicationGo):
obj = self.getjson()['body'] obj = self.getjson()['body']
self.assertEqual(obj['UID'], 0, 'uid nobody user=root') assert obj['UID'] == 0, 'uid nobody user=root'
self.assertEqual(obj['GID'], 0, 'gid nobody user=root') assert obj['GID'] == 0, 'gid nobody user=root'
self.load( self.load(
'ns_inspect', 'ns_inspect',
@@ -153,10 +146,8 @@ class TestGoIsolation(TestApplicationGo):
obj = self.getjson()['body'] obj = self.getjson()['body']
self.assertEqual(obj['UID'], 0, 'uid match user=root group=nogroup') assert obj['UID'] == 0, 'uid match user=root group=nogroup'
self.assertEqual( assert obj['GID'] == nogroup_gid, 'gid match user=root group=nogroup'
obj['GID'], nogroup_gid, 'gid match user=root group=nogroup'
)
self.load( self.load(
'ns_inspect', 'ns_inspect',
@@ -171,8 +162,8 @@ class TestGoIsolation(TestApplicationGo):
obj = self.getjson()['body'] obj = self.getjson()['body']
self.assertEqual(obj['UID'], 0, 'uid match uidmap user=root') assert obj['UID'] == 0, 'uid match uidmap user=root'
self.assertEqual(obj['GID'], 0, 'gid match gidmap user=root') assert obj['GID'] == 0, 'gid match gidmap user=root'
# map 65535 uids # map 65535 uids
self.load( self.load(
@@ -188,21 +179,15 @@ class TestGoIsolation(TestApplicationGo):
obj = self.getjson()['body'] obj = self.getjson()['body']
self.assertEqual( assert obj['UID'] == nobody_uid, 'uid match uidmap user=nobody'
obj['UID'], nobody_uid, 'uid match uidmap user=nobody' assert obj['GID'] == nogroup_gid, 'gid match uidmap user=nobody'
)
self.assertEqual(
obj['GID'], nogroup_gid, 'gid match uidmap user=nobody'
)
def test_isolation_mnt(self): def test_isolation_mnt(self):
if not self.isolation_key('mnt'): if not self.isolation_key('mnt'):
print('mnt namespace is not supported') pytest.skip('mnt namespace is not supported')
raise unittest.SkipTest()
if not self.isolation_key('unprivileged_userns_clone'): if not self.isolation_key('unprivileged_userns_clone'):
print('unprivileged clone is not available') pytest.skip('unprivileged clone is not available')
raise unittest.SkipTest()
self.load( self.load(
'ns_inspect', 'ns_inspect',
@@ -218,27 +203,20 @@ class TestGoIsolation(TestApplicationGo):
for ns in allns: for ns in allns:
if ns.upper() in obj['NS']: if ns.upper() in obj['NS']:
self.assertEqual( assert (
obj['NS'][ns.upper()], obj['NS'][ns.upper()]
self.available['features']['isolation'][ns], == self.available['features']['isolation'][ns]
'%s match' % ns, ), ('%s match' % ns)
)
self.assertNotEqual( assert obj['NS']['MNT'] != self.isolation.getns('mnt'), 'mnt set'
obj['NS']['MNT'], self.isolation.getns('mnt'), 'mnt set' assert obj['NS']['USER'] != self.isolation.getns('user'), 'user set'
)
self.assertNotEqual(
obj['NS']['USER'], self.isolation.getns('user'), 'user set'
)
def test_isolation_pid(self): def test_isolation_pid(self, is_su):
if not self.isolation_key('pid'): if not self.isolation_key('pid'):
print('pid namespace is not supported') pytest.skip('pid namespace is not supported')
raise unittest.SkipTest()
if not (self.is_su or self.isolation_key('unprivileged_userns_clone')): if not (is_su or self.isolation_key('unprivileged_userns_clone')):
print('requires root or unprivileged_userns_clone') pytest.skip('requires root or unprivileged_userns_clone')
raise unittest.SkipTest()
self.load( self.load(
'ns_inspect', 'ns_inspect',
@@ -247,7 +225,7 @@ class TestGoIsolation(TestApplicationGo):
obj = self.getjson()['body'] obj = self.getjson()['body']
self.assertEqual(obj['PID'], 1, 'pid of container is 1') assert obj['PID'] == 1, 'pid of container is 1'
def test_isolation_namespace_false(self): def test_isolation_namespace_false(self):
self.load('ns_inspect') self.load('ns_inspect')
@@ -275,78 +253,67 @@ class TestGoIsolation(TestApplicationGo):
for ns in allns: for ns in allns:
if ns.upper() in obj['NS']: if ns.upper() in obj['NS']:
self.assertEqual( assert (
obj['NS'][ns.upper()], obj['NS'][ns.upper()]
self.available['features']['isolation'][ns], == self.available['features']['isolation'][ns]
'%s match' % ns, ), ('%s match' % ns)
)
def test_go_isolation_rootfs_container(self): def test_go_isolation_rootfs_container(self):
if not self.isolation_key('unprivileged_userns_clone'): if not self.isolation_key('unprivileged_userns_clone'):
print('unprivileged clone is not available') pytest.skip('unprivileged clone is not available')
raise unittest.SkipTest()
if not self.isolation_key('mnt'): if not self.isolation_key('mnt'):
print('mnt namespace is not supported') pytest.skip('mnt namespace is not supported')
raise unittest.SkipTest()
isolation = { isolation = {
'namespaces': {'mount': True, 'credential': True}, 'namespaces': {'mount': True, 'credential': True},
'rootfs': self.testdir, 'rootfs': self.temp_dir,
} }
self.load('ns_inspect', isolation=isolation) self.load('ns_inspect', isolation=isolation)
obj = self.getjson(url='/?file=/go/app')['body'] obj = self.getjson(url='/?file=/go/app')['body']
self.assertEqual(obj['FileExists'], True, 'app relative to rootfs') assert obj['FileExists'] == True, 'app relative to rootfs'
obj = self.getjson(url='/?file=/bin/sh')['body'] obj = self.getjson(url='/?file=/bin/sh')['body']
self.assertEqual(obj['FileExists'], False, 'file should not exists') assert obj['FileExists'] == False, 'file should not exists'
def test_go_isolation_rootfs_container_priv(self): def test_go_isolation_rootfs_container_priv(self, is_su):
if not self.is_su: if not is_su:
print("requires root") pytest.skip('requires root')
raise unittest.SkipTest()
if not self.isolation_key('mnt'): if not self.isolation_key('mnt'):
print('mnt namespace is not supported') pytest.skip('mnt namespace is not supported')
raise unittest.SkipTest()
isolation = { isolation = {
'namespaces': {'mount': True}, 'namespaces': {'mount': True},
'rootfs': self.testdir, 'rootfs': self.temp_dir,
} }
self.load('ns_inspect', isolation=isolation) self.load('ns_inspect', isolation=isolation)
obj = self.getjson(url='/?file=/go/app')['body'] obj = self.getjson(url='/?file=/go/app')['body']
self.assertEqual(obj['FileExists'], True, 'app relative to rootfs') assert obj['FileExists'] == True, 'app relative to rootfs'
obj = self.getjson(url='/?file=/bin/sh')['body'] obj = self.getjson(url='/?file=/bin/sh')['body']
self.assertEqual(obj['FileExists'], False, 'file should not exists') assert obj['FileExists'] == False, 'file should not exists'
def test_go_isolation_rootfs_default_tmpfs(self): def test_go_isolation_rootfs_default_tmpfs(self):
if not self.isolation_key('unprivileged_userns_clone'): if not self.isolation_key('unprivileged_userns_clone'):
print('unprivileged clone is not available') pytest.skip('unprivileged clone is not available')
raise unittest.SkipTest()
if not self.isolation_key('mnt'): if not self.isolation_key('mnt'):
print('mnt namespace is not supported') pytest.skip('mnt namespace is not supported')
raise unittest.SkipTest()
isolation = { isolation = {
'namespaces': {'mount': True, 'credential': True}, 'namespaces': {'mount': True, 'credential': True},
'rootfs': self.testdir, 'rootfs': self.temp_dir,
} }
self.load('ns_inspect', isolation=isolation) self.load('ns_inspect', isolation=isolation)
obj = self.getjson(url='/?file=/tmp')['body'] obj = self.getjson(url='/?file=/tmp')['body']
self.assertEqual(obj['FileExists'], True, 'app has /tmp') assert obj['FileExists'] == True, 'app has /tmp'
if __name__ == '__main__':
TestGoIsolation.main()

View File

@@ -1,5 +1,5 @@
import os import os
import unittest import pytest
from unit.applications.lang.go import TestApplicationGo from unit.applications.lang.go import TestApplicationGo
@@ -7,28 +7,22 @@ from unit.applications.lang.go import TestApplicationGo
class TestGoIsolationRootfs(TestApplicationGo): class TestGoIsolationRootfs(TestApplicationGo):
prerequisites = {'modules': {'go': 'all'}} prerequisites = {'modules': {'go': 'all'}}
def test_go_isolation_rootfs_chroot(self): def test_go_isolation_rootfs_chroot(self, is_su):
if not self.is_su: if not is_su:
print("requires root") pytest.skip('requires root')
raise unittest.SkipTest()
if os.uname().sysname == 'Darwin': if os.uname().sysname == 'Darwin':
print('chroot tests not supported on OSX') pytest.skip('chroot tests not supported on OSX')
raise unittest.SkipTest()
isolation = { isolation = {
'rootfs': self.testdir, 'rootfs': self.temp_dir,
} }
self.load('ns_inspect', isolation=isolation) self.load('ns_inspect', isolation=isolation)
obj = self.getjson(url='/?file=/go/app')['body'] obj = self.getjson(url='/?file=/go/app')['body']
self.assertEqual(obj['FileExists'], True, 'app relative to rootfs') assert obj['FileExists'] == True, 'app relative to rootfs'
obj = self.getjson(url='/?file=/bin/sh')['body'] obj = self.getjson(url='/?file=/bin/sh')['body']
self.assertEqual(obj['FileExists'], False, 'file should not exists') assert obj['FileExists'] == False, 'file should not exists'
if __name__ == '__main__':
TestGoIsolationRootfs.main()

View File

@@ -1,4 +1,4 @@
import unittest import pytest
from unit.applications.lang.python import TestApplicationPython from unit.applications.lang.python import TestApplicationPython
@@ -17,12 +17,10 @@ class TestHTTPHeader(TestApplicationPython):
} }
) )
self.assertEqual(resp['status'], 200, 'value leading sp status') assert resp['status'] == 200, 'value leading sp status'
self.assertEqual( assert (
resp['headers']['Custom-Header'], resp['headers']['Custom-Header'] == ','
',', ), 'value leading sp custom header'
'value leading sp custom header',
)
def test_http_header_value_leading_htab(self): def test_http_header_value_leading_htab(self):
self.load('custom_header') self.load('custom_header')
@@ -35,12 +33,10 @@ class TestHTTPHeader(TestApplicationPython):
} }
) )
self.assertEqual(resp['status'], 200, 'value leading htab status') assert resp['status'] == 200, 'value leading htab status'
self.assertEqual( assert (
resp['headers']['Custom-Header'], resp['headers']['Custom-Header'] == ','
',', ), 'value leading htab custom header'
'value leading htab custom header',
)
def test_http_header_value_trailing_sp(self): def test_http_header_value_trailing_sp(self):
self.load('custom_header') self.load('custom_header')
@@ -53,12 +49,10 @@ class TestHTTPHeader(TestApplicationPython):
} }
) )
self.assertEqual(resp['status'], 200, 'value trailing sp status') assert resp['status'] == 200, 'value trailing sp status'
self.assertEqual( assert (
resp['headers']['Custom-Header'], resp['headers']['Custom-Header'] == ','
',', ), 'value trailing sp custom header'
'value trailing sp custom header',
)
def test_http_header_value_trailing_htab(self): def test_http_header_value_trailing_htab(self):
self.load('custom_header') self.load('custom_header')
@@ -71,12 +65,10 @@ class TestHTTPHeader(TestApplicationPython):
} }
) )
self.assertEqual(resp['status'], 200, 'value trailing htab status') assert resp['status'] == 200, 'value trailing htab status'
self.assertEqual( assert (
resp['headers']['Custom-Header'], resp['headers']['Custom-Header'] == ','
',', ), 'value trailing htab custom header'
'value trailing htab custom header',
)
def test_http_header_value_both_sp(self): def test_http_header_value_both_sp(self):
self.load('custom_header') self.load('custom_header')
@@ -89,12 +81,10 @@ class TestHTTPHeader(TestApplicationPython):
} }
) )
self.assertEqual(resp['status'], 200, 'value both sp status') assert resp['status'] == 200, 'value both sp status'
self.assertEqual( assert (
resp['headers']['Custom-Header'], resp['headers']['Custom-Header'] == ','
',', ), 'value both sp custom header'
'value both sp custom header',
)
def test_http_header_value_both_htab(self): def test_http_header_value_both_htab(self):
self.load('custom_header') self.load('custom_header')
@@ -107,12 +97,10 @@ class TestHTTPHeader(TestApplicationPython):
} }
) )
self.assertEqual(resp['status'], 200, 'value both htab status') assert resp['status'] == 200, 'value both htab status'
self.assertEqual( assert (
resp['headers']['Custom-Header'], resp['headers']['Custom-Header'] == ','
',', ), 'value both htab custom header'
'value both htab custom header',
)
def test_http_header_value_chars(self): def test_http_header_value_chars(self):
self.load('custom_header') self.load('custom_header')
@@ -125,12 +113,11 @@ class TestHTTPHeader(TestApplicationPython):
} }
) )
self.assertEqual(resp['status'], 200, 'value chars status') assert resp['status'] == 200, 'value chars status'
self.assertEqual( assert (
resp['headers']['Custom-Header'], resp['headers']['Custom-Header']
'(),/:;<=>?@[\]{}\t !#$%&\'*+-.^_`|~', == '(),/:;<=>?@[\]{}\t !#$%&\'*+-.^_`|~'
'value chars custom header', ), 'value chars custom header'
)
def test_http_header_value_chars_edge(self): def test_http_header_value_chars_edge(self):
self.load('custom_header') self.load('custom_header')
@@ -146,10 +133,8 @@ Connection: close
encoding='latin1', encoding='latin1',
) )
self.assertEqual(resp['status'], 200, 'value chars edge status') assert resp['status'] == 200, 'value chars edge status'
self.assertEqual( assert resp['headers']['Custom-Header'] == '\xFF', 'value chars edge'
resp['headers']['Custom-Header'], '\xFF', 'value chars edge'
)
def test_http_header_value_chars_below(self): def test_http_header_value_chars_below(self):
self.load('custom_header') self.load('custom_header')
@@ -164,7 +149,7 @@ Connection: close
raw=True, raw=True,
) )
self.assertEqual(resp['status'], 400, 'value chars below') assert resp['status'] == 400, 'value chars below'
def test_http_header_field_leading_sp(self): def test_http_header_field_leading_sp(self):
self.load('empty') self.load('empty')
@@ -177,7 +162,7 @@ Connection: close
} }
) )
self.assertEqual(resp['status'], 400, 'field leading sp') assert resp['status'] == 400, 'field leading sp'
def test_http_header_field_leading_htab(self): def test_http_header_field_leading_htab(self):
self.load('empty') self.load('empty')
@@ -190,7 +175,7 @@ Connection: close
} }
) )
self.assertEqual(resp['status'], 400, 'field leading htab') assert resp['status'] == 400, 'field leading htab'
def test_http_header_field_trailing_sp(self): def test_http_header_field_trailing_sp(self):
self.load('empty') self.load('empty')
@@ -203,7 +188,7 @@ Connection: close
} }
) )
self.assertEqual(resp['status'], 400, 'field trailing sp') assert resp['status'] == 400, 'field trailing sp'
def test_http_header_field_trailing_htab(self): def test_http_header_field_trailing_htab(self):
self.load('empty') self.load('empty')
@@ -216,12 +201,12 @@ Connection: close
} }
) )
self.assertEqual(resp['status'], 400, 'field trailing htab') assert resp['status'] == 400, 'field trailing htab'
def test_http_header_content_length_big(self): def test_http_header_content_length_big(self):
self.load('empty') self.load('empty')
self.assertEqual( assert (
self.post( self.post(
headers={ headers={
'Host': 'localhost', 'Host': 'localhost',
@@ -229,15 +214,14 @@ Connection: close
'Connection': 'close', 'Connection': 'close',
}, },
body='X' * 1000, body='X' * 1000,
)['status'], )['status']
400, == 400
'Content-Length big', ), 'Content-Length big'
)
def test_http_header_content_length_negative(self): def test_http_header_content_length_negative(self):
self.load('empty') self.load('empty')
self.assertEqual( assert (
self.post( self.post(
headers={ headers={
'Host': 'localhost', 'Host': 'localhost',
@@ -245,15 +229,14 @@ Connection: close
'Connection': 'close', 'Connection': 'close',
}, },
body='X' * 1000, body='X' * 1000,
)['status'], )['status']
400, == 400
'Content-Length negative', ), 'Content-Length negative'
)
def test_http_header_content_length_text(self): def test_http_header_content_length_text(self):
self.load('empty') self.load('empty')
self.assertEqual( assert (
self.post( self.post(
headers={ headers={
'Host': 'localhost', 'Host': 'localhost',
@@ -261,15 +244,14 @@ Connection: close
'Connection': 'close', 'Connection': 'close',
}, },
body='X' * 1000, body='X' * 1000,
)['status'], )['status']
400, == 400
'Content-Length text', ), 'Content-Length text'
)
def test_http_header_content_length_multiple_values(self): def test_http_header_content_length_multiple_values(self):
self.load('empty') self.load('empty')
self.assertEqual( assert (
self.post( self.post(
headers={ headers={
'Host': 'localhost', 'Host': 'localhost',
@@ -277,15 +259,14 @@ Connection: close
'Connection': 'close', 'Connection': 'close',
}, },
body='X' * 1000, body='X' * 1000,
)['status'], )['status']
400, == 400
'Content-Length multiple value', ), 'Content-Length multiple value'
)
def test_http_header_content_length_multiple_fields(self): def test_http_header_content_length_multiple_fields(self):
self.load('empty') self.load('empty')
self.assertEqual( assert (
self.post( self.post(
headers={ headers={
'Host': 'localhost', 'Host': 'localhost',
@@ -293,39 +274,35 @@ Connection: close
'Connection': 'close', 'Connection': 'close',
}, },
body='X' * 1000, body='X' * 1000,
)['status'], )['status']
400, == 400
'Content-Length multiple fields', ), 'Content-Length multiple fields'
)
@unittest.skip('not yet') @pytest.mark.skip('not yet')
def test_http_header_host_absent(self): def test_http_header_host_absent(self):
self.load('host') self.load('host')
resp = self.get(headers={'Connection': 'close'}) resp = self.get(headers={'Connection': 'close'})
self.assertEqual(resp['status'], 400, 'Host absent status') assert resp['status'] == 400, 'Host absent status'
def test_http_header_host_empty(self): def test_http_header_host_empty(self):
self.load('host') self.load('host')
resp = self.get(headers={'Host': '', 'Connection': 'close'}) resp = self.get(headers={'Host': '', 'Connection': 'close'})
self.assertEqual(resp['status'], 200, 'Host empty status') assert resp['status'] == 200, 'Host empty status'
self.assertNotEqual( assert resp['headers']['X-Server-Name'] != '', 'Host empty SERVER_NAME'
resp['headers']['X-Server-Name'], '', 'Host empty SERVER_NAME'
)
def test_http_header_host_big(self): def test_http_header_host_big(self):
self.load('empty') self.load('empty')
self.assertEqual( assert (
self.get(headers={'Host': 'X' * 10000, 'Connection': 'close'})[ self.get(headers={'Host': 'X' * 10000, 'Connection': 'close'})[
'status' 'status'
], ]
431, == 431
'Host big', ), 'Host big'
)
def test_http_header_host_port(self): def test_http_header_host_port(self):
self.load('host') self.load('host')
@@ -334,17 +311,13 @@ Connection: close
headers={'Host': 'exmaple.com:7080', 'Connection': 'close'} headers={'Host': 'exmaple.com:7080', 'Connection': 'close'}
) )
self.assertEqual(resp['status'], 200, 'Host port status') assert resp['status'] == 200, 'Host port status'
self.assertEqual( assert (
resp['headers']['X-Server-Name'], resp['headers']['X-Server-Name'] == 'exmaple.com'
'exmaple.com', ), 'Host port SERVER_NAME'
'Host port SERVER_NAME', assert (
) resp['headers']['X-Http-Host'] == 'exmaple.com:7080'
self.assertEqual( ), 'Host port HTTP_HOST'
resp['headers']['X-Http-Host'],
'exmaple.com:7080',
'Host port HTTP_HOST',
)
def test_http_header_host_port_empty(self): def test_http_header_host_port_empty(self):
self.load('host') self.load('host')
@@ -353,63 +326,49 @@ Connection: close
headers={'Host': 'exmaple.com:', 'Connection': 'close'} headers={'Host': 'exmaple.com:', 'Connection': 'close'}
) )
self.assertEqual(resp['status'], 200, 'Host port empty status') assert resp['status'] == 200, 'Host port empty status'
self.assertEqual( assert (
resp['headers']['X-Server-Name'], resp['headers']['X-Server-Name'] == 'exmaple.com'
'exmaple.com', ), 'Host port empty SERVER_NAME'
'Host port empty SERVER_NAME', assert (
) resp['headers']['X-Http-Host'] == 'exmaple.com:'
self.assertEqual( ), 'Host port empty HTTP_HOST'
resp['headers']['X-Http-Host'],
'exmaple.com:',
'Host port empty HTTP_HOST',
)
def test_http_header_host_literal(self): def test_http_header_host_literal(self):
self.load('host') self.load('host')
resp = self.get(headers={'Host': '127.0.0.1', 'Connection': 'close'}) resp = self.get(headers={'Host': '127.0.0.1', 'Connection': 'close'})
self.assertEqual(resp['status'], 200, 'Host literal status') assert resp['status'] == 200, 'Host literal status'
self.assertEqual( assert (
resp['headers']['X-Server-Name'], resp['headers']['X-Server-Name'] == '127.0.0.1'
'127.0.0.1', ), 'Host literal SERVER_NAME'
'Host literal SERVER_NAME',
)
def test_http_header_host_literal_ipv6(self): def test_http_header_host_literal_ipv6(self):
self.load('host') self.load('host')
resp = self.get(headers={'Host': '[::1]:7080', 'Connection': 'close'}) resp = self.get(headers={'Host': '[::1]:7080', 'Connection': 'close'})
self.assertEqual(resp['status'], 200, 'Host literal ipv6 status') assert resp['status'] == 200, 'Host literal ipv6 status'
self.assertEqual( assert (
resp['headers']['X-Server-Name'], resp['headers']['X-Server-Name'] == '[::1]'
'[::1]', ), 'Host literal ipv6 SERVER_NAME'
'Host literal ipv6 SERVER_NAME', assert (
) resp['headers']['X-Http-Host'] == '[::1]:7080'
self.assertEqual( ), 'Host literal ipv6 HTTP_HOST'
resp['headers']['X-Http-Host'],
'[::1]:7080',
'Host literal ipv6 HTTP_HOST',
)
def test_http_header_host_trailing_period(self): def test_http_header_host_trailing_period(self):
self.load('host') self.load('host')
resp = self.get(headers={'Host': '127.0.0.1.', 'Connection': 'close'}) resp = self.get(headers={'Host': '127.0.0.1.', 'Connection': 'close'})
self.assertEqual(resp['status'], 200, 'Host trailing period status') assert resp['status'] == 200, 'Host trailing period status'
self.assertEqual( assert (
resp['headers']['X-Server-Name'], resp['headers']['X-Server-Name'] == '127.0.0.1'
'127.0.0.1', ), 'Host trailing period SERVER_NAME'
'Host trailing period SERVER_NAME', assert (
) resp['headers']['X-Http-Host'] == '127.0.0.1.'
self.assertEqual( ), 'Host trailing period HTTP_HOST'
resp['headers']['X-Http-Host'],
'127.0.0.1.',
'Host trailing period HTTP_HOST',
)
def test_http_header_host_trailing_period_2(self): def test_http_header_host_trailing_period_2(self):
self.load('host') self.load('host')
@@ -418,66 +377,53 @@ Connection: close
headers={'Host': 'EXAMPLE.COM.', 'Connection': 'close'} headers={'Host': 'EXAMPLE.COM.', 'Connection': 'close'}
) )
self.assertEqual(resp['status'], 200, 'Host trailing period 2 status') assert resp['status'] == 200, 'Host trailing period 2 status'
self.assertEqual( assert (
resp['headers']['X-Server-Name'], resp['headers']['X-Server-Name'] == 'example.com'
'example.com', ), 'Host trailing period 2 SERVER_NAME'
'Host trailing period 2 SERVER_NAME', assert (
) resp['headers']['X-Http-Host'] == 'EXAMPLE.COM.'
self.assertEqual( ), 'Host trailing period 2 HTTP_HOST'
resp['headers']['X-Http-Host'],
'EXAMPLE.COM.',
'Host trailing period 2 HTTP_HOST',
)
def test_http_header_host_case_insensitive(self): def test_http_header_host_case_insensitive(self):
self.load('host') self.load('host')
resp = self.get(headers={'Host': 'EXAMPLE.COM', 'Connection': 'close'}) resp = self.get(headers={'Host': 'EXAMPLE.COM', 'Connection': 'close'})
self.assertEqual(resp['status'], 200, 'Host case insensitive') assert resp['status'] == 200, 'Host case insensitive'
self.assertEqual( assert (
resp['headers']['X-Server-Name'], resp['headers']['X-Server-Name'] == 'example.com'
'example.com', ), 'Host case insensitive SERVER_NAME'
'Host case insensitive SERVER_NAME',
)
def test_http_header_host_double_dot(self): def test_http_header_host_double_dot(self):
self.load('empty') self.load('empty')
self.assertEqual( assert (
self.get(headers={'Host': '127.0.0..1', 'Connection': 'close'})[ self.get(headers={'Host': '127.0.0..1', 'Connection': 'close'})[
'status' 'status'
], ]
400, == 400
'Host double dot', ), 'Host double dot'
)
def test_http_header_host_slash(self): def test_http_header_host_slash(self):
self.load('empty') self.load('empty')
self.assertEqual( assert (
self.get(headers={'Host': '/localhost', 'Connection': 'close'})[ self.get(headers={'Host': '/localhost', 'Connection': 'close'})[
'status' 'status'
], ]
400, == 400
'Host slash', ), 'Host slash'
)
def test_http_header_host_multiple_fields(self): def test_http_header_host_multiple_fields(self):
self.load('empty') self.load('empty')
self.assertEqual( assert (
self.get( self.get(
headers={ headers={
'Host': ['localhost', 'example.com'], 'Host': ['localhost', 'example.com'],
'Connection': 'close', 'Connection': 'close',
} }
)['status'], )['status']
400, == 400
'Host multiple fields', ), 'Host multiple fields'
)
if __name__ == '__main__':
TestHTTPHeader.main()

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
import os import os
import subprocess import subprocess
import unittest import pytest
from unit.applications.lang.java import TestApplicationJava from unit.applications.lang.java import TestApplicationJava
@@ -8,15 +8,15 @@ from unit.applications.lang.java import TestApplicationJava
class TestJavaIsolationRootfs(TestApplicationJava): class TestJavaIsolationRootfs(TestApplicationJava):
prerequisites = {'modules': {'java': 'all'}} prerequisites = {'modules': {'java': 'all'}}
def setUp(self): def setup_method(self, is_su):
if not self.is_su: super().setup_method()
if not is_su:
return return
super().setUp() os.makedirs(self.temp_dir + '/jars')
os.makedirs(self.temp_dir + '/tmp')
os.makedirs(self.testdir + '/jars') os.chmod(self.temp_dir + '/tmp', 0o777)
os.makedirs(self.testdir + '/tmp')
os.chmod(self.testdir + '/tmp', 0o777)
try: try:
process = subprocess.Popen( process = subprocess.Popen(
@@ -24,7 +24,7 @@ class TestJavaIsolationRootfs(TestApplicationJava):
"mount", "mount",
"--bind", "--bind",
self.pardir + "/build", self.pardir + "/build",
self.testdir + "/jars", self.temp_dir + "/jars",
], ],
stderr=subprocess.STDOUT, stderr=subprocess.STDOUT,
) )
@@ -32,54 +32,45 @@ class TestJavaIsolationRootfs(TestApplicationJava):
process.communicate() process.communicate()
except: except:
self.fail('Cann\'t run mount process.') pytest.fail('Cann\'t run mount process.')
def tearDown(self): def teardown_method(self, is_su):
if not self.is_su: if not is_su:
return return
try: try:
process = subprocess.Popen( process = subprocess.Popen(
["umount", "--lazy", self.testdir + "/jars"], ["umount", "--lazy", self.temp_dir + "/jars"],
stderr=subprocess.STDOUT, stderr=subprocess.STDOUT,
) )
process.communicate() process.communicate()
except: except:
self.fail('Cann\'t run mount process.') pytest.fail('Cann\'t run mount process.')
# super teardown must happen after unmount to avoid deletion of /build # super teardown must happen after unmount to avoid deletion of /build
super().tearDown() super().teardown_method()
def test_java_isolation_rootfs_chroot_war(self): def test_java_isolation_rootfs_chroot_war(self, is_su):
if not self.is_su: if not is_su:
print('require root') pytest.skip('require root')
raise unittest.SkipTest()
isolation = { isolation = {
'rootfs': self.testdir, 'rootfs': self.temp_dir,
} }
self.load('empty_war', isolation=isolation) self.load('empty_war', isolation=isolation)
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
'"/"', '/config/applications/empty_war/working_directory', '"/"', '/config/applications/empty_war/working_directory',
),
) )
self.assertIn( assert 'success' in self.conf(
'success', self.conf('"/jars"', 'applications/empty_war/unit_jars') '"/jars"', 'applications/empty_war/unit_jars'
) )
self.assertIn( assert 'success' in self.conf(
'success', '"/java/empty.war"', 'applications/empty_war/webapp'
self.conf('"/java/empty.war"', 'applications/empty_war/webapp'),
) )
self.assertEqual(self.get()['status'], 200, 'war') assert self.get()['status'] == 200, 'war'
if __name__ == '__main__':
TestJavaIsolationRootfs.main()

View File

@@ -1,9 +1,10 @@
import pytest
import struct import struct
import time import time
import unittest
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 conftest import option, skip_alert
class TestJavaWebsockets(TestApplicationJava): class TestJavaWebsockets(TestApplicationJava):
@@ -11,23 +12,17 @@ class TestJavaWebsockets(TestApplicationJava):
ws = TestApplicationWebsocket() ws = TestApplicationWebsocket()
def setUp(self): def setup_method(self):
super().setUp() super().setup_method()
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{'http': {'websocket': {'keepalive_interval': 0}}}, 'settings' {'http': {'websocket': {'keepalive_interval': 0}}}, 'settings'
), ), 'clear keepalive_interval'
'clear keepalive_interval',
)
self.skip_alerts.extend( skip_alert(r'socket close\(\d+\) failed')
[r'socket close\(\d+\) failed']
)
def close_connection(self, sock): def close_connection(self, sock):
self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', 'empty soc') assert self.recvall(sock, read_timeout=0.1) == b'', 'empty soc'
self.ws.frame_write(sock, self.ws.OP_CLOSE, self.ws.serialize_close()) self.ws.frame_write(sock, self.ws.OP_CLOSE, self.ws.serialize_close())
@@ -36,9 +31,9 @@ class TestJavaWebsockets(TestApplicationJava):
def check_close(self, sock, code=1000, no_close=False): def check_close(self, sock, code=1000, no_close=False):
frame = self.ws.frame_read(sock) frame = self.ws.frame_read(sock)
self.assertEqual(frame['fin'], True, 'close fin') assert frame['fin'] == True, 'close fin'
self.assertEqual(frame['opcode'], self.ws.OP_CLOSE, 'close opcode') assert frame['opcode'] == self.ws.OP_CLOSE, 'close opcode'
self.assertEqual(frame['code'], code, 'close code') assert frame['code'] == code, 'close code'
if not no_close: if not no_close:
sock.close() sock.close()
@@ -49,9 +44,9 @@ class TestJavaWebsockets(TestApplicationJava):
else: else:
data = frame['data'].decode('utf-8') data = frame['data'].decode('utf-8')
self.assertEqual(frame['fin'], fin, 'fin') assert frame['fin'] == fin, 'fin'
self.assertEqual(frame['opcode'], opcode, 'opcode') assert frame['opcode'] == opcode, 'opcode'
self.assertEqual(data, payload, 'payload') assert data == payload, 'payload'
def test_java_websockets_handshake(self): def test_java_websockets_handshake(self):
self.load('websockets_mirror') self.load('websockets_mirror')
@@ -59,14 +54,12 @@ class TestJavaWebsockets(TestApplicationJava):
resp, sock, key = self.ws.upgrade() resp, sock, key = self.ws.upgrade()
sock.close() sock.close()
self.assertEqual(resp['status'], 101, 'status') assert resp['status'] == 101, 'status'
self.assertEqual(resp['headers']['Upgrade'], 'websocket', 'upgrade') assert resp['headers']['Upgrade'] == 'websocket', 'upgrade'
self.assertEqual( assert resp['headers']['Connection'] == 'Upgrade', 'connection'
resp['headers']['Connection'], 'Upgrade', 'connection' assert resp['headers']['Sec-WebSocket-Accept'] == self.ws.accept(
) key
self.assertEqual( ), 'key'
resp['headers']['Sec-WebSocket-Accept'], self.ws.accept(key), 'key'
)
def test_java_websockets_mirror(self): def test_java_websockets_mirror(self):
self.load('websockets_mirror') self.load('websockets_mirror')
@@ -78,12 +71,12 @@ class TestJavaWebsockets(TestApplicationJava):
self.ws.frame_write(sock, self.ws.OP_TEXT, message) self.ws.frame_write(sock, self.ws.OP_TEXT, message)
frame = self.ws.frame_read(sock) frame = self.ws.frame_read(sock)
self.assertEqual(message, frame['data'].decode('utf-8'), 'mirror') assert message == frame['data'].decode('utf-8'), 'mirror'
self.ws.frame_write(sock, self.ws.OP_TEXT, message) self.ws.frame_write(sock, self.ws.OP_TEXT, message)
frame = self.ws.frame_read(sock) frame = self.ws.frame_read(sock)
self.assertEqual(message, frame['data'].decode('utf-8'), 'mirror 2') assert message == frame['data'].decode('utf-8'), 'mirror 2'
sock.close() sock.close()
@@ -98,8 +91,8 @@ class TestJavaWebsockets(TestApplicationJava):
frame = self.ws.frame_read(sock) frame = self.ws.frame_read(sock)
self.assertEqual(frame['opcode'], self.ws.OP_CLOSE, 'no mask opcode') assert frame['opcode'] == self.ws.OP_CLOSE, 'no mask opcode'
self.assertEqual(frame['code'], 1002, 'no mask close code') assert frame['code'] == 1002, 'no mask close code'
sock.close() sock.close()
@@ -116,11 +109,9 @@ class TestJavaWebsockets(TestApplicationJava):
frame = self.ws.frame_read(sock) frame = self.ws.frame_read(sock)
self.assertEqual( assert message + ' ' + message == frame['data'].decode(
message + ' ' + message, 'utf-8'
frame['data'].decode('utf-8'), ), 'mirror framing'
'mirror framing',
)
sock.close() sock.close()
@@ -136,9 +127,7 @@ class TestJavaWebsockets(TestApplicationJava):
frame = self.ws.frame_read(sock) frame = self.ws.frame_read(sock)
frame.pop('data') frame.pop('data')
self.assertDictEqual( assert frame == {
frame,
{
'fin': True, 'fin': True,
'rsv1': False, 'rsv1': False,
'rsv2': False, 'rsv2': False,
@@ -147,9 +136,7 @@ class TestJavaWebsockets(TestApplicationJava):
'mask': 0, 'mask': 0,
'code': 1002, 'code': 1002,
'reason': 'Fragmented control frame', 'reason': 'Fragmented control frame',
}, }, 'close frame'
'close frame',
)
sock.close() sock.close()
@@ -168,13 +155,13 @@ class TestJavaWebsockets(TestApplicationJava):
frame1 = self.ws.frame_read(sock1) frame1 = self.ws.frame_read(sock1)
frame2 = self.ws.frame_read(sock2) frame2 = self.ws.frame_read(sock2)
self.assertEqual(message1, frame1['data'].decode('utf-8'), 'client 1') assert message1 == frame1['data'].decode('utf-8'), 'client 1'
self.assertEqual(message2, frame2['data'].decode('utf-8'), 'client 2') assert message2 == frame2['data'].decode('utf-8'), 'client 2'
sock1.close() sock1.close()
sock2.close() sock2.close()
@unittest.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
@@ -190,7 +177,7 @@ class TestJavaWebsockets(TestApplicationJava):
}, },
) )
self.assertEqual(resp['status'], 400, 'upgrade absent') assert resp['status'] == 400, 'upgrade absent'
def test_java_websockets_handshake_case_insensitive(self): def test_java_websockets_handshake_case_insensitive(self):
self.load('websockets_mirror') self.load('websockets_mirror')
@@ -207,9 +194,9 @@ class TestJavaWebsockets(TestApplicationJava):
) )
sock.close() sock.close()
self.assertEqual(resp['status'], 101, 'status') assert resp['status'] == 101, 'status'
@unittest.skip('not yet') @pytest.mark.skip('not yet')
def test_java_websockets_handshake_connection_absent(self): # FAIL def test_java_websockets_handshake_connection_absent(self): # FAIL
self.load('websockets_mirror') self.load('websockets_mirror')
@@ -223,7 +210,7 @@ class TestJavaWebsockets(TestApplicationJava):
}, },
) )
self.assertEqual(resp['status'], 400, 'status') assert resp['status'] == 400, 'status'
def test_java_websockets_handshake_version_absent(self): def test_java_websockets_handshake_version_absent(self):
self.load('websockets_mirror') self.load('websockets_mirror')
@@ -238,9 +225,9 @@ class TestJavaWebsockets(TestApplicationJava):
}, },
) )
self.assertEqual(resp['status'], 426, 'status') assert resp['status'] == 426, 'status'
@unittest.skip('not yet') @pytest.mark.skip('not yet')
def test_java_websockets_handshake_key_invalid(self): def test_java_websockets_handshake_key_invalid(self):
self.load('websockets_mirror') self.load('websockets_mirror')
@@ -255,7 +242,7 @@ class TestJavaWebsockets(TestApplicationJava):
}, },
) )
self.assertEqual(resp['status'], 400, 'key length') assert resp['status'] == 400, 'key length'
key = self.ws.key() key = self.ws.key()
resp = self.get( resp = self.get(
@@ -269,9 +256,7 @@ class TestJavaWebsockets(TestApplicationJava):
}, },
) )
self.assertEqual( assert resp['status'] == 400, 'key double' # FAIL https://tools.ietf.org/html/rfc6455#section-11.3.1
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')
@@ -287,7 +272,7 @@ class TestJavaWebsockets(TestApplicationJava):
}, },
) )
self.assertEqual(resp['status'], 400, 'status') assert resp['status'] == 400, 'status'
def test_java_websockets_handshake_http_10(self): def test_java_websockets_handshake_http_10(self):
self.load('websockets_mirror') self.load('websockets_mirror')
@@ -304,7 +289,7 @@ class TestJavaWebsockets(TestApplicationJava):
http_10=True, http_10=True,
) )
self.assertEqual(resp['status'], 400, 'status') assert resp['status'] == 400, 'status'
def test_java_websockets_handshake_uri_invalid(self): def test_java_websockets_handshake_uri_invalid(self):
self.load('websockets_mirror') self.load('websockets_mirror')
@@ -321,7 +306,7 @@ class TestJavaWebsockets(TestApplicationJava):
url='!', url='!',
) )
self.assertEqual(resp['status'], 400, 'status') assert resp['status'] == 400, 'status'
def test_java_websockets_protocol_absent(self): def test_java_websockets_protocol_absent(self):
self.load('websockets_mirror') self.load('websockets_mirror')
@@ -338,14 +323,12 @@ class TestJavaWebsockets(TestApplicationJava):
) )
sock.close() sock.close()
self.assertEqual(resp['status'], 101, 'status') assert resp['status'] == 101, 'status'
self.assertEqual(resp['headers']['Upgrade'], 'websocket', 'upgrade') assert resp['headers']['Upgrade'] == 'websocket', 'upgrade'
self.assertEqual( assert resp['headers']['Connection'] == 'Upgrade', 'connection'
resp['headers']['Connection'], 'Upgrade', 'connection' assert resp['headers']['Sec-WebSocket-Accept'] == self.ws.accept(
) key
self.assertEqual( ), 'key'
resp['headers']['Sec-WebSocket-Accept'], self.ws.accept(key), 'key'
)
# autobahn-testsuite # autobahn-testsuite
# #
@@ -442,12 +425,12 @@ class TestJavaWebsockets(TestApplicationJava):
_, sock, _ = self.ws.upgrade() _, sock, _ = self.ws.upgrade()
self.ws.frame_write(sock, self.ws.OP_PONG, '') self.ws.frame_write(sock, self.ws.OP_PONG, '')
self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', '2_7') assert self.recvall(sock, read_timeout=0.1) == b'', '2_7'
# 2_8 # 2_8
self.ws.frame_write(sock, self.ws.OP_PONG, 'unsolicited pong payload') self.ws.frame_write(sock, self.ws.OP_PONG, 'unsolicited pong payload')
self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', '2_8') assert self.recvall(sock, read_timeout=0.1) == b'', '2_8'
# 2_9 # 2_9
@@ -487,7 +470,7 @@ class TestJavaWebsockets(TestApplicationJava):
self.close_connection(sock) self.close_connection(sock)
@unittest.skip('not yet') @pytest.mark.skip('not yet')
def test_java_websockets_3_1__3_7(self): def test_java_websockets_3_1__3_7(self):
self.load('websockets_mirror') self.load('websockets_mirror')
@@ -513,7 +496,7 @@ class TestJavaWebsockets(TestApplicationJava):
self.check_close(sock, 1002, no_close=True) self.check_close(sock, 1002, no_close=True)
self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', 'empty 3_2') assert self.recvall(sock, read_timeout=0.1) == b'', 'empty 3_2'
sock.close() sock.close()
# 3_3 # 3_3
@@ -531,7 +514,7 @@ class TestJavaWebsockets(TestApplicationJava):
self.check_close(sock, 1002, no_close=True) self.check_close(sock, 1002, no_close=True)
self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', 'empty 3_3') assert self.recvall(sock, read_timeout=0.1) == b'', 'empty 3_3'
sock.close() sock.close()
# 3_4 # 3_4
@@ -549,7 +532,7 @@ class TestJavaWebsockets(TestApplicationJava):
self.check_close(sock, 1002, no_close=True) self.check_close(sock, 1002, no_close=True)
self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', 'empty 3_4') assert self.recvall(sock, read_timeout=0.1) == b'', 'empty 3_4'
sock.close() sock.close()
# 3_5 # 3_5
@@ -735,7 +718,7 @@ class TestJavaWebsockets(TestApplicationJava):
# 5_4 # 5_4
self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False) self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', '5_4') assert self.recvall(sock, read_timeout=0.1) == b'', '5_4'
self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=True) self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=True)
frame = self.ws.frame_read(sock) frame = self.ws.frame_read(sock)
@@ -772,7 +755,7 @@ class TestJavaWebsockets(TestApplicationJava):
ping_payload = 'ping payload' ping_payload = 'ping payload'
self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False) self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', '5_7') assert self.recvall(sock, read_timeout=0.1) == b'', '5_7'
self.ws.frame_write(sock, self.ws.OP_PING, ping_payload) self.ws.frame_write(sock, self.ws.OP_PING, ping_payload)
@@ -956,7 +939,7 @@ class TestJavaWebsockets(TestApplicationJava):
frame = self.ws.frame_read(sock) frame = self.ws.frame_read(sock)
self.check_frame(frame, True, self.ws.OP_PONG, 'pongme 2!') self.check_frame(frame, True, self.ws.OP_PONG, 'pongme 2!')
self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', '5_20') assert self.recvall(sock, read_timeout=0.1) == b'', '5_20'
self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment5') self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment5')
self.check_frame( self.check_frame(
@@ -1089,7 +1072,7 @@ class TestJavaWebsockets(TestApplicationJava):
self.check_close(sock, no_close=True) self.check_close(sock, no_close=True)
self.ws.frame_write(sock, self.ws.OP_PING, '') self.ws.frame_write(sock, self.ws.OP_PING, '')
self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', 'empty soc') assert self.recvall(sock, read_timeout=0.1) == b'', 'empty soc'
sock.close() sock.close()
@@ -1101,7 +1084,7 @@ class TestJavaWebsockets(TestApplicationJava):
self.check_close(sock, no_close=True) self.check_close(sock, no_close=True)
self.ws.frame_write(sock, self.ws.OP_TEXT, payload) self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', 'empty soc') assert self.recvall(sock, read_timeout=0.1) == b'', 'empty soc'
sock.close() sock.close()
@@ -1114,7 +1097,7 @@ class TestJavaWebsockets(TestApplicationJava):
self.check_close(sock, no_close=True) self.check_close(sock, no_close=True)
self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2') self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2')
self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', 'empty soc') assert self.recvall(sock, read_timeout=0.1) == b'', 'empty soc'
sock.close() sock.close()
@@ -1129,7 +1112,7 @@ class TestJavaWebsockets(TestApplicationJava):
self.recvall(sock, read_timeout=1) self.recvall(sock, read_timeout=1)
self.ws.frame_write(sock, self.ws.OP_PING, '') self.ws.frame_write(sock, self.ws.OP_PING, '')
self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', 'empty soc') assert self.recvall(sock, read_timeout=0.1) == b'', 'empty soc'
sock.close() sock.close()
@@ -1249,15 +1232,13 @@ class TestJavaWebsockets(TestApplicationJava):
self.ws.frame_write(sock, self.ws.OP_CLOSE, payload) self.ws.frame_write(sock, self.ws.OP_CLOSE, payload)
self.check_close(sock, 1002) self.check_close(sock, 1002)
def test_java_websockets_9_1_1__9_6_6(self): def test_java_websockets_9_1_1__9_6_6(self, is_unsafe):
if not self.unsafe: if not is_unsafe:
self.skipTest("unsafe, long run") pytest.skip('unsafe, long run')
self.load('websockets_mirror') self.load('websockets_mirror')
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{ {
'http': { 'http': {
'websocket': { 'websocket': {
@@ -1267,9 +1248,7 @@ class TestJavaWebsockets(TestApplicationJava):
} }
}, },
'settings', 'settings',
), ), 'increase max_frame_size and keepalive_interval'
'increase max_frame_size and keepalive_interval',
)
_, sock, _ = self.ws.upgrade() _, sock, _ = self.ws.upgrade()
@@ -1310,7 +1289,7 @@ class TestJavaWebsockets(TestApplicationJava):
check_payload(op_binary, 8 * 2 ** 20) # 9_2_5 check_payload(op_binary, 8 * 2 ** 20) # 9_2_5
check_payload(op_binary, 16 * 2 ** 20) # 9_2_6 check_payload(op_binary, 16 * 2 ** 20) # 9_2_6
if self.system != 'Darwin' and self.system != 'FreeBSD': if option.system != 'Darwin' and option.system != 'FreeBSD':
check_message(op_text, 64) # 9_3_1 check_message(op_text, 64) # 9_3_1
check_message(op_text, 256) # 9_3_2 check_message(op_text, 256) # 9_3_2
check_message(op_text, 2 ** 10) # 9_3_3 check_message(op_text, 2 ** 10) # 9_3_3
@@ -1366,13 +1345,9 @@ class TestJavaWebsockets(TestApplicationJava):
def test_java_websockets_max_frame_size(self): def test_java_websockets_max_frame_size(self):
self.load('websockets_mirror') self.load('websockets_mirror')
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{'http': {'websocket': {'max_frame_size': 100}}}, 'settings' {'http': {'websocket': {'max_frame_size': 100}}}, 'settings'
), ), 'configure max_frame_size'
'configure max_frame_size',
)
_, sock, _ = self.ws.upgrade() _, sock, _ = self.ws.upgrade()
@@ -1392,13 +1367,9 @@ class TestJavaWebsockets(TestApplicationJava):
def test_java_websockets_read_timeout(self): def test_java_websockets_read_timeout(self):
self.load('websockets_mirror') self.load('websockets_mirror')
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{'http': {'websocket': {'read_timeout': 5}}}, 'settings' {'http': {'websocket': {'read_timeout': 5}}}, 'settings'
), ), 'configure read_timeout'
'configure read_timeout',
)
_, sock, _ = self.ws.upgrade() _, sock, _ = self.ws.upgrade()
@@ -1412,13 +1383,9 @@ class TestJavaWebsockets(TestApplicationJava):
def test_java_websockets_keepalive_interval(self): def test_java_websockets_keepalive_interval(self):
self.load('websockets_mirror') self.load('websockets_mirror')
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{'http': {'websocket': {'keepalive_interval': 5}}}, 'settings' {'http': {'websocket': {'keepalive_interval': 5}}}, 'settings'
), ), 'configure keepalive_interval'
'configure keepalive_interval',
)
_, sock, _ = self.ws.upgrade() _, sock, _ = self.ws.upgrade()
@@ -1431,7 +1398,3 @@ class TestJavaWebsockets(TestApplicationJava):
self.check_frame(frame, True, self.ws.OP_PING, '') # PING frame self.check_frame(frame, True, self.ws.OP_PING, '') # PING frame
sock.close() sock.close()
if __name__ == '__main__':
TestJavaWebsockets.main()

View File

@@ -1,6 +1,8 @@
import unittest import pytest
import re
from unit.applications.lang.node import TestApplicationNode from unit.applications.lang.node import TestApplicationNode
from conftest import waitforfiles
class TestNodeApplication(TestApplicationNode): class TestNodeApplication(TestApplicationNode):
@@ -10,16 +12,14 @@ class TestNodeApplication(TestApplicationNode):
self.load('basic') self.load('basic')
resp = self.get() resp = self.get()
self.assertEqual( assert resp['headers']['Content-Type'] == 'text/plain', 'basic header'
resp['headers']['Content-Type'], 'text/plain', 'basic header' assert resp['body'] == 'Hello World\n', 'basic body'
)
self.assertEqual(resp['body'], 'Hello World\n', 'basic body')
def test_node_application_seq(self): def test_node_application_seq(self):
self.load('basic') self.load('basic')
self.assertEqual(self.get()['status'], 200, 'seq') assert self.get()['status'] == 200, 'seq'
self.assertEqual(self.get()['status'], 200, 'seq 2') assert self.get()['status'] == 200, 'seq 2'
def test_node_application_variables(self): def test_node_application_variables(self):
self.load('variables') self.load('variables')
@@ -36,31 +36,26 @@ class TestNodeApplication(TestApplicationNode):
body=body, body=body,
) )
self.assertEqual(resp['status'], 200, 'status') assert resp['status'] == 200, 'status'
headers = resp['headers'] headers = resp['headers']
header_server = headers.pop('Server') header_server = headers.pop('Server')
self.assertRegex(header_server, r'Unit/[\d\.]+', 'server header') assert re.search(r'Unit/[\d\.]+', header_server), 'server header'
date = headers.pop('Date') date = headers.pop('Date')
self.assertEqual(date[-4:], ' GMT', 'date header timezone') assert date[-4:] == ' GMT', 'date header timezone'
self.assertLess( assert (
abs(self.date_to_sec_epoch(date) - self.sec_epoch()), abs(self.date_to_sec_epoch(date) - self.sec_epoch()) < 5
5, ), 'date header'
'date header',
)
raw_headers = headers.pop('Request-Raw-Headers') raw_headers = headers.pop('Request-Raw-Headers')
self.assertRegex( assert re.search(
raw_headers,
r'^(?:Host|localhost|Content-Type|' r'^(?:Host|localhost|Content-Type|'
'text\/html|Custom-Header|blah|Content-Length|17|Connection|' 'text\/html|Custom-Header|blah|Content-Length|17|Connection|'
'close|,)+$', 'close|,)+$',
'raw headers', raw_headers,
) ), 'raw headers'
self.assertDictEqual( assert headers == {
headers,
{
'Connection': 'close', 'Connection': 'close',
'Content-Length': str(len(body)), 'Content-Length': str(len(body)),
'Content-Type': 'text/html', 'Content-Type': 'text/html',
@@ -69,18 +64,16 @@ class TestNodeApplication(TestApplicationNode):
'Http-Host': 'localhost', 'Http-Host': 'localhost',
'Server-Protocol': 'HTTP/1.1', 'Server-Protocol': 'HTTP/1.1',
'Custom-Header': 'blah', 'Custom-Header': 'blah',
}, }, 'headers'
'headers', assert resp['body'] == body, 'body'
)
self.assertEqual(resp['body'], body, 'body')
def test_node_application_get_variables(self): def test_node_application_get_variables(self):
self.load('get_variables') self.load('get_variables')
resp = self.get(url='/?var1=val1&var2=&var3') resp = self.get(url='/?var1=val1&var2=&var3')
self.assertEqual(resp['headers']['X-Var-1'], 'val1', 'GET variables') assert resp['headers']['X-Var-1'] == 'val1', 'GET variables'
self.assertEqual(resp['headers']['X-Var-2'], '', 'GET variables 2') assert resp['headers']['X-Var-2'] == '', 'GET variables 2'
self.assertEqual(resp['headers']['X-Var-3'], '', 'GET variables 3') assert resp['headers']['X-Var-3'] == '', 'GET variables 3'
def test_node_application_post_variables(self): def test_node_application_post_variables(self):
self.load('post_variables') self.load('post_variables')
@@ -94,24 +87,24 @@ class TestNodeApplication(TestApplicationNode):
body='var1=val1&var2=&var3', body='var1=val1&var2=&var3',
) )
self.assertEqual(resp['headers']['X-Var-1'], 'val1', 'POST variables') assert resp['headers']['X-Var-1'] == 'val1', 'POST variables'
self.assertEqual(resp['headers']['X-Var-2'], '', 'POST variables 2') assert resp['headers']['X-Var-2'] == '', 'POST variables 2'
self.assertEqual(resp['headers']['X-Var-3'], '', 'POST variables 3') assert resp['headers']['X-Var-3'] == '', 'POST variables 3'
def test_node_application_404(self): def test_node_application_404(self):
self.load('404') self.load('404')
resp = self.get() resp = self.get()
self.assertEqual(resp['status'], 404, '404 status') assert resp['status'] == 404, '404 status'
self.assertRegex( assert re.search(
resp['body'], r'<title>404 Not Found</title>', '404 body' r'<title>404 Not Found</title>', resp['body']
) ), '404 body'
def test_node_keepalive_body(self): def test_node_keepalive_body(self):
self.load('mirror') self.load('mirror')
self.assertEqual(self.get()['status'], 200, 'init') assert self.get()['status'] == 200, 'init'
body = '0123456789' * 500 body = '0123456789' * 500
(resp, sock) = self.post( (resp, sock) = self.post(
@@ -125,7 +118,7 @@ class TestNodeApplication(TestApplicationNode):
read_timeout=1, read_timeout=1,
) )
self.assertEqual(resp['body'], '0123456789' * 500, 'keep-alive 1') assert resp['body'] == '0123456789' * 500, 'keep-alive 1'
body = '0123456789' body = '0123456789'
resp = self.post( resp = self.post(
@@ -138,47 +131,34 @@ class TestNodeApplication(TestApplicationNode):
body=body, body=body,
) )
self.assertEqual(resp['body'], body, 'keep-alive 2') assert resp['body'] == body, 'keep-alive 2'
def test_node_application_write_buffer(self): def test_node_application_write_buffer(self):
self.load('write_buffer') self.load('write_buffer')
self.assertEqual( assert self.get()['body'] == 'buffer', 'write buffer'
self.get()['body'], 'buffer', 'write buffer'
)
def test_node_application_write_callback(self): def test_node_application_write_callback(self):
self.load('write_callback') self.load('write_callback')
self.assertEqual( assert self.get()['body'] == 'helloworld', 'write callback order'
self.get()['body'], assert waitforfiles(self.temp_dir + '/node/callback'), 'write callback'
'helloworld',
'write callback order',
)
self.assertTrue(
self.waitforfiles(self.testdir + '/node/callback'),
'write callback',
)
def test_node_application_write_before_write_head(self): def test_node_application_write_before_write_head(self):
self.load('write_before_write_head') self.load('write_before_write_head')
self.assertEqual(self.get()['status'], 200, 'write before writeHead') assert self.get()['status'] == 200, 'write before writeHead'
def test_node_application_double_end(self): def test_node_application_double_end(self):
self.load('double_end') self.load('double_end')
self.assertEqual(self.get()['status'], 200, 'double end') assert self.get()['status'] == 200, 'double end'
self.assertEqual(self.get()['status'], 200, 'double end 2') assert self.get()['status'] == 200, 'double end 2'
def test_node_application_write_return(self): def test_node_application_write_return(self):
self.load('write_return') self.load('write_return')
self.assertEqual( assert self.get()['body'] == 'bodytrue', 'write return'
self.get()['body'],
'bodytrue',
'write return',
)
def test_node_application_remove_header(self): def test_node_application_remove_header(self):
self.load('remove_header') self.load('remove_header')
@@ -190,69 +170,61 @@ class TestNodeApplication(TestApplicationNode):
'Connection': 'close', 'Connection': 'close',
} }
) )
self.assertEqual(resp['headers']['Was-Header'], 'true', 'was header') assert resp['headers']['Was-Header'] == 'true', 'was header'
self.assertEqual(resp['headers']['Has-Header'], 'false', 'has header') assert resp['headers']['Has-Header'] == 'false', 'has header'
self.assertFalse('X-Header' in resp['headers'], 'remove header') assert not ('X-Header' in resp['headers']), 'remove header'
def test_node_application_remove_header_nonexisting(self): def test_node_application_remove_header_nonexisting(self):
self.load('remove_header') self.load('remove_header')
self.assertEqual( assert (
self.get( self.get(
headers={ headers={
'Host': 'localhost', 'Host': 'localhost',
'X-Remove': 'blah', 'X-Remove': 'blah',
'Connection': 'close', 'Connection': 'close',
} }
)['headers']['Has-Header'], )['headers']['Has-Header']
'true', == 'true'
'remove header nonexisting', ), 'remove header nonexisting'
)
def test_node_application_update_header(self): def test_node_application_update_header(self):
self.load('update_header') self.load('update_header')
self.assertEqual( assert self.get()['headers']['X-Header'] == 'new', 'update header'
self.get()['headers']['X-Header'], 'new', 'update header'
)
def test_node_application_set_header_array(self): def test_node_application_set_header_array(self):
self.load('set_header_array') self.load('set_header_array')
self.assertListEqual( assert self.get()['headers']['Set-Cookie'] == [
self.get()['headers']['Set-Cookie'], 'tc=one,two,three',
['tc=one,two,three', 'tc=four,five,six'], 'tc=four,five,six',
'set header array', ], 'set header array'
)
@unittest.skip('not yet') @pytest.mark.skip('not yet')
def test_node_application_status_message(self): def test_node_application_status_message(self):
self.load('status_message') self.load('status_message')
self.assertRegex( assert re.search(r'200 blah', self.get(raw_resp=True)), 'status message'
self.get(raw_resp=True), r'200 blah', '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')
self.assertEqual( assert self.get()['headers']['X-Type'] == 'number', 'get header type'
self.get()['headers']['X-Type'], 'number', 'get header type'
)
def test_node_application_header_name_case(self): def test_node_application_header_name_case(self):
self.load('header_name_case') self.load('header_name_case')
headers = self.get()['headers'] headers = self.get()['headers']
self.assertEqual(headers['X-HEADER'], '3', 'header value') assert headers['X-HEADER'] == '3', 'header value'
self.assertNotIn('X-Header', headers, 'insensitive') assert 'X-Header' not in headers, 'insensitive'
self.assertNotIn('X-header', headers, 'insensitive 2') assert 'X-header' not in headers, 'insensitive 2'
def test_node_application_promise_handler(self): def test_node_application_promise_handler(self):
self.load('promise_handler') self.load('promise_handler')
self.assertEqual( assert (
self.post( self.post(
headers={ headers={
'Host': 'localhost', 'Host': 'localhost',
@@ -260,19 +232,15 @@ class TestNodeApplication(TestApplicationNode):
'Connection': 'close', 'Connection': 'close',
}, },
body='callback', body='callback',
)['status'], )['status']
200, == 200
'promise handler request', ), 'promise handler request'
) assert waitforfiles(self.temp_dir + '/node/callback'), 'promise handler'
self.assertTrue(
self.waitforfiles(self.testdir + '/node/callback'),
'promise handler',
)
def test_node_application_promise_handler_write_after_end(self): def test_node_application_promise_handler_write_after_end(self):
self.load('promise_handler') self.load('promise_handler')
self.assertEqual( assert (
self.post( self.post(
headers={ headers={
'Host': 'localhost', 'Host': 'localhost',
@@ -281,15 +249,14 @@ class TestNodeApplication(TestApplicationNode):
'Connection': 'close', 'Connection': 'close',
}, },
body='callback', body='callback',
)['status'], )['status']
200, == 200
'promise handler request write after end', ), 'promise handler request write after end'
)
def test_node_application_promise_end(self): def test_node_application_promise_end(self):
self.load('promise_end') self.load('promise_end')
self.assertEqual( assert (
self.post( self.post(
headers={ headers={
'Host': 'localhost', 'Host': 'localhost',
@@ -297,13 +264,10 @@ class TestNodeApplication(TestApplicationNode):
'Connection': 'close', 'Connection': 'close',
}, },
body='end', body='end',
)['status'], )['status']
200, == 200
'promise end request', ), 'promise end request'
) assert waitforfiles(self.temp_dir + '/node/callback'), 'promise end'
self.assertTrue(
self.waitforfiles(self.testdir + '/node/callback'), 'promise end'
)
def test_node_application_promise_multiple_calls(self): def test_node_application_promise_multiple_calls(self):
self.load('promise_handler') self.load('promise_handler')
@@ -317,10 +281,9 @@ class TestNodeApplication(TestApplicationNode):
body='callback1', body='callback1',
) )
self.assertTrue( assert waitforfiles(
self.waitforfiles(self.testdir + '/node/callback1'), self.temp_dir + '/node/callback1'
'promise first call', ), 'promise first call'
)
self.post( self.post(
headers={ headers={
@@ -331,65 +294,55 @@ class TestNodeApplication(TestApplicationNode):
body='callback2', body='callback2',
) )
self.assertTrue( assert waitforfiles(
self.waitforfiles(self.testdir + '/node/callback2'), self.temp_dir + '/node/callback2'
'promise second call', ), 'promise second call'
)
@unittest.skip('not yet') @pytest.mark.skip('not yet')
def test_node_application_header_name_valid(self): def test_node_application_header_name_valid(self):
self.load('header_name_valid') self.load('header_name_valid')
self.assertNotIn('status', self.get(), 'header name valid') assert 'status' not in self.get(), 'header name valid'
def test_node_application_header_value_object(self): def test_node_application_header_value_object(self):
self.load('header_value_object') self.load('header_value_object')
self.assertIn('X-Header', self.get()['headers'], 'header value object') assert 'X-Header' in self.get()['headers'], 'header value object'
def test_node_application_get_header_names(self): def test_node_application_get_header_names(self):
self.load('get_header_names') self.load('get_header_names')
self.assertListEqual( assert self.get()['headers']['X-Names'] == [
self.get()['headers']['X-Names'], 'date',
['date', 'x-header'], 'x-header',
'get header names', ], 'get header names'
)
def test_node_application_has_header(self): def test_node_application_has_header(self):
self.load('has_header') self.load('has_header')
self.assertEqual( assert (
self.get( self.get(
headers={ headers={
'Host': 'localhost', 'Host': 'localhost',
'X-Header': 'length', 'X-Header': 'length',
'Connection': 'close', 'Connection': 'close',
} }
)['headers']['X-Has-Header'], )['headers']['X-Has-Header']
'false', == 'false'
'has header length', ), 'has header length'
)
self.assertEqual( assert (
self.get( self.get(
headers={ headers={
'Host': 'localhost', 'Host': 'localhost',
'X-Header': 'Date', 'X-Header': 'Date',
'Connection': 'close', 'Connection': 'close',
} }
)['headers']['X-Has-Header'], )['headers']['X-Has-Header']
'false', == 'false'
'has header date', ), 'has header date'
)
def test_node_application_write_multiple(self): def test_node_application_write_multiple(self):
self.load('write_multiple') self.load('write_multiple')
self.assertEqual( assert self.get()['body'] == 'writewrite2end', 'write multiple'
self.get()['body'], 'writewrite2end', 'write multiple'
)
if __name__ == '__main__':
TestNodeApplication.main()

View File

@@ -1,9 +1,10 @@
import pytest
import struct import struct
import time import time
import unittest
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 conftest import option, skip_alert
class TestNodeWebsockets(TestApplicationNode): class TestNodeWebsockets(TestApplicationNode):
@@ -11,23 +12,17 @@ class TestNodeWebsockets(TestApplicationNode):
ws = TestApplicationWebsocket() ws = TestApplicationWebsocket()
def setUp(self): def setup_method(self):
super().setUp() super().setup_method()
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{'http': {'websocket': {'keepalive_interval': 0}}}, 'settings' {'http': {'websocket': {'keepalive_interval': 0}}}, 'settings'
), ), 'clear keepalive_interval'
'clear keepalive_interval',
)
self.skip_alerts.extend( skip_alert(r'socket close\(\d+\) failed')
[r'socket close\(\d+\) failed']
)
def close_connection(self, sock): def close_connection(self, sock):
self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', 'empty soc') assert self.recvall(sock, read_timeout=0.1) == b'', 'empty soc'
self.ws.frame_write(sock, self.ws.OP_CLOSE, self.ws.serialize_close()) self.ws.frame_write(sock, self.ws.OP_CLOSE, self.ws.serialize_close())
@@ -36,9 +31,9 @@ class TestNodeWebsockets(TestApplicationNode):
def check_close(self, sock, code=1000, no_close=False): def check_close(self, sock, code=1000, no_close=False):
frame = self.ws.frame_read(sock) frame = self.ws.frame_read(sock)
self.assertEqual(frame['fin'], True, 'close fin') assert frame['fin'] == True, 'close fin'
self.assertEqual(frame['opcode'], self.ws.OP_CLOSE, 'close opcode') assert frame['opcode'] == self.ws.OP_CLOSE, 'close opcode'
self.assertEqual(frame['code'], code, 'close code') assert frame['code'] == code, 'close code'
if not no_close: if not no_close:
sock.close() sock.close()
@@ -49,9 +44,9 @@ class TestNodeWebsockets(TestApplicationNode):
else: else:
data = frame['data'].decode('utf-8') data = frame['data'].decode('utf-8')
self.assertEqual(frame['fin'], fin, 'fin') assert frame['fin'] == fin, 'fin'
self.assertEqual(frame['opcode'], opcode, 'opcode') assert frame['opcode'] == opcode, 'opcode'
self.assertEqual(data, payload, 'payload') assert data == payload, 'payload'
def test_node_websockets_handshake(self): def test_node_websockets_handshake(self):
self.load('websockets/mirror') self.load('websockets/mirror')
@@ -59,14 +54,12 @@ class TestNodeWebsockets(TestApplicationNode):
resp, sock, key = self.ws.upgrade() resp, sock, key = self.ws.upgrade()
sock.close() sock.close()
self.assertEqual(resp['status'], 101, 'status') assert resp['status'] == 101, 'status'
self.assertEqual(resp['headers']['Upgrade'], 'websocket', 'upgrade') assert resp['headers']['Upgrade'] == 'websocket', 'upgrade'
self.assertEqual( assert resp['headers']['Connection'] == 'Upgrade', 'connection'
resp['headers']['Connection'], 'Upgrade', 'connection' assert resp['headers']['Sec-WebSocket-Accept'] == self.ws.accept(
) key
self.assertEqual( ), 'key'
resp['headers']['Sec-WebSocket-Accept'], self.ws.accept(key), 'key'
)
def test_node_websockets_mirror(self): def test_node_websockets_mirror(self):
self.load('websockets/mirror') self.load('websockets/mirror')
@@ -78,12 +71,12 @@ class TestNodeWebsockets(TestApplicationNode):
self.ws.frame_write(sock, self.ws.OP_TEXT, message) self.ws.frame_write(sock, self.ws.OP_TEXT, message)
frame = self.ws.frame_read(sock) frame = self.ws.frame_read(sock)
self.assertEqual(message, frame['data'].decode('utf-8'), 'mirror') assert message == frame['data'].decode('utf-8'), 'mirror'
self.ws.frame_write(sock, self.ws.OP_TEXT, message) self.ws.frame_write(sock, self.ws.OP_TEXT, message)
frame = self.ws.frame_read(sock) frame = self.ws.frame_read(sock)
self.assertEqual(message, frame['data'].decode('utf-8'), 'mirror 2') assert message == frame['data'].decode('utf-8'), 'mirror 2'
sock.close() sock.close()
@@ -98,8 +91,8 @@ class TestNodeWebsockets(TestApplicationNode):
frame = self.ws.frame_read(sock) frame = self.ws.frame_read(sock)
self.assertEqual(frame['opcode'], self.ws.OP_CLOSE, 'no mask opcode') assert frame['opcode'] == self.ws.OP_CLOSE, 'no mask opcode'
self.assertEqual(frame['code'], 1002, 'no mask close code') assert frame['code'] == 1002, 'no mask close code'
sock.close() sock.close()
@@ -116,11 +109,9 @@ class TestNodeWebsockets(TestApplicationNode):
frame = self.ws.frame_read(sock) frame = self.ws.frame_read(sock)
self.assertEqual( assert message + ' ' + message == frame['data'].decode(
message + ' ' + message, 'utf-8'
frame['data'].decode('utf-8'), ), 'mirror framing'
'mirror framing',
)
sock.close() sock.close()
@@ -136,9 +127,7 @@ class TestNodeWebsockets(TestApplicationNode):
frame = self.ws.frame_read(sock) frame = self.ws.frame_read(sock)
frame.pop('data') frame.pop('data')
self.assertDictEqual( assert frame == {
frame,
{
'fin': True, 'fin': True,
'rsv1': False, 'rsv1': False,
'rsv2': False, 'rsv2': False,
@@ -147,9 +136,7 @@ class TestNodeWebsockets(TestApplicationNode):
'mask': 0, 'mask': 0,
'code': 1002, 'code': 1002,
'reason': 'Fragmented control frame', 'reason': 'Fragmented control frame',
}, }, 'close frame'
'close frame',
)
sock.close() sock.close()
@@ -168,7 +155,7 @@ class TestNodeWebsockets(TestApplicationNode):
frame = self.ws.frame_read(sock) frame = self.ws.frame_read(sock)
data += frame['data'].decode('utf-8') data += frame['data'].decode('utf-8')
self.assertEqual(message, data, 'large') assert message == data, 'large'
sock.close() sock.close()
@@ -187,13 +174,13 @@ class TestNodeWebsockets(TestApplicationNode):
frame1 = self.ws.frame_read(sock1) frame1 = self.ws.frame_read(sock1)
frame2 = self.ws.frame_read(sock2) frame2 = self.ws.frame_read(sock2)
self.assertEqual(message1, frame1['data'].decode('utf-8'), 'client 1') assert message1 == frame1['data'].decode('utf-8'), 'client 1'
self.assertEqual(message2, frame2['data'].decode('utf-8'), 'client 2') assert message2 == frame2['data'].decode('utf-8'), 'client 2'
sock1.close() sock1.close()
sock2.close() sock2.close()
@unittest.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
@@ -209,7 +196,7 @@ class TestNodeWebsockets(TestApplicationNode):
}, },
) )
self.assertEqual(resp['status'], 400, 'upgrade absent') assert resp['status'] == 400, 'upgrade absent'
def test_node_websockets_handshake_case_insensitive(self): def test_node_websockets_handshake_case_insensitive(self):
self.load('websockets/mirror') self.load('websockets/mirror')
@@ -226,9 +213,9 @@ class TestNodeWebsockets(TestApplicationNode):
) )
sock.close() sock.close()
self.assertEqual(resp['status'], 101, 'status') assert resp['status'] == 101, 'status'
@unittest.skip('not yet') @pytest.mark.skip('not yet')
def test_node_websockets_handshake_connection_absent(self): # FAIL def test_node_websockets_handshake_connection_absent(self): # FAIL
self.load('websockets/mirror') self.load('websockets/mirror')
@@ -242,7 +229,7 @@ class TestNodeWebsockets(TestApplicationNode):
}, },
) )
self.assertEqual(resp['status'], 400, 'status') assert resp['status'] == 400, 'status'
def test_node_websockets_handshake_version_absent(self): def test_node_websockets_handshake_version_absent(self):
self.load('websockets/mirror') self.load('websockets/mirror')
@@ -257,9 +244,9 @@ class TestNodeWebsockets(TestApplicationNode):
}, },
) )
self.assertEqual(resp['status'], 426, 'status') assert resp['status'] == 426, 'status'
@unittest.skip('not yet') @pytest.mark.skip('not yet')
def test_node_websockets_handshake_key_invalid(self): def test_node_websockets_handshake_key_invalid(self):
self.load('websockets/mirror') self.load('websockets/mirror')
@@ -274,7 +261,7 @@ class TestNodeWebsockets(TestApplicationNode):
}, },
) )
self.assertEqual(resp['status'], 400, 'key length') assert resp['status'] == 400, 'key length'
key = self.ws.key() key = self.ws.key()
resp = self.get( resp = self.get(
@@ -288,9 +275,7 @@ class TestNodeWebsockets(TestApplicationNode):
}, },
) )
self.assertEqual( assert resp['status'] == 400, 'key double' # FAIL https://tools.ietf.org/html/rfc6455#section-11.3.1
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')
@@ -306,7 +291,7 @@ class TestNodeWebsockets(TestApplicationNode):
}, },
) )
self.assertEqual(resp['status'], 400, 'status') assert resp['status'] == 400, 'status'
def test_node_websockets_handshake_http_10(self): def test_node_websockets_handshake_http_10(self):
self.load('websockets/mirror') self.load('websockets/mirror')
@@ -323,7 +308,7 @@ class TestNodeWebsockets(TestApplicationNode):
http_10=True, http_10=True,
) )
self.assertEqual(resp['status'], 400, 'status') assert resp['status'] == 400, 'status'
def test_node_websockets_handshake_uri_invalid(self): def test_node_websockets_handshake_uri_invalid(self):
self.load('websockets/mirror') self.load('websockets/mirror')
@@ -340,7 +325,7 @@ class TestNodeWebsockets(TestApplicationNode):
url='!', url='!',
) )
self.assertEqual(resp['status'], 400, 'status') assert resp['status'] == 400, 'status'
def test_node_websockets_protocol_absent(self): def test_node_websockets_protocol_absent(self):
self.load('websockets/mirror') self.load('websockets/mirror')
@@ -357,14 +342,12 @@ class TestNodeWebsockets(TestApplicationNode):
) )
sock.close() sock.close()
self.assertEqual(resp['status'], 101, 'status') assert resp['status'] == 101, 'status'
self.assertEqual(resp['headers']['Upgrade'], 'websocket', 'upgrade') assert resp['headers']['Upgrade'] == 'websocket', 'upgrade'
self.assertEqual( assert resp['headers']['Connection'] == 'Upgrade', 'connection'
resp['headers']['Connection'], 'Upgrade', 'connection' assert resp['headers']['Sec-WebSocket-Accept'] == self.ws.accept(
) key
self.assertEqual( ), 'key'
resp['headers']['Sec-WebSocket-Accept'], self.ws.accept(key), 'key'
)
# autobahn-testsuite # autobahn-testsuite
# #
@@ -461,12 +444,12 @@ class TestNodeWebsockets(TestApplicationNode):
_, sock, _ = self.ws.upgrade() _, sock, _ = self.ws.upgrade()
self.ws.frame_write(sock, self.ws.OP_PONG, '') self.ws.frame_write(sock, self.ws.OP_PONG, '')
self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', '2_7') assert self.recvall(sock, read_timeout=0.1) == b'', '2_7'
# 2_8 # 2_8
self.ws.frame_write(sock, self.ws.OP_PONG, 'unsolicited pong payload') self.ws.frame_write(sock, self.ws.OP_PONG, 'unsolicited pong payload')
self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', '2_8') assert self.recvall(sock, read_timeout=0.1) == b'', '2_8'
# 2_9 # 2_9
@@ -506,7 +489,7 @@ class TestNodeWebsockets(TestApplicationNode):
self.close_connection(sock) self.close_connection(sock)
@unittest.skip('not yet') @pytest.mark.skip('not yet')
def test_node_websockets_3_1__3_7(self): def test_node_websockets_3_1__3_7(self):
self.load('websockets/mirror') self.load('websockets/mirror')
@@ -532,7 +515,7 @@ class TestNodeWebsockets(TestApplicationNode):
self.check_close(sock, 1002, no_close=True) self.check_close(sock, 1002, no_close=True)
self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', 'empty 3_2') assert self.recvall(sock, read_timeout=0.1) == b'', 'empty 3_2'
sock.close() sock.close()
# 3_3 # 3_3
@@ -550,7 +533,7 @@ class TestNodeWebsockets(TestApplicationNode):
self.check_close(sock, 1002, no_close=True) self.check_close(sock, 1002, no_close=True)
self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', 'empty 3_3') assert self.recvall(sock, read_timeout=0.1) == b'', 'empty 3_3'
sock.close() sock.close()
# 3_4 # 3_4
@@ -568,7 +551,7 @@ class TestNodeWebsockets(TestApplicationNode):
self.check_close(sock, 1002, no_close=True) self.check_close(sock, 1002, no_close=True)
self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', 'empty 3_4') assert self.recvall(sock, read_timeout=0.1) == b'', 'empty 3_4'
sock.close() sock.close()
# 3_5 # 3_5
@@ -754,7 +737,7 @@ class TestNodeWebsockets(TestApplicationNode):
# 5_4 # 5_4
self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False) self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', '5_4') assert self.recvall(sock, read_timeout=0.1) == b'', '5_4'
self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=True) self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2', fin=True)
frame = self.ws.frame_read(sock) frame = self.ws.frame_read(sock)
@@ -791,7 +774,7 @@ class TestNodeWebsockets(TestApplicationNode):
ping_payload = 'ping payload' ping_payload = 'ping payload'
self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False) self.ws.frame_write(sock, self.ws.OP_TEXT, 'fragment1', fin=False)
self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', '5_7') assert self.recvall(sock, read_timeout=0.1) == b'', '5_7'
self.ws.frame_write(sock, self.ws.OP_PING, ping_payload) self.ws.frame_write(sock, self.ws.OP_PING, ping_payload)
@@ -975,7 +958,7 @@ class TestNodeWebsockets(TestApplicationNode):
frame = self.ws.frame_read(sock) frame = self.ws.frame_read(sock)
self.check_frame(frame, True, self.ws.OP_PONG, 'pongme 2!') self.check_frame(frame, True, self.ws.OP_PONG, 'pongme 2!')
self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', '5_20') assert self.recvall(sock, read_timeout=0.1) == b'', '5_20'
self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment5') self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment5')
self.check_frame( self.check_frame(
@@ -1108,7 +1091,7 @@ class TestNodeWebsockets(TestApplicationNode):
self.check_close(sock, no_close=True) self.check_close(sock, no_close=True)
self.ws.frame_write(sock, self.ws.OP_PING, '') self.ws.frame_write(sock, self.ws.OP_PING, '')
self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', 'empty soc') assert self.recvall(sock, read_timeout=0.1) == b'', 'empty soc'
sock.close() sock.close()
@@ -1120,7 +1103,7 @@ class TestNodeWebsockets(TestApplicationNode):
self.check_close(sock, no_close=True) self.check_close(sock, no_close=True)
self.ws.frame_write(sock, self.ws.OP_TEXT, payload) self.ws.frame_write(sock, self.ws.OP_TEXT, payload)
self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', 'empty soc') assert self.recvall(sock, read_timeout=0.1) == b'', 'empty soc'
sock.close() sock.close()
@@ -1133,7 +1116,7 @@ class TestNodeWebsockets(TestApplicationNode):
self.check_close(sock, no_close=True) self.check_close(sock, no_close=True)
self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2') self.ws.frame_write(sock, self.ws.OP_CONT, 'fragment2')
self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', 'empty soc') assert self.recvall(sock, read_timeout=0.1) == b'', 'empty soc'
sock.close() sock.close()
@@ -1148,7 +1131,7 @@ class TestNodeWebsockets(TestApplicationNode):
self.recvall(sock, read_timeout=1) self.recvall(sock, read_timeout=1)
self.ws.frame_write(sock, self.ws.OP_PING, '') self.ws.frame_write(sock, self.ws.OP_PING, '')
self.assertEqual(self.recvall(sock, read_timeout=0.1), b'', 'empty soc') assert self.recvall(sock, read_timeout=0.1) == b'', 'empty soc'
sock.close() sock.close()
@@ -1268,15 +1251,13 @@ class TestNodeWebsockets(TestApplicationNode):
self.ws.frame_write(sock, self.ws.OP_CLOSE, payload) self.ws.frame_write(sock, self.ws.OP_CLOSE, payload)
self.check_close(sock, 1002) self.check_close(sock, 1002)
def test_node_websockets_9_1_1__9_6_6(self): def test_node_websockets_9_1_1__9_6_6(self, is_unsafe):
if not self.unsafe: if not is_unsafe:
self.skipTest("unsafe, long run") pytest.skip('unsafe, long run')
self.load('websockets/mirror') self.load('websockets/mirror')
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{ {
'http': { 'http': {
'websocket': { 'websocket': {
@@ -1286,9 +1267,7 @@ class TestNodeWebsockets(TestApplicationNode):
} }
}, },
'settings', 'settings',
), ), 'increase max_frame_size and keepalive_interval'
'increase max_frame_size and keepalive_interval',
)
_, sock, _ = self.ws.upgrade() _, sock, _ = self.ws.upgrade()
@@ -1329,7 +1308,7 @@ class TestNodeWebsockets(TestApplicationNode):
check_payload(op_binary, 8 * 2 ** 20) # 9_2_5 check_payload(op_binary, 8 * 2 ** 20) # 9_2_5
check_payload(op_binary, 16 * 2 ** 20) # 9_2_6 check_payload(op_binary, 16 * 2 ** 20) # 9_2_6
if self.system != 'Darwin' and self.system != 'FreeBSD': if option.system != 'Darwin' and option.system != 'FreeBSD':
check_message(op_text, 64) # 9_3_1 check_message(op_text, 64) # 9_3_1
check_message(op_text, 256) # 9_3_2 check_message(op_text, 256) # 9_3_2
check_message(op_text, 2 ** 10) # 9_3_3 check_message(op_text, 2 ** 10) # 9_3_3
@@ -1385,13 +1364,9 @@ class TestNodeWebsockets(TestApplicationNode):
def test_node_websockets_max_frame_size(self): def test_node_websockets_max_frame_size(self):
self.load('websockets/mirror') self.load('websockets/mirror')
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{'http': {'websocket': {'max_frame_size': 100}}}, 'settings' {'http': {'websocket': {'max_frame_size': 100}}}, 'settings'
), ), 'configure max_frame_size'
'configure max_frame_size',
)
_, sock, _ = self.ws.upgrade() _, sock, _ = self.ws.upgrade()
@@ -1411,13 +1386,9 @@ class TestNodeWebsockets(TestApplicationNode):
def test_node_websockets_read_timeout(self): def test_node_websockets_read_timeout(self):
self.load('websockets/mirror') self.load('websockets/mirror')
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{'http': {'websocket': {'read_timeout': 5}}}, 'settings' {'http': {'websocket': {'read_timeout': 5}}}, 'settings'
), ), 'configure read_timeout'
'configure read_timeout',
)
_, sock, _ = self.ws.upgrade() _, sock, _ = self.ws.upgrade()
@@ -1431,13 +1402,9 @@ class TestNodeWebsockets(TestApplicationNode):
def test_node_websockets_keepalive_interval(self): def test_node_websockets_keepalive_interval(self):
self.load('websockets/mirror') self.load('websockets/mirror')
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{'http': {'websocket': {'keepalive_interval': 5}}}, 'settings' {'http': {'websocket': {'keepalive_interval': 5}}}, 'settings'
), ), 'configure keepalive_interval'
'configure keepalive_interval',
)
_, sock, _ = self.ws.upgrade() _, sock, _ = self.ws.upgrade()
@@ -1450,7 +1417,3 @@ class TestNodeWebsockets(TestApplicationNode):
self.check_frame(frame, True, self.ws.OP_PING, '') # PING frame self.check_frame(frame, True, self.ws.OP_PING, '') # PING frame
sock.close() sock.close()
if __name__ == '__main__':
TestNodeWebsockets.main()

View File

@@ -1,6 +1,8 @@
import unittest import pytest
import re
from unit.applications.lang.perl import TestApplicationPerl from unit.applications.lang.perl import TestApplicationPerl
from conftest import skip_alert
class TestPerlApplication(TestApplicationPerl): class TestPerlApplication(TestApplicationPerl):
@@ -21,27 +23,21 @@ class TestPerlApplication(TestApplicationPerl):
body=body, body=body,
) )
self.assertEqual(resp['status'], 200, 'status') assert resp['status'] == 200, 'status'
headers = resp['headers'] headers = resp['headers']
header_server = headers.pop('Server') header_server = headers.pop('Server')
self.assertRegex(header_server, r'Unit/[\d\.]+', 'server header') assert re.search(r'Unit/[\d\.]+', header_server), 'server header'
self.assertEqual( assert (
headers.pop('Server-Software'), headers.pop('Server-Software') == header_server
header_server, ), 'server software header'
'server software header',
)
date = headers.pop('Date') date = headers.pop('Date')
self.assertEqual(date[-4:], ' GMT', 'date header timezone') assert date[-4:] == ' GMT', 'date header timezone'
self.assertLess( assert (
abs(self.date_to_sec_epoch(date) - self.sec_epoch()), abs(self.date_to_sec_epoch(date) - self.sec_epoch()) < 5
5, ), 'date header'
'date header',
)
self.assertDictEqual( assert headers == {
headers,
{
'Connection': 'close', 'Connection': 'close',
'Content-Length': str(len(body)), 'Content-Length': str(len(body)),
'Content-Type': 'text/html', 'Content-Type': 'text/html',
@@ -57,113 +53,100 @@ class TestPerlApplication(TestApplicationPerl):
'Psgi-Run-Once': '', 'Psgi-Run-Once': '',
'Psgi-Nonblocking': '', 'Psgi-Nonblocking': '',
'Psgi-Streaming': '1', 'Psgi-Streaming': '1',
}, }, 'headers'
'headers', assert resp['body'] == body, 'body'
)
self.assertEqual(resp['body'], body, 'body')
def test_perl_application_query_string(self): def test_perl_application_query_string(self):
self.load('query_string') self.load('query_string')
resp = self.get(url='/?var1=val1&var2=val2') resp = self.get(url='/?var1=val1&var2=val2')
self.assertEqual( assert (
resp['headers']['Query-String'], resp['headers']['Query-String'] == 'var1=val1&var2=val2'
'var1=val1&var2=val2', ), 'Query-String header'
'Query-String header',
)
def test_perl_application_query_string_empty(self): def test_perl_application_query_string_empty(self):
self.load('query_string') self.load('query_string')
resp = self.get(url='/?') resp = self.get(url='/?')
self.assertEqual(resp['status'], 200, 'query string empty status') assert resp['status'] == 200, 'query string empty status'
self.assertEqual( assert resp['headers']['Query-String'] == '', 'query string empty'
resp['headers']['Query-String'], '', 'query string empty'
)
def test_perl_application_query_string_absent(self): def test_perl_application_query_string_absent(self):
self.load('query_string') self.load('query_string')
resp = self.get() resp = self.get()
self.assertEqual(resp['status'], 200, 'query string absent status') assert resp['status'] == 200, 'query string absent status'
self.assertEqual( assert resp['headers']['Query-String'] == '', 'query string absent'
resp['headers']['Query-String'], '', 'query string absent'
)
@unittest.skip('not yet') @pytest.mark.skip('not yet')
def test_perl_application_server_port(self): def test_perl_application_server_port(self):
self.load('server_port') self.load('server_port')
self.assertEqual( assert (
self.get()['headers']['Server-Port'], '7080', 'Server-Port header' self.get()['headers']['Server-Port'] == '7080'
) ), 'Server-Port header'
def test_perl_application_input_read_empty(self): def test_perl_application_input_read_empty(self):
self.load('input_read_empty') self.load('input_read_empty')
self.assertEqual(self.get()['body'], '', 'read empty') assert self.get()['body'] == '', 'read empty'
def test_perl_application_input_read_parts(self): def test_perl_application_input_read_parts(self):
self.load('input_read_parts') self.load('input_read_parts')
self.assertEqual( assert (
self.post(body='0123456789')['body'], self.post(body='0123456789')['body'] == '0123456789'
'0123456789', ), 'input read parts'
'input read parts',
)
@unittest.skip('not yet') @pytest.mark.skip('not yet')
def test_perl_application_input_read_offset(self): def test_perl_application_input_read_offset(self):
self.load('input_read_offset') self.load('input_read_offset')
self.assertEqual( assert self.post(body='0123456789')['body'] == '4567', 'read offset'
self.post(body='0123456789')['body'], '4567', 'read offset'
)
def test_perl_application_input_copy(self): def test_perl_application_input_copy(self):
self.load('input_copy') self.load('input_copy')
body = '0123456789' body = '0123456789'
self.assertEqual(self.post(body=body)['body'], body, 'input copy') assert self.post(body=body)['body'] == body, 'input copy'
def test_perl_application_errors_print(self): def test_perl_application_errors_print(self):
self.load('errors_print') self.load('errors_print')
self.assertEqual(self.get()['body'], '1', 'errors result') assert self.get()['body'] == '1', 'errors result'
self.stop() self.stop()
self.assertIsNotNone( assert (
self.wait_for_record(r'\[error\].+Error in application'), self.wait_for_record(r'\[error\].+Error in application')
'errors print', is not None
) ), 'errors print'
def test_perl_application_header_equal_names(self): def test_perl_application_header_equal_names(self):
self.load('header_equal_names') self.load('header_equal_names')
self.assertListEqual( assert self.get()['headers']['Set-Cookie'] == [
self.get()['headers']['Set-Cookie'], 'tc=one,two,three',
['tc=one,two,three', 'tc=four,five,six'], 'tc=four,five,six',
'header equal names', ], 'header equal names'
)
def test_perl_application_header_pairs(self): def test_perl_application_header_pairs(self):
self.load('header_pairs') self.load('header_pairs')
self.assertEqual(self.get()['headers']['blah'], 'blah', 'header pairs') assert self.get()['headers']['blah'] == 'blah', 'header pairs'
def test_perl_application_body_empty(self): def test_perl_application_body_empty(self):
self.load('body_empty') self.load('body_empty')
self.assertEqual(self.get()['body'], '', 'body empty') assert self.get()['body'] == '', 'body empty'
def test_perl_application_body_array(self): def test_perl_application_body_array(self):
self.load('body_array') self.load('body_array')
self.assertEqual(self.get()['body'], '0123456789', 'body array') assert self.get()['body'] == '0123456789', 'body array'
def test_perl_application_body_large(self): def test_perl_application_body_large(self):
self.load('variables') self.load('variables')
@@ -172,31 +155,29 @@ class TestPerlApplication(TestApplicationPerl):
resp = self.post(body=body)['body'] resp = self.post(body=body)['body']
self.assertEqual(resp, body, 'body large') assert resp == body, 'body large'
def test_perl_application_body_io_empty(self): def test_perl_application_body_io_empty(self):
self.load('body_io_empty') self.load('body_io_empty')
self.assertEqual(self.get()['status'], 200, 'body io empty') assert self.get()['status'] == 200, 'body io empty'
def test_perl_application_body_io_file(self): def test_perl_application_body_io_file(self):
self.load('body_io_file') self.load('body_io_file')
self.assertEqual(self.get()['body'], 'body\n', 'body io file') assert self.get()['body'] == 'body\n', 'body io file'
@unittest.skip('not yet, unsafe') @pytest.mark.skip('not yet')
def test_perl_application_syntax_error(self): def test_perl_application_syntax_error(self):
self.skip_alerts.extend( skip_alert(r'PSGI: Failed to parse script')
[r'PSGI: Failed to parse script']
)
self.load('syntax_error') self.load('syntax_error')
self.assertEqual(self.get()['status'], 500, 'syntax error') assert self.get()['status'] == 500, 'syntax error'
def test_perl_keepalive_body(self): def test_perl_keepalive_body(self):
self.load('variables') self.load('variables')
self.assertEqual(self.get()['status'], 200, 'init') assert self.get()['status'] == 200, 'init'
body = '0123456789' * 500 body = '0123456789' * 500
(resp, sock) = self.post( (resp, sock) = self.post(
@@ -210,7 +191,7 @@ class TestPerlApplication(TestApplicationPerl):
read_timeout=1, read_timeout=1,
) )
self.assertEqual(resp['body'], body, 'keep-alive 1') assert resp['body'] == body, 'keep-alive 1'
body = '0123456789' body = '0123456789'
resp = self.post( resp = self.post(
@@ -223,39 +204,35 @@ class TestPerlApplication(TestApplicationPerl):
body=body, body=body,
) )
self.assertEqual(resp['body'], body, 'keep-alive 2') assert resp['body'] == body, 'keep-alive 2'
def test_perl_body_io_fake(self): def test_perl_body_io_fake(self):
self.load('body_io_fake') self.load('body_io_fake')
self.assertEqual(self.get()['body'], '21', 'body io fake') assert self.get()['body'] == '21', 'body io fake'
self.assertIsNotNone( assert (
self.wait_for_record(r'\[error\].+IOFake getline\(\) \$\/ is \d+'), self.wait_for_record(r'\[error\].+IOFake getline\(\) \$\/ is \d+')
'body io fake $/ value', is not None
) ), 'body io fake $/ value'
self.assertIsNotNone( assert (
self.wait_for_record(r'\[error\].+IOFake close\(\) called'), self.wait_for_record(r'\[error\].+IOFake close\(\) called')
'body io fake close', is not None
) ), 'body io fake close'
def test_perl_delayed_response(self): def test_perl_delayed_response(self):
self.load('delayed_response') self.load('delayed_response')
resp = self.get() resp = self.get()
self.assertEqual(resp['status'], 200, 'status') assert resp['status'] == 200, 'status'
self.assertEqual(resp['body'], 'Hello World!', 'body') assert resp['body'] == 'Hello World!', 'body'
def test_perl_streaming_body(self): def test_perl_streaming_body(self):
self.load('streaming_body') self.load('streaming_body')
resp = self.get() resp = self.get()
self.assertEqual(resp['status'], 200, 'status') assert resp['status'] == 200, 'status'
self.assertEqual(resp['body'], 'Hello World!', 'body') assert resp['body'] == 'Hello World!', 'body'
if __name__ == '__main__':
TestPerlApplication.main()

View File

@@ -1,10 +1,11 @@
import os import os
import pytest
import re import re
import shutil import shutil
import time import time
import unittest
from unit.applications.lang.php import TestApplicationPHP from unit.applications.lang.php import TestApplicationPHP
from conftest import option
class TestPHPApplication(TestApplicationPHP): class TestPHPApplication(TestApplicationPHP):
prerequisites = {'modules': {'php': 'all'}} prerequisites = {'modules': {'php': 'all'}}
@@ -12,30 +13,21 @@ class TestPHPApplication(TestApplicationPHP):
def before_disable_functions(self): def before_disable_functions(self):
body = self.get()['body'] body = self.get()['body']
self.assertRegex(body, r'time: \d+', 'disable_functions before time') assert re.search(r'time: \d+', body), 'disable_functions before time'
self.assertRegex(body, r'exec: \/\w+', 'disable_functions before exec') assert re.search(r'exec: \/\w+', body), 'disable_functions before exec'
def set_opcache(self, app, val): def set_opcache(self, app, val):
self.assertIn( assert 'success' in self.conf(
'success', {"admin": {"opcache.enable": val, "opcache.enable_cli": val,},},
self.conf(
{
"admin": {
"opcache.enable": val,
"opcache.enable_cli": val,
},
},
'applications/' + app + '/options', 'applications/' + app + '/options',
),
) )
opcache = self.get()['headers']['X-OPcache'] opcache = self.get()['headers']['X-OPcache']
if not opcache or opcache == '-1': if not opcache or opcache == '-1':
print('opcache is not supported') pytest.skip('opcache is not supported')
raise unittest.SkipTest()
self.assertEqual(opcache, val, 'opcache value') assert opcache == val, 'opcache value'
def test_php_application_variables(self): def test_php_application_variables(self):
self.load('variables') self.load('variables')
@@ -50,34 +42,28 @@ class TestPHPApplication(TestApplicationPHP):
'Connection': 'close', 'Connection': 'close',
}, },
body=body, body=body,
url='/index.php/blah?var=val' url='/index.php/blah?var=val',
) )
self.assertEqual(resp['status'], 200, 'status') assert resp['status'] == 200, 'status'
headers = resp['headers'] headers = resp['headers']
header_server = headers.pop('Server') header_server = headers.pop('Server')
self.assertRegex(header_server, r'Unit/[\d\.]+', 'server header') assert re.search(r'Unit/[\d\.]+', header_server), 'server header'
self.assertEqual( assert (
headers.pop('Server-Software'), headers.pop('Server-Software') == header_server
header_server, ), 'server software header'
'server software header',
)
date = headers.pop('Date') date = headers.pop('Date')
self.assertEqual(date[-4:], ' GMT', 'date header timezone') assert date[-4:] == ' GMT', 'date header timezone'
self.assertLess( assert (
abs(self.date_to_sec_epoch(date) - self.sec_epoch()), abs(self.date_to_sec_epoch(date) - self.sec_epoch()) < 5
5, ), 'date header'
'date header',
)
if 'X-Powered-By' in headers: if 'X-Powered-By' in headers:
headers.pop('X-Powered-By') headers.pop('X-Powered-By')
headers.pop('Content-type') headers.pop('Content-type')
self.assertDictEqual( assert headers == {
headers,
{
'Connection': 'close', 'Connection': 'close',
'Content-Length': str(len(body)), 'Content-Length': str(len(body)),
'Request-Method': 'POST', 'Request-Method': 'POST',
@@ -86,104 +72,92 @@ class TestPHPApplication(TestApplicationPHP):
'Http-Host': 'localhost', 'Http-Host': 'localhost',
'Server-Protocol': 'HTTP/1.1', 'Server-Protocol': 'HTTP/1.1',
'Custom-Header': 'blah', 'Custom-Header': 'blah',
}, }, 'headers'
'headers', assert resp['body'] == body, 'body'
)
self.assertEqual(resp['body'], body, 'body')
def test_php_application_query_string(self): def test_php_application_query_string(self):
self.load('query_string') self.load('query_string')
resp = self.get(url='/?var1=val1&var2=val2') resp = self.get(url='/?var1=val1&var2=val2')
self.assertEqual( assert (
resp['headers']['Query-String'], resp['headers']['Query-String'] == 'var1=val1&var2=val2'
'var1=val1&var2=val2', ), 'query string'
'query string',
)
def test_php_application_query_string_empty(self): def test_php_application_query_string_empty(self):
self.load('query_string') self.load('query_string')
resp = self.get(url='/?') resp = self.get(url='/?')
self.assertEqual(resp['status'], 200, 'query string empty status') assert resp['status'] == 200, 'query string empty status'
self.assertEqual( assert resp['headers']['Query-String'] == '', 'query string empty'
resp['headers']['Query-String'], '', 'query string empty'
)
def test_php_application_query_string_absent(self): def test_php_application_query_string_absent(self):
self.load('query_string') self.load('query_string')
resp = self.get() resp = self.get()
self.assertEqual(resp['status'], 200, 'query string absent status') assert resp['status'] == 200, 'query string absent status'
self.assertEqual( assert resp['headers']['Query-String'] == '', 'query string absent'
resp['headers']['Query-String'], '', 'query string absent'
)
def test_php_application_phpinfo(self): def test_php_application_phpinfo(self):
self.load('phpinfo') self.load('phpinfo')
resp = self.get() resp = self.get()
self.assertEqual(resp['status'], 200, 'status') assert resp['status'] == 200, 'status'
self.assertNotEqual(resp['body'], '', 'body not empty') assert resp['body'] != '', 'body not empty'
def test_php_application_header_status(self): def test_php_application_header_status(self):
self.load('header') self.load('header')
self.assertEqual( assert (
self.get( self.get(
headers={ headers={
'Host': 'localhost', 'Host': 'localhost',
'Connection': 'close', 'Connection': 'close',
'X-Header': 'HTTP/1.1 404 Not Found', 'X-Header': 'HTTP/1.1 404 Not Found',
} }
)['status'], )['status']
404, == 404
'status', ), 'status'
)
self.assertEqual( assert (
self.get( self.get(
headers={ headers={
'Host': 'localhost', 'Host': 'localhost',
'Connection': 'close', 'Connection': 'close',
'X-Header': 'http/1.1 404 Not Found', 'X-Header': 'http/1.1 404 Not Found',
} }
)['status'], )['status']
404, == 404
'status case insensitive', ), 'status case insensitive'
)
self.assertEqual( assert (
self.get( self.get(
headers={ headers={
'Host': 'localhost', 'Host': 'localhost',
'Connection': 'close', 'Connection': 'close',
'X-Header': 'HTTP/ 404 Not Found', 'X-Header': 'HTTP/ 404 Not Found',
} }
)['status'], )['status']
404, == 404
'status version empty', ), 'status version empty'
)
def test_php_application_404(self): def test_php_application_404(self):
self.load('404') self.load('404')
resp = self.get() resp = self.get()
self.assertEqual(resp['status'], 404, '404 status') assert resp['status'] == 404, '404 status'
self.assertRegex( assert re.search(
resp['body'], r'<title>404 Not Found</title>', '404 body' r'<title>404 Not Found</title>', resp['body']
) ), '404 body'
def test_php_application_keepalive_body(self): def test_php_application_keepalive_body(self):
self.load('mirror') self.load('mirror')
self.assertEqual(self.get()['status'], 200, 'init') assert self.get()['status'] == 200, 'init'
body = '0123456789' * 500 body = '0123456789' * 500
(resp, sock) = self.post( (resp, sock) = self.post(
@@ -197,7 +171,7 @@ class TestPHPApplication(TestApplicationPHP):
read_timeout=1, read_timeout=1,
) )
self.assertEqual(resp['body'], body, 'keep-alive 1') assert resp['body'] == body, 'keep-alive 1'
body = '0123456789' body = '0123456789'
resp = self.post( resp = self.post(
@@ -210,22 +184,22 @@ class TestPHPApplication(TestApplicationPHP):
body=body, body=body,
) )
self.assertEqual(resp['body'], body, 'keep-alive 2') assert resp['body'] == body, 'keep-alive 2'
def test_php_application_conditional(self): def test_php_application_conditional(self):
self.load('conditional') self.load('conditional')
self.assertRegex(self.get()['body'], r'True', 'conditional true') assert re.search(r'True', self.get()['body']), 'conditional true'
self.assertRegex(self.post()['body'], r'False', 'conditional false') assert re.search(r'False', self.post()['body']), 'conditional false'
def test_php_application_get_variables(self): def test_php_application_get_variables(self):
self.load('get_variables') self.load('get_variables')
resp = self.get(url='/?var1=val1&var2=&var3') resp = self.get(url='/?var1=val1&var2=&var3')
self.assertEqual(resp['headers']['X-Var-1'], 'val1', 'GET variables') assert resp['headers']['X-Var-1'] == 'val1', 'GET variables'
self.assertEqual(resp['headers']['X-Var-2'], '1', 'GET variables 2') assert resp['headers']['X-Var-2'] == '1', 'GET variables 2'
self.assertEqual(resp['headers']['X-Var-3'], '1', 'GET variables 3') assert resp['headers']['X-Var-3'] == '1', 'GET variables 3'
self.assertEqual(resp['headers']['X-Var-4'], '', 'GET variables 4') assert resp['headers']['X-Var-4'] == '', 'GET variables 4'
def test_php_application_post_variables(self): def test_php_application_post_variables(self):
self.load('post_variables') self.load('post_variables')
@@ -238,9 +212,9 @@ class TestPHPApplication(TestApplicationPHP):
}, },
body='var1=val1&var2=', body='var1=val1&var2=',
) )
self.assertEqual(resp['headers']['X-Var-1'], 'val1', 'POST variables') assert resp['headers']['X-Var-1'] == 'val1', 'POST variables'
self.assertEqual(resp['headers']['X-Var-2'], '1', 'POST variables 2') assert resp['headers']['X-Var-2'] == '1', 'POST variables 2'
self.assertEqual(resp['headers']['X-Var-3'], '', 'POST variables 3') assert resp['headers']['X-Var-3'] == '', 'POST variables 3'
def test_php_application_cookies(self): def test_php_application_cookies(self):
self.load('cookies') self.load('cookies')
@@ -253,41 +227,32 @@ class TestPHPApplication(TestApplicationPHP):
} }
) )
self.assertEqual(resp['headers']['X-Cookie-1'], 'val', 'cookie') assert resp['headers']['X-Cookie-1'] == 'val', 'cookie'
self.assertEqual(resp['headers']['X-Cookie-2'], 'val2', 'cookie') assert resp['headers']['X-Cookie-2'] == 'val2', 'cookie'
def test_php_application_ini_precision(self): def test_php_application_ini_precision(self):
self.load('ini_precision') self.load('ini_precision')
self.assertNotEqual( assert self.get()['headers']['X-Precision'] != '4', 'ini value default'
self.get()['headers']['X-Precision'], '4', 'ini value default'
)
self.conf( self.conf(
{"file": "ini/php.ini"}, 'applications/ini_precision/options' {"file": "ini/php.ini"}, 'applications/ini_precision/options'
) )
self.assertEqual( assert (
self.get()['headers']['X-File'], self.get()['headers']['X-File']
self.current_dir + '/php/ini_precision/ini/php.ini', == option.test_dir + '/php/ini_precision/ini/php.ini'
'ini file', ), 'ini file'
) assert self.get()['headers']['X-Precision'] == '4', 'ini value'
self.assertEqual(
self.get()['headers']['X-Precision'], '4', 'ini value'
)
@unittest.skip('not yet') @pytest.mark.skip('not yet')
def test_php_application_ini_admin_user(self): def test_php_application_ini_admin_user(self):
self.load('ini_precision') self.load('ini_precision')
self.assertIn( assert 'error' in self.conf(
'error',
self.conf(
{"user": {"precision": "4"}, "admin": {"precision": "5"}}, {"user": {"precision": "4"}, "admin": {"precision": "5"}},
'applications/ini_precision/options', 'applications/ini_precision/options',
), ), 'ini admin user'
'ini admin user',
)
def test_php_application_ini_admin(self): def test_php_application_ini_admin(self):
self.load('ini_precision') self.load('ini_precision')
@@ -297,9 +262,7 @@ class TestPHPApplication(TestApplicationPHP):
'applications/ini_precision/options', 'applications/ini_precision/options',
) )
self.assertEqual( assert self.get()['headers']['X-Precision'] == '5', 'ini value admin'
self.get()['headers']['X-Precision'], '5', 'ini value admin'
)
def test_php_application_ini_user(self): def test_php_application_ini_user(self):
self.load('ini_precision') self.load('ini_precision')
@@ -309,9 +272,7 @@ class TestPHPApplication(TestApplicationPHP):
'applications/ini_precision/options', 'applications/ini_precision/options',
) )
self.assertEqual( assert self.get()['headers']['X-Precision'] == '5', 'ini value user'
self.get()['headers']['X-Precision'], '5', 'ini value user'
)
def test_php_application_ini_user_2(self): def test_php_application_ini_user_2(self):
self.load('ini_precision') self.load('ini_precision')
@@ -320,17 +281,13 @@ class TestPHPApplication(TestApplicationPHP):
{"file": "ini/php.ini"}, 'applications/ini_precision/options' {"file": "ini/php.ini"}, 'applications/ini_precision/options'
) )
self.assertEqual( assert self.get()['headers']['X-Precision'] == '4', 'ini user file'
self.get()['headers']['X-Precision'], '4', 'ini user file'
)
self.conf( self.conf(
{"precision": "5"}, 'applications/ini_precision/options/user' {"precision": "5"}, 'applications/ini_precision/options/user'
) )
self.assertEqual( assert self.get()['headers']['X-Precision'] == '5', 'ini value user'
self.get()['headers']['X-Precision'], '5', 'ini value user'
)
def test_php_application_ini_set_admin(self): def test_php_application_ini_set_admin(self):
self.load('ini_precision') self.load('ini_precision')
@@ -339,11 +296,9 @@ class TestPHPApplication(TestApplicationPHP):
{"admin": {"precision": "5"}}, 'applications/ini_precision/options' {"admin": {"precision": "5"}}, 'applications/ini_precision/options'
) )
self.assertEqual( assert (
self.get(url='/?precision=6')['headers']['X-Precision'], self.get(url='/?precision=6')['headers']['X-Precision'] == '5'
'5', ), 'ini set admin'
'ini set admin',
)
def test_php_application_ini_set_user(self): def test_php_application_ini_set_user(self):
self.load('ini_precision') self.load('ini_precision')
@@ -352,11 +307,9 @@ class TestPHPApplication(TestApplicationPHP):
{"user": {"precision": "5"}}, 'applications/ini_precision/options' {"user": {"precision": "5"}}, 'applications/ini_precision/options'
) )
self.assertEqual( assert (
self.get(url='/?precision=6')['headers']['X-Precision'], self.get(url='/?precision=6')['headers']['X-Precision'] == '6'
'6', ), 'ini set user'
'ini set user',
)
def test_php_application_ini_repeat(self): def test_php_application_ini_repeat(self):
self.load('ini_precision') self.load('ini_precision')
@@ -365,13 +318,9 @@ class TestPHPApplication(TestApplicationPHP):
{"user": {"precision": "5"}}, 'applications/ini_precision/options' {"user": {"precision": "5"}}, 'applications/ini_precision/options'
) )
self.assertEqual( assert self.get()['headers']['X-Precision'] == '5', 'ini value'
self.get()['headers']['X-Precision'], '5', 'ini value'
)
self.assertEqual( assert self.get()['headers']['X-Precision'] == '5', 'ini value repeat'
self.get()['headers']['X-Precision'], '5', 'ini value repeat'
)
def test_php_application_disable_functions_exec(self): def test_php_application_disable_functions_exec(self):
self.load('time_exec') self.load('time_exec')
@@ -385,8 +334,8 @@ class TestPHPApplication(TestApplicationPHP):
body = self.get()['body'] body = self.get()['body']
self.assertRegex(body, r'time: \d+', 'disable_functions time') assert re.search(r'time: \d+', body), 'disable_functions time'
self.assertNotRegex(body, r'exec: \/\w+', 'disable_functions exec') assert not re.search(r'exec: \/\w+', body), 'disable_functions exec'
def test_php_application_disable_functions_comma(self): def test_php_application_disable_functions_comma(self):
self.load('time_exec') self.load('time_exec')
@@ -400,10 +349,12 @@ class TestPHPApplication(TestApplicationPHP):
body = self.get()['body'] body = self.get()['body']
self.assertNotRegex(body, r'time: \d+', 'disable_functions comma time') assert not re.search(
self.assertNotRegex( r'time: \d+', body
body, r'exec: \/\w+', 'disable_functions comma exec' ), 'disable_functions comma time'
) assert not re.search(
r'exec: \/\w+', body
), 'disable_functions comma exec'
def test_php_application_disable_functions_space(self): def test_php_application_disable_functions_space(self):
self.load('time_exec') self.load('time_exec')
@@ -417,10 +368,12 @@ class TestPHPApplication(TestApplicationPHP):
body = self.get()['body'] body = self.get()['body']
self.assertNotRegex(body, r'time: \d+', 'disable_functions space time') assert not re.search(
self.assertNotRegex( r'time: \d+', body
body, r'exec: \/\w+', 'disable_functions space exec' ), 'disable_functions space time'
) assert not re.search(
r'exec: \/\w+', body
), 'disable_functions space exec'
def test_php_application_disable_functions_user(self): def test_php_application_disable_functions_user(self):
self.load('time_exec') self.load('time_exec')
@@ -434,10 +387,10 @@ class TestPHPApplication(TestApplicationPHP):
body = self.get()['body'] body = self.get()['body']
self.assertRegex(body, r'time: \d+', 'disable_functions user time') assert re.search(r'time: \d+', body), 'disable_functions user time'
self.assertNotRegex( assert not re.search(
body, r'exec: \/\w+', 'disable_functions user exec' r'exec: \/\w+', body
) ), 'disable_functions user exec'
def test_php_application_disable_functions_nonexistent(self): def test_php_application_disable_functions_nonexistent(self):
self.load('time_exec') self.load('time_exec')
@@ -451,130 +404,118 @@ class TestPHPApplication(TestApplicationPHP):
body = self.get()['body'] body = self.get()['body']
self.assertRegex( assert re.search(
body, r'time: \d+', 'disable_functions nonexistent time' r'time: \d+', body
) ), 'disable_functions nonexistent time'
self.assertRegex( assert re.search(
body, r'exec: \/\w+', 'disable_functions nonexistent exec' r'exec: \/\w+', body
) ), 'disable_functions nonexistent exec'
def test_php_application_disable_classes(self): def test_php_application_disable_classes(self):
self.load('date_time') self.load('date_time')
self.assertRegex( assert re.search(
self.get()['body'], r'012345', 'disable_classes before' r'012345', self.get()['body']
) ), 'disable_classes before'
self.conf( self.conf(
{"admin": {"disable_classes": "DateTime"}}, {"admin": {"disable_classes": "DateTime"}},
'applications/date_time/options', 'applications/date_time/options',
) )
self.assertNotRegex( assert not re.search(
self.get()['body'], r'012345', 'disable_classes before' r'012345', self.get()['body']
) ), 'disable_classes before'
def test_php_application_disable_classes_user(self): def test_php_application_disable_classes_user(self):
self.load('date_time') self.load('date_time')
self.assertRegex( assert re.search(
self.get()['body'], r'012345', 'disable_classes before' r'012345', self.get()['body']
) ), 'disable_classes before'
self.conf( self.conf(
{"user": {"disable_classes": "DateTime"}}, {"user": {"disable_classes": "DateTime"}},
'applications/date_time/options', 'applications/date_time/options',
) )
self.assertNotRegex( assert not re.search(
self.get()['body'], r'012345', 'disable_classes before' r'012345', self.get()['body']
) ), 'disable_classes before'
def test_php_application_error_log(self): def test_php_application_error_log(self):
self.load('error_log') self.load('error_log')
self.assertEqual(self.get()['status'], 200, 'status') assert self.get()['status'] == 200, 'status'
time.sleep(1) time.sleep(1)
self.assertEqual(self.get()['status'], 200, 'status 2') assert self.get()['status'] == 200, 'status 2'
self.stop() self.stop()
pattern = r'\d{4}\/\d\d\/\d\d\s\d\d:.+\[notice\].+Error in application' pattern = r'\d{4}\/\d\d\/\d\d\s\d\d:.+\[notice\].+Error in application'
self.assertIsNotNone(self.wait_for_record(pattern), 'errors print') assert self.wait_for_record(pattern) is not None, 'errors print'
with open(self.testdir + '/unit.log', 'r', errors='ignore') as f: with open(self.temp_dir + '/unit.log', 'r', errors='ignore') as f:
errs = re.findall(pattern, f.read()) errs = re.findall(pattern, f.read())
self.assertEqual(len(errs), 2, 'error_log count') assert len(errs) == 2, 'error_log count'
date = errs[0].split('[')[0] date = errs[0].split('[')[0]
date2 = errs[1].split('[')[0] date2 = errs[1].split('[')[0]
self.assertNotEqual(date, date2, 'date diff') assert date != date2, 'date diff'
def test_php_application_script(self): def test_php_application_script(self):
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{ {
"listeners": {"*:7080": {"pass": "applications/script"}}, "listeners": {"*:7080": {"pass": "applications/script"}},
"applications": { "applications": {
"script": { "script": {
"type": "php", "type": "php",
"processes": {"spare": 0}, "processes": {"spare": 0},
"root": self.current_dir + "/php/script", "root": option.test_dir + "/php/script",
"script": "phpinfo.php", "script": "phpinfo.php",
} }
}, },
} }
), ), 'configure script'
'configure script',
)
resp = self.get() resp = self.get()
self.assertEqual(resp['status'], 200, 'status') assert resp['status'] == 200, 'status'
self.assertNotEqual(resp['body'], '', 'body not empty') assert resp['body'] != '', 'body not empty'
def test_php_application_index_default(self): def test_php_application_index_default(self):
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{ {
"listeners": {"*:7080": {"pass": "applications/phpinfo"}}, "listeners": {"*:7080": {"pass": "applications/phpinfo"}},
"applications": { "applications": {
"phpinfo": { "phpinfo": {
"type": "php", "type": "php",
"processes": {"spare": 0}, "processes": {"spare": 0},
"root": self.current_dir + "/php/phpinfo", "root": option.test_dir + "/php/phpinfo",
} }
}, },
} }
), ), 'configure index default'
'configure index default',
)
resp = self.get() resp = self.get()
self.assertEqual(resp['status'], 200, 'status') assert resp['status'] == 200, 'status'
self.assertNotEqual(resp['body'], '', 'body not empty') assert resp['body'] != '', 'body not empty'
def test_php_application_extension_check(self): def test_php_application_extension_check(self):
self.load('phpinfo') self.load('phpinfo')
self.assertNotEqual( assert self.get(url='/index.wrong')['status'] != 200, 'status'
self.get(url='/index.wrong')['status'], 200, 'status'
)
new_root = self.testdir + "/php" new_root = self.temp_dir + "/php"
os.mkdir(new_root) os.mkdir(new_root)
shutil.copy(self.current_dir + '/php/phpinfo/index.wrong', new_root) shutil.copy(option.test_dir + '/php/phpinfo/index.wrong', new_root)
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{ {
"listeners": {"*:7080": {"pass": "applications/phpinfo"}}, "listeners": {"*:7080": {"pass": "applications/phpinfo"}},
"applications": { "applications": {
@@ -586,52 +527,42 @@ class TestPHPApplication(TestApplicationPHP):
} }
}, },
} }
), ), 'configure new root'
'configure new root',
)
resp = self.get() resp = self.get()
self.assertNotEqual( assert str(resp['status']) + resp['body'] != '200', 'status new root'
str(resp['status']) + resp['body'], '200', 'status new root'
)
def run_php_application_cwd_root_tests(self): def run_php_application_cwd_root_tests(self):
self.assertIn( assert 'success' in self.conf_delete(
'success', self.conf_delete('applications/cwd/working_directory') 'applications/cwd/working_directory'
) )
script_cwd = self.current_dir + '/php/cwd' script_cwd = option.test_dir + '/php/cwd'
resp = self.get() resp = self.get()
self.assertEqual(resp['status'], 200, 'status ok') assert resp['status'] == 200, 'status ok'
self.assertEqual(resp['body'], script_cwd, 'default cwd') assert resp['body'] == script_cwd, 'default cwd'
self.assertIn( assert 'success' in self.conf(
'success', '"' + option.test_dir + '"', 'applications/cwd/working_directory',
self.conf(
'"' + self.current_dir + '"',
'applications/cwd/working_directory',
),
) )
resp = self.get() resp = self.get()
self.assertEqual(resp['status'], 200, 'status ok') assert resp['status'] == 200, 'status ok'
self.assertEqual(resp['body'], script_cwd, 'wdir cwd') assert resp['body'] == script_cwd, 'wdir cwd'
resp = self.get(url='/?chdir=/') resp = self.get(url='/?chdir=/')
self.assertEqual(resp['status'], 200, 'status ok') assert resp['status'] == 200, 'status ok'
self.assertEqual(resp['body'], '/', 'cwd after chdir') assert resp['body'] == '/', 'cwd after chdir'
# cwd must be restored # cwd must be restored
resp = self.get() resp = self.get()
self.assertEqual(resp['status'], 200, 'status ok') assert resp['status'] == 200, 'status ok'
self.assertEqual(resp['body'], script_cwd, 'cwd restored') assert resp['body'] == script_cwd, 'cwd restored'
resp = self.get(url='/subdir/') resp = self.get(url='/subdir/')
self.assertEqual( assert resp['body'] == script_cwd + '/subdir', 'cwd subdir'
resp['body'], script_cwd + '/subdir', 'cwd subdir',
)
def test_php_application_cwd_root(self): def test_php_application_cwd_root(self):
self.load('cwd') self.load('cwd')
@@ -650,26 +581,20 @@ class TestPHPApplication(TestApplicationPHP):
def run_php_application_cwd_script_tests(self): def run_php_application_cwd_script_tests(self):
self.load('cwd') self.load('cwd')
script_cwd = self.current_dir + '/php/cwd' script_cwd = option.test_dir + '/php/cwd'
self.assertIn( assert 'success' in self.conf_delete(
'success', self.conf_delete('applications/cwd/working_directory') 'applications/cwd/working_directory'
) )
self.assertIn( assert 'success' in self.conf('"index.php"', 'applications/cwd/script')
'success', self.conf('"index.php"', 'applications/cwd/script')
)
self.assertEqual( assert self.get()['body'] == script_cwd, 'default cwd'
self.get()['body'], script_cwd, 'default cwd',
)
self.assertEqual( assert self.get(url='/?chdir=/')['body'] == '/', 'cwd after chdir'
self.get(url='/?chdir=/')['body'], '/', 'cwd after chdir',
)
# cwd must be restored # cwd must be restored
self.assertEqual(self.get()['body'], script_cwd, 'cwd restored') assert self.get()['body'] == script_cwd, 'cwd restored'
def test_php_application_cwd_script(self): def test_php_application_cwd_script(self):
self.load('cwd') self.load('cwd')
@@ -688,14 +613,10 @@ class TestPHPApplication(TestApplicationPHP):
def test_php_application_path_relative(self): def test_php_application_path_relative(self):
self.load('open') self.load('open')
self.assertEqual(self.get()['body'], 'test', 'relative path') assert self.get()['body'] == 'test', 'relative path'
self.assertNotEqual( assert (
self.get(url='/?chdir=/')['body'], 'test', 'relative path w/ chdir' self.get(url='/?chdir=/')['body'] != 'test'
) ), 'relative path w/ chdir'
self.assertEqual(self.get()['body'], 'test', 'relative path 2') assert self.get()['body'] == 'test', 'relative path 2'
if __name__ == '__main__':
TestPHPApplication.main()

View File

@@ -23,133 +23,97 @@ class TestPHPBasic(TestControl):
conf = self.conf_get() conf = self.conf_get()
self.assertEqual(conf['listeners'], {}, 'listeners') assert conf['listeners'] == {}, 'listeners'
self.assertEqual( assert conf['applications'] == {
conf['applications'],
{
"app": { "app": {
"type": "php", "type": "php",
"processes": {"spare": 0}, "processes": {"spare": 0},
"root": "/app", "root": "/app",
"index": "index.php", "index": "index.php",
} }
}, }, 'applications'
'applications',
)
self.assertEqual( assert self.conf_get('applications') == {
self.conf_get('applications'),
{
"app": { "app": {
"type": "php", "type": "php",
"processes": {"spare": 0}, "processes": {"spare": 0},
"root": "/app", "root": "/app",
"index": "index.php", "index": "index.php",
} }
}, }, 'applications prefix'
'applications prefix',
)
self.assertEqual( assert self.conf_get('applications/app') == {
self.conf_get('applications/app'),
{
"type": "php", "type": "php",
"processes": {"spare": 0}, "processes": {"spare": 0},
"root": "/app", "root": "/app",
"index": "index.php", "index": "index.php",
}, }, 'applications prefix 2'
'applications prefix 2',
)
self.assertEqual(self.conf_get('applications/app/type'), 'php', 'type') assert self.conf_get('applications/app/type') == 'php', 'type'
self.assertEqual( assert (
self.conf_get('applications/app/processes/spare'), self.conf_get('applications/app/processes/spare') == 0
0, ), 'spare processes'
'spare processes',
)
def test_php_get_listeners(self): def test_php_get_listeners(self):
self.conf(self.conf_basic) self.conf(self.conf_basic)
self.assertEqual( assert self.conf_get()['listeners'] == {
self.conf_get()['listeners'], "*:7080": {"pass": "applications/app"}
{"*:7080": {"pass": "applications/app"}}, }, 'listeners'
'listeners',
)
self.assertEqual( assert self.conf_get('listeners') == {
self.conf_get('listeners'), "*:7080": {"pass": "applications/app"}
{"*:7080": {"pass": "applications/app"}}, }, 'listeners prefix'
'listeners prefix',
)
self.assertEqual( assert self.conf_get('listeners/*:7080') == {
self.conf_get('listeners/*:7080'), "pass": "applications/app"
{"pass": "applications/app"}, }, 'listeners prefix 2'
'listeners prefix 2',
)
def test_php_change_listener(self): def test_php_change_listener(self):
self.conf(self.conf_basic) self.conf(self.conf_basic)
self.conf({"*:7081": {"pass": "applications/app"}}, 'listeners') self.conf({"*:7081": {"pass": "applications/app"}}, 'listeners')
self.assertEqual( assert self.conf_get('listeners') == {
self.conf_get('listeners'), "*:7081": {"pass": "applications/app"}
{"*:7081": {"pass": "applications/app"}}, }, 'change listener'
'change listener',
)
def test_php_add_listener(self): def test_php_add_listener(self):
self.conf(self.conf_basic) self.conf(self.conf_basic)
self.conf({"pass": "applications/app"}, 'listeners/*:7082') self.conf({"pass": "applications/app"}, 'listeners/*:7082')
self.assertEqual( assert self.conf_get('listeners') == {
self.conf_get('listeners'),
{
"*:7080": {"pass": "applications/app"}, "*:7080": {"pass": "applications/app"},
"*:7082": {"pass": "applications/app"}, "*:7082": {"pass": "applications/app"},
}, }, 'add listener'
'add listener',
)
def test_php_change_application(self): def test_php_change_application(self):
self.conf(self.conf_basic) self.conf(self.conf_basic)
self.conf('30', 'applications/app/processes/max') self.conf('30', 'applications/app/processes/max')
self.assertEqual( assert (
self.conf_get('applications/app/processes/max'), self.conf_get('applications/app/processes/max') == 30
30, ), 'change application max'
'change application max',
)
self.conf('"/www"', 'applications/app/root') self.conf('"/www"', 'applications/app/root')
self.assertEqual( assert (
self.conf_get('applications/app/root'), self.conf_get('applications/app/root') == '/www'
'/www', ), 'change application root'
'change application root',
)
def test_php_delete(self): def test_php_delete(self):
self.conf(self.conf_basic) self.conf(self.conf_basic)
self.assertIn('error', self.conf_delete('applications/app')) assert 'error' in self.conf_delete('applications/app')
self.assertIn('success', self.conf_delete('listeners/*:7080')) assert 'success' in self.conf_delete('listeners/*:7080')
self.assertIn('success', self.conf_delete('applications/app')) assert 'success' in self.conf_delete('applications/app')
self.assertIn('error', self.conf_delete('applications/app')) assert 'error' in self.conf_delete('applications/app')
def test_php_delete_blocks(self): def test_php_delete_blocks(self):
self.conf(self.conf_basic) self.conf(self.conf_basic)
self.assertIn('success', self.conf_delete('listeners')) assert 'success' in self.conf_delete('listeners')
self.assertIn('success', self.conf_delete('applications')) assert 'success' in self.conf_delete('applications')
self.assertIn('success', self.conf(self.conf_app, 'applications')) assert 'success' in self.conf(self.conf_app, 'applications')
self.assertIn( assert 'success' in self.conf(
'success', {"*:7081": {"pass": "applications/app"}}, 'listeners'
self.conf({"*:7081": {"pass": "applications/app"}}, 'listeners'), ), 'applications restore'
'applications restore',
)
if __name__ == '__main__':
TestPHPBasic.main()

View File

@@ -1,7 +1,8 @@
import unittest import pytest
from unit.applications.lang.php import TestApplicationPHP from unit.applications.lang.php import TestApplicationPHP
from unit.feature.isolation import TestFeatureIsolation from unit.feature.isolation import TestFeatureIsolation
from conftest import option
class TestPHPIsolation(TestApplicationPHP): class TestPHPIsolation(TestApplicationPHP):
@@ -10,171 +11,128 @@ class TestPHPIsolation(TestApplicationPHP):
isolation = TestFeatureIsolation() isolation = TestFeatureIsolation()
@classmethod @classmethod
def setUpClass(cls, complete_check=True): def setup_class(cls, complete_check=True):
unit = super().setUpClass(complete_check=False) unit = super().setup_class(complete_check=False)
TestFeatureIsolation().check(cls.available, unit.testdir) TestFeatureIsolation().check(cls.available, unit.temp_dir)
return unit if not complete_check else unit.complete() return unit if not complete_check else unit.complete()
def test_php_isolation_rootfs(self): def test_php_isolation_rootfs(self, is_su):
isolation_features = self.available['features']['isolation'].keys() isolation_features = self.available['features']['isolation'].keys()
if 'mnt' not in isolation_features: if 'mnt' not in isolation_features:
print('requires mnt ns') pytest.skip('requires mnt ns')
raise unittest.SkipTest()
if not self.is_su: if not is_su:
if 'user' not in isolation_features: if 'user' not in isolation_features:
print('requires unprivileged userns or root') pytest.skip('requires unprivileged userns or root')
raise unittest.SkipTest()
if not 'unprivileged_userns_clone' in isolation_features: if not 'unprivileged_userns_clone' in isolation_features:
print('requires unprivileged userns or root') pytest.skip('requires unprivileged userns or root')
raise unittest.SkipTest()
isolation = { isolation = {
'namespaces': {'credential': not self.is_su, 'mount': True}, 'namespaces': {'credential': not is_su, 'mount': True},
'rootfs': self.current_dir, 'rootfs': option.test_dir,
} }
self.load('phpinfo', isolation=isolation) self.load('phpinfo', isolation=isolation)
self.assertIn( assert 'success' in self.conf(
'success', self.conf('"/php/phpinfo"', 'applications/phpinfo/root') '"/php/phpinfo"', 'applications/phpinfo/root'
) )
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
'"/php/phpinfo"', 'applications/phpinfo/working_directory' '"/php/phpinfo"', 'applications/phpinfo/working_directory'
),
) )
self.assertEqual(self.get()['status'], 200, 'empty rootfs') assert self.get()['status'] == 200, 'empty rootfs'
def test_php_isolation_rootfs_extensions(self): def test_php_isolation_rootfs_extensions(self, is_su):
isolation_features = self.available['features']['isolation'].keys() isolation_features = self.available['features']['isolation'].keys()
if not self.is_su: if not is_su:
if 'user' not in isolation_features: if 'user' not in isolation_features:
print('requires unprivileged userns or root') pytest.skip('requires unprivileged userns or root')
raise unittest.SkipTest()
if not 'unprivileged_userns_clone' in isolation_features: if not 'unprivileged_userns_clone' in isolation_features:
print('requires unprivileged userns or root') pytest.skip('requires unprivileged userns or root')
raise unittest.SkipTest()
if 'mnt' not in isolation_features: if 'mnt' not in isolation_features:
print('requires mnt ns') pytest.skip('requires mnt ns')
raise unittest.SkipTest()
isolation = { isolation = {
'rootfs': self.current_dir, 'rootfs': option.test_dir,
'namespaces': { 'namespaces': {'credential': not is_su, 'mount': not is_su},
'credential': not self.is_su,
'mount': not self.is_su,
},
} }
self.load('list-extensions', isolation=isolation) self.load('list-extensions', isolation=isolation)
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
'"/php/list-extensions"', 'applications/list-extensions/root' '"/php/list-extensions"', 'applications/list-extensions/root'
),
) )
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{'file': '/php/list-extensions/php.ini'}, {'file': '/php/list-extensions/php.ini'},
'applications/list-extensions/options', 'applications/list-extensions/options',
),
) )
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
'"/php/list-extensions"', '"/php/list-extensions"',
'applications/list-extensions/working_directory', 'applications/list-extensions/working_directory',
),
) )
extensions = self.getjson()['body'] extensions = self.getjson()['body']
self.assertIn('json', extensions, 'json in extensions list') assert 'json' in extensions, 'json in extensions list'
self.assertIn('unit', extensions, 'unit in extensions list') assert 'unit' in extensions, 'unit in extensions list'
def test_php_isolation_rootfs_no_language_libs(self, is_su):
def test_php_isolation_rootfs_no_language_libs(self):
isolation_features = self.available['features']['isolation'].keys() isolation_features = self.available['features']['isolation'].keys()
if not self.is_su: if not is_su:
if 'user' not in isolation_features: if 'user' not in isolation_features:
print('requires unprivileged userns or root') pytest.skip('requires unprivileged userns or root')
raise unittest.SkipTest()
if not 'unprivileged_userns_clone' in isolation_features: if not 'unprivileged_userns_clone' in isolation_features:
print('requires unprivileged userns or root') pytest.skip('requires unprivileged userns or root')
raise unittest.SkipTest()
if 'mnt' not in isolation_features: if 'mnt' not in isolation_features:
print('requires mnt ns') pytest.skip('requires mnt ns')
raise unittest.SkipTest()
isolation = { isolation = {
'rootfs': self.current_dir, 'rootfs': option.test_dir,
'automount': {'language_deps': False}, 'automount': {'language_deps': False},
'namespaces': { 'namespaces': {'credential': not is_su, 'mount': not is_su},
'credential': not self.is_su,
'mount': not self.is_su,
},
} }
self.load('list-extensions', isolation=isolation) self.load('list-extensions', isolation=isolation)
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
'"/php/list-extensions"', 'applications/list-extensions/root' '"/php/list-extensions"', 'applications/list-extensions/root'
),
) )
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{'file': '/php/list-extensions/php.ini'}, {'file': '/php/list-extensions/php.ini'},
'applications/list-extensions/options', 'applications/list-extensions/options',
),
) )
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
'"/php/list-extensions"', '"/php/list-extensions"',
'applications/list-extensions/working_directory', 'applications/list-extensions/working_directory',
),
) )
extensions = self.getjson()['body'] extensions = self.getjson()['body']
self.assertIn('unit', extensions, 'unit in extensions list') assert 'unit' in extensions, 'unit in extensions list'
self.assertNotIn('json', extensions, 'json not in extensions list') assert 'json' not in extensions, 'json not in extensions list'
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{'language_deps': True}, {'language_deps': True},
'applications/list-extensions/isolation/automount', 'applications/list-extensions/isolation/automount',
),
) )
extensions = self.getjson()['body'] extensions = self.getjson()['body']
self.assertIn('unit', extensions, 'unit in extensions list 2') assert 'unit' in extensions, 'unit in extensions list 2'
self.assertIn('json', extensions, 'json in extensions list 2') assert 'json' in extensions, 'json in extensions list 2'
if __name__ == '__main__':
TestPHPIsolation.main()

View File

@@ -1,12 +1,12 @@
from unit.applications.lang.php import TestApplicationPHP from unit.applications.lang.php import TestApplicationPHP
from conftest import option
class TestPHPTargets(TestApplicationPHP): class TestPHPTargets(TestApplicationPHP):
prerequisites = {'modules': {'php': 'any'}} prerequisites = {'modules': {'php': 'any'}}
def test_php_application_targets(self): def test_php_application_targets(self):
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{ {
"listeners": {"*:7080": {"pass": "routes"}}, "listeners": {"*:7080": {"pass": "routes"}},
"routes": [ "routes": [
@@ -27,49 +27,39 @@ class TestPHPTargets(TestApplicationPHP):
"targets": { "targets": {
"1": { "1": {
"script": "1.php", "script": "1.php",
"root": self.current_dir + "/php/targets", "root": option.test_dir + "/php/targets",
}, },
"2": { "2": {
"script": "2.php", "script": "2.php",
"root": self.current_dir "root": option.test_dir + "/php/targets/2",
+ "/php/targets/2",
}, },
"default": { "default": {
"index": "index.php", "index": "index.php",
"root": self.current_dir + "/php/targets", "root": option.test_dir + "/php/targets",
}, },
}, },
} }
}, },
} }
),
) )
self.assertEqual(self.get(url='/1')['body'], '1') assert self.get(url='/1')['body'] == '1'
self.assertEqual(self.get(url='/2')['body'], '2') assert self.get(url='/2')['body'] == '2'
self.assertEqual(self.get(url='/blah')['status'], 503) # TODO 404 assert self.get(url='/blah')['status'] == 503 # TODO 404
self.assertEqual(self.get(url='/')['body'], 'index') assert self.get(url='/')['body'] == 'index'
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
"\"1.php\"", 'applications/targets/targets/default/index' "\"1.php\"", 'applications/targets/targets/default/index'
), ), 'change targets index'
'change targets index', assert self.get(url='/')['body'] == '1'
)
self.assertEqual(self.get(url='/')['body'], '1')
self.assertIn( assert 'success' in self.conf_delete(
'success', 'applications/targets/targets/default/index'
self.conf_delete('applications/targets/targets/default/index'), ), 'remove targets index'
'remove targets index', assert self.get(url='/')['body'] == 'index'
)
self.assertEqual(self.get(url='/')['body'], 'index')
def test_php_application_targets_error(self): def test_php_application_targets_error(self):
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{ {
"listeners": { "listeners": {
"*:7080": {"pass": "applications/targets/default"} "*:7080": {"pass": "applications/targets/default"}
@@ -81,48 +71,28 @@ class TestPHPTargets(TestApplicationPHP):
"targets": { "targets": {
"default": { "default": {
"index": "index.php", "index": "index.php",
"root": self.current_dir + "/php/targets", "root": option.test_dir + "/php/targets",
}, },
}, },
} }
}, },
} }
), ), 'initial configuration'
'initial configuration', assert self.get()['status'] == 200
)
self.assertEqual(self.get()['status'], 200)
self.assertIn( assert 'error' in self.conf(
'error',
self.conf(
{"pass": "applications/targets/blah"}, 'listeners/*:7080' {"pass": "applications/targets/blah"}, 'listeners/*:7080'
), ), 'invalid targets pass'
'invalid targets pass', assert 'error' in self.conf(
) '"' + option.test_dir + '/php/targets\"',
self.assertIn(
'error',
self.conf(
'"' + self.current_dir + '/php/targets\"',
'applications/targets/root', 'applications/targets/root',
), ), 'invalid root'
'invalid root', assert 'error' in self.conf(
) '"index.php"', 'applications/targets/index'
self.assertIn( ), 'invalid index'
'error', assert 'error' in self.conf(
self.conf('"index.php"', 'applications/targets/index'), '"index.php"', 'applications/targets/script'
'invalid index', ), 'invalid script'
) assert 'error' in self.conf_delete(
self.assertIn( 'applications/targets/default/root'
'error', ), 'root remove'
self.conf('"index.php"', 'applications/targets/script'),
'invalid script',
)
self.assertIn(
'error',
self.conf_delete('applications/targets/default/root'),
'root remove',
)
if __name__ == '__main__':
TestPHPTargets.main()

View File

@@ -1,9 +1,10 @@
import pytest
import re import re
import socket import socket
import time import time
import unittest
from unit.applications.lang.python import TestApplicationPython from unit.applications.lang.python import TestApplicationPython
from conftest import option, skip_alert
class TestProxy(TestApplicationPython): class TestProxy(TestApplicationPython):
@@ -42,7 +43,7 @@ Content-Length: 10
to_send = req to_send = req
m = re.search('X-Len: (\d+)', data) m = re.search(r'X-Len: (\d+)', data)
if m: if m:
to_send += b'X' * int(m.group(1)) to_send += b'X' * int(m.group(1))
@@ -56,15 +57,13 @@ Content-Length: 10
def post_http10(self, *args, **kwargs): def post_http10(self, *args, **kwargs):
return self.post(*args, http_10=True, **kwargs) return self.post(*args, http_10=True, **kwargs)
def setUp(self): def setup_method(self):
super().setUp() super().setup_method()
self.run_process(self.run_server, self.SERVER_PORT) self.run_process(self.run_server, self.SERVER_PORT)
self.waitforsocket(self.SERVER_PORT) self.waitforsocket(self.SERVER_PORT)
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{ {
"listeners": { "listeners": {
"*:7080": {"pass": "routes"}, "*:7080": {"pass": "routes"},
@@ -75,41 +74,37 @@ Content-Length: 10
"mirror": { "mirror": {
"type": "python", "type": "python",
"processes": {"spare": 0}, "processes": {"spare": 0},
"path": self.current_dir + "/python/mirror", "path": option.test_dir + "/python/mirror",
"working_directory": self.current_dir "working_directory": option.test_dir
+ "/python/mirror", + "/python/mirror",
"module": "wsgi", "module": "wsgi",
}, },
"custom_header": { "custom_header": {
"type": "python", "type": "python",
"processes": {"spare": 0}, "processes": {"spare": 0},
"path": self.current_dir + "/python/custom_header", "path": option.test_dir + "/python/custom_header",
"working_directory": self.current_dir "working_directory": option.test_dir
+ "/python/custom_header", + "/python/custom_header",
"module": "wsgi", "module": "wsgi",
}, },
"delayed": { "delayed": {
"type": "python", "type": "python",
"processes": {"spare": 0}, "processes": {"spare": 0},
"path": self.current_dir + "/python/delayed", "path": option.test_dir + "/python/delayed",
"working_directory": self.current_dir "working_directory": option.test_dir
+ "/python/delayed", + "/python/delayed",
"module": "wsgi", "module": "wsgi",
}, },
}, },
} }
), ), 'proxy initial configuration'
'proxy initial configuration',
)
def test_proxy_http10(self): def test_proxy_http10(self):
for _ in range(10): for _ in range(10):
self.assertEqual(self.get_http10()['status'], 200, 'status') assert self.get_http10()['status'] == 200, 'status'
def test_proxy_chain(self): def test_proxy_chain(self):
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{ {
"listeners": { "listeners": {
"*:7080": {"pass": "routes/first"}, "*:7080": {"pass": "routes/first"},
@@ -120,81 +115,69 @@ Content-Length: 10
"*:7085": {"pass": "applications/mirror"}, "*:7085": {"pass": "applications/mirror"},
}, },
"routes": { "routes": {
"first": [ "first": [{"action": {"proxy": "http://127.0.0.1:7081"}}],
{"action": {"proxy": "http://127.0.0.1:7081"}} "second": [{"action": {"proxy": "http://127.0.0.1:7082"}}],
], "third": [{"action": {"proxy": "http://127.0.0.1:7083"}}],
"second": [ "fourth": [{"action": {"proxy": "http://127.0.0.1:7084"}}],
{"action": {"proxy": "http://127.0.0.1:7082"}} "fifth": [{"action": {"proxy": "http://127.0.0.1:7085"}}],
],
"third": [
{"action": {"proxy": "http://127.0.0.1:7083"}}
],
"fourth": [
{"action": {"proxy": "http://127.0.0.1:7084"}}
],
"fifth": [
{"action": {"proxy": "http://127.0.0.1:7085"}}
],
}, },
"applications": { "applications": {
"mirror": { "mirror": {
"type": "python", "type": "python",
"processes": {"spare": 0}, "processes": {"spare": 0},
"path": self.current_dir + "/python/mirror", "path": option.test_dir + "/python/mirror",
"working_directory": self.current_dir "working_directory": option.test_dir
+ "/python/mirror", + "/python/mirror",
"module": "wsgi", "module": "wsgi",
} }
}, },
} }
), ), 'proxy chain configuration'
'proxy chain configuration',
)
self.assertEqual(self.get_http10()['status'], 200, 'status') assert self.get_http10()['status'] == 200, 'status'
def test_proxy_body(self): def test_proxy_body(self):
payload = '0123456789' payload = '0123456789'
for _ in range(10): for _ in range(10):
resp = self.post_http10(body=payload) resp = self.post_http10(body=payload)
self.assertEqual(resp['status'], 200, 'status') assert resp['status'] == 200, 'status'
self.assertEqual(resp['body'], payload, 'body') assert resp['body'] == payload, 'body'
payload = 'X' * 4096 payload = 'X' * 4096
for _ in range(10): for _ in range(10):
resp = self.post_http10(body=payload) resp = self.post_http10(body=payload)
self.assertEqual(resp['status'], 200, 'status') assert resp['status'] == 200, 'status'
self.assertEqual(resp['body'], payload, 'body') assert resp['body'] == payload, 'body'
payload = 'X' * 4097 payload = 'X' * 4097
for _ in range(10): for _ in range(10):
resp = self.post_http10(body=payload) resp = self.post_http10(body=payload)
self.assertEqual(resp['status'], 200, 'status') assert resp['status'] == 200, 'status'
self.assertEqual(resp['body'], payload, 'body') assert resp['body'] == payload, 'body'
payload = 'X' * 4096 * 256 payload = 'X' * 4096 * 256
for _ in range(10): for _ in range(10):
resp = self.post_http10(body=payload, read_buffer_size=4096 * 128) resp = self.post_http10(body=payload, read_buffer_size=4096 * 128)
self.assertEqual(resp['status'], 200, 'status') assert resp['status'] == 200, 'status'
self.assertEqual(resp['body'], payload, 'body') assert resp['body'] == payload, 'body'
payload = 'X' * 4096 * 257 payload = 'X' * 4096 * 257
for _ in range(10): for _ in range(10):
resp = self.post_http10(body=payload, read_buffer_size=4096 * 128) resp = self.post_http10(body=payload, read_buffer_size=4096 * 128)
self.assertEqual(resp['status'], 200, 'status') assert resp['status'] == 200, 'status'
self.assertEqual(resp['body'], payload, 'body') assert resp['body'] == payload, 'body'
self.conf({'http': {'max_body_size': 32 * 1024 * 1024}}, 'settings') self.conf({'http': {'max_body_size': 32 * 1024 * 1024}}, 'settings')
payload = '0123456789abcdef' * 32 * 64 * 1024 payload = '0123456789abcdef' * 32 * 64 * 1024
resp = self.post_http10(body=payload, read_buffer_size=1024 * 1024) resp = self.post_http10(body=payload, read_buffer_size=1024 * 1024)
self.assertEqual(resp['status'], 200, 'status') assert resp['status'] == 200, 'status'
self.assertEqual(resp['body'], payload, 'body') assert resp['body'] == payload, 'body'
def test_proxy_parallel(self): def test_proxy_parallel(self):
payload = 'X' * 4096 * 257 payload = 'X' * 4096 * 257
@@ -216,62 +199,53 @@ Content-Length: 10
resp = self._resp_to_dict(resp) resp = self._resp_to_dict(resp)
self.assertEqual(resp['status'], 200, 'status') assert resp['status'] == 200, 'status'
self.assertEqual(resp['body'], payload + str(i), 'body') assert resp['body'] == payload + str(i), 'body'
def test_proxy_header(self): def test_proxy_header(self):
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{"pass": "applications/custom_header"}, 'listeners/*:7081' {"pass": "applications/custom_header"}, 'listeners/*:7081'
), ), 'custom_header configure'
'custom_header configure',
)
header_value = 'blah' header_value = 'blah'
self.assertEqual( assert (
self.get_http10( self.get_http10(
headers={'Host': 'localhost', 'Custom-Header': header_value} headers={'Host': 'localhost', 'Custom-Header': header_value}
)['headers']['Custom-Header'], )['headers']['Custom-Header']
header_value, == header_value
'custom header', ), 'custom header'
)
header_value = '(),/:;<=>?@[\]{}\t !#$%&\'*+-.^_`|~' header_value = '(),/:;<=>?@[\]{}\t !#$%&\'*+-.^_`|~'
self.assertEqual( assert (
self.get_http10( self.get_http10(
headers={'Host': 'localhost', 'Custom-Header': header_value} headers={'Host': 'localhost', 'Custom-Header': header_value}
)['headers']['Custom-Header'], )['headers']['Custom-Header']
header_value, == header_value
'custom header 2', ), 'custom header 2'
)
header_value = 'X' * 4096 header_value = 'X' * 4096
self.assertEqual( assert (
self.get_http10( self.get_http10(
headers={'Host': 'localhost', 'Custom-Header': header_value} headers={'Host': 'localhost', 'Custom-Header': header_value}
)['headers']['Custom-Header'], )['headers']['Custom-Header']
header_value, == header_value
'custom header 3', ), 'custom header 3'
)
header_value = 'X' * 8191 header_value = 'X' * 8191
self.assertEqual( assert (
self.get_http10( self.get_http10(
headers={'Host': 'localhost', 'Custom-Header': header_value} headers={'Host': 'localhost', 'Custom-Header': header_value}
)['headers']['Custom-Header'], )['headers']['Custom-Header']
header_value, == header_value
'custom header 4', ), 'custom header 4'
)
header_value = 'X' * 8192 header_value = 'X' * 8192
self.assertEqual( assert (
self.get_http10( self.get_http10(
headers={'Host': 'localhost', 'Custom-Header': header_value} headers={'Host': 'localhost', 'Custom-Header': header_value}
)['status'], )['status']
431, == 431
'custom header 5', ), 'custom header 5'
)
def test_proxy_fragmented(self): def test_proxy_fragmented(self):
_, sock = self.http( _, sock = self.http(
@@ -286,9 +260,9 @@ Content-Length: 10
sock.sendall("t\r\n\r\n".encode()) sock.sendall("t\r\n\r\n".encode())
self.assertRegex( assert re.search(
self.recvall(sock).decode(), '200 OK', 'fragmented send' '200 OK', self.recvall(sock).decode()
) ), 'fragmented send'
sock.close() sock.close()
def test_proxy_fragmented_close(self): def test_proxy_fragmented_close(self):
@@ -328,8 +302,8 @@ Content-Length: 10
resp = self._resp_to_dict(self.recvall(sock).decode()) resp = self._resp_to_dict(self.recvall(sock).decode())
sock.close() sock.close()
self.assertEqual(resp['status'], 200, 'status') assert resp['status'] == 200, 'status'
self.assertEqual(resp['body'], "X" * 30000, 'body') assert resp['body'] == "X" * 30000, 'body'
def test_proxy_fragmented_body_close(self): def test_proxy_fragmented_body_close(self):
_, sock = self.http( _, sock = self.http(
@@ -349,70 +323,48 @@ Content-Length: 10
sock.close() sock.close()
def test_proxy_nowhere(self): def test_proxy_nowhere(self):
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
[{"action": {"proxy": "http://127.0.0.1:7082"}}], 'routes' [{"action": {"proxy": "http://127.0.0.1:7082"}}], 'routes'
), ), 'proxy path changed'
'proxy path changed',
)
self.assertEqual(self.get_http10()['status'], 502, 'status') assert self.get_http10()['status'] == 502, 'status'
def test_proxy_ipv6(self): def test_proxy_ipv6(self):
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{ {
"*:7080": {"pass": "routes"}, "*:7080": {"pass": "routes"},
"[::1]:7081": {'application': 'mirror'}, "[::1]:7081": {'application': 'mirror'},
}, },
'listeners', 'listeners',
), ), 'add ipv6 listener configure'
'add ipv6 listener configure',
)
self.assertIn( assert 'success' in self.conf(
'success', [{"action": {"proxy": "http://[::1]:7081"}}], 'routes'
self.conf([{"action": {"proxy": "http://[::1]:7081"}}], 'routes'), ), 'proxy ipv6 configure'
'proxy ipv6 configure',
)
self.assertEqual(self.get_http10()['status'], 200, 'status') assert self.get_http10()['status'] == 200, 'status'
def test_proxy_unix(self): def test_proxy_unix(self):
addr = self.testdir + '/sock' addr = self.temp_dir + '/sock'
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{ {
"*:7080": {"pass": "routes"}, "*:7080": {"pass": "routes"},
"unix:" + addr: {'application': 'mirror'}, "unix:" + addr: {'application': 'mirror'},
}, },
'listeners', 'listeners',
), ), 'add unix listener configure'
'add unix listener configure',
)
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
[{"action": {"proxy": 'http://unix:' + addr}}], 'routes' [{"action": {"proxy": 'http://unix:' + addr}}], 'routes'
), ), 'proxy unix configure'
'proxy unix configure',
)
self.assertEqual(self.get_http10()['status'], 200, 'status') assert self.get_http10()['status'] == 200, 'status'
def test_proxy_delayed(self): def test_proxy_delayed(self):
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{"pass": "applications/delayed"}, 'listeners/*:7081' {"pass": "applications/delayed"}, 'listeners/*:7081'
), ), 'delayed configure'
'delayed configure',
)
body = '0123456789' * 1000 body = '0123456789' * 1000
resp = self.post_http10( resp = self.post_http10(
@@ -426,8 +378,8 @@ Content-Length: 10
body=body, body=body,
) )
self.assertEqual(resp['status'], 200, 'status') assert resp['status'] == 200, 'status'
self.assertEqual(resp['body'], body, 'body') assert resp['body'] == body, 'body'
resp = self.post_http10( resp = self.post_http10(
headers={ headers={
@@ -440,17 +392,13 @@ Content-Length: 10
body=body, body=body,
) )
self.assertEqual(resp['status'], 200, 'status') assert resp['status'] == 200, 'status'
self.assertEqual(resp['body'], body, 'body') assert resp['body'] == body, 'body'
def test_proxy_delayed_close(self): def test_proxy_delayed_close(self):
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{"pass": "applications/delayed"}, 'listeners/*:7081' {"pass": "applications/delayed"}, 'listeners/*:7081'
), ), 'delayed configure'
'delayed configure',
)
_, sock = self.post_http10( _, sock = self.post_http10(
headers={ headers={
@@ -465,9 +413,7 @@ Content-Length: 10
no_recv=True, no_recv=True,
) )
self.assertRegex( assert re.search('200 OK', sock.recv(100).decode()), 'first'
sock.recv(100).decode(), '200 OK', 'first'
)
sock.close() sock.close()
_, sock = self.post_http10( _, sock = self.post_http10(
@@ -483,51 +429,42 @@ Content-Length: 10
no_recv=True, no_recv=True,
) )
self.assertRegex( assert re.search('200 OK', sock.recv(100).decode()), 'second'
sock.recv(100).decode(), '200 OK', 'second'
)
sock.close() sock.close()
@unittest.skip('not yet') @pytest.mark.skip('not yet')
def test_proxy_content_length(self): def test_proxy_content_length(self):
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
[ [
{ {
"action": { "action": {
"proxy": "http://127.0.0.1:" "proxy": "http://127.0.0.1:" + str(self.SERVER_PORT)
+ str(self.SERVER_PORT)
} }
} }
], ],
'routes', 'routes',
), ), 'proxy backend configure'
'proxy backend configure',
)
resp = self.get_http10() resp = self.get_http10()
self.assertEqual(len(resp['body']), 0, 'body lt Content-Length 0') assert len(resp['body']) == 0, 'body lt Content-Length 0'
resp = self.get_http10(headers={'Host': 'localhost', 'X-Len': '5'}) resp = self.get_http10(headers={'Host': 'localhost', 'X-Len': '5'})
self.assertEqual(len(resp['body']), 5, 'body lt Content-Length 5') assert len(resp['body']) == 5, 'body lt Content-Length 5'
resp = self.get_http10(headers={'Host': 'localhost', 'X-Len': '9'}) resp = self.get_http10(headers={'Host': 'localhost', 'X-Len': '9'})
self.assertEqual(len(resp['body']), 9, 'body lt Content-Length 9') assert len(resp['body']) == 9, 'body lt Content-Length 9'
resp = self.get_http10(headers={'Host': 'localhost', 'X-Len': '11'}) resp = self.get_http10(headers={'Host': 'localhost', 'X-Len': '11'})
self.assertEqual(len(resp['body']), 10, 'body gt Content-Length 11') assert len(resp['body']) == 10, 'body gt Content-Length 11'
resp = self.get_http10(headers={'Host': 'localhost', 'X-Len': '15'}) resp = self.get_http10(headers={'Host': 'localhost', 'X-Len': '15'})
self.assertEqual(len(resp['body']), 10, 'body gt Content-Length 15') assert len(resp['body']) == 10, 'body gt Content-Length 15'
def test_proxy_invalid(self): def test_proxy_invalid(self):
def check_proxy(proxy): def check_proxy(proxy):
self.assertIn( assert 'error' in \
'error', self.conf([{"action": {"proxy": proxy}}], 'routes'), \
self.conf([{"action": {"proxy": proxy}}], 'routes'), 'proxy invalid'
'proxy invalid',
)
check_proxy('blah') check_proxy('blah')
check_proxy('/blah') check_proxy('/blah')
@@ -544,12 +481,10 @@ Content-Length: 10
check_proxy('http://[::7080') check_proxy('http://[::7080')
def test_proxy_loop(self): def test_proxy_loop(self):
self.skip_alerts.extend( skip_alert(
[
r'socket.*failed', r'socket.*failed',
r'accept.*failed', r'accept.*failed',
r'new connections are not accepted', r'new connections are not accepted',
]
) )
self.conf( self.conf(
{ {
@@ -563,9 +498,8 @@ Content-Length: 10
"mirror": { "mirror": {
"type": "python", "type": "python",
"processes": {"spare": 0}, "processes": {"spare": 0},
"path": self.current_dir + "/python/mirror", "path": option.test_dir + "/python/mirror",
"working_directory": self.current_dir "working_directory": option.test_dir + "/python/mirror",
+ "/python/mirror",
"module": "wsgi", "module": "wsgi",
}, },
}, },
@@ -574,6 +508,3 @@ Content-Length: 10
self.get_http10(no_recv=True) self.get_http10(no_recv=True)
self.get_http10(read_timeout=1) self.get_http10(read_timeout=1)
if __name__ == '__main__':
TestProxy.main()

View File

@@ -12,7 +12,7 @@ class TestProxyChunked(TestApplicationPython):
SERVER_PORT = 7999 SERVER_PORT = 7999
@staticmethod @staticmethod
def run_server(server_port, testdir): def run_server(server_port, temp_dir):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
@@ -81,15 +81,13 @@ class TestProxyChunked(TestApplicationPython):
def get_http10(self, *args, **kwargs): def get_http10(self, *args, **kwargs):
return self.get(*args, http_10=True, **kwargs) return self.get(*args, http_10=True, **kwargs)
def setUp(self): def setup_method(self):
super().setUp() super().setup_method()
self.run_process(self.run_server, self.SERVER_PORT, self.testdir) self.run_process(self.run_server, self.SERVER_PORT, self.temp_dir)
self.waitforsocket(self.SERVER_PORT) self.waitforsocket(self.SERVER_PORT)
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{ {
"listeners": {"*:7080": {"pass": "routes"},}, "listeners": {"*:7080": {"pass": "routes"},},
"routes": [ "routes": [
@@ -101,48 +99,44 @@ class TestProxyChunked(TestApplicationPython):
} }
], ],
} }
), ), 'proxy initial configuration'
'proxy initial configuration',
)
def test_proxy_chunked(self): def test_proxy_chunked(self):
for _ in range(10): for _ in range(10):
self.assertEqual( assert self.get_http10(body='\r\n\r\n0\r\n\r\n')['status'] == 200
self.get_http10(body='\r\n\r\n0\r\n\r\n')['status'], 200
)
def test_proxy_chunked_body(self): def test_proxy_chunked_body(self):
part = '0123456789abcdef' part = '0123456789abcdef'
self.assertEqual( assert (
self.get_http10(body=self.chunks([('1000', part + ' X 256')]))[ self.get_http10(body=self.chunks([('1000', part + ' X 256')]))[
'body' 'body'
], ]
part * 256, == part * 256
) )
self.assertEqual( assert (
self.get_http10(body=self.chunks([('100000', part + ' X 65536')]))[ self.get_http10(body=self.chunks([('100000', part + ' X 65536')]))[
'body' 'body'
], ]
part * 65536, == part * 65536
) )
self.assertEqual( assert (
self.get_http10( self.get_http10(
body=self.chunks([('1000000', part + ' X 1048576')]), body=self.chunks([('1000000', part + ' X 1048576')]),
read_buffer_size=4096 * 4096, read_buffer_size=4096 * 4096,
)['body'], )['body']
part * 1048576, == part * 1048576
) )
self.assertEqual( assert (
self.get_http10( self.get_http10(
body=self.chunks( body=self.chunks(
[('1000', part + ' X 256'), ('1000', part + ' X 256')] [('1000', part + ' X 256'), ('1000', part + ' X 256')]
) )
)['body'], )['body']
part * 256 * 2, == part * 256 * 2
) )
self.assertEqual( assert (
self.get_http10( self.get_http10(
body=self.chunks( body=self.chunks(
[ [
@@ -150,10 +144,10 @@ class TestProxyChunked(TestApplicationPython):
('100000', part + ' X 65536'), ('100000', part + ' X 65536'),
] ]
) )
)['body'], )['body']
part * 65536 * 2, == part * 65536 * 2
) )
self.assertEqual( assert (
self.get_http10( self.get_http10(
body=self.chunks( body=self.chunks(
[ [
@@ -162,42 +156,40 @@ class TestProxyChunked(TestApplicationPython):
] ]
), ),
read_buffer_size=4096 * 4096, read_buffer_size=4096 * 4096,
)['body'], )['body']
part * 1048576 * 2, == part * 1048576 * 2
) )
def test_proxy_chunked_fragmented(self): def test_proxy_chunked_fragmented(self):
part = '0123456789abcdef' part = '0123456789abcdef'
self.assertEqual( assert (
self.get_http10( self.get_http10(
body=self.chunks( body=self.chunks(
[('1', hex(i % 16)[2:]) for i in range(4096)] [('1', hex(i % 16)[2:]) for i in range(4096)]
), ),
)['body'], )['body']
part * 256, == part * 256
) )
def test_proxy_chunked_send(self): def test_proxy_chunked_send(self):
self.assertEqual( assert self.get_http10(body='\r\n\r\n@0@\r\n\r\n')['status'] == 200
self.get_http10(body='\r\n\r\n@0@\r\n\r\n')['status'], 200 assert (
)
self.assertEqual(
self.get_http10( self.get_http10(
body='\r@\n\r\n2\r@\na@b\r\n2\r\ncd@\r\n0\r@\n\r\n' body='\r@\n\r\n2\r@\na@b\r\n2\r\ncd@\r\n0\r@\n\r\n'
)['body'], )['body']
'abcd', == 'abcd'
) )
self.assertEqual( assert (
self.get_http10( self.get_http10(
body='\r\n\r\n2\r#\na#b\r\n##2\r\n#cd\r\n0\r\n#\r#\n' body='\r\n\r\n2\r#\na#b\r\n##2\r\n#cd\r\n0\r\n#\r#\n'
)['body'], )['body']
'abcd', == 'abcd'
) )
def test_proxy_chunked_invalid(self): def test_proxy_chunked_invalid(self):
def check_invalid(body): def check_invalid(body):
self.assertNotEqual(self.get_http10(body=body)['status'], 200) assert self.get_http10(body=body)['status'] != 200
check_invalid('\r\n\r0') check_invalid('\r\n\r0')
check_invalid('\r\n\r\n\r0') check_invalid('\r\n\r\n\r0')
@@ -209,41 +201,38 @@ class TestProxyChunked(TestApplicationPython):
check_invalid('\r\n\r\n0\r\nX') check_invalid('\r\n\r\n0\r\nX')
resp = self.get_http10(body='\r\n\r\n65#\r\nA X 100') resp = self.get_http10(body='\r\n\r\n65#\r\nA X 100')
self.assertEqual(resp['status'], 200, 'incomplete chunk status') assert resp['status'] == 200, 'incomplete chunk status'
self.assertNotEqual(resp['body'][-5:], '0\r\n\r\n', 'incomplete chunk') assert resp['body'][-5:] != '0\r\n\r\n', 'incomplete chunk'
resp = self.get_http10(body='\r\n\r\n64#\r\nA X 100') resp = self.get_http10(body='\r\n\r\n64#\r\nA X 100')
self.assertEqual(resp['status'], 200, 'no zero chunk status') assert resp['status'] == 200, 'no zero chunk status'
self.assertNotEqual(resp['body'][-5:], '0\r\n\r\n', 'no zero chunk') assert resp['body'][-5:] != '0\r\n\r\n', 'no zero chunk'
self.assertEqual( assert (
self.get_http10(body='\r\n\r\n80000000\r\nA X 100')['status'], 200, self.get_http10(body='\r\n\r\n80000000\r\nA X 100')['status']
== 200
) )
self.assertEqual( assert (
self.get_http10(body='\r\n\r\n10000000000000000\r\nA X 100')[ self.get_http10(body='\r\n\r\n10000000000000000\r\nA X 100')[
'status' 'status'
], ]
502, == 502
) )
self.assertGreaterEqual( assert (
len( len(
self.get_http10( self.get_http10(
body='\r\n\r\n1000000\r\nA X 1048576\r\n1000000\r\nA X 100', body='\r\n\r\n1000000\r\nA X 1048576\r\n1000000\r\nA X 100',
read_buffer_size=4096 * 4096, read_buffer_size=4096 * 4096,
)['body'] )['body']
),
1048576,
) )
self.assertGreaterEqual( >= 1048576
)
assert (
len( len(
self.get_http10( self.get_http10(
body='\r\n\r\n1000000\r\nA X 1048576\r\nXXX\r\nA X 100', body='\r\n\r\n1000000\r\nA X 1048576\r\nXXX\r\nA X 100',
read_buffer_size=4096 * 4096, read_buffer_size=4096 * 4096,
)['body'] )['body']
),
1048576,
) )
>= 1048576
)
if __name__ == '__main__':
TestProxyChunked.main()

View File

@@ -1,17 +1,18 @@
import grp import grp
import pytest
import pwd import pwd
import re import re
import time import time
import unittest
from unit.applications.lang.python import TestApplicationPython from unit.applications.lang.python import TestApplicationPython
from conftest import skip_alert
class TestPythonApplication(TestApplicationPython): class TestPythonApplication(TestApplicationPython):
prerequisites = {'modules': {'python': 'all'}} prerequisites = {'modules': {'python': 'all'}}
def findall(self, pattern): def findall(self, pattern):
with open(self.testdir + '/unit.log', 'r', errors='ignore') as f: with open(self.temp_dir + '/unit.log', 'r', errors='ignore') as f:
return re.findall(pattern, f.read()) return re.findall(pattern, f.read())
def test_python_application_variables(self): def test_python_application_variables(self):
@@ -29,27 +30,21 @@ class TestPythonApplication(TestApplicationPython):
body=body, body=body,
) )
self.assertEqual(resp['status'], 200, 'status') assert resp['status'] == 200, 'status'
headers = resp['headers'] headers = resp['headers']
header_server = headers.pop('Server') header_server = headers.pop('Server')
self.assertRegex(header_server, r'Unit/[\d\.]+', 'server header') assert re.search(r'Unit/[\d\.]+', header_server), 'server header'
self.assertEqual( assert (
headers.pop('Server-Software'), headers.pop('Server-Software') == header_server
header_server, ), 'server software header'
'server software header',
)
date = headers.pop('Date') date = headers.pop('Date')
self.assertEqual(date[-4:], ' GMT', 'date header timezone') assert date[-4:] == ' GMT', 'date header timezone'
self.assertLess( assert (
abs(self.date_to_sec_epoch(date) - self.sec_epoch()), abs(self.date_to_sec_epoch(date) - self.sec_epoch()) < 5
5, ), 'date header'
'date header',
)
self.assertDictEqual( assert headers == {
headers,
{
'Connection': 'close', 'Connection': 'close',
'Content-Length': str(len(body)), 'Content-Length': str(len(body)),
'Content-Type': 'text/html', 'Content-Type': 'text/html',
@@ -63,101 +58,81 @@ class TestPythonApplication(TestApplicationPython):
'Wsgi-Multithread': 'False', 'Wsgi-Multithread': 'False',
'Wsgi-Multiprocess': 'True', 'Wsgi-Multiprocess': 'True',
'Wsgi-Run-Once': 'False', 'Wsgi-Run-Once': 'False',
}, }, 'headers'
'headers', assert resp['body'] == body, 'body'
)
self.assertEqual(resp['body'], body, 'body')
def test_python_application_query_string(self): def test_python_application_query_string(self):
self.load('query_string') self.load('query_string')
resp = self.get(url='/?var1=val1&var2=val2') resp = self.get(url='/?var1=val1&var2=val2')
self.assertEqual( assert (
resp['headers']['Query-String'], resp['headers']['Query-String'] == 'var1=val1&var2=val2'
'var1=val1&var2=val2', ), 'Query-String header'
'Query-String header',
)
def test_python_application_query_string_space(self): def test_python_application_query_string_space(self):
self.load('query_string') self.load('query_string')
resp = self.get(url='/ ?var1=val1&var2=val2') resp = self.get(url='/ ?var1=val1&var2=val2')
self.assertEqual( assert (
resp['headers']['Query-String'], resp['headers']['Query-String'] == 'var1=val1&var2=val2'
'var1=val1&var2=val2', ), 'Query-String space'
'Query-String space',
)
resp = self.get(url='/ %20?var1=val1&var2=val2') resp = self.get(url='/ %20?var1=val1&var2=val2')
self.assertEqual( assert (
resp['headers']['Query-String'], resp['headers']['Query-String'] == 'var1=val1&var2=val2'
'var1=val1&var2=val2', ), 'Query-String space 2'
'Query-String space 2',
)
resp = self.get(url='/ %20 ?var1=val1&var2=val2') resp = self.get(url='/ %20 ?var1=val1&var2=val2')
self.assertEqual( assert (
resp['headers']['Query-String'], resp['headers']['Query-String'] == 'var1=val1&var2=val2'
'var1=val1&var2=val2', ), 'Query-String space 3'
'Query-String space 3',
)
resp = self.get(url='/blah %20 blah? var1= val1 & var2=val2') resp = self.get(url='/blah %20 blah? var1= val1 & var2=val2')
self.assertEqual( assert (
resp['headers']['Query-String'], resp['headers']['Query-String'] == ' var1= val1 & var2=val2'
' var1= val1 & var2=val2', ), 'Query-String space 4'
'Query-String space 4',
)
def test_python_application_query_string_empty(self): def test_python_application_query_string_empty(self):
self.load('query_string') self.load('query_string')
resp = self.get(url='/?') resp = self.get(url='/?')
self.assertEqual(resp['status'], 200, 'query string empty status') assert resp['status'] == 200, 'query string empty status'
self.assertEqual( assert resp['headers']['Query-String'] == '', 'query string empty'
resp['headers']['Query-String'], '', 'query string empty'
)
def test_python_application_query_string_absent(self): def test_python_application_query_string_absent(self):
self.load('query_string') self.load('query_string')
resp = self.get() resp = self.get()
self.assertEqual(resp['status'], 200, 'query string absent status') assert resp['status'] == 200, 'query string absent status'
self.assertEqual( assert resp['headers']['Query-String'] == '', 'query string absent'
resp['headers']['Query-String'], '', 'query string absent'
)
@unittest.skip('not yet') @pytest.mark.skip('not yet')
def test_python_application_server_port(self): def test_python_application_server_port(self):
self.load('server_port') self.load('server_port')
self.assertEqual( assert (
self.get()['headers']['Server-Port'], '7080', 'Server-Port header' self.get()['headers']['Server-Port'] == '7080'
) ), 'Server-Port header'
@unittest.skip('not yet') @pytest.mark.skip('not yet')
def test_python_application_working_directory_invalid(self): def test_python_application_working_directory_invalid(self):
self.load('empty') self.load('empty')
self.assertIn( assert 'success' in self.conf(
'success', '"/blah"', 'applications/empty/working_directory'
self.conf('"/blah"', 'applications/empty/working_directory'), ), 'configure invalid working_directory'
'configure invalid working_directory',
)
self.assertEqual(self.get()['status'], 500, 'status') assert self.get()['status'] == 500, 'status'
def test_python_application_204_transfer_encoding(self): def test_python_application_204_transfer_encoding(self):
self.load('204_no_content') self.load('204_no_content')
self.assertNotIn( assert (
'Transfer-Encoding', 'Transfer-Encoding' not in self.get()['headers']
self.get()['headers'], ), '204 header transfer encoding'
'204 header transfer encoding',
)
def test_python_application_ctx_iter_atexit(self): def test_python_application_ctx_iter_atexit(self):
self.load('ctx_iter_atexit') self.load('ctx_iter_atexit')
@@ -171,21 +146,21 @@ class TestPythonApplication(TestApplicationPython):
body='0123456789', body='0123456789',
) )
self.assertEqual(resp['status'], 200, 'ctx iter status') assert resp['status'] == 200, 'ctx iter status'
self.assertEqual(resp['body'], '0123456789', 'ctx iter body') assert resp['body'] == '0123456789', 'ctx iter body'
self.conf({"listeners": {}, "applications": {}}) self.conf({"listeners": {}, "applications": {}})
self.stop() self.stop()
self.assertIsNotNone( assert (
self.wait_for_record(r'RuntimeError'), 'ctx iter atexit' self.wait_for_record(r'RuntimeError') is not None
) ), 'ctx iter atexit'
def test_python_keepalive_body(self): def test_python_keepalive_body(self):
self.load('mirror') self.load('mirror')
self.assertEqual(self.get()['status'], 200, 'init') assert self.get()['status'] == 200, 'init'
body = '0123456789' * 500 body = '0123456789' * 500
(resp, sock) = self.post( (resp, sock) = self.post(
@@ -199,7 +174,7 @@ class TestPythonApplication(TestApplicationPython):
read_timeout=1, read_timeout=1,
) )
self.assertEqual(resp['body'], body, 'keep-alive 1') assert resp['body'] == body, 'keep-alive 1'
body = '0123456789' body = '0123456789'
resp = self.post( resp = self.post(
@@ -212,19 +187,17 @@ class TestPythonApplication(TestApplicationPython):
body=body, body=body,
) )
self.assertEqual(resp['body'], body, 'keep-alive 2') assert resp['body'] == body, 'keep-alive 2'
def test_python_keepalive_reconfigure(self): def test_python_keepalive_reconfigure(self):
self.skip_alerts.extend( skip_alert(
[
r'pthread_mutex.+failed', r'pthread_mutex.+failed',
r'failed to apply', r'failed to apply',
r'process \d+ exited on signal', r'process \d+ exited on signal',
]
) )
self.load('mirror') self.load('mirror')
self.assertEqual(self.get()['status'], 200, 'init') assert self.get()['status'] == 200, 'init'
body = '0123456789' body = '0123456789'
conns = 3 conns = 3
@@ -242,12 +215,10 @@ class TestPythonApplication(TestApplicationPython):
read_timeout=1, read_timeout=1,
) )
self.assertEqual(resp['body'], body, 'keep-alive open') assert resp['body'] == body, 'keep-alive open'
self.assertIn( assert 'success' in self.conf(
'success', str(i + 1), 'applications/mirror/processes'
self.conf(str(i + 1), 'applications/mirror/processes'), ), 'reconfigure'
'reconfigure',
)
socks.append(sock) socks.append(sock)
@@ -264,12 +235,10 @@ class TestPythonApplication(TestApplicationPython):
read_timeout=1, read_timeout=1,
) )
self.assertEqual(resp['body'], body, 'keep-alive request') assert resp['body'] == body, 'keep-alive request'
self.assertIn( assert 'success' in self.conf(
'success', str(i + 1), 'applications/mirror/processes'
self.conf(str(i + 1), 'applications/mirror/processes'), ), 'reconfigure 2'
'reconfigure 2',
)
for i in range(conns): for i in range(conns):
resp = self.post( resp = self.post(
@@ -282,17 +251,15 @@ class TestPythonApplication(TestApplicationPython):
body=body, body=body,
) )
self.assertEqual(resp['body'], body, 'keep-alive close') assert resp['body'] == body, 'keep-alive close'
self.assertIn( assert 'success' in self.conf(
'success', str(i + 1), 'applications/mirror/processes'
self.conf(str(i + 1), 'applications/mirror/processes'), ), 'reconfigure 3'
'reconfigure 3',
)
def test_python_keepalive_reconfigure_2(self): def test_python_keepalive_reconfigure_2(self):
self.load('mirror') self.load('mirror')
self.assertEqual(self.get()['status'], 200, 'init') assert self.get()['status'] == 200, 'init'
body = '0123456789' body = '0123456789'
@@ -307,11 +274,11 @@ class TestPythonApplication(TestApplicationPython):
read_timeout=1, read_timeout=1,
) )
self.assertEqual(resp['body'], body, 'reconfigure 2 keep-alive 1') assert resp['body'] == body, 'reconfigure 2 keep-alive 1'
self.load('empty') self.load('empty')
self.assertEqual(self.get()['status'], 200, 'init') assert self.get()['status'] == 200, 'init'
(resp, sock) = self.post( (resp, sock) = self.post(
headers={ headers={
@@ -324,23 +291,21 @@ class TestPythonApplication(TestApplicationPython):
body=body, body=body,
) )
self.assertEqual(resp['status'], 200, 'reconfigure 2 keep-alive 2') assert resp['status'] == 200, 'reconfigure 2 keep-alive 2'
self.assertEqual(resp['body'], '', 'reconfigure 2 keep-alive 2 body') assert resp['body'] == '', 'reconfigure 2 keep-alive 2 body'
self.assertIn( assert 'success' in self.conf(
'success', {"listeners": {}, "applications": {}}
self.conf({"listeners": {}, "applications": {}}), ), 'reconfigure 2 clear configuration'
'reconfigure 2 clear configuration',
)
resp = self.get(sock=sock) resp = self.get(sock=sock)
self.assertEqual(resp, {}, 'reconfigure 2 keep-alive 3') assert resp == {}, 'reconfigure 2 keep-alive 3'
def test_python_keepalive_reconfigure_3(self): def test_python_keepalive_reconfigure_3(self):
self.load('empty') self.load('empty')
self.assertEqual(self.get()['status'], 200, 'init') assert self.get()['status'] == 200, 'init'
(_, sock) = self.http( (_, sock) = self.http(
b"""GET / HTTP/1.1 b"""GET / HTTP/1.1
@@ -350,13 +315,11 @@ class TestPythonApplication(TestApplicationPython):
no_recv=True, no_recv=True,
) )
self.assertEqual(self.get()['status'], 200) assert self.get()['status'] == 200
self.assertIn( assert 'success' in self.conf(
'success', {"listeners": {}, "applications": {}}
self.conf({"listeners": {}, "applications": {}}), ), 'reconfigure 3 clear configuration'
'reconfigure 3 clear configuration',
)
resp = self.http( resp = self.http(
b"""Host: localhost b"""Host: localhost
@@ -367,7 +330,7 @@ Connection: close
raw=True, raw=True,
) )
self.assertEqual(resp['status'], 200, 'reconfigure 3') assert resp['status'] == 200, 'reconfigure 3'
def test_python_atexit(self): def test_python_atexit(self):
self.load('atexit') self.load('atexit')
@@ -378,25 +341,24 @@ Connection: close
self.stop() self.stop()
self.assertIsNotNone( assert self.wait_for_record(r'At exit called\.') is not None, 'atexit'
self.wait_for_record(r'At exit called\.'), 'atexit'
)
def test_python_process_switch(self): def test_python_process_switch(self):
self.load('delayed') self.load('delayed')
self.assertIn( assert 'success' in self.conf(
'success', '2', 'applications/delayed/processes'
self.conf('2', 'applications/delayed/processes'), ), 'configure 2 processes'
'configure 2 processes',
)
self.get(headers={ self.get(
headers={
'Host': 'localhost', 'Host': 'localhost',
'Content-Length': '0', 'Content-Length': '0',
'X-Delay': '5', 'X-Delay': '5',
'Connection': 'close', 'Connection': 'close',
}, no_recv=True) },
no_recv=True,
)
headers_delay_1 = { headers_delay_1 = {
'Connection': 'close', 'Connection': 'close',
@@ -414,11 +376,11 @@ Connection: close
self.get(headers=headers_delay_1) self.get(headers=headers_delay_1)
@unittest.skip('not yet') @pytest.mark.skip('not yet')
def test_python_application_start_response_exit(self): def test_python_application_start_response_exit(self):
self.load('start_response_exit') self.load('start_response_exit')
self.assertEqual(self.get()['status'], 500, 'start response exit') assert self.get()['status'] == 500, 'start response exit'
def test_python_application_input_iter(self): def test_python_application_input_iter(self):
self.load('input_iter') self.load('input_iter')
@@ -429,10 +391,8 @@ next line
last line''' last line'''
resp = self.post(body=body) resp = self.post(body=body)
self.assertEqual(resp['body'], body, 'input iter') assert resp['body'] == body, 'input iter'
self.assertEqual( assert resp['headers']['X-Lines-Count'] == '4', 'input iter lines'
resp['headers']['X-Lines-Count'], '4', 'input iter lines'
)
def test_python_application_input_readline(self): def test_python_application_input_readline(self):
self.load('input_readline') self.load('input_readline')
@@ -443,10 +403,8 @@ next line
last line''' last line'''
resp = self.post(body=body) resp = self.post(body=body)
self.assertEqual(resp['body'], body, 'input readline') assert resp['body'] == body, 'input readline'
self.assertEqual( assert resp['headers']['X-Lines-Count'] == '4', 'input readline lines'
resp['headers']['X-Lines-Count'], '4', 'input readline lines'
)
def test_python_application_input_readline_size(self): def test_python_application_input_readline_size(self):
self.load('input_readline_size') self.load('input_readline_size')
@@ -456,12 +414,10 @@ next line
last line''' last line'''
self.assertEqual( assert self.post(body=body)['body'] == body, 'input readline size'
self.post(body=body)['body'], body, 'input readline size' assert (
) self.post(body='0123')['body'] == '0123'
self.assertEqual( ), 'input readline size less'
self.post(body='0123')['body'], '0123', 'input readline size less'
)
def test_python_application_input_readlines(self): def test_python_application_input_readlines(self):
self.load('input_readlines') self.load('input_readlines')
@@ -472,10 +428,8 @@ next line
last line''' last line'''
resp = self.post(body=body) resp = self.post(body=body)
self.assertEqual(resp['body'], body, 'input readlines') assert resp['body'] == body, 'input readlines'
self.assertEqual( assert resp['headers']['X-Lines-Count'] == '4', 'input readlines lines'
resp['headers']['X-Lines-Count'], '4', 'input readlines lines'
)
def test_python_application_input_readlines_huge(self): def test_python_application_input_readlines_huge(self):
self.load('input_readlines') self.load('input_readlines')
@@ -489,11 +443,9 @@ last line: 987654321
* 512 * 512
) )
self.assertEqual( assert (
self.post(body=body, read_buffer_size=16384)['body'], self.post(body=body, read_buffer_size=16384)['body'] == body
body, ), 'input readlines huge'
'input readlines huge',
)
def test_python_application_input_read_length(self): def test_python_application_input_read_length(self):
self.load('input_read_length') self.load('input_read_length')
@@ -509,7 +461,7 @@ last line: 987654321
body=body, body=body,
) )
self.assertEqual(resp['body'], body[:5], 'input read length lt body') assert resp['body'] == body[:5], 'input read length lt body'
resp = self.post( resp = self.post(
headers={ headers={
@@ -520,7 +472,7 @@ last line: 987654321
body=body, body=body,
) )
self.assertEqual(resp['body'], body, 'input read length gt body') assert resp['body'] == body, 'input read length gt body'
resp = self.post( resp = self.post(
headers={ headers={
@@ -531,7 +483,7 @@ last line: 987654321
body=body, body=body,
) )
self.assertEqual(resp['body'], '', 'input read length zero') assert resp['body'] == '', 'input read length zero'
resp = self.post( resp = self.post(
headers={ headers={
@@ -542,9 +494,9 @@ last line: 987654321
body=body, body=body,
) )
self.assertEqual(resp['body'], body, 'input read length negative') assert resp['body'] == body, 'input read length negative'
@unittest.skip('not yet') @pytest.mark.skip('not yet')
def test_python_application_errors_write(self): def test_python_application_errors_write(self):
self.load('errors_write') self.load('errors_write')
@@ -552,43 +504,41 @@ last line: 987654321
self.stop() self.stop()
self.assertIsNotNone( assert (
self.wait_for_record(r'\[error\].+Error in application\.'), self.wait_for_record(r'\[error\].+Error in application\.')
'errors write', is not None
) ), 'errors write'
def test_python_application_body_array(self): def test_python_application_body_array(self):
self.load('body_array') self.load('body_array')
self.assertEqual(self.get()['body'], '0123456789', 'body array') assert self.get()['body'] == '0123456789', 'body array'
def test_python_application_body_io(self): def test_python_application_body_io(self):
self.load('body_io') self.load('body_io')
self.assertEqual(self.get()['body'], '0123456789', 'body io') assert self.get()['body'] == '0123456789', 'body io'
def test_python_application_body_io_file(self): def test_python_application_body_io_file(self):
self.load('body_io_file') self.load('body_io_file')
self.assertEqual(self.get()['body'], 'body\n', 'body io file') assert self.get()['body'] == 'body\n', 'body io file'
@unittest.skip('not yet') @pytest.mark.skip('not yet')
def test_python_application_syntax_error(self): def test_python_application_syntax_error(self):
self.skip_alerts.append(r'Python failed to import module "wsgi"') skip_alert(r'Python failed to import module "wsgi"')
self.load('syntax_error') self.load('syntax_error')
self.assertEqual(self.get()['status'], 500, 'syntax error') assert self.get()['status'] == 500, 'syntax error'
def test_python_application_loading_error(self): def test_python_application_loading_error(self):
self.skip_alerts.append(r'Python failed to import module "blah"') skip_alert(r'Python failed to import module "blah"')
self.load('empty') self.load('empty')
self.assertIn( assert 'success' in self.conf('"blah"', 'applications/empty/module')
'success', self.conf('"blah"', 'applications/empty/module'),
)
self.assertEqual(self.get()['status'], 503, 'loading error') assert self.get()['status'] == 503, 'loading error'
def test_python_application_close(self): def test_python_application_close(self):
self.load('close') self.load('close')
@@ -597,7 +547,7 @@ last line: 987654321
self.stop() self.stop()
self.assertIsNotNone(self.wait_for_record(r'Close called\.'), 'close') assert self.wait_for_record(r'Close called\.') is not None, 'close'
def test_python_application_close_error(self): def test_python_application_close_error(self):
self.load('close_error') self.load('close_error')
@@ -606,9 +556,9 @@ last line: 987654321
self.stop() self.stop()
self.assertIsNotNone( assert (
self.wait_for_record(r'Close called\.'), 'close error' self.wait_for_record(r'Close called\.') is not None
) ), 'close error'
def test_python_application_not_iterable(self): def test_python_application_not_iterable(self):
self.load('not_iterable') self.load('not_iterable')
@@ -617,17 +567,17 @@ last line: 987654321
self.stop() self.stop()
self.assertIsNotNone( assert (
self.wait_for_record( self.wait_for_record(
r'\[error\].+the application returned not an iterable object' r'\[error\].+the application returned not an iterable object'
),
'not iterable',
) )
is not None
), 'not iterable'
def test_python_application_write(self): def test_python_application_write(self):
self.load('write') self.load('write')
self.assertEqual(self.get()['body'], '0123456789', 'write') assert self.get()['body'] == '0123456789', 'write'
def test_python_application_threading(self): def test_python_application_threading(self):
"""wait_for_record() timeouts after 5s while every thread works at """wait_for_record() timeouts after 5s while every thread works at
@@ -639,9 +589,9 @@ last line: 987654321
for _ in range(10): for _ in range(10):
self.get(no_recv=True) self.get(no_recv=True)
self.assertIsNotNone( assert (
self.wait_for_record(r'\(5\) Thread: 100'), 'last thread finished' self.wait_for_record(r'\(5\) Thread: 100') is not None
) ), 'last thread finished'
def test_python_application_iter_exception(self): def test_python_application_iter_exception(self):
self.load('iter_exception') self.load('iter_exception')
@@ -656,43 +606,38 @@ last line: 987654321
'Connection': 'close', 'Connection': 'close',
} }
) )
self.assertEqual(resp['status'], 200, 'status') assert resp['status'] == 200, 'status'
self.assertEqual(resp['body'], 'XXXXXXX', 'body') assert resp['body'] == 'XXXXXXX', 'body'
# Exception before start_response(). # Exception before start_response().
self.assertEqual(self.get()['status'], 503, 'error') assert self.get()['status'] == 503, 'error'
self.assertIsNotNone(self.wait_for_record(r'Traceback'), 'traceback') assert self.wait_for_record(r'Traceback') is not None, 'traceback'
self.assertIsNotNone( assert (
self.wait_for_record(r'raise Exception\(\'first exception\'\)'), self.wait_for_record(r'raise Exception\(\'first exception\'\)')
'first exception raise', is not None
) ), 'first exception raise'
self.assertEqual( assert len(self.findall(r'Traceback')) == 1, 'traceback count 1'
len(self.findall(r'Traceback')), 1, 'traceback count 1'
)
# Exception after start_response(), before first write(). # Exception after start_response(), before first write().
self.assertEqual( assert (
self.get( self.get(
headers={ headers={
'Host': 'localhost', 'Host': 'localhost',
'X-Skip': '1', 'X-Skip': '1',
'Connection': 'close', 'Connection': 'close',
} }
)['status'], )['status']
503, == 503
'error 2', ), 'error 2'
)
self.assertIsNotNone( assert (
self.wait_for_record(r'raise Exception\(\'second exception\'\)'), self.wait_for_record(r'raise Exception\(\'second exception\'\)')
'exception raise second', is not None
) ), 'exception raise second'
self.assertEqual( assert len(self.findall(r'Traceback')) == 2, 'traceback count 2'
len(self.findall(r'Traceback')), 2, 'traceback count 2'
)
# Exception after first write(), before first __next__(). # Exception after first write(), before first __next__().
@@ -705,15 +650,13 @@ last line: 987654321
start=True, start=True,
) )
self.assertIsNotNone( assert (
self.wait_for_record(r'raise Exception\(\'third exception\'\)'), self.wait_for_record(r'raise Exception\(\'third exception\'\)')
'exception raise third', is not None
) ), 'exception raise third'
self.assertEqual( assert len(self.findall(r'Traceback')) == 3, 'traceback count 3'
len(self.findall(r'Traceback')), 3, 'traceback count 3'
)
self.assertDictEqual(self.get(sock=sock), {}, 'closed connection') assert self.get(sock=sock) == {}, 'closed connection'
# Exception after first write(), before first __next__(), # Exception after first write(), before first __next__(),
# chunked (incomplete body). # chunked (incomplete body).
@@ -725,13 +668,11 @@ last line: 987654321
'X-Chunked': '1', 'X-Chunked': '1',
'Connection': 'close', 'Connection': 'close',
}, },
raw_resp=True raw_resp=True,
) )
if resp: if resp:
self.assertNotEqual(resp[-5:], '0\r\n\r\n', 'incomplete body') assert resp[-5:] != '0\r\n\r\n', 'incomplete body'
self.assertEqual( assert len(self.findall(r'Traceback')) == 4, 'traceback count 4'
len(self.findall(r'Traceback')), 4, 'traceback count 4'
)
# Exception in __next__(). # Exception in __next__().
@@ -744,15 +685,13 @@ last line: 987654321
start=True, start=True,
) )
self.assertIsNotNone( assert (
self.wait_for_record(r'raise Exception\(\'next exception\'\)'), self.wait_for_record(r'raise Exception\(\'next exception\'\)')
'exception raise next', is not None
) ), 'exception raise next'
self.assertEqual( assert len(self.findall(r'Traceback')) == 5, 'traceback count 5'
len(self.findall(r'Traceback')), 5, 'traceback count 5'
)
self.assertDictEqual(self.get(sock=sock), {}, 'closed connection 2') assert self.get(sock=sock) == {}, 'closed connection 2'
# Exception in __next__(), chunked (incomplete body). # Exception in __next__(), chunked (incomplete body).
@@ -763,40 +702,34 @@ last line: 987654321
'X-Chunked': '1', 'X-Chunked': '1',
'Connection': 'close', 'Connection': 'close',
}, },
raw_resp=True raw_resp=True,
) )
if resp: if resp:
self.assertNotEqual(resp[-5:], '0\r\n\r\n', 'incomplete body 2') assert resp[-5:] != '0\r\n\r\n', 'incomplete body 2'
self.assertEqual( assert len(self.findall(r'Traceback')) == 6, 'traceback count 6'
len(self.findall(r'Traceback')), 6, 'traceback count 6'
)
# Exception before start_response() and in close(). # Exception before start_response() and in close().
self.assertEqual( assert (
self.get( self.get(
headers={ headers={
'Host': 'localhost', 'Host': 'localhost',
'X-Not-Skip-Close': '1', 'X-Not-Skip-Close': '1',
'Connection': 'close', 'Connection': 'close',
} }
)['status'], )['status']
503, == 503
'error', ), 'error'
)
self.assertIsNotNone( assert (
self.wait_for_record(r'raise Exception\(\'close exception\'\)'), self.wait_for_record(r'raise Exception\(\'close exception\'\)')
'exception raise close', is not None
) ), 'exception raise close'
self.assertEqual( assert len(self.findall(r'Traceback')) == 8, 'traceback count 8'
len(self.findall(r'Traceback')), 8, 'traceback count 8'
)
def test_python_user_group(self): def test_python_user_group(self, is_su):
if not self.is_su: if not is_su:
print("requires root") pytest.skip('requires root')
raise unittest.SkipTest()
nobody_uid = pwd.getpwnam('nobody').pw_uid nobody_uid = pwd.getpwnam('nobody').pw_uid
@@ -811,40 +744,38 @@ last line: 987654321
self.load('user_group') self.load('user_group')
obj = self.getjson()['body'] obj = self.getjson()['body']
self.assertEqual(obj['UID'], nobody_uid, 'nobody uid') assert obj['UID'] == nobody_uid, 'nobody uid'
self.assertEqual(obj['GID'], group_id, 'nobody gid') assert obj['GID'] == group_id, 'nobody gid'
self.load('user_group', user='nobody') self.load('user_group', user='nobody')
obj = self.getjson()['body'] obj = self.getjson()['body']
self.assertEqual(obj['UID'], nobody_uid, 'nobody uid user=nobody') assert obj['UID'] == nobody_uid, 'nobody uid user=nobody'
self.assertEqual(obj['GID'], group_id, 'nobody gid user=nobody') assert obj['GID'] == group_id, 'nobody gid user=nobody'
self.load('user_group', user='nobody', group=group) self.load('user_group', user='nobody', group=group)
obj = self.getjson()['body'] obj = self.getjson()['body']
self.assertEqual( assert obj['UID'] == nobody_uid, (
obj['UID'], nobody_uid, 'nobody uid user=nobody group=%s' % group 'nobody uid user=nobody group=%s' % group
) )
self.assertEqual( assert obj['GID'] == group_id, (
obj['GID'], group_id, 'nobody gid user=nobody group=%s' % group 'nobody gid user=nobody group=%s' % group
) )
self.load('user_group', group=group) self.load('user_group', group=group)
obj = self.getjson()['body'] obj = self.getjson()['body']
self.assertEqual( assert obj['UID'] == nobody_uid, 'nobody uid group=%s' % group
obj['UID'], nobody_uid, 'nobody uid group=%s' % group
)
self.assertEqual(obj['GID'], group_id, 'nobody gid group=%s' % group) assert obj['GID'] == group_id, 'nobody gid group=%s' % group
self.load('user_group', user='root') self.load('user_group', user='root')
obj = self.getjson()['body'] obj = self.getjson()['body']
self.assertEqual(obj['UID'], 0, 'root uid user=root') assert obj['UID'] == 0, 'root uid user=root'
self.assertEqual(obj['GID'], 0, 'root gid user=root') assert obj['GID'] == 0, 'root gid user=root'
group = 'root' group = 'root'
@@ -858,14 +789,11 @@ last line: 987654321
self.load('user_group', user='root', group='root') self.load('user_group', user='root', group='root')
obj = self.getjson()['body'] obj = self.getjson()['body']
self.assertEqual(obj['UID'], 0, 'root uid user=root group=root') assert obj['UID'] == 0, 'root uid user=root group=root'
self.assertEqual(obj['GID'], 0, 'root gid user=root group=root') assert obj['GID'] == 0, 'root gid user=root group=root'
self.load('user_group', group='root') self.load('user_group', group='root')
obj = self.getjson()['body'] obj = self.getjson()['body']
self.assertEqual(obj['UID'], nobody_uid, 'root uid group=root') assert obj['UID'] == nobody_uid, 'root uid group=root'
self.assertEqual(obj['GID'], 0, 'root gid group=root') assert obj['GID'] == 0, 'root gid group=root'
if __name__ == '__main__':
TestPythonApplication.main()

View File

@@ -19,142 +19,104 @@ class TestPythonBasic(TestControl):
} }
def test_python_get_empty(self): def test_python_get_empty(self):
self.assertEqual(self.conf_get(), {'listeners': {}, 'applications': {}}) assert self.conf_get() == {'listeners': {}, 'applications': {}}
self.assertEqual(self.conf_get('listeners'), {}) assert self.conf_get('listeners') == {}
self.assertEqual(self.conf_get('applications'), {}) assert self.conf_get('applications') == {}
def test_python_get_applications(self): def test_python_get_applications(self):
self.conf(self.conf_app, 'applications') self.conf(self.conf_app, 'applications')
conf = self.conf_get() conf = self.conf_get()
self.assertEqual(conf['listeners'], {}, 'listeners') assert conf['listeners'] == {}, 'listeners'
self.assertEqual( assert conf['applications'] == {
conf['applications'],
{
"app": { "app": {
"type": "python", "type": "python",
"processes": {"spare": 0}, "processes": {"spare": 0},
"path": "/app", "path": "/app",
"module": "wsgi", "module": "wsgi",
} }
}, }, 'applications'
'applications',
)
self.assertEqual( assert self.conf_get('applications') == {
self.conf_get('applications'),
{
"app": { "app": {
"type": "python", "type": "python",
"processes": {"spare": 0}, "processes": {"spare": 0},
"path": "/app", "path": "/app",
"module": "wsgi", "module": "wsgi",
} }
}, }, 'applications prefix'
'applications prefix',
)
self.assertEqual( assert self.conf_get('applications/app') == {
self.conf_get('applications/app'),
{
"type": "python", "type": "python",
"processes": {"spare": 0}, "processes": {"spare": 0},
"path": "/app", "path": "/app",
"module": "wsgi", "module": "wsgi",
}, }, 'applications prefix 2'
'applications prefix 2',
)
self.assertEqual( assert self.conf_get('applications/app/type') == 'python', 'type'
self.conf_get('applications/app/type'), 'python', 'type' assert self.conf_get('applications/app/processes/spare') == 0, 'spare'
)
self.assertEqual(
self.conf_get('applications/app/processes/spare'), 0, 'spare'
)
def test_python_get_listeners(self): def test_python_get_listeners(self):
self.conf(self.conf_basic) self.conf(self.conf_basic)
self.assertEqual( assert self.conf_get()['listeners'] == {
self.conf_get()['listeners'], "*:7080": {"pass": "applications/app"}
{"*:7080": {"pass": "applications/app"}}, }, 'listeners'
'listeners',
)
self.assertEqual( assert self.conf_get('listeners') == {
self.conf_get('listeners'), "*:7080": {"pass": "applications/app"}
{"*:7080": {"pass": "applications/app"}}, }, 'listeners prefix'
'listeners prefix',
)
self.assertEqual( assert self.conf_get('listeners/*:7080') == {
self.conf_get('listeners/*:7080'), "pass": "applications/app"
{"pass": "applications/app"}, }, 'listeners prefix 2'
'listeners prefix 2',
)
def test_python_change_listener(self): def test_python_change_listener(self):
self.conf(self.conf_basic) self.conf(self.conf_basic)
self.conf({"*:7081": {"pass": "applications/app"}}, 'listeners') self.conf({"*:7081": {"pass": "applications/app"}}, 'listeners')
self.assertEqual( assert self.conf_get('listeners') == {
self.conf_get('listeners'), "*:7081": {"pass": "applications/app"}
{"*:7081": {"pass": "applications/app"}}, }, 'change listener'
'change listener',
)
def test_python_add_listener(self): def test_python_add_listener(self):
self.conf(self.conf_basic) self.conf(self.conf_basic)
self.conf({"pass": "applications/app"}, 'listeners/*:7082') self.conf({"pass": "applications/app"}, 'listeners/*:7082')
self.assertEqual( assert self.conf_get('listeners') == {
self.conf_get('listeners'),
{
"*:7080": {"pass": "applications/app"}, "*:7080": {"pass": "applications/app"},
"*:7082": {"pass": "applications/app"}, "*:7082": {"pass": "applications/app"},
}, }, 'add listener'
'add listener',
)
def test_python_change_application(self): def test_python_change_application(self):
self.conf(self.conf_basic) self.conf(self.conf_basic)
self.conf('30', 'applications/app/processes/max') self.conf('30', 'applications/app/processes/max')
self.assertEqual( assert (
self.conf_get('applications/app/processes/max'), self.conf_get('applications/app/processes/max') == 30
30, ), 'change application max'
'change application max',
)
self.conf('"/www"', 'applications/app/path') self.conf('"/www"', 'applications/app/path')
self.assertEqual( assert (
self.conf_get('applications/app/path'), self.conf_get('applications/app/path') == '/www'
'/www', ), 'change application path'
'change application path',
)
def test_python_delete(self): def test_python_delete(self):
self.conf(self.conf_basic) self.conf(self.conf_basic)
self.assertIn('error', self.conf_delete('applications/app')) assert 'error' in self.conf_delete('applications/app')
self.assertIn('success', self.conf_delete('listeners/*:7080')) assert 'success' in self.conf_delete('listeners/*:7080')
self.assertIn('success', self.conf_delete('applications/app')) assert 'success' in self.conf_delete('applications/app')
self.assertIn('error', self.conf_delete('applications/app')) assert 'error' in self.conf_delete('applications/app')
def test_python_delete_blocks(self): def test_python_delete_blocks(self):
self.conf(self.conf_basic) self.conf(self.conf_basic)
self.assertIn('success', self.conf_delete('listeners')) assert 'success' in self.conf_delete('listeners')
self.assertIn('success', self.conf_delete('applications')) assert 'success' in self.conf_delete('applications')
self.assertIn('success', self.conf(self.conf_app, 'applications')) assert 'success' in self.conf(self.conf_app, 'applications')
self.assertIn( assert 'success' in self.conf(
'success', {"*:7081": {"pass": "applications/app"}}, 'listeners'
self.conf({"*:7081": {"pass": "applications/app"}}, 'listeners'), ), 'applications restore'
'applications restore',
)
if __name__ == '__main__':
TestPythonBasic.main()

View File

@@ -7,97 +7,81 @@ class TestPythonEnvironment(TestApplicationPython):
def test_python_environment_name_null(self): def test_python_environment_name_null(self):
self.load('environment') self.load('environment')
self.assertIn( assert 'error' in self.conf(
'error',
self.conf(
{"va\0r": "val1"}, 'applications/environment/environment' {"va\0r": "val1"}, 'applications/environment/environment'
), ), 'name null'
'name null',
)
def test_python_environment_name_equals(self): def test_python_environment_name_equals(self):
self.load('environment') self.load('environment')
self.assertIn( assert 'error' in self.conf(
'error',
self.conf(
{"var=": "val1"}, 'applications/environment/environment' {"var=": "val1"}, 'applications/environment/environment'
), ), 'name equals'
'name equals',
)
def test_python_environment_value_null(self): def test_python_environment_value_null(self):
self.load('environment') self.load('environment')
self.assertIn( assert 'error' in self.conf(
'error',
self.conf(
{"var": "\0val"}, 'applications/environment/environment' {"var": "\0val"}, 'applications/environment/environment'
), ), 'value null'
'value null',
)
def test_python_environment_update(self): def test_python_environment_update(self):
self.load('environment') self.load('environment')
self.conf({"var": "val1"}, 'applications/environment/environment') self.conf({"var": "val1"}, 'applications/environment/environment')
self.assertEqual( assert (
self.get( self.get(
headers={ headers={
'Host': 'localhost', 'Host': 'localhost',
'X-Variables': 'var', 'X-Variables': 'var',
'Connection': 'close', 'Connection': 'close',
} }
)['body'], )['body']
'val1,', == 'val1,'
'set', ), 'set'
)
self.conf({"var": "val2"}, 'applications/environment/environment') self.conf({"var": "val2"}, 'applications/environment/environment')
self.assertEqual( assert (
self.get( self.get(
headers={ headers={
'Host': 'localhost', 'Host': 'localhost',
'X-Variables': 'var', 'X-Variables': 'var',
'Connection': 'close', 'Connection': 'close',
} }
)['body'], )['body']
'val2,', == 'val2,'
'update', ), 'update'
)
def test_python_environment_replace(self): def test_python_environment_replace(self):
self.load('environment') self.load('environment')
self.conf({"var1": "val1"}, 'applications/environment/environment') self.conf({"var1": "val1"}, 'applications/environment/environment')
self.assertEqual( assert (
self.get( self.get(
headers={ headers={
'Host': 'localhost', 'Host': 'localhost',
'X-Variables': 'var1', 'X-Variables': 'var1',
'Connection': 'close', 'Connection': 'close',
} }
)['body'], )['body']
'val1,', == 'val1,'
'set', ), 'set'
)
self.conf({"var2": "val2"}, 'applications/environment/environment') self.conf({"var2": "val2"}, 'applications/environment/environment')
self.assertEqual( assert (
self.get( self.get(
headers={ headers={
'Host': 'localhost', 'Host': 'localhost',
'X-Variables': 'var1,var2', 'X-Variables': 'var1,var2',
'Connection': 'close', 'Connection': 'close',
} }
)['body'], )['body']
'val2,', == 'val2,'
'replace', ), 'replace'
)
def test_python_environment_clear(self): def test_python_environment_clear(self):
self.load('environment') self.load('environment')
@@ -107,31 +91,29 @@ class TestPythonEnvironment(TestApplicationPython):
'applications/environment/environment', 'applications/environment/environment',
) )
self.assertEqual( assert (
self.get( self.get(
headers={ headers={
'Host': 'localhost', 'Host': 'localhost',
'X-Variables': 'var1,var2', 'X-Variables': 'var1,var2',
'Connection': 'close', 'Connection': 'close',
} }
)['body'], )['body']
'val1,val2,', == 'val1,val2,'
'set', ), 'set'
)
self.conf({}, 'applications/environment/environment') self.conf({}, 'applications/environment/environment')
self.assertEqual( assert (
self.get( self.get(
headers={ headers={
'Host': 'localhost', 'Host': 'localhost',
'X-Variables': 'var1,var2', 'X-Variables': 'var1,var2',
'Connection': 'close', 'Connection': 'close',
} }
)['body'], )['body']
'', == ''
'clear', ), 'clear'
)
def test_python_environment_replace_default(self): def test_python_environment_replace_default(self):
self.load('environment') self.load('environment')
@@ -144,36 +126,30 @@ class TestPythonEnvironment(TestApplicationPython):
} }
)['body'] )['body']
self.assertGreater(len(home_default), 1, 'get default') assert len(home_default) > 1, 'get default'
self.conf({"HOME": "/"}, 'applications/environment/environment') self.conf({"HOME": "/"}, 'applications/environment/environment')
self.assertEqual( assert (
self.get( self.get(
headers={ headers={
'Host': 'localhost', 'Host': 'localhost',
'X-Variables': 'HOME', 'X-Variables': 'HOME',
'Connection': 'close', 'Connection': 'close',
} }
)['body'], )['body']
'/,', == '/,'
'replace default', ), 'replace default'
)
self.conf({}, 'applications/environment/environment') self.conf({}, 'applications/environment/environment')
self.assertEqual( assert (
self.get( self.get(
headers={ headers={
'Host': 'localhost', 'Host': 'localhost',
'X-Variables': 'HOME', 'X-Variables': 'HOME',
'Connection': 'close', 'Connection': 'close',
} }
)['body'], )['body']
home_default, == home_default
'restore default', ), 'restore default'
)
if __name__ == '__main__':
TestPythonEnvironment.main()

View File

@@ -1,4 +1,4 @@
import unittest import pytest
from unit.applications.lang.python import TestApplicationPython from unit.applications.lang.python import TestApplicationPython
from unit.feature.isolation import TestFeatureIsolation from unit.feature.isolation import TestFeatureIsolation
@@ -10,70 +10,58 @@ class TestPythonIsolation(TestApplicationPython):
isolation = TestFeatureIsolation() isolation = TestFeatureIsolation()
@classmethod @classmethod
def setUpClass(cls, complete_check=True): def setup_class(cls, complete_check=True):
unit = super().setUpClass(complete_check=False) unit = super().setup_class(complete_check=False)
TestFeatureIsolation().check(cls.available, unit.testdir) TestFeatureIsolation().check(cls.available, unit.temp_dir)
return unit if not complete_check else unit.complete() return unit if not complete_check else unit.complete()
def test_python_isolation_rootfs(self): def test_python_isolation_rootfs(self, is_su):
isolation_features = self.available['features']['isolation'].keys() isolation_features = self.available['features']['isolation'].keys()
if 'mnt' not in isolation_features: if 'mnt' not in isolation_features:
print('requires mnt ns') pytest.skip('requires mnt ns')
raise unittest.SkipTest()
if not self.is_su: if not is_su:
if 'user' not in isolation_features: if 'user' not in isolation_features:
print('requires unprivileged userns or root') pytest.skip('requires unprivileged userns or root')
raise unittest.SkipTest()
if not 'unprivileged_userns_clone' in isolation_features: if not 'unprivileged_userns_clone' in isolation_features:
print('requires unprivileged userns or root') pytest.skip('requires unprivileged userns or root')
raise unittest.SkipTest()
isolation = { isolation = {
'namespaces': {'credential': not self.is_su, 'mount': True}, 'namespaces': {'credential': not is_su, 'mount': True},
'rootfs': self.testdir, 'rootfs': self.temp_dir,
} }
self.load('empty', isolation=isolation) self.load('empty', isolation=isolation)
self.assertEqual(self.get()['status'], 200, 'python rootfs') assert self.get()['status'] == 200, 'python rootfs'
self.load('ns_inspect', isolation=isolation) self.load('ns_inspect', isolation=isolation)
self.assertEqual( assert (
self.getjson(url='/?path=' + self.testdir)['body']['FileExists'], self.getjson(url='/?path=' + self.temp_dir)['body']['FileExists']
False, == False
'testdir does not exists in rootfs', ), 'temp_dir does not exists in rootfs'
)
self.assertEqual( assert (
self.getjson(url='/?path=/proc/self')['body']['FileExists'], self.getjson(url='/?path=/proc/self')['body']['FileExists']
False, == False
'no /proc/self', ), 'no /proc/self'
)
self.assertEqual( assert (
self.getjson(url='/?path=/dev/pts')['body']['FileExists'], self.getjson(url='/?path=/dev/pts')['body']['FileExists'] == False
False, ), 'no /dev/pts'
'no /dev/pts',
)
self.assertEqual( assert (
self.getjson(url='/?path=/sys/kernel')['body']['FileExists'], self.getjson(url='/?path=/sys/kernel')['body']['FileExists']
False, == False
'no /sys/kernel', ), 'no /sys/kernel'
)
ret = self.getjson(url='/?path=/app/python/ns_inspect') ret = self.getjson(url='/?path=/app/python/ns_inspect')
self.assertEqual( assert (
ret['body']['FileExists'], True, 'application exists in rootfs', ret['body']['FileExists'] == True
) ), 'application exists in rootfs'
if __name__ == '__main__':
TestPythonIsolation.main()

View File

@@ -1,4 +1,4 @@
import unittest import pytest
from unit.applications.lang.python import TestApplicationPython from unit.applications.lang.python import TestApplicationPython
from unit.feature.isolation import TestFeatureIsolation from unit.feature.isolation import TestFeatureIsolation
@@ -7,51 +7,41 @@ from unit.feature.isolation import TestFeatureIsolation
class TestPythonIsolation(TestApplicationPython): class TestPythonIsolation(TestApplicationPython):
prerequisites = {'modules': {'python': 'any'}} prerequisites = {'modules': {'python': 'any'}}
def test_python_isolation_chroot(self): def test_python_isolation_chroot(self, is_su):
if not self.is_su: if not is_su:
print('requires root') pytest.skip('requires root')
raise unittest.SkipTest()
isolation = { isolation = {
'rootfs': self.testdir, 'rootfs': self.temp_dir,
} }
self.load('empty', isolation=isolation) self.load('empty', isolation=isolation)
self.assertEqual(self.get()['status'], 200, 'python chroot') assert self.get()['status'] == 200, 'python chroot'
self.load('ns_inspect', isolation=isolation) self.load('ns_inspect', isolation=isolation)
self.assertEqual( assert (
self.getjson(url='/?path=' + self.testdir)['body']['FileExists'], self.getjson(url='/?path=' + self.temp_dir)['body']['FileExists']
False, == False
'testdir does not exists in rootfs', ), 'temp_dir does not exists in rootfs'
)
self.assertEqual( assert (
self.getjson(url='/?path=/proc/self')['body']['FileExists'], self.getjson(url='/?path=/proc/self')['body']['FileExists']
False, == False
'no /proc/self', ), 'no /proc/self'
)
self.assertEqual( assert (
self.getjson(url='/?path=/dev/pts')['body']['FileExists'], self.getjson(url='/?path=/dev/pts')['body']['FileExists'] == False
False, ), 'no /dev/pts'
'no /dev/pts',
)
self.assertEqual( assert (
self.getjson(url='/?path=/sys/kernel')['body']['FileExists'], self.getjson(url='/?path=/sys/kernel')['body']['FileExists']
False, == False
'no /sys/kernel', ), 'no /sys/kernel'
)
ret = self.getjson(url='/?path=/app/python/ns_inspect') ret = self.getjson(url='/?path=/app/python/ns_inspect')
self.assertEqual( assert (
ret['body']['FileExists'], True, 'application exists in rootfs', ret['body']['FileExists'] == True
) ), 'application exists in rootfs'
if __name__ == '__main__':
TestPythonIsolation.main()

View File

@@ -1,7 +1,7 @@
import pytest
import re import re
import subprocess import subprocess
import time import time
import unittest
from unit.applications.lang.python import TestApplicationPython from unit.applications.lang.python import TestApplicationPython
@@ -9,10 +9,10 @@ from unit.applications.lang.python import TestApplicationPython
class TestPythonProcman(TestApplicationPython): class TestPythonProcman(TestApplicationPython):
prerequisites = {'modules': {'python': 'any'}} prerequisites = {'modules': {'python': 'any'}}
def setUp(self): def setup_method(self):
super().setUp() super().setup_method()
self.app_name = "app-" + self.testdir.split('/')[-1] self.app_name = "app-" + self.temp_dir.split('/')[-1]
self.app_proc = 'applications/' + self.app_name + '/processes' self.app_proc = 'applications/' + self.app_name + '/processes'
self.load('empty', self.app_name) self.load('empty', self.app_name)
@@ -23,7 +23,7 @@ class TestPythonProcman(TestApplicationPython):
pids = set() pids = set()
for m in re.findall('.*' + self.app_name, output.decode()): for m in re.findall('.*' + self.app_name, output.decode()):
pids.add(re.search('^\s*(\d+)', m).group(1)) pids.add(re.search(r'^\s*(\d+)', m).group(1))
return pids return pids
@@ -31,35 +31,35 @@ class TestPythonProcman(TestApplicationPython):
if path is None: if path is None:
path = self.app_proc path = self.app_proc
self.assertIn('success', self.conf(conf, path), 'configure processes') assert 'success' in self.conf(conf, path), 'configure processes'
@unittest.skip('not yet') @pytest.mark.skip('not yet')
def test_python_processes_idle_timeout_zero(self): def test_python_processes_idle_timeout_zero(self):
self.conf_proc({"spare": 0, "max": 2, "idle_timeout": 0}) self.conf_proc({"spare": 0, "max": 2, "idle_timeout": 0})
self.get() self.get()
self.assertEqual(len(self.pids_for_process()), 0, 'idle timeout 0') assert len(self.pids_for_process()) == 0, 'idle timeout 0'
def test_python_prefork(self): def test_python_prefork(self):
self.conf_proc('2') self.conf_proc('2')
pids = self.pids_for_process() pids = self.pids_for_process()
self.assertEqual(len(pids), 2, 'prefork 2') assert len(pids) == 2, 'prefork 2'
self.get() self.get()
self.assertSetEqual(self.pids_for_process(), pids, 'prefork still 2') assert self.pids_for_process() == pids, 'prefork still 2'
self.conf_proc('4') self.conf_proc('4')
pids = self.pids_for_process() pids = self.pids_for_process()
self.assertEqual(len(pids), 4, 'prefork 4') assert len(pids) == 4, 'prefork 4'
self.get() self.get()
self.assertSetEqual(self.pids_for_process(), pids, 'prefork still 4') assert self.pids_for_process() == pids, 'prefork still 4'
self.stop_all() self.stop_all()
@unittest.skip('not yet') @pytest.mark.skip('not yet')
def test_python_prefork_same_processes(self): def test_python_prefork_same_processes(self):
self.conf_proc('2') self.conf_proc('2')
pids = self.pids_for_process() pids = self.pids_for_process()
@@ -67,25 +67,23 @@ class TestPythonProcman(TestApplicationPython):
self.conf_proc('4') self.conf_proc('4')
pids_new = self.pids_for_process() pids_new = self.pids_for_process()
self.assertTrue(pids.issubset(pids_new), 'prefork same processes') assert pids.issubset(pids_new), 'prefork same processes'
def test_python_ondemand(self): def test_python_ondemand(self):
self.conf_proc({"spare": 0, "max": 8, "idle_timeout": 1}) self.conf_proc({"spare": 0, "max": 8, "idle_timeout": 1})
self.assertEqual(len(self.pids_for_process()), 0, 'on-demand 0') assert len(self.pids_for_process()) == 0, 'on-demand 0'
self.get() self.get()
pids = self.pids_for_process() pids = self.pids_for_process()
self.assertEqual(len(pids), 1, 'on-demand 1') assert len(pids) == 1, 'on-demand 1'
self.get() self.get()
self.assertSetEqual(self.pids_for_process(), pids, 'on-demand still 1') assert self.pids_for_process() == pids, 'on-demand still 1'
time.sleep(1) time.sleep(1)
self.assertEqual( assert len(self.pids_for_process()) == 0, 'on-demand stop idle'
len(self.pids_for_process()), 0, 'on-demand stop idle'
)
self.stop_all() self.stop_all()
@@ -93,27 +91,25 @@ class TestPythonProcman(TestApplicationPython):
self.conf_proc({"spare": 2, "max": 8, "idle_timeout": 1}) self.conf_proc({"spare": 2, "max": 8, "idle_timeout": 1})
pids = self.pids_for_process() pids = self.pids_for_process()
self.assertEqual(len(pids), 2, 'updown 2') assert len(pids) == 2, 'updown 2'
self.get() self.get()
pids_new = self.pids_for_process() pids_new = self.pids_for_process()
self.assertEqual(len(pids_new), 3, 'updown 3') assert len(pids_new) == 3, 'updown 3'
self.assertTrue(pids.issubset(pids_new), 'updown 3 only 1 new') assert pids.issubset(pids_new), 'updown 3 only 1 new'
self.get() self.get()
self.assertSetEqual( assert self.pids_for_process() == pids_new, 'updown still 3'
self.pids_for_process(), pids_new, 'updown still 3'
)
time.sleep(1) time.sleep(1)
pids = self.pids_for_process() pids = self.pids_for_process()
self.assertEqual(len(pids), 2, 'updown stop idle') assert len(pids) == 2, 'updown stop idle'
self.get() self.get()
pids_new = self.pids_for_process() pids_new = self.pids_for_process()
self.assertEqual(len(pids_new), 3, 'updown again 3') assert len(pids_new) == 3, 'updown again 3'
self.assertTrue(pids.issubset(pids_new), 'updown again 3 only 1 new') assert pids.issubset(pids_new), 'updown again 3 only 1 new'
self.stop_all() self.stop_all()
@@ -121,20 +117,20 @@ class TestPythonProcman(TestApplicationPython):
self.conf_proc({"spare": 2, "max": 6, "idle_timeout": 1}) self.conf_proc({"spare": 2, "max": 6, "idle_timeout": 1})
pids = self.pids_for_process() pids = self.pids_for_process()
self.assertEqual(len(pids), 2, 'reconf 2') assert len(pids) == 2, 'reconf 2'
self.get() self.get()
pids_new = self.pids_for_process() pids_new = self.pids_for_process()
self.assertEqual(len(pids_new), 3, 'reconf 3') assert len(pids_new) == 3, 'reconf 3'
self.assertTrue(pids.issubset(pids_new), 'reconf 3 only 1 new') assert pids.issubset(pids_new), 'reconf 3 only 1 new'
self.conf_proc('6', self.app_proc + '/spare') self.conf_proc('6', self.app_proc + '/spare')
pids = self.pids_for_process() pids = self.pids_for_process()
self.assertEqual(len(pids), 6, 'reconf 6') assert len(pids) == 6, 'reconf 6'
self.get() self.get()
self.assertSetEqual(self.pids_for_process(), pids, 'reconf still 6') assert self.pids_for_process() == pids, 'reconf still 6'
self.stop_all() self.stop_all()
@@ -143,7 +139,7 @@ class TestPythonProcman(TestApplicationPython):
self.get() self.get()
pids = self.pids_for_process() pids = self.pids_for_process()
self.assertEqual(len(pids), 1, 'idle timeout 1') assert len(pids) == 1, 'idle timeout 1'
time.sleep(1) time.sleep(1)
@@ -152,14 +148,12 @@ class TestPythonProcman(TestApplicationPython):
time.sleep(1) time.sleep(1)
pids_new = self.pids_for_process() pids_new = self.pids_for_process()
self.assertEqual(len(pids_new), 1, 'idle timeout still 1') assert len(pids_new) == 1, 'idle timeout still 1'
self.assertSetEqual( assert self.pids_for_process() == pids, 'idle timeout still 1 same pid'
self.pids_for_process(), pids, 'idle timeout still 1 same pid'
)
time.sleep(1) time.sleep(1)
self.assertEqual(len(self.pids_for_process()), 0, 'idle timed out') assert len(self.pids_for_process()) == 0, 'idle timed out'
def test_python_processes_connection_keepalive(self): def test_python_processes_connection_keepalive(self):
self.conf_proc({"spare": 0, "max": 6, "idle_timeout": 2}) self.conf_proc({"spare": 0, "max": 6, "idle_timeout": 2})
@@ -169,15 +163,11 @@ class TestPythonProcman(TestApplicationPython):
start=True, start=True,
read_timeout=1, read_timeout=1,
) )
self.assertEqual( assert len(self.pids_for_process()) == 1, 'keepalive connection 1'
len(self.pids_for_process()), 1, 'keepalive connection 1'
)
time.sleep(2) time.sleep(2)
self.assertEqual( assert len(self.pids_for_process()) == 0, 'keepalive connection 0'
len(self.pids_for_process()), 0, 'keepalive connection 0'
)
sock.close() sock.close()
@@ -185,43 +175,29 @@ class TestPythonProcman(TestApplicationPython):
self.conf_proc('1') self.conf_proc('1')
path = '/' + self.app_proc path = '/' + self.app_proc
self.assertIn('error', self.conf_get(path + '/max')) assert 'error' in self.conf_get(path + '/max')
self.assertIn('error', self.conf_get(path + '/spare')) assert 'error' in self.conf_get(path + '/spare')
self.assertIn('error', self.conf_get(path + '/idle_timeout')) assert 'error' in self.conf_get(path + '/idle_timeout')
def test_python_processes_invalid(self): def test_python_processes_invalid(self):
self.assertIn( assert 'error' in self.conf(
'error', self.conf({"spare": -1}, self.app_proc), 'negative spare', {"spare": -1}, self.app_proc
) ), 'negative spare'
self.assertIn( assert 'error' in self.conf({"max": -1}, self.app_proc), 'negative max'
'error', self.conf({"max": -1}, self.app_proc), 'negative max', assert 'error' in self.conf(
) {"idle_timeout": -1}, self.app_proc
self.assertIn( ), 'negative idle_timeout'
'error', assert 'error' in self.conf(
self.conf({"idle_timeout": -1}, self.app_proc), {"spare": 2}, self.app_proc
'negative idle_timeout', ), 'spare gt max default'
) assert 'error' in self.conf(
self.assertIn( {"spare": 2, "max": 1}, self.app_proc
'error', ), 'spare gt max'
self.conf({"spare": 2}, self.app_proc), assert 'error' in self.conf(
'spare gt max default', {"spare": 0, "max": 0}, self.app_proc
) ), 'max zero'
self.assertIn(
'error',
self.conf({"spare": 2, "max": 1}, self.app_proc),
'spare gt max',
)
self.assertIn(
'error',
self.conf({"spare": 0, "max": 0}, self.app_proc),
'max zero',
)
def stop_all(self): def stop_all(self):
self.conf({"listeners": {}, "applications": {}}) self.conf({"listeners": {}, "applications": {}})
self.assertEqual(len(self.pids_for_process()), 0, 'stop all') assert len(self.pids_for_process()) == 0, 'stop all'
if __name__ == '__main__':
TestPythonProcman.main()

View File

@@ -3,6 +3,7 @@ import subprocess
import time import time
from unit.applications.lang.python import TestApplicationPython from unit.applications.lang.python import TestApplicationPython
from conftest import skip_alert
class TestRespawn(TestApplicationPython): class TestRespawn(TestApplicationPython):
@@ -11,21 +12,20 @@ class TestRespawn(TestApplicationPython):
PATTERN_ROUTER = 'unit: router' PATTERN_ROUTER = 'unit: router'
PATTERN_CONTROLLER = 'unit: controller' PATTERN_CONTROLLER = 'unit: controller'
def setUp(self): def setup_method(self):
super().setUp() super().setup_method()
self.app_name = "app-" + self.testdir.split('/')[-1] self.app_name = "app-" + self.temp_dir.split('/')[-1]
self.load('empty', self.app_name) self.load('empty', self.app_name)
self.assertIn( assert 'success' in self.conf(
'success', '1', 'applications/' + self.app_name + '/processes'
self.conf('1', 'applications/' + self.app_name + '/processes')
) )
def pid_by_name(self, name): def pid_by_name(self, name):
output = subprocess.check_output(['ps', 'ax']).decode() output = subprocess.check_output(['ps', 'ax']).decode()
m = re.search('\s*(\d+).*' + name, output) m = re.search(r'\s*(\d+).*' + name, output)
return m if m is None else m.group(1) return m if m is None else m.group(1)
def kill_pids(self, *pids): def kill_pids(self, *pids):
@@ -44,27 +44,26 @@ class TestRespawn(TestApplicationPython):
def smoke_test(self): def smoke_test(self):
for _ in range(5): for _ in range(5):
self.assertIn( assert 'success' in self.conf(
'success', '1', 'applications/' + self.app_name + '/processes'
self.conf('1', 'applications/' + self.app_name + '/processes')
) )
self.assertEqual(self.get()['status'], 200) assert self.get()['status'] == 200
# Check if the only one router, controller, # Check if the only one router, controller,
# and application processes running. # and application processes running.
output = subprocess.check_output(['ps', 'ax']).decode() output = subprocess.check_output(['ps', 'ax']).decode()
self.assertEqual(len(re.findall(self.PATTERN_ROUTER, output)), 1) assert len(re.findall(self.PATTERN_ROUTER, output)) == 1
self.assertEqual(len(re.findall(self.PATTERN_CONTROLLER, output)), 1) assert len(re.findall(self.PATTERN_CONTROLLER, output)) == 1
self.assertEqual(len(re.findall(self.app_name, output)), 1) assert len(re.findall(self.app_name, output)) == 1
def test_respawn_router(self): def test_respawn_router(self):
pid = self.pid_by_name(self.PATTERN_ROUTER) pid = self.pid_by_name(self.PATTERN_ROUTER)
self.kill_pids(pid) self.kill_pids(pid)
self.skip_alerts.append(r'process %s exited on signal 9' % pid) skip_alert(r'process %s exited on signal 9' % pid)
self.assertIsNotNone(self.wait_for_process(self.PATTERN_ROUTER)) assert self.wait_for_process(self.PATTERN_ROUTER) is not None
self.smoke_test() self.smoke_test()
@@ -72,11 +71,11 @@ class TestRespawn(TestApplicationPython):
pid = self.pid_by_name(self.PATTERN_CONTROLLER) pid = self.pid_by_name(self.PATTERN_CONTROLLER)
self.kill_pids(pid) self.kill_pids(pid)
self.skip_alerts.append(r'process %s exited on signal 9' % pid) skip_alert(r'process %s exited on signal 9' % pid)
self.assertIsNotNone(self.wait_for_process(self.PATTERN_CONTROLLER)) assert self.wait_for_process(self.PATTERN_CONTROLLER) is not None
self.assertEqual(self.get()['status'], 200) assert self.get()['status'] == 200
self.smoke_test() self.smoke_test()
@@ -84,12 +83,8 @@ class TestRespawn(TestApplicationPython):
pid = self.pid_by_name(self.app_name) pid = self.pid_by_name(self.app_name)
self.kill_pids(pid) self.kill_pids(pid)
self.skip_alerts.append(r'process %s exited on signal 9' % pid) skip_alert(r'process %s exited on signal 9' % pid)
self.assertIsNotNone(self.wait_for_process(self.app_name)) assert self.wait_for_process(self.app_name) is not None
self.smoke_test() self.smoke_test()
if __name__ == '__main__':
TestRespawn.main()

View File

@@ -6,8 +6,8 @@ from unit.applications.proto import TestApplicationProto
class TestReturn(TestApplicationProto): class TestReturn(TestApplicationProto):
prerequisites = {} prerequisites = {}
def setUp(self): def setup_method(self):
super().setUp() super().setup_method()
self._load_conf( self._load_conf(
{ {
@@ -35,59 +35,61 @@ Connection: close
def test_return(self): def test_return(self):
resp = self.get() resp = self.get()
self.assertEqual(resp['status'], 200) assert resp['status'] == 200
self.assertIn('Server', resp['headers']) assert 'Server' in resp['headers']
self.assertIn('Date', resp['headers']) assert 'Date' in resp['headers']
self.assertEqual(resp['headers']['Content-Length'], '0') assert resp['headers']['Content-Length'] == '0'
self.assertEqual(resp['headers']['Connection'], 'close') assert resp['headers']['Connection'] == 'close'
self.assertEqual(resp['body'], '', 'body') assert resp['body'] == '', 'body'
resp = self.post(body='blah') resp = self.post(body='blah')
self.assertEqual(resp['status'], 200) assert resp['status'] == 200
self.assertEqual(resp['body'], '', 'body') assert resp['body'] == '', 'body'
resp = self.get_resps_sc() resp = self.get_resps_sc()
self.assertEqual(len(re.findall('200 OK', resp)), 10) assert len(re.findall('200 OK', resp)) == 10
self.assertEqual(len(re.findall('Connection:', resp)), 1) assert len(re.findall('Connection:', resp)) == 1
self.assertEqual(len(re.findall('Connection: close', resp)), 1) assert len(re.findall('Connection: close', resp)) == 1
resp = self.get(http_10=True) resp = self.get(http_10=True)
self.assertEqual(resp['status'], 200) assert resp['status'] == 200
self.assertIn('Server', resp['headers']) assert 'Server' in resp['headers']
self.assertIn('Date', resp['headers']) assert 'Date' in resp['headers']
self.assertEqual(resp['headers']['Content-Length'], '0') assert resp['headers']['Content-Length'] == '0'
self.assertNotIn('Connection', resp['headers']) assert 'Connection' not in resp['headers']
self.assertEqual(resp['body'], '', 'body') assert resp['body'] == '', 'body'
def test_return_update(self): def test_return_update(self):
self.assertIn('success', self.conf('0', 'routes/0/action/return')) assert 'success' in self.conf('0', 'routes/0/action/return')
resp = self.get() resp = self.get()
self.assertEqual(resp['status'], 0) assert resp['status'] == 0
self.assertEqual(resp['body'], '') assert resp['body'] == ''
self.assertIn('success', self.conf('404', 'routes/0/action/return')) assert 'success' in self.conf('404', 'routes/0/action/return')
resp = self.get() resp = self.get()
self.assertEqual(resp['status'], 404) assert resp['status'] == 404
self.assertNotEqual(resp['body'], '') assert resp['body'] != ''
self.assertIn('success', self.conf('598', 'routes/0/action/return')) assert 'success' in self.conf('598', 'routes/0/action/return')
resp = self.get() resp = self.get()
self.assertEqual(resp['status'], 598) assert resp['status'] == 598
self.assertNotEqual(resp['body'], '') assert resp['body'] != ''
self.assertIn('success', self.conf('999', 'routes/0/action/return')) assert 'success' in self.conf('999', 'routes/0/action/return')
resp = self.get() resp = self.get()
self.assertEqual(resp['status'], 999) assert resp['status'] == 999
self.assertEqual(resp['body'], '') assert resp['body'] == ''
def test_return_location(self): def test_return_location(self):
reserved = ":/?#[]@!$&'()*+,;=" reserved = ":/?#[]@!$&'()*+,;="
unreserved = ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" unreserved = (
"0123456789-._~") "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
"0123456789-._~"
)
unsafe = " \"%<>\\^`{|}" unsafe = " \"%<>\\^`{|}"
unsafe_enc = "%20%22%25%3C%3E%5C%5E%60%7B%7C%7D" unsafe_enc = "%20%22%25%3C%3E%5C%5E%60%7B%7C%7D"
@@ -95,15 +97,11 @@ Connection: close
if expect is None: if expect is None:
expect = location expect = location
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{"return": 301, "location": location}, 'routes/0/action' {"return": 301, "location": location}, 'routes/0/action'
), ), 'configure location'
'configure location'
)
self.assertEqual(self.get()['headers']['Location'], expect) assert self.get()['headers']['Location'] == expect
# FAIL: can't specify empty header value. # FAIL: can't specify empty header value.
# check_location("") # check_location("")
@@ -145,39 +143,29 @@ Connection: close
check_location("/%20?%20#%20 ", "/%2520?%2520#%2520%20") check_location("/%20?%20#%20 ", "/%2520?%2520#%2520%20")
def test_return_location_edit(self): def test_return_location_edit(self):
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{"return": 302, "location": "blah"}, 'routes/0/action' {"return": 302, "location": "blah"}, 'routes/0/action'
), ), 'configure init location'
'configure init location' assert self.get()['headers']['Location'] == 'blah'
)
self.assertEqual(self.get()['headers']['Location'], 'blah')
self.assertIn( assert 'success' in self.conf_delete(
'success', 'routes/0/action/location'
self.conf_delete('routes/0/action/location'), ), 'location delete'
'location delete' assert 'Location' not in self.get()['headers']
)
self.assertNotIn('Location', self.get()['headers'])
self.assertIn( assert 'success' in self.conf(
'success', '"blah"', 'routes/0/action/location'
self.conf('"blah"', 'routes/0/action/location'), ), 'location restore'
'location restore' assert self.get()['headers']['Location'] == 'blah'
)
self.assertEqual(self.get()['headers']['Location'], 'blah')
self.assertIn( assert 'error' in self.conf_post(
'error', '"blah"', 'routes/0/action/location'
self.conf_post('"blah"', 'routes/0/action/location'), ), 'location method not allowed'
'location method not allowed' assert self.get()['headers']['Location'] == 'blah'
)
self.assertEqual(self.get()['headers']['Location'], 'blah')
def test_return_invalid(self): def test_return_invalid(self):
def check_error(conf): def check_error(conf):
self.assertIn('error', self.conf(conf, 'routes/0/action')) assert 'error' in self.conf(conf, 'routes/0/action')
check_error({"return": "200"}) check_error({"return": "200"})
check_error({"return": []}) check_error({"return": []})
@@ -186,13 +174,9 @@ Connection: close
check_error({"return": -1}) check_error({"return": -1})
check_error({"return": 200, "share": "/blah"}) check_error({"return": 200, "share": "/blah"})
self.assertIn( assert 'error' in self.conf(
'error', self.conf('001', 'routes/0/action/return'), 'leading zero' '001', 'routes/0/action/return'
) ), 'leading zero'
check_error({"return": 301, "location": 0}) check_error({"return": 301, "location": 0})
check_error({"return": 301, "location": []}) check_error({"return": 301, "location": []})
if __name__ == '__main__':
TestReturn.main()

File diff suppressed because it is too large Load Diff

View File

@@ -7,9 +7,7 @@ class TestRoutingTLS(TestApplicationTLS):
def test_routes_match_scheme_tls(self): def test_routes_match_scheme_tls(self):
self.certificate() self.certificate()
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{ {
"listeners": { "listeners": {
"*:7080": {"pass": "routes"}, "*:7080": {"pass": "routes"},
@@ -19,24 +17,12 @@ class TestRoutingTLS(TestApplicationTLS):
}, },
}, },
"routes": [ "routes": [
{ {"match": {"scheme": "http"}, "action": {"return": 200}},
"match": {"scheme": "http"}, {"match": {"scheme": "https"}, "action": {"return": 201}},
"action": {"return": 200},
},
{
"match": {"scheme": "https"},
"action": {"return": 201},
},
], ],
"applications": {}, "applications": {},
} }
), ), 'scheme configure'
'scheme configure',
)
self.assertEqual(self.get()['status'], 200, 'http') assert self.get()['status'] == 200, 'http'
self.assertEqual(self.get_ssl(port=7081)['status'], 201, 'https') assert self.get_ssl(port=7081)['status'] == 201, 'https'
if __name__ == '__main__':
TestRoutingTLS.main()

View File

@@ -1,6 +1,8 @@
import unittest import pytest
import re
from unit.applications.lang.ruby import TestApplicationRuby from unit.applications.lang.ruby import TestApplicationRuby
from conftest import skip_alert
class TestRubyApplication(TestApplicationRuby): class TestRubyApplication(TestApplicationRuby):
@@ -21,27 +23,21 @@ class TestRubyApplication(TestApplicationRuby):
body=body, body=body,
) )
self.assertEqual(resp['status'], 200, 'status') assert resp['status'] == 200, 'status'
headers = resp['headers'] headers = resp['headers']
header_server = headers.pop('Server') header_server = headers.pop('Server')
self.assertRegex(header_server, r'Unit/[\d\.]+', 'server header') assert re.search(r'Unit/[\d\.]+', header_server), 'server header'
self.assertEqual( assert (
headers.pop('Server-Software'), headers.pop('Server-Software') == header_server
header_server, ), 'server software header'
'server software header',
)
date = headers.pop('Date') date = headers.pop('Date')
self.assertEqual(date[-4:], ' GMT', 'date header timezone') assert date[-4:] == ' GMT', 'date header timezone'
self.assertLess( assert (
abs(self.date_to_sec_epoch(date) - self.sec_epoch()), abs(self.date_to_sec_epoch(date) - self.sec_epoch()) < 5
5, ), 'date header'
'date header',
)
self.assertDictEqual( assert headers == {
headers,
{
'Connection': 'close', 'Connection': 'close',
'Content-Length': str(len(body)), 'Content-Length': str(len(body)),
'Content-Type': 'text/html', 'Content-Type': 'text/html',
@@ -58,136 +54,120 @@ class TestRubyApplication(TestApplicationRuby):
'Rack-Hijack-Q': 'false', 'Rack-Hijack-Q': 'false',
'Rack-Hijack': '', 'Rack-Hijack': '',
'Rack-Hijack-IO': '', 'Rack-Hijack-IO': '',
}, }, 'headers'
'headers', assert resp['body'] == body, 'body'
)
self.assertEqual(resp['body'], body, 'body')
def test_ruby_application_query_string(self): def test_ruby_application_query_string(self):
self.load('query_string') self.load('query_string')
resp = self.get(url='/?var1=val1&var2=val2') resp = self.get(url='/?var1=val1&var2=val2')
self.assertEqual( assert (
resp['headers']['Query-String'], resp['headers']['Query-String'] == 'var1=val1&var2=val2'
'var1=val1&var2=val2', ), 'Query-String header'
'Query-String header',
)
def test_ruby_application_query_string_empty(self): def test_ruby_application_query_string_empty(self):
self.load('query_string') self.load('query_string')
resp = self.get(url='/?') resp = self.get(url='/?')
self.assertEqual(resp['status'], 200, 'query string empty status') assert resp['status'] == 200, 'query string empty status'
self.assertEqual( assert resp['headers']['Query-String'] == '', 'query string empty'
resp['headers']['Query-String'], '', 'query string empty'
)
def test_ruby_application_query_string_absent(self): def test_ruby_application_query_string_absent(self):
self.load('query_string') self.load('query_string')
resp = self.get() resp = self.get()
self.assertEqual(resp['status'], 200, 'query string absent status') assert resp['status'] == 200, 'query string absent status'
self.assertEqual( assert resp['headers']['Query-String'] == '', 'query string absent'
resp['headers']['Query-String'], '', 'query string absent'
)
@unittest.skip('not yet') @pytest.mark.skip('not yet')
def test_ruby_application_server_port(self): def test_ruby_application_server_port(self):
self.load('server_port') self.load('server_port')
self.assertEqual( assert (
self.get()['headers']['Server-Port'], '7080', 'Server-Port header' self.get()['headers']['Server-Port'] == '7080'
) ), 'Server-Port header'
def test_ruby_application_status_int(self): def test_ruby_application_status_int(self):
self.load('status_int') self.load('status_int')
self.assertEqual(self.get()['status'], 200, 'status int') assert self.get()['status'] == 200, 'status int'
def test_ruby_application_input_read_empty(self): def test_ruby_application_input_read_empty(self):
self.load('input_read_empty') self.load('input_read_empty')
self.assertEqual(self.get()['body'], '', 'read empty') assert self.get()['body'] == '', 'read empty'
def test_ruby_application_input_read_parts(self): def test_ruby_application_input_read_parts(self):
self.load('input_read_parts') self.load('input_read_parts')
self.assertEqual( assert (
self.post(body='0123456789')['body'], self.post(body='0123456789')['body'] == '012345678'
'012345678', ), 'input read parts'
'input read parts',
)
def test_ruby_application_input_read_buffer(self): def test_ruby_application_input_read_buffer(self):
self.load('input_read_buffer') self.load('input_read_buffer')
self.assertEqual( assert (
self.post(body='0123456789')['body'], self.post(body='0123456789')['body'] == '0123456789'
'0123456789', ), 'input read buffer'
'input read buffer',
)
def test_ruby_application_input_read_buffer_not_empty(self): def test_ruby_application_input_read_buffer_not_empty(self):
self.load('input_read_buffer_not_empty') self.load('input_read_buffer_not_empty')
self.assertEqual( assert (
self.post(body='0123456789')['body'], self.post(body='0123456789')['body'] == '0123456789'
'0123456789', ), 'input read buffer not empty'
'input read buffer not empty',
)
def test_ruby_application_input_gets(self): def test_ruby_application_input_gets(self):
self.load('input_gets') self.load('input_gets')
body = '0123456789' body = '0123456789'
self.assertEqual(self.post(body=body)['body'], body, 'input gets') assert self.post(body=body)['body'] == body, 'input gets'
def test_ruby_application_input_gets_2(self): def test_ruby_application_input_gets_2(self):
self.load('input_gets') self.load('input_gets')
self.assertEqual( assert (
self.post(body='01234\n56789\n')['body'], '01234\n', 'input gets 2' self.post(body='01234\n56789\n')['body'] == '01234\n'
) ), 'input gets 2'
def test_ruby_application_input_gets_all(self): def test_ruby_application_input_gets_all(self):
self.load('input_gets_all') self.load('input_gets_all')
body = '\n01234\n56789\n\n' body = '\n01234\n56789\n\n'
self.assertEqual(self.post(body=body)['body'], body, 'input gets all') assert self.post(body=body)['body'] == body, 'input gets all'
def test_ruby_application_input_each(self): def test_ruby_application_input_each(self):
self.load('input_each') self.load('input_each')
body = '\n01234\n56789\n\n' body = '\n01234\n56789\n\n'
self.assertEqual(self.post(body=body)['body'], body, 'input each') assert self.post(body=body)['body'] == body, 'input each'
@unittest.skip('not yet') @pytest.mark.skip('not yet')
def test_ruby_application_input_rewind(self): def test_ruby_application_input_rewind(self):
self.load('input_rewind') self.load('input_rewind')
body = '0123456789' body = '0123456789'
self.assertEqual(self.post(body=body)['body'], body, 'input rewind') assert self.post(body=body)['body'] == body, 'input rewind'
@unittest.skip('not yet') @pytest.mark.skip('not yet')
def test_ruby_application_syntax_error(self): def test_ruby_application_syntax_error(self):
self.skip_alerts.extend( skip_alert(
[
r'Failed to parse rack script', r'Failed to parse rack script',
r'syntax error', r'syntax error',
r'new_from_string', r'new_from_string',
r'parse_file', r'parse_file',
]
) )
self.load('syntax_error') self.load('syntax_error')
self.assertEqual(self.get()['status'], 500, 'syntax error') assert self.get()['status'] == 500, 'syntax error'
def test_ruby_application_errors_puts(self): def test_ruby_application_errors_puts(self):
self.load('errors_puts') self.load('errors_puts')
@@ -196,10 +176,10 @@ class TestRubyApplication(TestApplicationRuby):
self.stop() self.stop()
self.assertIsNotNone( assert (
self.wait_for_record(r'\[error\].+Error in application'), self.wait_for_record(r'\[error\].+Error in application')
'errors puts', is not None
) ), 'errors puts'
def test_ruby_application_errors_puts_int(self): def test_ruby_application_errors_puts_int(self):
self.load('errors_puts_int') self.load('errors_puts_int')
@@ -208,9 +188,9 @@ class TestRubyApplication(TestApplicationRuby):
self.stop() self.stop()
self.assertIsNotNone( assert (
self.wait_for_record(r'\[error\].+1234567890'), 'errors puts int' self.wait_for_record(r'\[error\].+1234567890') is not None
) ), 'errors puts int'
def test_ruby_application_errors_write(self): def test_ruby_application_errors_write(self):
self.load('errors_write') self.load('errors_write')
@@ -219,15 +199,15 @@ class TestRubyApplication(TestApplicationRuby):
self.stop() self.stop()
self.assertIsNotNone( assert (
self.wait_for_record(r'\[error\].+Error in application'), self.wait_for_record(r'\[error\].+Error in application')
'errors write', is not None
) ), 'errors write'
def test_ruby_application_errors_write_to_s_custom(self): def test_ruby_application_errors_write_to_s_custom(self):
self.load('errors_write_to_s_custom') self.load('errors_write_to_s_custom')
self.assertEqual(self.get()['status'], 200, 'errors write to_s custom') assert self.get()['status'] == 200, 'errors write to_s custom'
def test_ruby_application_errors_write_int(self): def test_ruby_application_errors_write_int(self):
self.load('errors_write_int') self.load('errors_write_int')
@@ -236,9 +216,9 @@ class TestRubyApplication(TestApplicationRuby):
self.stop() self.stop()
self.assertIsNotNone( assert (
self.wait_for_record(r'\[error\].+1234567890'), 'errors write int' self.wait_for_record(r'\[error\].+1234567890') is not None
) ), 'errors write int'
def test_ruby_application_at_exit(self): def test_ruby_application_at_exit(self):
self.load('at_exit') self.load('at_exit')
@@ -249,79 +229,81 @@ class TestRubyApplication(TestApplicationRuby):
self.stop() self.stop()
self.assertIsNotNone( assert (
self.wait_for_record(r'\[error\].+At exit called\.'), 'at exit' self.wait_for_record(r'\[error\].+At exit called\.') is not None
) ), 'at exit'
def test_ruby_application_header_custom(self): def test_ruby_application_header_custom(self):
self.load('header_custom') self.load('header_custom')
resp = self.post(body="\ntc=one,two\ntc=three,four,\n\n") resp = self.post(body="\ntc=one,two\ntc=three,four,\n\n")
self.assertEqual( assert resp['headers']['Custom-Header'] == [
resp['headers']['Custom-Header'], '',
['', 'tc=one,two', 'tc=three,four,', '', ''], 'tc=one,two',
'header custom', 'tc=three,four,',
) '',
'',
], 'header custom'
@unittest.skip('not yet') @pytest.mark.skip('not yet')
def test_ruby_application_header_custom_non_printable(self): def test_ruby_application_header_custom_non_printable(self):
self.load('header_custom') self.load('header_custom')
self.assertEqual( assert (
self.post(body='\b')['status'], 500, 'header custom non printable' self.post(body='\b')['status'] == 500
) ), 'header custom non printable'
def test_ruby_application_header_status(self): def test_ruby_application_header_status(self):
self.load('header_status') self.load('header_status')
self.assertEqual(self.get()['status'], 200, 'header status') assert self.get()['status'] == 200, 'header status'
@unittest.skip('not yet') @pytest.mark.skip('not yet')
def test_ruby_application_header_rack(self): def test_ruby_application_header_rack(self):
self.load('header_rack') self.load('header_rack')
self.assertEqual(self.get()['status'], 500, 'header rack') assert self.get()['status'] == 500, 'header rack'
def test_ruby_application_body_empty(self): def test_ruby_application_body_empty(self):
self.load('body_empty') self.load('body_empty')
self.assertEqual(self.get()['body'], '', 'body empty') assert self.get()['body'] == '', 'body empty'
def test_ruby_application_body_array(self): def test_ruby_application_body_array(self):
self.load('body_array') self.load('body_array')
self.assertEqual(self.get()['body'], '0123456789', 'body array') assert self.get()['body'] == '0123456789', 'body array'
def test_ruby_application_body_large(self): def test_ruby_application_body_large(self):
self.load('mirror') self.load('mirror')
body = '0123456789' * 1000 body = '0123456789' * 1000
self.assertEqual(self.post(body=body)['body'], body, 'body large') assert self.post(body=body)['body'] == body, 'body large'
@unittest.skip('not yet') @pytest.mark.skip('not yet')
def test_ruby_application_body_each_error(self): def test_ruby_application_body_each_error(self):
self.load('body_each_error') self.load('body_each_error')
self.assertEqual(self.get()['status'], 500, 'body each error status') assert self.get()['status'] == 500, 'body each error status'
self.stop() self.stop()
self.assertIsNotNone( assert (
self.wait_for_record(r'\[error\].+Failed to run ruby script'), self.wait_for_record(r'\[error\].+Failed to run ruby script')
'body each error', is not None
) ), 'body each error'
def test_ruby_application_body_file(self): def test_ruby_application_body_file(self):
self.load('body_file') self.load('body_file')
self.assertEqual(self.get()['body'], 'body\n', 'body file') assert self.get()['body'] == 'body\n', 'body file'
def test_ruby_keepalive_body(self): def test_ruby_keepalive_body(self):
self.load('mirror') self.load('mirror')
self.assertEqual(self.get()['status'], 200, 'init') assert self.get()['status'] == 200, 'init'
body = '0123456789' * 500 body = '0123456789' * 500
(resp, sock) = self.post( (resp, sock) = self.post(
@@ -335,7 +317,7 @@ class TestRubyApplication(TestApplicationRuby):
read_timeout=1, read_timeout=1,
) )
self.assertEqual(resp['body'], body, 'keep-alive 1') assert resp['body'] == body, 'keep-alive 1'
body = '0123456789' body = '0123456789'
resp = self.post( resp = self.post(
@@ -348,31 +330,22 @@ class TestRubyApplication(TestApplicationRuby):
body=body, body=body,
) )
self.assertEqual(resp['body'], body, 'keep-alive 2') assert resp['body'] == body, 'keep-alive 2'
def test_ruby_application_constants(self): def test_ruby_application_constants(self):
self.load('constants') self.load('constants')
resp = self.get() resp = self.get()
self.assertEqual(resp['status'], 200, 'status') assert resp['status'] == 200, 'status'
headers = resp['headers'] headers = resp['headers']
self.assertGreater(len(headers['X-Copyright']), 0, 'RUBY_COPYRIGHT') assert len(headers['X-Copyright']) > 0, 'RUBY_COPYRIGHT'
self.assertGreater( assert len(headers['X-Description']) > 0, 'RUBY_DESCRIPTION'
len(headers['X-Description']), 0, 'RUBY_DESCRIPTION' assert len(headers['X-Engine']) > 0, 'RUBY_ENGINE'
) assert len(headers['X-Engine-Version']) > 0, 'RUBY_ENGINE_VERSION'
self.assertGreater(len(headers['X-Engine']), 0, 'RUBY_ENGINE') assert len(headers['X-Patchlevel']) > 0, 'RUBY_PATCHLEVEL'
self.assertGreater( assert len(headers['X-Platform']) > 0, 'RUBY_PLATFORM'
len(headers['X-Engine-Version']), 0, 'RUBY_ENGINE_VERSION' assert len(headers['X-Release-Date']) > 0, 'RUBY_RELEASE_DATE'
) assert len(headers['X-Revision']) > 0, 'RUBY_REVISION'
self.assertGreater(len(headers['X-Patchlevel']), 0, 'RUBY_PATCHLEVEL') assert len(headers['X-Version']) > 0, 'RUBY_VERSION'
self.assertGreater(len(headers['X-Platform']), 0, 'RUBY_PLATFORM')
self.assertGreater(
len(headers['X-Release-Date']), 0, 'RUBY_RELEASE_DATE'
)
self.assertGreater(len(headers['X-Revision']), 0, 'RUBY_REVISION')
self.assertGreater(len(headers['X-Version']), 0, 'RUBY_VERSION')
if __name__ == '__main__':
TestRubyApplication.main()

View File

@@ -1,9 +1,10 @@
import os import os
import pytest
import shutil import shutil
import unittest
from unit.applications.lang.ruby import TestApplicationRuby from unit.applications.lang.ruby import TestApplicationRuby
from unit.feature.isolation import TestFeatureIsolation from unit.feature.isolation import TestFeatureIsolation
from conftest import option
class TestRubyIsolation(TestApplicationRuby): class TestRubyIsolation(TestApplicationRuby):
@@ -12,60 +13,45 @@ class TestRubyIsolation(TestApplicationRuby):
isolation = TestFeatureIsolation() isolation = TestFeatureIsolation()
@classmethod @classmethod
def setUpClass(cls, complete_check=True): def setup_class(cls, complete_check=True):
unit = super().setUpClass(complete_check=False) unit = super().setup_class(complete_check=False)
TestFeatureIsolation().check(cls.available, unit.testdir) TestFeatureIsolation().check(cls.available, unit.temp_dir)
return unit if not complete_check else unit.complete() return unit if not complete_check else unit.complete()
def test_ruby_isolation_rootfs(self): def test_ruby_isolation_rootfs(self, is_su):
isolation_features = self.available['features']['isolation'].keys() isolation_features = self.available['features']['isolation'].keys()
if 'mnt' not in isolation_features: if 'mnt' not in isolation_features:
print('requires mnt ns') pytest.skip('requires mnt ns')
raise unittest.SkipTest()
if not self.is_su: if not is_su:
if 'user' not in isolation_features: if 'user' not in isolation_features:
print('requires unprivileged userns or root') pytest.skip('requires unprivileged userns or root')
raise unittest.SkipTest()
if not 'unprivileged_userns_clone' in isolation_features: if not 'unprivileged_userns_clone' in isolation_features:
print('requires unprivileged userns or root') pytest.skip('requires unprivileged userns or root')
raise unittest.SkipTest()
os.mkdir(self.testdir + '/ruby') os.mkdir(self.temp_dir + '/ruby')
shutil.copytree( shutil.copytree(
self.current_dir + '/ruby/status_int', option.test_dir + '/ruby/status_int',
self.testdir + '/ruby/status_int', self.temp_dir + '/ruby/status_int',
) )
isolation = { isolation = {
'namespaces': {'credential': not self.is_su, 'mount': True}, 'namespaces': {'credential': not is_su, 'mount': True},
'rootfs': self.testdir, 'rootfs': self.temp_dir,
} }
self.load('status_int', isolation=isolation) self.load('status_int', isolation=isolation)
self.assertIn( assert 'success' in self.conf(
'success', '"/ruby/status_int/config.ru"', 'applications/status_int/script',
self.conf(
'"/ruby/status_int/config.ru"',
'applications/status_int/script',
),
) )
self.assertIn( assert 'success' in self.conf(
'success', '"/ruby/status_int"', 'applications/status_int/working_directory',
self.conf(
'"/ruby/status_int"',
'applications/status_int/working_directory',
),
) )
self.assertEqual(self.get()['status'], 200, 'status int') assert self.get()['status'] == 200, 'status int'
if __name__ == '__main__':
TestRubyIsolation.main()

View File

@@ -1,8 +1,9 @@
import pytest
import socket import socket
import time import time
import unittest
from unit.applications.lang.python import TestApplicationPython from unit.applications.lang.python import TestApplicationPython
import re
class TestSettings(TestApplicationPython): class TestSettings(TestApplicationPython):
@@ -32,7 +33,7 @@ Connection: close
raw=True, raw=True,
) )
self.assertEqual(resp['status'], 408, 'status header read timeout') assert resp['status'] == 408, 'status header read timeout'
def test_settings_header_read_timeout_update(self): def test_settings_header_read_timeout_update(self):
self.load('empty') self.load('empty')
@@ -83,9 +84,7 @@ Connection: close
raw=True, raw=True,
) )
self.assertEqual( assert resp['status'] == 408, 'status header read timeout update'
resp['status'], 408, 'status header read timeout update'
)
def test_settings_body_read_timeout(self): def test_settings_body_read_timeout(self):
self.load('empty') self.load('empty')
@@ -109,7 +108,7 @@ Connection: close
resp = self.http(b"""0123456789""", sock=sock, raw=True) resp = self.http(b"""0123456789""", sock=sock, raw=True)
self.assertEqual(resp['status'], 408, 'status body read timeout') assert resp['status'] == 408, 'status body read timeout'
def test_settings_body_read_timeout_update(self): def test_settings_body_read_timeout_update(self):
self.load('empty') self.load('empty')
@@ -144,9 +143,7 @@ Connection: close
resp = self.http(b"""6789""", sock=sock, raw=True) resp = self.http(b"""6789""", sock=sock, raw=True)
self.assertEqual( assert resp['status'] == 200, 'status body read timeout update'
resp['status'], 200, 'status body read timeout update'
)
def test_settings_send_timeout(self): def test_settings_send_timeout(self):
self.load('mirror') self.load('mirror')
@@ -155,7 +152,7 @@ Connection: close
self.conf({'http': {'send_timeout': 1}}, 'settings') self.conf({'http': {'send_timeout': 1}}, 'settings')
addr = self.testdir + '/sock' addr = self.temp_dir + '/sock'
self.conf({"unix:" + addr: {'application': 'mirror'}}, 'listeners') self.conf({"unix:" + addr: {'application': 'mirror'}}, 'listeners')
@@ -182,13 +179,13 @@ Connection: close
sock.close() sock.close()
self.assertRegex(data, r'200 OK', 'status send timeout') assert re.search(r'200 OK', data), 'status send timeout'
self.assertLess(len(data), data_len, 'data send timeout') assert len(data) < data_len, 'data send timeout'
def test_settings_idle_timeout(self): def test_settings_idle_timeout(self):
self.load('empty') self.load('empty')
self.assertEqual(self.get()['status'], 200, 'init') assert self.get()['status'] == 200, 'init'
self.conf({'http': {'idle_timeout': 2}}, 'settings') self.conf({'http': {'idle_timeout': 2}}, 'settings')
@@ -204,17 +201,15 @@ Connection: close
headers={'Host': 'localhost', 'Connection': 'close'}, sock=sock headers={'Host': 'localhost', 'Connection': 'close'}, sock=sock
) )
self.assertEqual(resp['status'], 408, 'status idle timeout') assert resp['status'] == 408, 'status idle timeout'
def test_settings_max_body_size(self): def test_settings_max_body_size(self):
self.load('empty') self.load('empty')
self.conf({'http': {'max_body_size': 5}}, 'settings') self.conf({'http': {'max_body_size': 5}}, 'settings')
self.assertEqual(self.post(body='01234')['status'], 200, 'status size') assert self.post(body='01234')['status'] == 200, 'status size'
self.assertEqual( assert self.post(body='012345')['status'] == 413, 'status size max'
self.post(body='012345')['status'], 413, 'status size max'
)
def test_settings_max_body_size_large(self): def test_settings_max_body_size_large(self):
self.load('mirror') self.load('mirror')
@@ -223,32 +218,26 @@ Connection: close
body = '0123456789abcdef' * 4 * 64 * 1024 body = '0123456789abcdef' * 4 * 64 * 1024
resp = self.post(body=body, read_buffer_size=1024 * 1024) resp = self.post(body=body, read_buffer_size=1024 * 1024)
self.assertEqual(resp['status'], 200, 'status size 4') assert resp['status'] == 200, 'status size 4'
self.assertEqual(resp['body'], body, 'status body 4') assert resp['body'] == body, 'status body 4'
body = '0123456789abcdef' * 8 * 64 * 1024 body = '0123456789abcdef' * 8 * 64 * 1024
resp = self.post(body=body, read_buffer_size=1024 * 1024) resp = self.post(body=body, read_buffer_size=1024 * 1024)
self.assertEqual(resp['status'], 200, 'status size 8') assert resp['status'] == 200, 'status size 8'
self.assertEqual(resp['body'], body, 'status body 8') assert resp['body'] == body, 'status body 8'
body = '0123456789abcdef' * 16 * 64 * 1024 body = '0123456789abcdef' * 16 * 64 * 1024
resp = self.post(body=body, read_buffer_size=1024 * 1024) resp = self.post(body=body, read_buffer_size=1024 * 1024)
self.assertEqual(resp['status'], 200, 'status size 16') assert resp['status'] == 200, 'status size 16'
self.assertEqual(resp['body'], body, 'status body 16') assert resp['body'] == body, 'status body 16'
body = '0123456789abcdef' * 32 * 64 * 1024 body = '0123456789abcdef' * 32 * 64 * 1024
resp = self.post(body=body, read_buffer_size=1024 * 1024) resp = self.post(body=body, read_buffer_size=1024 * 1024)
self.assertEqual(resp['status'], 200, 'status size 32') assert resp['status'] == 200, 'status size 32'
self.assertEqual(resp['body'], body, 'status body 32') assert resp['body'] == body, 'status body 32'
@unittest.skip('not yet') @pytest.mark.skip('not yet')
def test_settings_negative_value(self): def test_settings_negative_value(self):
self.assertIn( assert 'error' in self.conf(
'error', {'http': {'max_body_size': -1}}, 'settings'
self.conf({'http': {'max_body_size': -1}}, 'settings'), ), 'settings negative value'
'settings negative value',
)
if __name__ == '__main__':
TestSettings.main()

View File

@@ -1,20 +1,21 @@
import os import os
from unit.applications.proto import TestApplicationProto from unit.applications.proto import TestApplicationProto
from conftest import skip_alert
class TestStatic(TestApplicationProto): class TestStatic(TestApplicationProto):
prerequisites = {} prerequisites = {}
def setUp(self): def setup_method(self):
super().setUp() super().setup_method()
os.makedirs(self.testdir + '/assets/dir') os.makedirs(self.temp_dir + '/assets/dir')
with open(self.testdir + '/assets/index.html', 'w') as index: with open(self.temp_dir + '/assets/index.html', 'w') as index:
index.write('0123456789') index.write('0123456789')
os.makedirs(self.testdir + '/assets/403') os.makedirs(self.temp_dir + '/assets/403')
os.chmod(self.testdir + '/assets/403', 0o000) os.chmod(self.temp_dir + '/assets/403', 0o000)
self._load_conf( self._load_conf(
{ {
@@ -22,48 +23,46 @@ class TestStatic(TestApplicationProto):
"*:7080": {"pass": "routes"}, "*:7080": {"pass": "routes"},
"*:7081": {"pass": "routes"}, "*:7081": {"pass": "routes"},
}, },
"routes": [{"action": {"share": self.testdir + "/assets"}}], "routes": [{"action": {"share": self.temp_dir + "/assets"}}],
"applications": {}, "applications": {},
} }
) )
def tearDown(self): def teardown_method(self):
os.chmod(self.testdir + '/assets/403', 0o777) os.chmod(self.temp_dir + '/assets/403', 0o777)
super().tearDown() super().teardown_method()
def action_update(self, conf): def action_update(self, conf):
self.assertIn('success', self.conf(conf, 'routes/0/action')) assert 'success' in self.conf(conf, 'routes/0/action')
def test_fallback(self): def test_fallback(self):
self.action_update({"share": "/blah"}) self.action_update({"share": "/blah"})
self.assertEqual(self.get()['status'], 404, 'bad path no fallback') assert self.get()['status'] == 404, 'bad path no fallback'
self.action_update({"share": "/blah", "fallback": {"return": 200}}) self.action_update({"share": "/blah", "fallback": {"return": 200}})
resp = self.get() resp = self.get()
self.assertEqual(resp['status'], 200, 'bad path fallback status') assert resp['status'] == 200, 'bad path fallback status'
self.assertEqual(resp['body'], '', 'bad path fallback') assert resp['body'] == '', 'bad path fallback'
def test_fallback_valid_path(self): def test_fallback_valid_path(self):
self.action_update( self.action_update(
{"share": self.testdir + "/assets", "fallback": {"return": 200}} {"share": self.temp_dir + "/assets", "fallback": {"return": 200}}
) )
resp = self.get() resp = self.get()
self.assertEqual(resp['status'], 200, 'fallback status') assert resp['status'] == 200, 'fallback status'
self.assertEqual(resp['body'], '0123456789', 'fallback') assert resp['body'] == '0123456789', 'fallback'
resp = self.get(url='/403/') resp = self.get(url='/403/')
self.assertEqual(resp['status'], 200, 'fallback status 403') assert resp['status'] == 200, 'fallback status 403'
self.assertEqual(resp['body'], '', 'fallback 403') assert resp['body'] == '', 'fallback 403'
resp = self.post() resp = self.post()
self.assertEqual(resp['status'], 200, 'fallback status 405') assert resp['status'] == 200, 'fallback status 405'
self.assertEqual(resp['body'], '', 'fallback 405') assert resp['body'] == '', 'fallback 405'
self.assertEqual( assert self.get(url='/dir')['status'] == 301, 'fallback status 301'
self.get(url='/dir')['status'], 301, 'fallback status 301'
)
def test_fallback_nested(self): def test_fallback_nested(self):
self.action_update( self.action_update(
@@ -77,33 +76,31 @@ class TestStatic(TestApplicationProto):
) )
resp = self.get() resp = self.get()
self.assertEqual(resp['status'], 200, 'fallback nested status') assert resp['status'] == 200, 'fallback nested status'
self.assertEqual(resp['body'], '', 'fallback nested') assert resp['body'] == '', 'fallback nested'
def test_fallback_share(self): def test_fallback_share(self):
self.action_update( self.action_update(
{ {
"share": "/blah", "share": "/blah",
"fallback": {"share": self.testdir + "/assets"}, "fallback": {"share": self.temp_dir + "/assets"},
} }
) )
resp = self.get() resp = self.get()
self.assertEqual(resp['status'], 200, 'fallback share status') assert resp['status'] == 200, 'fallback share status'
self.assertEqual(resp['body'], '0123456789', 'fallback share') assert resp['body'] == '0123456789', 'fallback share'
resp = self.head() resp = self.head()
self.assertEqual(resp['status'], 200, 'fallback share status HEAD') assert resp['status'] == 200, 'fallback share status HEAD'
self.assertEqual(resp['body'], '', 'fallback share HEAD') assert resp['body'] == '', 'fallback share HEAD'
self.assertEqual( assert (
self.get(url='/dir')['status'], 301, 'fallback share status 301' self.get(url='/dir')['status'] == 301
) ), 'fallback share status 301'
def test_fallback_proxy(self): def test_fallback_proxy(self):
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
[ [
{ {
"match": {"destination": "*:7081"}, "match": {"destination": "*:7081"},
@@ -117,22 +114,18 @@ class TestStatic(TestApplicationProto):
}, },
], ],
'routes', 'routes',
), ), 'configure fallback proxy route'
'configure fallback proxy route',
)
resp = self.get() resp = self.get()
self.assertEqual(resp['status'], 200, 'fallback proxy status') assert resp['status'] == 200, 'fallback proxy status'
self.assertEqual(resp['body'], '', 'fallback proxy') assert resp['body'] == '', 'fallback proxy'
def test_fallback_proxy_loop(self): def test_fallback_proxy_loop(self):
self.skip_alerts.extend( skip_alert(
[
r'open.*/blah/index.html.*failed', r'open.*/blah/index.html.*failed',
r'accept.*failed', r'accept.*failed',
r'socket.*failed', r'socket.*failed',
r'new connections are not accepted', r'new connections are not accepted',
]
) )
self.action_update( self.action_update(
@@ -140,12 +133,12 @@ class TestStatic(TestApplicationProto):
) )
self.get(no_recv=True) self.get(no_recv=True)
self.assertIn('success', self.conf_delete('listeners/*:7081')) assert 'success' in self.conf_delete('listeners/*:7081')
self.get(read_timeout=1) self.get(read_timeout=1)
def test_fallback_invalid(self): def test_fallback_invalid(self):
def check_error(conf): def check_error(conf):
self.assertIn('error', self.conf(conf, 'routes/0/action')) assert 'error' in self.conf(conf, 'routes/0/action')
check_error({"share": "/blah", "fallback": {}}) check_error({"share": "/blah", "fallback": {}})
check_error({"share": "/blah", "fallback": ""}) check_error({"share": "/blah", "fallback": ""})
@@ -154,7 +147,3 @@ class TestStatic(TestApplicationProto):
{"proxy": "http://127.0.0.1:7081", "fallback": {"share": "/blah"}} {"proxy": "http://127.0.0.1:7081", "fallback": {"share": "/blah"}}
) )
check_error({"fallback": {"share": "/blah"}}) check_error({"fallback": {"share": "/blah"}})
if __name__ == '__main__':
TestStatic.main()

View File

@@ -1,21 +1,23 @@
import os import os
import pytest
import socket import socket
import unittest
from unit.applications.proto import TestApplicationProto from unit.applications.proto import TestApplicationProto
from conftest import waitforfiles
class TestStatic(TestApplicationProto): class TestStatic(TestApplicationProto):
prerequisites = {} prerequisites = {}
def setUp(self): def setup_method(self):
super().setUp() super().setup_method()
os.makedirs(self.testdir + '/assets/dir') os.makedirs(self.temp_dir + '/assets/dir')
with open(self.testdir + '/assets/index.html', 'w') as index, \ with open(self.temp_dir + '/assets/index.html', 'w') as index, open(
open(self.testdir + '/assets/README', 'w') as readme, \ self.temp_dir + '/assets/README', 'w'
open(self.testdir + '/assets/log.log', 'w') as log, \ ) as readme, open(self.temp_dir + '/assets/log.log', 'w') as log, open(
open(self.testdir + '/assets/dir/file', 'w') as file: self.temp_dir + '/assets/dir/file', 'w'
) as file:
index.write('0123456789') index.write('0123456789')
readme.write('readme') readme.write('readme')
log.write('[debug]') log.write('[debug]')
@@ -24,7 +26,7 @@ class TestStatic(TestApplicationProto):
self._load_conf( self._load_conf(
{ {
"listeners": {"*:7080": {"pass": "routes"}}, "listeners": {"*:7080": {"pass": "routes"}},
"routes": [{"action": {"share": self.testdir + "/assets"}}], "routes": [{"action": {"share": self.temp_dir + "/assets"}}],
"settings": { "settings": {
"http": { "http": {
"static": { "static": {
@@ -36,123 +38,95 @@ class TestStatic(TestApplicationProto):
) )
def test_static_index(self): def test_static_index(self):
self.assertEqual( assert self.get(url='/index.html')['body'] == '0123456789', 'index'
self.get(url='/index.html')['body'], '0123456789', 'index' assert self.get(url='/')['body'] == '0123456789', 'index 2'
) assert self.get(url='//')['body'] == '0123456789', 'index 3'
self.assertEqual(self.get(url='/')['body'], '0123456789', 'index 2') assert self.get(url='/.')['body'] == '0123456789', 'index 4'
self.assertEqual(self.get(url='//')['body'], '0123456789', 'index 3') assert self.get(url='/./')['body'] == '0123456789', 'index 5'
self.assertEqual(self.get(url='/.')['body'], '0123456789', 'index 4') assert self.get(url='/?blah')['body'] == '0123456789', 'index vars'
self.assertEqual(self.get(url='/./')['body'], '0123456789', 'index 5') assert self.get(url='/#blah')['body'] == '0123456789', 'index anchor'
self.assertEqual( assert self.get(url='/dir/')['status'] == 404, 'index not found'
self.get(url='/?blah')['body'], '0123456789', 'index vars'
)
self.assertEqual(
self.get(url='/#blah')['body'], '0123456789', 'index anchor'
)
self.assertEqual(
self.get(url='/dir/')['status'], 404, 'index not found'
)
resp = self.get(url='/index.html/') resp = self.get(url='/index.html/')
self.assertEqual(resp['status'], 404, 'index not found 2 status') assert resp['status'] == 404, 'index not found 2 status'
self.assertEqual( assert (
resp['headers']['Content-Type'], resp['headers']['Content-Type'] == 'text/html'
'text/html', ), 'index not found 2 Content-Type'
'index not found 2 Content-Type',
)
def test_static_large_file(self): def test_static_large_file(self):
file_size = 32 * 1024 * 1024 file_size = 32 * 1024 * 1024
with open(self.testdir + '/assets/large', 'wb') as f: with open(self.temp_dir + '/assets/large', 'wb') as f:
f.seek(file_size - 1) f.seek(file_size - 1)
f.write(b'\0') f.write(b'\0')
self.assertEqual( assert (
len( len(self.get(url='/large', read_buffer_size=1024 * 1024)['body'])
self.get(url='/large', read_buffer_size=1024 * 1024)['body'] == file_size
), ), 'large file'
file_size,
'large file',
)
def test_static_etag(self): def test_static_etag(self):
etag = self.get(url='/')['headers']['ETag'] etag = self.get(url='/')['headers']['ETag']
etag_2 = self.get(url='/README')['headers']['ETag'] etag_2 = self.get(url='/README')['headers']['ETag']
self.assertNotEqual(etag, etag_2, 'different ETag') assert etag != etag_2, 'different ETag'
self.assertEqual( assert etag == self.get(url='/')['headers']['ETag'], 'same ETag'
etag, self.get(url='/')['headers']['ETag'], 'same ETag'
)
with open(self.testdir + '/assets/index.html', 'w') as f: with open(self.temp_dir + '/assets/index.html', 'w') as f:
f.write('blah') f.write('blah')
self.assertNotEqual( assert etag != self.get(url='/')['headers']['ETag'], 'new ETag'
etag, self.get(url='/')['headers']['ETag'], 'new ETag'
)
def test_static_redirect(self): def test_static_redirect(self):
resp = self.get(url='/dir') resp = self.get(url='/dir')
self.assertEqual(resp['status'], 301, 'redirect status') assert resp['status'] == 301, 'redirect status'
self.assertEqual( assert resp['headers']['Location'] == '/dir/', 'redirect Location'
resp['headers']['Location'], '/dir/', 'redirect Location' assert 'Content-Type' not in resp['headers'], 'redirect Content-Type'
)
self.assertNotIn(
'Content-Type', resp['headers'], 'redirect Content-Type'
)
def test_static_space_in_name(self): def test_static_space_in_name(self):
os.rename( os.rename(
self.testdir + '/assets/dir/file', self.temp_dir + '/assets/dir/file',
self.testdir + '/assets/dir/fi le', self.temp_dir + '/assets/dir/fi le',
)
self.waitforfiles(self.testdir + '/assets/dir/fi le')
self.assertEqual(
self.get(url='/dir/fi le')['body'], 'blah', 'file name'
) )
assert waitforfiles(self.temp_dir + '/assets/dir/fi le')
assert self.get(url='/dir/fi le')['body'] == 'blah', 'file name'
os.rename(self.testdir + '/assets/dir', self.testdir + '/assets/di r') os.rename(self.temp_dir + '/assets/dir', self.temp_dir + '/assets/di r')
self.waitforfiles(self.testdir + '/assets/di r/fi le') assert waitforfiles(self.temp_dir + '/assets/di r/fi le')
self.assertEqual( assert self.get(url='/di r/fi le')['body'] == 'blah', 'dir name'
self.get(url='/di r/fi le')['body'], 'blah', 'dir name'
)
os.rename( os.rename(
self.testdir + '/assets/di r', self.testdir + '/assets/ di r ' self.temp_dir + '/assets/di r', self.temp_dir + '/assets/ di r '
)
self.waitforfiles(self.testdir + '/assets/ di r /fi le')
self.assertEqual(
self.get(url='/ di r /fi le')['body'], 'blah', 'dir name enclosing'
) )
assert waitforfiles(self.temp_dir + '/assets/ di r /fi le')
assert (
self.get(url='/ di r /fi le')['body'] == 'blah'
), 'dir name enclosing'
self.assertEqual( assert (
self.get(url='/%20di%20r%20/fi le')['body'], 'blah', 'dir encoded' self.get(url='/%20di%20r%20/fi le')['body'] == 'blah'
) ), 'dir encoded'
self.assertEqual( assert (
self.get(url='/ di r %2Ffi le')['body'], 'blah', 'slash encoded' self.get(url='/ di r %2Ffi le')['body'] == 'blah'
) ), 'slash encoded'
self.assertEqual( assert (
self.get(url='/ di r /fi%20le')['body'], 'blah', 'file encoded' self.get(url='/ di r /fi%20le')['body'] == 'blah'
) ), 'file encoded'
self.assertEqual( assert (
self.get(url='/%20di%20r%20%2Ffi%20le')['body'], 'blah', 'encoded' self.get(url='/%20di%20r%20%2Ffi%20le')['body'] == 'blah'
) ), 'encoded'
self.assertEqual( assert (
self.get(url='/%20%64%69%20%72%20%2F%66%69%20%6C%65')['body'], self.get(url='/%20%64%69%20%72%20%2F%66%69%20%6C%65')['body']
'blah', == 'blah'
'encoded 2', ), 'encoded 2'
)
os.rename( os.rename(
self.testdir + '/assets/ di r /fi le', self.temp_dir + '/assets/ di r /fi le',
self.testdir + '/assets/ di r / fi le ', self.temp_dir + '/assets/ di r / fi le ',
)
self.waitforfiles(self.testdir + '/assets/ di r / fi le ')
self.assertEqual(
self.get(url='/%20di%20r%20/%20fi%20le%20')['body'],
'blah',
'file name enclosing',
) )
assert waitforfiles(self.temp_dir + '/assets/ di r / fi le ')
assert (
self.get(url='/%20di%20r%20/%20fi%20le%20')['body'] == 'blah'
), 'file name enclosing'
try: try:
print('файл') print('файл')
@@ -163,254 +137,182 @@ class TestStatic(TestApplicationProto):
if utf8: if utf8:
os.rename( os.rename(
self.testdir + '/assets/ di r / fi le ', self.temp_dir + '/assets/ di r / fi le ',
self.testdir + '/assets/ di r /фа йл', self.temp_dir + '/assets/ di r /фа йл',
)
self.waitforfiles(self.testdir + '/assets/ di r /фа йл')
self.assertEqual(
self.get(url='/ di r /фа йл')['body'], 'blah', 'file name 2'
) )
assert waitforfiles(self.temp_dir + '/assets/ di r /фа йл')
assert (
self.get(url='/ di r /фа йл')['body'] == 'blah'
), 'file name 2'
os.rename( os.rename(
self.testdir + '/assets/ di r ', self.temp_dir + '/assets/ di r ',
self.testdir + '/assets/ди ректория', self.temp_dir + '/assets/ди ректория',
)
self.waitforfiles(self.testdir + '/assets/ди ректория/фа йл')
self.assertEqual(
self.get(url='/ди ректория/фа йл')['body'], 'blah', 'dir name 2'
) )
assert waitforfiles(self.temp_dir + '/assets/ди ректория/фа йл')
assert (
self.get(url='/ди ректория/фа йл')['body'] == 'blah'
), 'dir name 2'
def test_static_unix_socket(self): def test_static_unix_socket(self):
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.bind(self.testdir + '/assets/unix_socket') sock.bind(self.temp_dir + '/assets/unix_socket')
self.assertEqual(self.get(url='/unix_socket')['status'], 404, 'socket') assert self.get(url='/unix_socket')['status'] == 404, 'socket'
sock.close() sock.close()
def test_static_unix_fifo(self): def test_static_unix_fifo(self):
os.mkfifo(self.testdir + '/assets/fifo') os.mkfifo(self.temp_dir + '/assets/fifo')
self.assertEqual(self.get(url='/fifo')['status'], 404, 'fifo') assert self.get(url='/fifo')['status'] == 404, 'fifo'
def test_static_symlink(self): def test_static_symlink(self):
os.symlink(self.testdir + '/assets/dir', self.testdir + '/assets/link') os.symlink(self.temp_dir + '/assets/dir', self.temp_dir + '/assets/link')
self.assertEqual(self.get(url='/dir')['status'], 301, 'dir') assert self.get(url='/dir')['status'] == 301, 'dir'
self.assertEqual(self.get(url='/dir/file')['status'], 200, 'file') assert self.get(url='/dir/file')['status'] == 200, 'file'
self.assertEqual(self.get(url='/link')['status'], 301, 'symlink dir') assert self.get(url='/link')['status'] == 301, 'symlink dir'
self.assertEqual( assert self.get(url='/link/file')['status'] == 200, 'symlink file'
self.get(url='/link/file')['status'], 200, 'symlink file'
)
def test_static_method(self): def test_static_method(self):
resp = self.head() resp = self.head()
self.assertEqual(resp['status'], 200, 'HEAD status') assert resp['status'] == 200, 'HEAD status'
self.assertEqual(resp['body'], '', 'HEAD empty body') assert resp['body'] == '', 'HEAD empty body'
self.assertEqual(self.delete()['status'], 405, 'DELETE') assert self.delete()['status'] == 405, 'DELETE'
self.assertEqual(self.post()['status'], 405, 'POST') assert self.post()['status'] == 405, 'POST'
self.assertEqual(self.put()['status'], 405, 'PUT') assert self.put()['status'] == 405, 'PUT'
def test_static_path(self): def test_static_path(self):
self.assertEqual( assert self.get(url='/dir/../dir/file')['status'] == 200, 'relative'
self.get(url='/dir/../dir/file')['status'], 200, 'relative'
)
self.assertEqual(self.get(url='./')['status'], 400, 'path invalid') assert self.get(url='./')['status'] == 400, 'path invalid'
self.assertEqual(self.get(url='../')['status'], 400, 'path invalid 2') assert self.get(url='../')['status'] == 400, 'path invalid 2'
self.assertEqual(self.get(url='/..')['status'], 400, 'path invalid 3') assert self.get(url='/..')['status'] == 400, 'path invalid 3'
self.assertEqual( assert self.get(url='../assets/')['status'] == 400, 'path invalid 4'
self.get(url='../assets/')['status'], 400, 'path invalid 4' assert self.get(url='/../assets/')['status'] == 400, 'path invalid 5'
)
self.assertEqual(
self.get(url='/../assets/')['status'], 400, 'path invalid 5'
)
def test_static_two_clients(self): def test_static_two_clients(self):
_, sock = self.get(url='/', start=True, no_recv=True) _, sock = self.get(url='/', start=True, no_recv=True)
_, sock2 = self.get(url='/', start=True, no_recv=True) _, sock2 = self.get(url='/', start=True, no_recv=True)
self.assertEqual(sock.recv(1), b'H', 'client 1') assert sock.recv(1) == b'H', 'client 1'
self.assertEqual(sock2.recv(1), b'H', 'client 2') assert sock2.recv(1) == b'H', 'client 2'
self.assertEqual(sock.recv(1), b'T', 'client 1 again') assert sock.recv(1) == b'T', 'client 1 again'
self.assertEqual(sock2.recv(1), b'T', 'client 2 again') assert sock2.recv(1) == b'T', 'client 2 again'
sock.close() sock.close()
sock2.close() sock2.close()
def test_static_mime_types(self): def test_static_mime_types(self):
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{ {
"text/x-code/x-blah/x-blah": "readme", "text/x-code/x-blah/x-blah": "readme",
"text/plain": [".html", ".log", "file"], "text/plain": [".html", ".log", "file"],
}, },
'settings/http/static/mime_types', 'settings/http/static/mime_types',
), ), 'configure mime_types'
'configure mime_types',
)
self.assertEqual( assert (
self.get(url='/README')['headers']['Content-Type'], self.get(url='/README')['headers']['Content-Type']
'text/x-code/x-blah/x-blah', == 'text/x-code/x-blah/x-blah'
'mime_types string case insensitive', ), 'mime_types string case insensitive'
) assert (
self.assertEqual( self.get(url='/index.html')['headers']['Content-Type']
self.get(url='/index.html')['headers']['Content-Type'], == 'text/plain'
'text/plain', ), 'mime_types html'
'mime_types html', assert (
) self.get(url='/')['headers']['Content-Type'] == 'text/plain'
self.assertEqual( ), 'mime_types index default'
self.get(url='/')['headers']['Content-Type'], assert (
'text/plain', self.get(url='/dir/file')['headers']['Content-Type']
'mime_types index default', == 'text/plain'
) ), 'mime_types file in dir'
self.assertEqual(
self.get(url='/dir/file')['headers']['Content-Type'],
'text/plain',
'mime_types file in dir',
)
def test_static_mime_types_partial_match(self): def test_static_mime_types_partial_match(self):
self.assertIn( assert 'success' in self.conf(
'success', {"text/x-blah": ["ile", "fil", "f", "e", ".file"],},
self.conf(
{
"text/x-blah": ["ile", "fil", "f", "e", ".file"],
},
'settings/http/static/mime_types', 'settings/http/static/mime_types',
), ), 'configure mime_types'
'configure mime_types', assert 'Content-Type' not in self.get(url='/dir/file'), 'partial match'
)
self.assertNotIn(
'Content-Type', self.get(url='/dir/file'), 'partial match'
)
def test_static_mime_types_reconfigure(self): def test_static_mime_types_reconfigure(self):
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{ {
"text/x-code": "readme", "text/x-code": "readme",
"text/plain": [".html", ".log", "file"], "text/plain": [".html", ".log", "file"],
}, },
'settings/http/static/mime_types', 'settings/http/static/mime_types',
), ), 'configure mime_types'
'configure mime_types',
)
self.assertEqual( assert self.conf_get('settings/http/static/mime_types') == {
self.conf_get('settings/http/static/mime_types'), 'text/x-code': 'readme',
{'text/x-code': 'readme', 'text/plain': ['.html', '.log', 'file']}, 'text/plain': ['.html', '.log', 'file'],
'mime_types get', }, 'mime_types get'
) assert (
self.assertEqual( self.conf_get('settings/http/static/mime_types/text%2Fx-code')
self.conf_get('settings/http/static/mime_types/text%2Fx-code'), == 'readme'
'readme', ), 'mime_types get string'
'mime_types get string', assert self.conf_get(
) 'settings/http/static/mime_types/text%2Fplain'
self.assertEqual( ) == ['.html', '.log', 'file'], 'mime_types get array'
self.conf_get('settings/http/static/mime_types/text%2Fplain'), assert (
['.html', '.log', 'file'], self.conf_get('settings/http/static/mime_types/text%2Fplain/1')
'mime_types get array', == '.log'
) ), 'mime_types get array element'
self.assertEqual(
self.conf_get('settings/http/static/mime_types/text%2Fplain/1'),
'.log',
'mime_types get array element',
)
self.assertIn( assert 'success' in self.conf_delete(
'success', 'settings/http/static/mime_types/text%2Fplain/2'
self.conf_delete('settings/http/static/mime_types/text%2Fplain/2'), ), 'mime_types remove array element'
'mime_types remove array element', assert (
) 'Content-Type' not in self.get(url='/dir/file')['headers']
self.assertNotIn( ), 'mime_types removed'
'Content-Type',
self.get(url='/dir/file')['headers'],
'mime_types removed',
)
self.assertIn( assert 'success' in self.conf_post(
'success',
self.conf_post(
'"file"', 'settings/http/static/mime_types/text%2Fplain' '"file"', 'settings/http/static/mime_types/text%2Fplain'
), ), 'mime_types add array element'
'mime_types add array element', assert (
) self.get(url='/dir/file')['headers']['Content-Type']
self.assertEqual( == 'text/plain'
self.get(url='/dir/file')['headers']['Content-Type'], ), 'mime_types reverted'
'text/plain',
'mime_types reverted',
)
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
'"file"', 'settings/http/static/mime_types/text%2Fplain' '"file"', 'settings/http/static/mime_types/text%2Fplain'
), ), 'configure mime_types update'
'configure mime_types update', assert (
) self.get(url='/dir/file')['headers']['Content-Type']
self.assertEqual( == 'text/plain'
self.get(url='/dir/file')['headers']['Content-Type'], ), 'mime_types updated'
'text/plain', assert (
'mime_types updated', 'Content-Type' not in self.get(url='/log.log')['headers']
) ), 'mime_types updated 2'
self.assertNotIn(
'Content-Type',
self.get(url='/log.log')['headers'],
'mime_types updated 2',
)
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
'".log"', 'settings/http/static/mime_types/text%2Fblahblahblah' '".log"', 'settings/http/static/mime_types/text%2Fblahblahblah'
), ), 'configure mime_types create'
'configure mime_types create', assert (
) self.get(url='/log.log')['headers']['Content-Type']
self.assertEqual( == 'text/blahblahblah'
self.get(url='/log.log')['headers']['Content-Type'], ), 'mime_types create'
'text/blahblahblah',
'mime_types create',
)
def test_static_mime_types_correct(self): def test_static_mime_types_correct(self):
self.assertIn( assert 'error' in self.conf(
'error',
self.conf(
{"text/x-code": "readme", "text/plain": "readme"}, {"text/x-code": "readme", "text/plain": "readme"},
'settings/http/static/mime_types', 'settings/http/static/mime_types',
), ), 'mime_types same extensions'
'mime_types same extensions', assert 'error' in self.conf(
)
self.assertIn(
'error',
self.conf(
{"text/x-code": [".h", ".c"], "text/plain": ".c"}, {"text/x-code": [".h", ".c"], "text/plain": ".c"},
'settings/http/static/mime_types', 'settings/http/static/mime_types',
), ), 'mime_types same extensions array'
'mime_types same extensions array', assert 'error' in self.conf(
) {"text/x-code": [".h", ".c", "readme"], "text/plain": "README",},
self.assertIn(
'error',
self.conf(
{
"text/x-code": [".h", ".c", "readme"],
"text/plain": "README",
},
'settings/http/static/mime_types', 'settings/http/static/mime_types',
), ), 'mime_types same extensions case insensitive'
'mime_types same extensions case insensitive',
)
@unittest.skip('not yet') @pytest.mark.skip('not yet')
def test_static_mime_types_invalid(self): def test_static_mime_types_invalid(self):
self.assertIn( assert 'error' in self.http(
'error',
self.http(
b"""PUT /config/settings/http/static/mime_types/%0%00% HTTP/1.1\r b"""PUT /config/settings/http/static/mime_types/%0%00% HTTP/1.1\r
Host: localhost\r Host: localhost\r
Connection: close\r Connection: close\r
@@ -420,10 +322,5 @@ Content-Length: 6\r
raw_resp=True, raw_resp=True,
raw=True, raw=True,
sock_type='unix', sock_type='unix',
addr=self.testdir + '/control.unit.sock', addr=self.temp_dir + '/control.unit.sock',
), ), 'mime_types invalid'
'mime_types invalid',
)
if __name__ == '__main__':
TestStatic.main()

View File

@@ -1,17 +1,18 @@
import io import io
import pytest
import re import re
import ssl import ssl
import subprocess import subprocess
import unittest
from unit.applications.tls import TestApplicationTLS from unit.applications.tls import TestApplicationTLS
from conftest import skip_alert
class TestTLS(TestApplicationTLS): class TestTLS(TestApplicationTLS):
prerequisites = {'modules': {'python': 'any', 'openssl': 'any'}} prerequisites = {'modules': {'python': 'any', 'openssl': 'any'}}
def findall(self, pattern): def findall(self, pattern):
with open(self.testdir + '/unit.log', 'r', errors='ignore') as f: with open(self.temp_dir + '/unit.log', 'r', errors='ignore') as f:
return re.findall(pattern, f.read()) return re.findall(pattern, f.read())
def openssl_date_to_sec_epoch(self, date): def openssl_date_to_sec_epoch(self, date):
@@ -38,7 +39,7 @@ class TestTLS(TestApplicationTLS):
self.add_tls() self.add_tls()
self.assertEqual(self.get_ssl()['status'], 200, 'add listener option') assert self.get_ssl()['status'] == 200, 'add listener option'
def test_tls_listener_option_remove(self): def test_tls_listener_option_remove(self):
self.load('empty') self.load('empty')
@@ -51,18 +52,16 @@ class TestTLS(TestApplicationTLS):
self.remove_tls() self.remove_tls()
self.assertEqual(self.get()['status'], 200, 'remove listener option') assert self.get()['status'] == 200, 'remove listener option'
def test_tls_certificate_remove(self): def test_tls_certificate_remove(self):
self.load('empty') self.load('empty')
self.certificate() self.certificate()
self.assertIn( assert 'success' in self.conf_delete(
'success', '/certificates/default'
self.conf_delete('/certificates/default'), ), 'remove certificate'
'remove certificate',
)
def test_tls_certificate_remove_used(self): def test_tls_certificate_remove_used(self):
self.load('empty') self.load('empty')
@@ -71,11 +70,9 @@ class TestTLS(TestApplicationTLS):
self.add_tls() self.add_tls()
self.assertIn( assert 'error' in self.conf_delete(
'error', '/certificates/default'
self.conf_delete('/certificates/default'), ), 'remove certificate'
'remove certificate',
)
def test_tls_certificate_remove_nonexisting(self): def test_tls_certificate_remove_nonexisting(self):
self.load('empty') self.load('empty')
@@ -84,13 +81,11 @@ class TestTLS(TestApplicationTLS):
self.add_tls() self.add_tls()
self.assertIn( assert 'error' in self.conf_delete(
'error', '/certificates/blah'
self.conf_delete('/certificates/blah'), ), 'remove nonexistings certificate'
'remove nonexistings certificate',
)
@unittest.skip('not yet') @pytest.mark.skip('not yet')
def test_tls_certificate_update(self): def test_tls_certificate_update(self):
self.load('empty') self.load('empty')
@@ -102,20 +97,18 @@ class TestTLS(TestApplicationTLS):
self.certificate() self.certificate()
self.assertNotEqual( assert cert_old != self.get_server_certificate(), 'update certificate'
cert_old, self.get_server_certificate(), 'update certificate'
)
@unittest.skip('not yet') @pytest.mark.skip('not yet')
def test_tls_certificate_key_incorrect(self): def test_tls_certificate_key_incorrect(self):
self.load('empty') self.load('empty')
self.certificate('first', False) self.certificate('first', False)
self.certificate('second', False) self.certificate('second', False)
self.assertIn( assert 'error' in self.certificate_load(
'error', self.certificate_load('first', 'second'), 'key incorrect' 'first', 'second'
) ), 'key incorrect'
def test_tls_certificate_change(self): def test_tls_certificate_change(self):
self.load('empty') self.load('empty')
@@ -129,20 +122,16 @@ class TestTLS(TestApplicationTLS):
self.add_tls(cert='new') self.add_tls(cert='new')
self.assertNotEqual( assert cert_old != self.get_server_certificate(), 'change certificate'
cert_old, self.get_server_certificate(), 'change certificate'
)
def test_tls_certificate_key_rsa(self): def test_tls_certificate_key_rsa(self):
self.load('empty') self.load('empty')
self.certificate() self.certificate()
self.assertEqual( assert (
self.conf_get('/certificates/default/key'), self.conf_get('/certificates/default/key') == 'RSA (2048 bits)'
'RSA (2048 bits)', ), 'certificate key rsa'
'certificate key rsa',
)
def test_tls_certificate_key_ec(self): def test_tls_certificate_key_ec(self):
self.load('empty') self.load('empty')
@@ -155,8 +144,10 @@ class TestTLS(TestApplicationTLS):
'ecparam', 'ecparam',
'-noout', '-noout',
'-genkey', '-genkey',
'-out', self.testdir + '/ec.key', '-out',
'-name', 'prime256v1', self.temp_dir + '/ec.key',
'-name',
'prime256v1',
], ],
stderr=subprocess.STDOUT, stderr=subprocess.STDOUT,
) )
@@ -167,19 +158,23 @@ class TestTLS(TestApplicationTLS):
'req', 'req',
'-x509', '-x509',
'-new', '-new',
'-subj', '/CN=ec/', '-subj',
'-config', self.testdir + '/openssl.conf', '/CN=ec/',
'-key', self.testdir + '/ec.key', '-config',
'-out', self.testdir + '/ec.crt', self.temp_dir + '/openssl.conf',
'-key',
self.temp_dir + '/ec.key',
'-out',
self.temp_dir + '/ec.crt',
], ],
stderr=subprocess.STDOUT, stderr=subprocess.STDOUT,
) )
self.certificate_load('ec') self.certificate_load('ec')
self.assertEqual( assert (
self.conf_get('/certificates/ec/key'), 'ECDH', 'certificate key ec' self.conf_get('/certificates/ec/key') == 'ECDH'
) ), 'certificate key ec'
def test_tls_certificate_chain_options(self): def test_tls_certificate_chain_options(self):
self.load('empty') self.load('empty')
@@ -188,35 +183,29 @@ class TestTLS(TestApplicationTLS):
chain = self.conf_get('/certificates/default/chain') chain = self.conf_get('/certificates/default/chain')
self.assertEqual(len(chain), 1, 'certificate chain length') assert len(chain) == 1, 'certificate chain length'
cert = chain[0] cert = chain[0]
self.assertEqual( assert (
cert['subject']['common_name'], cert['subject']['common_name'] == 'default'
'default', ), 'certificate subject common name'
'certificate subject common name', assert (
) cert['issuer']['common_name'] == 'default'
self.assertEqual( ), 'certificate issuer common name'
cert['issuer']['common_name'],
'default',
'certificate issuer common name',
)
self.assertLess( assert (
abs( abs(
self.sec_epoch() self.sec_epoch()
- self.openssl_date_to_sec_epoch(cert['validity']['since']) - self.openssl_date_to_sec_epoch(cert['validity']['since'])
),
5,
'certificate validity since',
) )
self.assertEqual( < 5
), 'certificate validity since'
assert (
self.openssl_date_to_sec_epoch(cert['validity']['until']) self.openssl_date_to_sec_epoch(cert['validity']['until'])
- self.openssl_date_to_sec_epoch(cert['validity']['since']), - self.openssl_date_to_sec_epoch(cert['validity']['since'])
2592000, == 2592000
'certificate validity until', ), 'certificate validity until'
)
def test_tls_certificate_chain(self): def test_tls_certificate_chain(self):
self.load('empty') self.load('empty')
@@ -228,10 +217,14 @@ class TestTLS(TestApplicationTLS):
'openssl', 'openssl',
'req', 'req',
'-new', '-new',
'-subj', '/CN=int/', '-subj',
'-config', self.testdir + '/openssl.conf', '/CN=int/',
'-out', self.testdir + '/int.csr', '-config',
'-keyout', self.testdir + '/int.key', self.temp_dir + '/openssl.conf',
'-out',
self.temp_dir + '/int.csr',
'-keyout',
self.temp_dir + '/int.key',
], ],
stderr=subprocess.STDOUT, stderr=subprocess.STDOUT,
) )
@@ -241,15 +234,19 @@ class TestTLS(TestApplicationTLS):
'openssl', 'openssl',
'req', 'req',
'-new', '-new',
'-subj', '/CN=end/', '-subj',
'-config', self.testdir + '/openssl.conf', '/CN=end/',
'-out', self.testdir + '/end.csr', '-config',
'-keyout', self.testdir + '/end.key', self.temp_dir + '/openssl.conf',
'-out',
self.temp_dir + '/end.csr',
'-keyout',
self.temp_dir + '/end.key',
], ],
stderr=subprocess.STDOUT, stderr=subprocess.STDOUT,
) )
with open(self.testdir + '/ca.conf', 'w') as f: with open(self.temp_dir + '/ca.conf', 'w') as f:
f.write( f.write(
"""[ ca ] """[ ca ]
default_ca = myca default_ca = myca
@@ -269,16 +266,16 @@ commonName = supplied
[ myca_extensions ] [ myca_extensions ]
basicConstraints = critical,CA:TRUE""" basicConstraints = critical,CA:TRUE"""
% { % {
'dir': self.testdir, 'dir': self.temp_dir,
'database': self.testdir + '/certindex', 'database': self.temp_dir + '/certindex',
'certserial': self.testdir + '/certserial', 'certserial': self.temp_dir + '/certserial',
} }
) )
with open(self.testdir + '/certserial', 'w') as f: with open(self.temp_dir + '/certserial', 'w') as f:
f.write('1000') f.write('1000')
with open(self.testdir + '/certindex', 'w') as f: with open(self.temp_dir + '/certindex', 'w') as f:
f.write('') f.write('')
subprocess.call( subprocess.call(
@@ -286,12 +283,18 @@ basicConstraints = critical,CA:TRUE"""
'openssl', 'openssl',
'ca', 'ca',
'-batch', '-batch',
'-subj', '/CN=int/', '-subj',
'-config', self.testdir + '/ca.conf', '/CN=int/',
'-keyfile', self.testdir + '/root.key', '-config',
'-cert', self.testdir + '/root.crt', self.temp_dir + '/ca.conf',
'-in', self.testdir + '/int.csr', '-keyfile',
'-out', self.testdir + '/int.crt', self.temp_dir + '/root.key',
'-cert',
self.temp_dir + '/root.crt',
'-in',
self.temp_dir + '/int.csr',
'-out',
self.temp_dir + '/int.crt',
], ],
stderr=subprocess.STDOUT, stderr=subprocess.STDOUT,
) )
@@ -301,50 +304,50 @@ basicConstraints = critical,CA:TRUE"""
'openssl', 'openssl',
'ca', 'ca',
'-batch', '-batch',
'-subj', '/CN=end/', '-subj',
'-config', self.testdir + '/ca.conf', '/CN=end/',
'-keyfile', self.testdir + '/int.key', '-config',
'-cert', self.testdir + '/int.crt', self.temp_dir + '/ca.conf',
'-in', self.testdir + '/end.csr', '-keyfile',
'-out', self.testdir + '/end.crt', self.temp_dir + '/int.key',
'-cert',
self.temp_dir + '/int.crt',
'-in',
self.temp_dir + '/end.csr',
'-out',
self.temp_dir + '/end.crt',
], ],
stderr=subprocess.STDOUT, stderr=subprocess.STDOUT,
) )
crt_path = self.testdir + '/end-int.crt' crt_path = self.temp_dir + '/end-int.crt'
end_path = self.testdir + '/end.crt' end_path = self.temp_dir + '/end.crt'
int_path = self.testdir + '/int.crt' int_path = self.temp_dir + '/int.crt'
with open(crt_path, 'wb') as crt, \ with open(crt_path, 'wb') as crt, open(end_path, 'rb') as end, open(
open(end_path, 'rb') as end, \ int_path, 'rb'
open(int_path, 'rb') as int: ) as int:
crt.write(end.read() + int.read()) crt.write(end.read() + int.read())
self.context = ssl.create_default_context() self.context = ssl.create_default_context()
self.context.check_hostname = False self.context.check_hostname = False
self.context.verify_mode = ssl.CERT_REQUIRED self.context.verify_mode = ssl.CERT_REQUIRED
self.context.load_verify_locations(self.testdir + '/root.crt') self.context.load_verify_locations(self.temp_dir + '/root.crt')
# incomplete chain # incomplete chain
self.assertIn( assert 'success' in self.certificate_load(
'success', 'end', 'end'
self.certificate_load('end', 'end'), ), 'certificate chain end upload'
'certificate chain end upload',
)
chain = self.conf_get('/certificates/end/chain') chain = self.conf_get('/certificates/end/chain')
self.assertEqual(len(chain), 1, 'certificate chain end length') assert len(chain) == 1, 'certificate chain end length'
self.assertEqual( assert (
chain[0]['subject']['common_name'], chain[0]['subject']['common_name'] == 'end'
'end', ), 'certificate chain end subject common name'
'certificate chain end subject common name', assert (
) chain[0]['issuer']['common_name'] == 'int'
self.assertEqual( ), 'certificate chain end issuer common name'
chain[0]['issuer']['common_name'],
'int',
'certificate chain end issuer common name',
)
self.add_tls(cert='end') self.add_tls(cert='end')
@@ -353,79 +356,61 @@ basicConstraints = critical,CA:TRUE"""
except ssl.SSLError: except ssl.SSLError:
resp = None resp = None
self.assertEqual(resp, None, 'certificate chain incomplete chain') assert resp == None, 'certificate chain incomplete chain'
# intermediate # intermediate
self.assertIn( assert 'success' in self.certificate_load(
'success', 'int', 'int'
self.certificate_load('int', 'int'), ), 'certificate chain int upload'
'certificate chain int upload',
)
chain = self.conf_get('/certificates/int/chain') chain = self.conf_get('/certificates/int/chain')
self.assertEqual(len(chain), 1, 'certificate chain int length') assert len(chain) == 1, 'certificate chain int length'
self.assertEqual( assert (
chain[0]['subject']['common_name'], chain[0]['subject']['common_name'] == 'int'
'int', ), 'certificate chain int subject common name'
'certificate chain int subject common name', assert (
) chain[0]['issuer']['common_name'] == 'root'
self.assertEqual( ), 'certificate chain int issuer common name'
chain[0]['issuer']['common_name'],
'root',
'certificate chain int issuer common name',
)
self.add_tls(cert='int') self.add_tls(cert='int')
self.assertEqual( assert (
self.get_ssl()['status'], 200, 'certificate chain intermediate' self.get_ssl()['status'] == 200
) ), 'certificate chain intermediate'
# intermediate server # intermediate server
self.assertIn( assert 'success' in self.certificate_load(
'success', 'end-int', 'end'
self.certificate_load('end-int', 'end'), ), 'certificate chain end-int upload'
'certificate chain end-int upload',
)
chain = self.conf_get('/certificates/end-int/chain') chain = self.conf_get('/certificates/end-int/chain')
self.assertEqual(len(chain), 2, 'certificate chain end-int length') assert len(chain) == 2, 'certificate chain end-int length'
self.assertEqual( assert (
chain[0]['subject']['common_name'], chain[0]['subject']['common_name'] == 'end'
'end', ), 'certificate chain end-int int subject common name'
'certificate chain end-int int subject common name', assert (
) chain[0]['issuer']['common_name'] == 'int'
self.assertEqual( ), 'certificate chain end-int int issuer common name'
chain[0]['issuer']['common_name'], assert (
'int', chain[1]['subject']['common_name'] == 'int'
'certificate chain end-int int issuer common name', ), 'certificate chain end-int end subject common name'
) assert (
self.assertEqual( chain[1]['issuer']['common_name'] == 'root'
chain[1]['subject']['common_name'], ), 'certificate chain end-int end issuer common name'
'int',
'certificate chain end-int end subject common name',
)
self.assertEqual(
chain[1]['issuer']['common_name'],
'root',
'certificate chain end-int end issuer common name',
)
self.add_tls(cert='end-int') self.add_tls(cert='end-int')
self.assertEqual( assert (
self.get_ssl()['status'], self.get_ssl()['status'] == 200
200, ), 'certificate chain intermediate server'
'certificate chain intermediate server',
)
@unittest.skip('not yet') @pytest.mark.skip('not yet')
def test_tls_reconfigure(self): def test_tls_reconfigure(self):
self.load('empty') self.load('empty')
self.assertEqual(self.get()['status'], 200, 'init') assert self.get()['status'] == 200, 'init'
self.certificate() self.certificate()
@@ -435,21 +420,17 @@ basicConstraints = critical,CA:TRUE"""
read_timeout=1, read_timeout=1,
) )
self.assertEqual(resp['status'], 200, 'initial status') assert resp['status'] == 200, 'initial status'
self.add_tls() self.add_tls()
self.assertEqual( assert self.get(sock=sock)['status'] == 200, 'reconfigure status'
self.get(sock=sock)['status'], 200, 'reconfigure status' assert self.get_ssl()['status'] == 200, 'reconfigure tls status'
)
self.assertEqual(
self.get_ssl()['status'], 200, 'reconfigure tls status'
)
def test_tls_keepalive(self): def test_tls_keepalive(self):
self.load('mirror') self.load('mirror')
self.assertEqual(self.get()['status'], 200, 'init') assert self.get()['status'] == 200, 'init'
self.certificate() self.certificate()
@@ -466,7 +447,7 @@ basicConstraints = critical,CA:TRUE"""
read_timeout=1, read_timeout=1,
) )
self.assertEqual(resp['body'], '0123456789', 'keepalive 1') assert resp['body'] == '0123456789', 'keepalive 1'
resp = self.post_ssl( resp = self.post_ssl(
headers={ headers={
@@ -478,13 +459,13 @@ basicConstraints = critical,CA:TRUE"""
body='0123456789', body='0123456789',
) )
self.assertEqual(resp['body'], '0123456789', 'keepalive 2') assert resp['body'] == '0123456789', 'keepalive 2'
@unittest.skip('not yet') @pytest.mark.skip('not yet')
def test_tls_keepalive_certificate_remove(self): def test_tls_keepalive_certificate_remove(self):
self.load('empty') self.load('empty')
self.assertEqual(self.get()['status'], 200, 'init') assert self.get()['status'] == 200, 'init'
self.certificate() self.certificate()
@@ -506,19 +487,17 @@ basicConstraints = critical,CA:TRUE"""
except: except:
resp = None resp = None
self.assertEqual(resp, None, 'keepalive remove certificate') assert resp == None, 'keepalive remove certificate'
@unittest.skip('not yet') @pytest.mark.skip('not yet')
def test_tls_certificates_remove_all(self): def test_tls_certificates_remove_all(self):
self.load('empty') self.load('empty')
self.certificate() self.certificate()
self.assertIn( assert 'success' in self.conf_delete(
'success', '/certificates'
self.conf_delete('/certificates'), ), 'remove all certificates'
'remove all certificates',
)
def test_tls_application_respawn(self): def test_tls_application_respawn(self):
self.load('mirror') self.load('mirror')
@@ -544,7 +523,7 @@ basicConstraints = critical,CA:TRUE"""
subprocess.call(['kill', '-9', app_id]) subprocess.call(['kill', '-9', app_id])
self.skip_alerts.append(r'process %s exited on signal 9' % app_id) skip_alert(r'process %s exited on signal 9' % app_id)
self.wait_for_record( self.wait_for_record(
re.compile( re.compile(
@@ -562,15 +541,13 @@ basicConstraints = critical,CA:TRUE"""
body='0123456789', body='0123456789',
) )
self.assertEqual(resp['status'], 200, 'application respawn status') assert resp['status'] == 200, 'application respawn status'
self.assertEqual( assert resp['body'] == '0123456789', 'application respawn body'
resp['body'], '0123456789', 'application respawn body'
)
def test_tls_url_scheme(self): def test_tls_url_scheme(self):
self.load('variables') self.load('variables')
self.assertEqual( assert (
self.post( self.post(
headers={ headers={
'Host': 'localhost', 'Host': 'localhost',
@@ -578,16 +555,15 @@ basicConstraints = critical,CA:TRUE"""
'Custom-Header': '', 'Custom-Header': '',
'Connection': 'close', 'Connection': 'close',
} }
)['headers']['Wsgi-Url-Scheme'], )['headers']['Wsgi-Url-Scheme']
'http', == 'http'
'url scheme http', ), 'url scheme http'
)
self.certificate() self.certificate()
self.add_tls(application='variables') self.add_tls(application='variables')
self.assertEqual( assert (
self.post_ssl( self.post_ssl(
headers={ headers={
'Host': 'localhost', 'Host': 'localhost',
@@ -595,10 +571,9 @@ basicConstraints = critical,CA:TRUE"""
'Custom-Header': '', 'Custom-Header': '',
'Connection': 'close', 'Connection': 'close',
} }
)['headers']['Wsgi-Url-Scheme'], )['headers']['Wsgi-Url-Scheme']
'https', == 'https'
'url scheme https', ), 'url scheme https'
)
def test_tls_big_upload(self): def test_tls_big_upload(self):
self.load('upload') self.load('upload')
@@ -610,15 +585,14 @@ basicConstraints = critical,CA:TRUE"""
filename = 'test.txt' filename = 'test.txt'
data = '0123456789' * 9000 data = '0123456789' * 9000
res = self.post_ssl(body={ res = self.post_ssl(
body={
'file': { 'file': {
'filename': filename, 'filename': filename,
'type': 'text/plain', 'type': 'text/plain',
'data': io.StringIO(data), 'data': io.StringIO(data),
} }
}) }
self.assertEqual(res['status'], 200, 'status ok') )
self.assertEqual(res['body'], filename + data) assert res['status'] == 200, 'status ok'
assert res['body'] == filename + data
if __name__ == '__main__':
TestTLS.main()

View File

@@ -2,17 +2,16 @@ import os
import re import re
from unit.applications.lang.python import TestApplicationPython from unit.applications.lang.python import TestApplicationPython
from conftest import option
class TestUpstreamsRR(TestApplicationPython): class TestUpstreamsRR(TestApplicationPython):
prerequisites = {'modules': {'python': 'any'}} prerequisites = {'modules': {'python': 'any'}}
def setUp(self): def setup_method(self):
super().setUp() super().setup_method()
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{ {
"listeners": { "listeners": {
"*:7080": {"pass": "upstreams/one"}, "*:7080": {"pass": "upstreams/one"},
@@ -42,9 +41,7 @@ class TestUpstreamsRR(TestApplicationPython):
}, },
"applications": {}, "applications": {},
}, },
), ), 'upstreams initial configuration'
'upstreams initial configuration',
)
self.cpu_count = os.cpu_count() self.cpu_count = os.cpu_count()
@@ -91,113 +88,87 @@ Connection: close
def test_upstreams_rr_no_weight(self): def test_upstreams_rr_no_weight(self):
resps = self.get_resps() resps = self.get_resps()
self.assertEqual(sum(resps), 100, 'no weight sum') assert sum(resps) == 100, 'no weight sum'
self.assertLessEqual( assert abs(resps[0] - resps[1]) <= self.cpu_count, 'no weight'
abs(resps[0] - resps[1]), self.cpu_count, 'no weight'
)
self.assertIn( assert 'success' in self.conf_delete(
'success', 'upstreams/one/servers/127.0.0.1:7081'
self.conf_delete('upstreams/one/servers/127.0.0.1:7081'), ), 'no weight server remove'
'no weight server remove',
)
resps = self.get_resps(req=50) resps = self.get_resps(req=50)
self.assertEqual(resps[1], 50, 'no weight 2') assert resps[1] == 50, 'no weight 2'
self.assertIn( assert 'success' in self.conf(
'success', {}, 'upstreams/one/servers/127.0.0.1:7081'
self.conf({}, 'upstreams/one/servers/127.0.0.1:7081'), ), 'no weight server revert'
'no weight server revert',
)
resps = self.get_resps() resps = self.get_resps()
self.assertEqual(sum(resps), 100, 'no weight 3 sum') assert sum(resps) == 100, 'no weight 3 sum'
self.assertLessEqual( assert abs(resps[0] - resps[1]) <= self.cpu_count, 'no weight 3'
abs(resps[0] - resps[1]), self.cpu_count, 'no weight 3'
)
self.assertIn( assert 'success' in self.conf(
'success', {}, 'upstreams/one/servers/127.0.0.1:7083'
self.conf({}, 'upstreams/one/servers/127.0.0.1:7083'), ), 'no weight server new'
'no weight server new',
)
resps = self.get_resps() resps = self.get_resps()
self.assertEqual(sum(resps), 100, 'no weight 4 sum') assert sum(resps) == 100, 'no weight 4 sum'
self.assertLessEqual( assert max(resps) - min(resps) <= self.cpu_count, 'no weight 4'
max(resps) - min(resps), self.cpu_count, 'no weight 4'
)
resps = self.get_resps_sc(req=30) resps = self.get_resps_sc(req=30)
self.assertEqual(resps[0], 10, 'no weight 4 0') assert resps[0] == 10, 'no weight 4 0'
self.assertEqual(resps[1], 10, 'no weight 4 1') assert resps[1] == 10, 'no weight 4 1'
self.assertEqual(resps[2], 10, 'no weight 4 2') assert resps[2] == 10, 'no weight 4 2'
def test_upstreams_rr_weight(self): def test_upstreams_rr_weight(self):
self.assertIn( assert 'success' in self.conf(
'success', {"weight": 3}, 'upstreams/one/servers/127.0.0.1:7081'
self.conf({"weight": 3}, 'upstreams/one/servers/127.0.0.1:7081'), ), 'configure weight'
'configure weight',
)
resps = self.get_resps_sc() resps = self.get_resps_sc()
self.assertEqual(resps[0], 75, 'weight 3 0') assert resps[0] == 75, 'weight 3 0'
self.assertEqual(resps[1], 25, 'weight 3 1') assert resps[1] == 25, 'weight 3 1'
self.assertIn( assert 'success' in self.conf_delete(
'success', 'upstreams/one/servers/127.0.0.1:7081/weight'
self.conf_delete('upstreams/one/servers/127.0.0.1:7081/weight'), ), 'configure weight remove'
'configure weight remove',
)
resps = self.get_resps_sc(req=10) resps = self.get_resps_sc(req=10)
self.assertEqual(resps[0], 5, 'weight 0 0') assert resps[0] == 5, 'weight 0 0'
self.assertEqual(resps[1], 5, 'weight 0 1') assert resps[1] == 5, 'weight 0 1'
self.assertIn( assert 'success' in self.conf(
'success', '1', 'upstreams/one/servers/127.0.0.1:7081/weight'
self.conf('1', 'upstreams/one/servers/127.0.0.1:7081/weight'), ), 'configure weight 1'
'configure weight 1',
)
resps = self.get_resps_sc() resps = self.get_resps_sc()
self.assertEqual(resps[0], 50, 'weight 1 0') assert resps[0] == 50, 'weight 1 0'
self.assertEqual(resps[1], 50, 'weight 1 1') assert resps[1] == 50, 'weight 1 1'
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{ {
"127.0.0.1:7081": {"weight": 3}, "127.0.0.1:7081": {"weight": 3},
"127.0.0.1:7083": {"weight": 2}, "127.0.0.1:7083": {"weight": 2},
}, },
'upstreams/one/servers', 'upstreams/one/servers',
), ), 'configure weight 2'
'configure weight 2',
)
resps = self.get_resps_sc() resps = self.get_resps_sc()
self.assertEqual(resps[0], 60, 'weight 2 0') assert resps[0] == 60, 'weight 2 0'
self.assertEqual(resps[2], 40, 'weight 2 1') assert resps[2] == 40, 'weight 2 1'
def test_upstreams_rr_weight_rational(self): def test_upstreams_rr_weight_rational(self):
def set_weights(w1, w2): def set_weights(w1, w2):
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{ {
"127.0.0.1:7081": {"weight": w1}, "127.0.0.1:7081": {"weight": w1},
"127.0.0.1:7082": {"weight": w2}, "127.0.0.1:7082": {"weight": w2},
}, },
'upstreams/one/servers', 'upstreams/one/servers',
), ), 'configure weights'
'configure weights',
)
def check_reqs(w1, w2, reqs=10): def check_reqs(w1, w2, reqs=10):
resps = self.get_resps_sc(req=reqs) resps = self.get_resps_sc(req=reqs)
self.assertEqual(resps[0], reqs * w1 / (w1 + w2), 'weight 1') assert resps[0] == reqs * w1 / (w1 + w2), 'weight 1'
self.assertEqual(resps[1], reqs * w2 / (w1 + w2), 'weight 2') assert resps[1] == reqs * w2 / (w1 + w2), 'weight 2'
def check_weights(w1, w2): def check_weights(w1, w2):
set_weights(w1, w2) set_weights(w1, w2)
@@ -207,39 +178,33 @@ Connection: close
check_weights(0, 999999.0123456) check_weights(0, 999999.0123456)
check_weights(1, 9) check_weights(1, 9)
check_weights(100000, 900000) check_weights(100000, 900000)
check_weights(1, .25)
check_weights(1, 0.25) check_weights(1, 0.25)
check_weights(0.2, .8) check_weights(1, 0.25)
check_weights(0.2, 0.8)
check_weights(1, 1.5) check_weights(1, 1.5)
check_weights(1e-3, 1E-3) check_weights(1e-3, 1e-3)
check_weights(1e-20, 1e-20) check_weights(1e-20, 1e-20)
check_weights(1e4, 1e4) check_weights(1e4, 1e4)
check_weights(1000000, 1000000) check_weights(1000000, 1000000)
set_weights(0.25, 0.25) set_weights(0.25, 0.25)
self.assertIn( assert 'success' in self.conf_delete(
'success', 'upstreams/one/servers/127.0.0.1:7081/weight'
self.conf_delete('upstreams/one/servers/127.0.0.1:7081/weight'), ), 'delete weight'
'delete weight',
)
check_reqs(1, 0.25) check_reqs(1, 0.25)
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{ {
"127.0.0.1:7081": {"weight": 0.1}, "127.0.0.1:7081": {"weight": 0.1},
"127.0.0.1:7082": {"weight": 1}, "127.0.0.1:7082": {"weight": 1},
"127.0.0.1:7083": {"weight": 0.9}, "127.0.0.1:7083": {"weight": 0.9},
}, },
'upstreams/one/servers', 'upstreams/one/servers',
), ), 'configure weights'
'configure weights',
)
resps = self.get_resps_sc(req=20) resps = self.get_resps_sc(req=20)
self.assertEqual(resps[0], 1, 'weight 3 1') assert resps[0] == 1, 'weight 3 1'
self.assertEqual(resps[1], 10, 'weight 3 2') assert resps[1] == 10, 'weight 3 2'
self.assertEqual(resps[2], 9, 'weight 3 3') assert resps[2] == 9, 'weight 3 3'
def test_upstreams_rr_independent(self): def test_upstreams_rr_independent(self):
def sum_resps(*args): def sum_resps(*args):
@@ -250,52 +215,41 @@ Connection: close
return sum return sum
resps = self.get_resps_sc(req=30, port=7090) resps = self.get_resps_sc(req=30, port=7090)
self.assertEqual(resps[0], 15, 'dep two before 0') assert resps[0] == 15, 'dep two before 0'
self.assertEqual(resps[1], 15, 'dep two before 1') assert resps[1] == 15, 'dep two before 1'
resps = self.get_resps_sc(req=30) resps = self.get_resps_sc(req=30)
self.assertEqual(resps[0], 15, 'dep one before 0') assert resps[0] == 15, 'dep one before 0'
self.assertEqual(resps[1], 15, 'dep one before 1') assert resps[1] == 15, 'dep one before 1'
self.assertIn( assert 'success' in self.conf(
'success', '2', 'upstreams/two/servers/127.0.0.1:7081/weight'
self.conf('2', 'upstreams/two/servers/127.0.0.1:7081/weight'), ), 'configure dep weight'
'configure dep weight',
)
resps = self.get_resps_sc(req=30, port=7090) resps = self.get_resps_sc(req=30, port=7090)
self.assertEqual(resps[0], 20, 'dep two 0') assert resps[0] == 20, 'dep two 0'
self.assertEqual(resps[1], 10, 'dep two 1') assert resps[1] == 10, 'dep two 1'
resps = self.get_resps_sc(req=30) resps = self.get_resps_sc(req=30)
self.assertEqual(resps[0], 15, 'dep one 0') assert resps[0] == 15, 'dep one 0'
self.assertEqual(resps[1], 15, 'dep one 1') assert resps[1] == 15, 'dep one 1'
self.assertIn( assert 'success' in self.conf(
'success', '1', 'upstreams/two/servers/127.0.0.1:7081/weight'
self.conf('1', 'upstreams/two/servers/127.0.0.1:7081/weight'), ), 'configure dep weight 1'
'configure dep weight 1',
)
r_one, r_two = [0, 0], [0, 0] r_one, r_two = [0, 0], [0, 0]
for _ in range(10): for _ in range(10):
r_one = sum_resps(r_one, self.get_resps(req=10)) r_one = sum_resps(r_one, self.get_resps(req=10))
r_two = sum_resps(r_two, self.get_resps(req=10, port=7090)) r_two = sum_resps(r_two, self.get_resps(req=10, port=7090))
assert sum(r_one) == 100, 'dep one mix sum'
self.assertEqual(sum(r_one), 100, 'dep one mix sum') assert abs(r_one[0] - r_one[1]) <= self.cpu_count, 'dep one mix'
self.assertLessEqual( assert sum(r_two) == 100, 'dep two mix sum'
abs(r_one[0] - r_one[1]), self.cpu_count, 'dep one mix' assert abs(r_two[0] - r_two[1]) <= self.cpu_count, 'dep two mix'
)
self.assertEqual(sum(r_two), 100, 'dep two mix sum')
self.assertLessEqual(
abs(r_two[0] - r_two[1]), self.cpu_count, 'dep two mix'
)
def test_upstreams_rr_delay(self): def test_upstreams_rr_delay(self):
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{ {
"listeners": { "listeners": {
"*:7080": {"pass": "upstreams/one"}, "*:7080": {"pass": "upstreams/one"},
@@ -324,16 +278,14 @@ Connection: close
"delayed": { "delayed": {
"type": "python", "type": "python",
"processes": {"spare": 0}, "processes": {"spare": 0},
"path": self.current_dir + "/python/delayed", "path": option.test_dir + "/python/delayed",
"working_directory": self.current_dir "working_directory": option.test_dir
+ "/python/delayed", + "/python/delayed",
"module": "wsgi", "module": "wsgi",
} }
}, },
}, },
), ), 'upstreams initial configuration'
'upstreams initial configuration',
)
req = 50 req = 50
@@ -357,12 +309,12 @@ Connection: close
resp = self.recvall(socks[i]).decode() resp = self.recvall(socks[i]).decode()
socks[i].close() socks[i].close()
m = re.search('HTTP/1.1 20(\d)', resp) m = re.search(r'HTTP/1.1 20(\d)', resp)
self.assertIsNotNone(m, 'status') assert m is not None, 'status'
resps[int(m.group(1))] += 1 resps[int(m.group(1))] += 1
self.assertEqual(sum(resps), req, 'delay sum') assert sum(resps) == req, 'delay sum'
self.assertLessEqual(abs(resps[0] - resps[1]), self.cpu_count, 'delay') assert abs(resps[0] - resps[1]) <= self.cpu_count, 'delay'
def test_upstreams_rr_active_req(self): def test_upstreams_rr_active_req(self):
conns = 5 conns = 5
@@ -389,59 +341,46 @@ Connection: close
# Send one more request and read response to make sure that previous # Send one more request and read response to make sure that previous
# requests had enough time to reach server. # requests had enough time to reach server.
self.assertEqual(self.get()['body'], '') assert self.get()['body'] == ''
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{"127.0.0.1:7083": {"weight": 2}}, 'upstreams/one/servers', {"127.0.0.1:7083": {"weight": 2}}, 'upstreams/one/servers',
), ), 'active req new server'
'active req new server', assert 'success' in self.conf_delete(
) 'upstreams/one/servers/127.0.0.1:7083'
self.assertIn( ), 'active req server remove'
'success', assert 'success' in self.conf_delete(
self.conf_delete('upstreams/one/servers/127.0.0.1:7083'), 'listeners/*:7080'
'active req server remove', ), 'delete listener'
) assert 'success' in self.conf_delete(
self.assertIn( 'upstreams/one'
'success', self.conf_delete('listeners/*:7080'), 'delete listener' ), 'active req upstream remove'
)
self.assertIn(
'success',
self.conf_delete('upstreams/one'),
'active req upstream remove',
)
for i in range(conns): for i in range(conns):
self.assertEqual( assert (
self.http(b'', sock=socks[i], raw=True)['body'], self.http(b'', sock=socks[i], raw=True)['body'] == ''
'', ), 'active req GET'
'active req GET',
)
self.assertEqual( assert (
self.http(b"""0123456789""", sock=socks2[i], raw=True)['body'], self.http(b"""0123456789""", sock=socks2[i], raw=True)['body']
'', == ''
'active req POST', ), 'active req POST'
)
def test_upstreams_rr_bad_server(self): def test_upstreams_rr_bad_server(self):
self.assertIn( assert 'success' in self.conf(
'success', {"weight": 1}, 'upstreams/one/servers/127.0.0.1:7084'
self.conf({"weight": 1}, 'upstreams/one/servers/127.0.0.1:7084'), ), 'configure bad server'
'configure bad server',
)
resps = self.get_resps_sc(req=30) resps = self.get_resps_sc(req=30)
self.assertEqual(resps[0], 10, 'bad server 0') assert resps[0] == 10, 'bad server 0'
self.assertEqual(resps[1], 10, 'bad server 1') assert resps[1] == 10, 'bad server 1'
self.assertEqual(sum(resps), 20, 'bad server sum') assert sum(resps) == 20, 'bad server sum'
def test_upstreams_rr_pipeline(self): def test_upstreams_rr_pipeline(self):
resps = self.get_resps_sc() resps = self.get_resps_sc()
self.assertEqual(resps[0], 50, 'pipeline 0') assert resps[0] == 50, 'pipeline 0'
self.assertEqual(resps[1], 50, 'pipeline 1') assert resps[1] == 50, 'pipeline 1'
def test_upstreams_rr_post(self): def test_upstreams_rr_post(self):
resps = [0, 0] resps = [0, 0]
@@ -449,120 +388,87 @@ Connection: close
resps[self.get()['status'] % 10] += 1 resps[self.get()['status'] % 10] += 1
resps[self.post(body='0123456789')['status'] % 10] += 1 resps[self.post(body='0123456789')['status'] % 10] += 1
self.assertEqual(sum(resps), 100, 'post sum') assert sum(resps) == 100, 'post sum'
self.assertLessEqual(abs(resps[0] - resps[1]), self.cpu_count, 'post') assert abs(resps[0] - resps[1]) <= self.cpu_count, 'post'
def test_upstreams_rr_unix(self): def test_upstreams_rr_unix(self):
addr_0 = self.testdir + '/sock_0' addr_0 = self.temp_dir + '/sock_0'
addr_1 = self.testdir + '/sock_1' addr_1 = self.temp_dir + '/sock_1'
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{ {
"*:7080": {"pass": "upstreams/one"}, "*:7080": {"pass": "upstreams/one"},
"unix:" + addr_0: {"pass": "routes/one"}, "unix:" + addr_0: {"pass": "routes/one"},
"unix:" + addr_1: {"pass": "routes/two"}, "unix:" + addr_1: {"pass": "routes/two"},
}, },
'listeners', 'listeners',
), ), 'configure listeners unix'
'configure listeners unix',
)
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{"unix:" + addr_0: {}, "unix:" + addr_1: {}}, {"unix:" + addr_0: {}, "unix:" + addr_1: {}},
'upstreams/one/servers', 'upstreams/one/servers',
), ), 'configure servers unix'
'configure servers unix',
)
resps = self.get_resps_sc() resps = self.get_resps_sc()
self.assertEqual(resps[0], 50, 'unix 0') assert resps[0] == 50, 'unix 0'
self.assertEqual(resps[1], 50, 'unix 1') assert resps[1] == 50, 'unix 1'
def test_upstreams_rr_ipv6(self): def test_upstreams_rr_ipv6(self):
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{ {
"*:7080": {"pass": "upstreams/one"}, "*:7080": {"pass": "upstreams/one"},
"[::1]:7081": {"pass": "routes/one"}, "[::1]:7081": {"pass": "routes/one"},
"[::1]:7082": {"pass": "routes/two"}, "[::1]:7082": {"pass": "routes/two"},
}, },
'listeners', 'listeners',
), ), 'configure listeners ipv6'
'configure listeners ipv6',
)
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{"[::1]:7081": {}, "[::1]:7082": {}}, 'upstreams/one/servers' {"[::1]:7081": {}, "[::1]:7082": {}}, 'upstreams/one/servers'
), ), 'configure servers ipv6'
'configure servers ipv6',
)
resps = self.get_resps_sc() resps = self.get_resps_sc()
self.assertEqual(resps[0], 50, 'ipv6 0') assert resps[0] == 50, 'ipv6 0'
self.assertEqual(resps[1], 50, 'ipv6 1') assert resps[1] == 50, 'ipv6 1'
def test_upstreams_rr_servers_empty(self): def test_upstreams_rr_servers_empty(self):
self.assertIn( assert 'success' in self.conf(
'success', {}, 'upstreams/one/servers'
self.conf({}, 'upstreams/one/servers'), ), 'configure servers empty'
'configure servers empty', assert self.get()['status'] == 502, 'servers empty'
)
self.assertEqual(self.get()['status'], 502, 'servers empty')
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{"127.0.0.1:7081": {"weight": 0}}, 'upstreams/one/servers' {"127.0.0.1:7081": {"weight": 0}}, 'upstreams/one/servers'
), ), 'configure servers empty one'
'configure servers empty one', assert self.get()['status'] == 502, 'servers empty one'
) assert 'success' in self.conf(
self.assertEqual(self.get()['status'], 502, 'servers empty one')
self.assertIn(
'success',
self.conf(
{ {
"127.0.0.1:7081": {"weight": 0}, "127.0.0.1:7081": {"weight": 0},
"127.0.0.1:7082": {"weight": 0}, "127.0.0.1:7082": {"weight": 0},
}, },
'upstreams/one/servers', 'upstreams/one/servers',
), ), 'configure servers empty two'
'configure servers empty two', assert self.get()['status'] == 502, 'servers empty two'
)
self.assertEqual(self.get()['status'], 502, 'servers empty two')
def test_upstreams_rr_invalid(self): def test_upstreams_rr_invalid(self):
self.assertIn( assert 'error' in self.conf({}, 'upstreams'), 'upstreams empty'
'error', self.conf({}, 'upstreams'), 'upstreams empty', assert 'error' in self.conf(
) {}, 'upstreams/one'
self.assertIn( ), 'named upstreams empty'
'error', self.conf({}, 'upstreams/one'), 'named upstreams empty', assert 'error' in self.conf(
) {}, 'upstreams/one/servers/127.0.0.1'
self.assertIn( ), 'invalid address'
'error', assert 'error' in self.conf(
self.conf({}, 'upstreams/one/servers/127.0.0.1'), {}, 'upstreams/one/servers/127.0.0.1:7081/blah'
'invalid address', ), 'invalid server option'
)
self.assertIn(
'error',
self.conf({}, 'upstreams/one/servers/127.0.0.1:7081/blah'),
'invalid server option',
)
def check_weight(w): def check_weight(w):
self.assertIn( assert 'error' in self.conf(
'error', w, 'upstreams/one/servers/127.0.0.1:7081/weight'
self.conf(w, 'upstreams/one/servers/127.0.0.1:7081/weight'), ), 'invalid weight option'
'invalid weight option',
)
check_weight({}) check_weight({})
check_weight('-1') check_weight('-1')
check_weight('1.') check_weight('1.')
@@ -571,7 +477,3 @@ Connection: close
check_weight('.01234567890123') check_weight('.01234567890123')
check_weight('1000001') check_weight('1000001')
check_weight('2e6') check_weight('2e6')
if __name__ == '__main__':
TestUpstreamsRR.main()

View File

@@ -2,6 +2,7 @@ import os
from subprocess import call from subprocess import call
from unit.applications.lang.python import TestApplicationPython from unit.applications.lang.python import TestApplicationPython
from conftest import waitforfiles
class TestUSR1(TestApplicationPython): class TestUSR1(TestApplicationPython):
@@ -12,83 +13,74 @@ class TestUSR1(TestApplicationPython):
log = 'access.log' log = 'access.log'
log_new = 'new.log' log_new = 'new.log'
log_path = self.testdir + '/' + log log_path = self.temp_dir + '/' + log
self.assertIn( assert 'success' in self.conf(
'success', '"' + log_path + '"', 'access_log'
self.conf('"' + log_path + '"', 'access_log'), ), 'access log configure'
'access log configure',
)
self.assertTrue(self.waitforfiles(log_path), 'open') assert waitforfiles(log_path), 'open'
os.rename(log_path, self.testdir + '/' + log_new) os.rename(log_path, self.temp_dir + '/' + log_new)
self.assertEqual(self.get()['status'], 200) assert self.get()['status'] == 200
self.assertIsNotNone( assert (
self.wait_for_record(r'"GET / HTTP/1.1" 200 0 "-" "-"', log_new), self.wait_for_record(r'"GET / HTTP/1.1" 200 0 "-" "-"', log_new)
'rename new', is not None
) ), 'rename new'
self.assertFalse(os.path.isfile(log_path), 'rename old') assert not os.path.isfile(log_path), 'rename old'
with open(self.testdir + '/unit.pid', 'r') as f: with open(self.temp_dir + '/unit.pid', 'r') as f:
pid = f.read().rstrip() pid = f.read().rstrip()
call(['kill', '-s', 'USR1', pid]) call(['kill', '-s', 'USR1', pid])
self.assertTrue(self.waitforfiles(log_path), 'reopen') assert waitforfiles(log_path), 'reopen'
self.assertEqual(self.get(url='/usr1')['status'], 200) assert self.get(url='/usr1')['status'] == 200
self.stop() self.stop()
self.assertIsNotNone( assert (
self.wait_for_record(r'"GET /usr1 HTTP/1.1" 200 0 "-" "-"', log), self.wait_for_record(r'"GET /usr1 HTTP/1.1" 200 0 "-" "-"', log)
'reopen 2', is not None
) ), 'reopen 2'
self.assertIsNone( assert self.search_in_log(r'/usr1', log_new) is None, 'rename new 2'
self.search_in_log(r'/usr1', log_new), 'rename new 2'
)
def test_usr1_unit_log(self): def test_usr1_unit_log(self):
self.load('log_body') self.load('log_body')
log_new = 'new.log' log_new = 'new.log'
log_path = self.testdir + '/unit.log' log_path = self.temp_dir + '/unit.log'
log_path_new = self.testdir + '/' + log_new log_path_new = self.temp_dir + '/' + log_new
os.rename(log_path, log_path_new) os.rename(log_path, log_path_new)
body = 'body_for_a_log_new' body = 'body_for_a_log_new'
self.assertEqual(self.post(body=body)['status'], 200) assert self.post(body=body)['status'] == 200
self.assertIsNotNone( assert self.wait_for_record(body, log_new) is not None, 'rename new'
self.wait_for_record(body, log_new), 'rename new' assert not os.path.isfile(log_path), 'rename old'
)
self.assertFalse(os.path.isfile(log_path), 'rename old')
with open(self.testdir + '/unit.pid', 'r') as f: with open(self.temp_dir + '/unit.pid', 'r') as f:
pid = f.read().rstrip() pid = f.read().rstrip()
call(['kill', '-s', 'USR1', pid]) call(['kill', '-s', 'USR1', pid])
self.assertTrue(self.waitforfiles(log_path), 'reopen') assert waitforfiles(log_path), 'reopen'
body = 'body_for_a_log_unit' body = 'body_for_a_log_unit'
self.assertEqual(self.post(body=body)['status'], 200) assert self.post(body=body)['status'] == 200
self.stop() self.stop()
self.assertIsNotNone(self.wait_for_record(body), 'rename new') assert self.wait_for_record(body) is not None, 'rename new'
self.assertIsNone(self.search_in_log(body, log_new), 'rename new 2') assert self.search_in_log(body, log_new) is None, 'rename new 2'
# merge two log files into unit.log to check alerts # merge two log files into unit.log to check alerts
with open(log_path, 'w') as unit_log, \ with open(log_path, 'w') as unit_log, open(
open(log_path_new, 'r') as unit_log_new: log_path_new, 'r'
) as unit_log_new:
unit_log.write(unit_log_new.read()) unit_log.write(unit_log_new.read())
if __name__ == '__main__':
TestUSR1.main()

View File

@@ -4,12 +4,10 @@ from unit.applications.proto import TestApplicationProto
class TestVariables(TestApplicationProto): class TestVariables(TestApplicationProto):
prerequisites = {} prerequisites = {}
def setUp(self): def setup_method(self):
super().setUp() super().setup_method()
self.assertIn( assert 'success' in self.conf(
'success',
self.conf(
{ {
"listeners": {"*:7080": {"pass": "routes/$method"}}, "listeners": {"*:7080": {"pass": "routes/$method"}},
"routes": { "routes": {
@@ -23,33 +21,31 @@ class TestVariables(TestApplicationProto):
"localhost": [{"action": {"return": 208}}], "localhost": [{"action": {"return": 208}}],
}, },
}, },
), ), 'configure routes'
'configure routes',
)
def conf_routes(self, routes): def conf_routes(self, routes):
self.assertIn('success', self.conf(routes, 'listeners/*:7080/pass')) assert 'success' in self.conf(routes, 'listeners/*:7080/pass')
def test_variables_method(self): def test_variables_method(self):
self.assertEqual(self.get()['status'], 201, 'method GET') assert self.get()['status'] == 201, 'method GET'
self.assertEqual(self.post()['status'], 202, 'method POST') assert self.post()['status'] == 202, 'method POST'
def test_variables_uri(self): def test_variables_uri(self):
self.conf_routes("\"routes$uri\"") self.conf_routes("\"routes$uri\"")
self.assertEqual(self.get(url='/3')['status'], 203, 'uri') assert self.get(url='/3')['status'] == 203, 'uri'
self.assertEqual(self.get(url='/4*')['status'], 204, 'uri 2') assert self.get(url='/4*')['status'] == 204, 'uri 2'
self.assertEqual(self.get(url='/4%2A')['status'], 204, 'uri 3') assert self.get(url='/4%2A')['status'] == 204, 'uri 3'
def test_variables_host(self): def test_variables_host(self):
self.conf_routes("\"routes/$host\"") self.conf_routes("\"routes/$host\"")
def check_host(host, status=208): def check_host(host, status=208):
self.assertEqual( assert (
self.get(headers={'Host': host, 'Connection': 'close'})[ self.get(headers={'Host': host, 'Connection': 'close'})[
'status' 'status'
], ]
status, == status
) )
check_host('localhost') check_host('localhost')
@@ -61,43 +57,41 @@ class TestVariables(TestApplicationProto):
def test_variables_many(self): def test_variables_many(self):
self.conf_routes("\"routes$uri$method\"") self.conf_routes("\"routes$uri$method\"")
self.assertEqual(self.get(url='/5')['status'], 206, 'many') assert self.get(url='/5')['status'] == 206, 'many'
self.conf_routes("\"routes${uri}${method}\"") self.conf_routes("\"routes${uri}${method}\"")
self.assertEqual(self.get(url='/5')['status'], 206, 'many 2') assert self.get(url='/5')['status'] == 206, 'many 2'
self.conf_routes("\"routes${uri}$method\"") self.conf_routes("\"routes${uri}$method\"")
self.assertEqual(self.get(url='/5')['status'], 206, 'many 3') assert self.get(url='/5')['status'] == 206, 'many 3'
self.conf_routes("\"routes/$method$method\"") self.conf_routes("\"routes/$method$method\"")
self.assertEqual(self.get()['status'], 207, 'many 4') assert self.get()['status'] == 207, 'many 4'
self.conf_routes("\"routes/$method$uri\"") self.conf_routes("\"routes/$method$uri\"")
self.assertEqual(self.get()['status'], 404, 'no route') assert self.get()['status'] == 404, 'no route'
self.assertEqual(self.get(url='/blah')['status'], 404, 'no route 2') assert self.get(url='/blah')['status'] == 404, 'no route 2'
def test_variables_replace(self): def test_variables_replace(self):
self.assertEqual(self.get()['status'], 201) assert self.get()['status'] == 201
self.conf_routes("\"routes$uri\"") self.conf_routes("\"routes$uri\"")
self.assertEqual(self.get(url='/3')['status'], 203) assert self.get(url='/3')['status'] == 203
self.conf_routes("\"routes/${method}\"") self.conf_routes("\"routes/${method}\"")
self.assertEqual(self.post()['status'], 202) assert self.post()['status'] == 202
self.conf_routes("\"routes${uri}\"") self.conf_routes("\"routes${uri}\"")
self.assertEqual(self.get(url='/4*')['status'], 204) assert self.get(url='/4*')['status'] == 204
self.conf_routes("\"routes/blah$method}\"") self.conf_routes("\"routes/blah$method}\"")
self.assertEqual(self.get()['status'], 205) assert self.get()['status'] == 205
def test_variables_invalid(self): def test_variables_invalid(self):
def check_variables(routes): def check_variables(routes):
self.assertIn( assert 'error' in self.conf(
'error', routes, 'listeners/*:7080/pass'
self.conf(routes, 'listeners/*:7080/pass'), ), 'invalid variables'
'invalid variables',
)
check_variables("\"routes$\"") check_variables("\"routes$\"")
check_variables("\"routes${\"") check_variables("\"routes${\"")
@@ -106,6 +100,3 @@ class TestVariables(TestApplicationProto):
check_variables("\"routes$uriblah\"") check_variables("\"routes$uriblah\"")
check_variables("\"routes${uri\"") check_variables("\"routes${uri\"")
check_variables("\"routes${{uri}\"") check_variables("\"routes${{uri}\"")
if __name__ == '__main__':
TestVariables.main()

View File

@@ -2,17 +2,18 @@ import os
import subprocess import subprocess
from unit.applications.proto import TestApplicationProto from unit.applications.proto import TestApplicationProto
from conftest import option
class TestApplicationGo(TestApplicationProto): class TestApplicationGo(TestApplicationProto):
@classmethod @classmethod
def setUpClass(cls, complete_check=True): def setup_class(cls, complete_check=True):
unit = super().setUpClass(complete_check=False) unit = super().setup_class(complete_check=False)
# check go module # check go module
go_app = TestApplicationGo() go_app = TestApplicationGo()
go_app.testdir = unit.testdir go_app.temp_dir = unit.temp_dir
proc = go_app.prepare_env('empty', 'app') proc = go_app.prepare_env('empty', 'app')
if proc and proc.returncode == 0: if proc and proc.returncode == 0:
cls.available['modules']['go'] = [] cls.available['modules']['go'] = []
@@ -20,8 +21,8 @@ class TestApplicationGo(TestApplicationProto):
return unit if not complete_check else unit.complete() return unit if not complete_check else unit.complete()
def prepare_env(self, script, name, static=False): def prepare_env(self, script, name, static=False):
if not os.path.exists(self.testdir + '/go'): if not os.path.exists(self.temp_dir + '/go'):
os.mkdir(self.testdir + '/go') os.mkdir(self.temp_dir + '/go')
env = os.environ.copy() env = os.environ.copy()
env['GOPATH'] = self.pardir + '/build/go' env['GOPATH'] = self.pardir + '/build/go'
@@ -35,16 +36,16 @@ class TestApplicationGo(TestApplicationProto):
'-ldflags', '-ldflags',
'-extldflags "-static"', '-extldflags "-static"',
'-o', '-o',
self.testdir + '/go/' + name, self.temp_dir + '/go/' + name,
self.current_dir + '/go/' + script + '/' + name + '.go', option.test_dir + '/go/' + script + '/' + name + '.go',
] ]
else: else:
args = [ args = [
'go', 'go',
'build', 'build',
'-o', '-o',
self.testdir + '/go/' + name, self.temp_dir + '/go/' + name,
self.current_dir + '/go/' + script + '/' + name + '.go', option.test_dir + '/go/' + script + '/' + name + '.go',
] ]
try: try:
@@ -59,8 +60,8 @@ class TestApplicationGo(TestApplicationProto):
def load(self, script, name='app', **kwargs): def load(self, script, name='app', **kwargs):
static_build = False static_build = False
wdir = self.current_dir + "/go/" + script wdir = option.test_dir + "/go/" + script
executable = self.testdir + "/go/" + name executable = self.temp_dir + "/go/" + name
if 'isolation' in kwargs and 'rootfs' in kwargs['isolation']: if 'isolation' in kwargs and 'rootfs' in kwargs['isolation']:
wdir = "/go/" wdir = "/go/"

View File

@@ -1,17 +1,19 @@
import glob import glob
import os import os
import pytest
import shutil import shutil
import subprocess import subprocess
from unit.applications.proto import TestApplicationProto from unit.applications.proto import TestApplicationProto
from conftest import option
class TestApplicationJava(TestApplicationProto): class TestApplicationJava(TestApplicationProto):
def load(self, script, name='app', **kwargs): def load(self, script, name='app', **kwargs):
app_path = self.testdir + '/java' app_path = self.temp_dir + '/java'
web_inf_path = app_path + '/WEB-INF/' web_inf_path = app_path + '/WEB-INF/'
classes_path = web_inf_path + 'classes/' classes_path = web_inf_path + 'classes/'
script_path = self.current_dir + '/java/' + script + '/' script_path = option.test_dir + '/java/' + script + '/'
if not os.path.isdir(app_path): if not os.path.isdir(app_path):
os.makedirs(app_path) os.makedirs(app_path)
@@ -54,7 +56,7 @@ class TestApplicationJava(TestApplicationProto):
) )
if not ws_jars: if not ws_jars:
self.fail('websocket api jar not found.') pytest.fail('websocket api jar not found.')
javac = [ javac = [
'javac', 'javac',
@@ -69,7 +71,7 @@ class TestApplicationJava(TestApplicationProto):
process.communicate() process.communicate()
except: except:
self.fail('Cann\'t run javac process.') pytest.fail('Cann\'t run javac process.')
self._load_conf( self._load_conf(
{ {

View File

@@ -3,12 +3,13 @@ import shutil
from urllib.parse import quote from urllib.parse import quote
from unit.applications.proto import TestApplicationProto from unit.applications.proto import TestApplicationProto
from conftest import option, public_dir
class TestApplicationNode(TestApplicationProto): class TestApplicationNode(TestApplicationProto):
@classmethod @classmethod
def setUpClass(cls, complete_check=True): def setup_class(cls, complete_check=True):
unit = super().setUpClass(complete_check=False) unit = super().setup_class(complete_check=False)
# check node module # check node module
@@ -21,17 +22,17 @@ class TestApplicationNode(TestApplicationProto):
# copy application # copy application
shutil.copytree( shutil.copytree(
self.current_dir + '/node/' + script, self.testdir + '/node' option.test_dir + '/node/' + script, self.temp_dir + '/node'
) )
# copy modules # copy modules
shutil.copytree( shutil.copytree(
self.pardir + '/node/node_modules', self.pardir + '/node/node_modules',
self.testdir + '/node/node_modules', self.temp_dir + '/node/node_modules',
) )
self.public_dir(self.testdir + '/node') public_dir(self.temp_dir + '/node')
self._load_conf( self._load_conf(
{ {
@@ -42,7 +43,7 @@ class TestApplicationNode(TestApplicationProto):
script: { script: {
"type": "external", "type": "external",
"processes": {"spare": 0}, "processes": {"spare": 0},
"working_directory": self.testdir + '/node', "working_directory": self.temp_dir + '/node',
"executable": name, "executable": name,
} }
}, },

View File

@@ -1,11 +1,12 @@
from unit.applications.proto import TestApplicationProto from unit.applications.proto import TestApplicationProto
from conftest import option
class TestApplicationPerl(TestApplicationProto): class TestApplicationPerl(TestApplicationProto):
application_type = "perl" application_type = "perl"
def load(self, script, name='psgi.pl', **kwargs): def load(self, script, name='psgi.pl', **kwargs):
script_path = self.current_dir + '/perl/' + script script_path = option.test_dir + '/perl/' + script
self._load_conf( self._load_conf(
{ {

View File

@@ -1,11 +1,12 @@
from unit.applications.proto import TestApplicationProto from unit.applications.proto import TestApplicationProto
from conftest import option
class TestApplicationPHP(TestApplicationProto): class TestApplicationPHP(TestApplicationProto):
application_type = "php" application_type = "php"
def load(self, script, index='index.php', **kwargs): def load(self, script, index='index.php', **kwargs):
script_path = self.current_dir + '/php/' + script script_path = option.test_dir + '/php/' + script
self._load_conf( self._load_conf(
{ {

View File

@@ -1,20 +1,23 @@
import os import os
import shutil import shutil
import pytest
from unit.applications.proto import TestApplicationProto from unit.applications.proto import TestApplicationProto
from conftest import option
class TestApplicationPython(TestApplicationProto): class TestApplicationPython(TestApplicationProto):
application_type = "python" application_type = "python"
def load(self, script, name=None, **kwargs): def load(self, script, name=None, **kwargs):
print()
if name is None: if name is None:
name = script name = script
if script[0] == '/': if script[0] == '/':
script_path = script script_path = script
else: else:
script_path = self.current_dir + '/python/' + script script_path = option.test_dir + '/python/' + script
if kwargs.get('isolation') and kwargs['isolation'].get('rootfs'): if kwargs.get('isolation') and kwargs['isolation'].get('rootfs'):
rootfs = kwargs['isolation']['rootfs'] rootfs = kwargs['isolation']['rootfs']
@@ -27,12 +30,17 @@ class TestApplicationPython(TestApplicationProto):
script_path = '/app/python/' + name script_path = '/app/python/' + name
appication_type = self.get_appication_type()
if appication_type is None:
appication_type = self.application_type
self._load_conf( self._load_conf(
{ {
"listeners": {"*:7080": {"pass": "applications/" + name}}, "listeners": {"*:7080": {"pass": "applications/" + name}},
"applications": { "applications": {
name: { name: {
"type": self.application_type, "type": appication_type,
"processes": {"spare": 0}, "processes": {"spare": 0},
"path": script_path, "path": script_path,
"working_directory": script_path, "working_directory": script_path,

View File

@@ -1,11 +1,12 @@
from unit.applications.proto import TestApplicationProto from unit.applications.proto import TestApplicationProto
from conftest import option
class TestApplicationRuby(TestApplicationProto): class TestApplicationRuby(TestApplicationProto):
application_type = "ruby" application_type = "ruby"
def load(self, script, name='config.ru', **kwargs): def load(self, script, name='config.ru', **kwargs):
script_path = self.current_dir + '/ruby/' + script script_path = option.test_dir + '/ruby/' + script
self._load_conf( self._load_conf(
{ {

View File

@@ -1,7 +1,9 @@
import os
import re import re
import time import time
from unit.control import TestControl from unit.control import TestControl
from conftest import option
class TestApplicationProto(TestControl): class TestApplicationProto(TestControl):
@@ -12,7 +14,7 @@ class TestApplicationProto(TestControl):
return time.mktime(time.strptime(date, template)) return time.mktime(time.strptime(date, template))
def search_in_log(self, pattern, name='unit.log'): def search_in_log(self, pattern, name='unit.log'):
with open(self.testdir + '/' + name, 'r', errors='ignore') as f: with open(self.temp_dir + '/' + name, 'r', errors='ignore') as f:
return re.search(pattern, f.read()) return re.search(pattern, f.read())
def wait_for_record(self, pattern, name='unit.log'): def wait_for_record(self, pattern, name='unit.log'):
@@ -26,6 +28,16 @@ class TestApplicationProto(TestControl):
return found return found
def get_appication_type(self):
current_test = (
os.environ.get('PYTEST_CURRENT_TEST').split(':')[-1].split(' ')[0]
)
if current_test in option.generated_tests:
return option.generated_tests[current_test]
return None
def _load_conf(self, conf, **kwargs): def _load_conf(self, conf, **kwargs):
if 'applications' in conf: if 'applications' in conf:
for app in conf['applications'].keys(): for app in conf['applications'].keys():
@@ -39,6 +51,4 @@ class TestApplicationProto(TestControl):
if 'isolation' in kwargs: if 'isolation' in kwargs:
app_conf['isolation'] = kwargs['isolation'] app_conf['isolation'] = kwargs['isolation']
self.assertIn( assert 'success' in self.conf(conf), 'load application configuration'
'success', self.conf(conf), 'load application configuration'
)

View File

@@ -4,19 +4,20 @@ import ssl
import subprocess import subprocess
from unit.applications.proto import TestApplicationProto from unit.applications.proto import TestApplicationProto
from conftest import option
class TestApplicationTLS(TestApplicationProto): class TestApplicationTLS(TestApplicationProto):
def __init__(self, test): def setup_method(self):
super().__init__(test) super().setup_method()
self.context = ssl.create_default_context() self.context = ssl.create_default_context()
self.context.check_hostname = False self.context.check_hostname = False
self.context.verify_mode = ssl.CERT_NONE self.context.verify_mode = ssl.CERT_NONE
@classmethod @classmethod
def setUpClass(cls, complete_check=True): def setup_class(cls, complete_check=True):
unit = super().setUpClass(complete_check=False) unit = super().setup_class(complete_check=False)
# check tls module # check tls module
@@ -45,9 +46,9 @@ class TestApplicationTLS(TestApplicationProto):
'-x509', '-x509',
'-new', '-new',
'-subj', '/CN=' + name + '/', '-subj', '/CN=' + name + '/',
'-config', self.testdir + '/openssl.conf', '-config', self.temp_dir + '/openssl.conf',
'-out', self.testdir + '/' + name + '.crt', '-out', self.temp_dir + '/' + name + '.crt',
'-keyout', self.testdir + '/' + name + '.key', '-keyout', self.temp_dir + '/' + name + '.key',
], ],
stderr=subprocess.STDOUT, stderr=subprocess.STDOUT,
) )
@@ -59,8 +60,8 @@ class TestApplicationTLS(TestApplicationProto):
if key is None: if key is None:
key = crt key = crt
key_path = self.testdir + '/' + key + '.key' key_path = self.temp_dir + '/' + key + '.key'
crt_path = self.testdir + '/' + crt + '.crt' crt_path = self.temp_dir + '/' + crt + '.crt'
with open(key_path, 'rb') as k, open(crt_path, 'rb') as c: with open(key_path, 'rb') as k, open(crt_path, 'rb') as c:
return self.conf(k.read() + c.read(), '/certificates/' + crt) return self.conf(k.read() + c.read(), '/certificates/' + crt)
@@ -87,7 +88,7 @@ class TestApplicationTLS(TestApplicationProto):
return ssl.get_server_certificate(addr, ssl_version=ssl_version) return ssl.get_server_certificate(addr, ssl_version=ssl_version)
def openssl_conf(self): def openssl_conf(self):
conf_path = self.testdir + '/openssl.conf' conf_path = self.temp_dir + '/openssl.conf'
if os.path.exists(conf_path): if os.path.exists(conf_path):
return return
@@ -105,7 +106,7 @@ distinguished_name = req_distinguished_name
if name is None: if name is None:
name = script name = script
script_path = self.current_dir + '/python/' + script script_path = option.test_dir + '/python/' + script
self._load_conf( self._load_conf(
{ {

View File

@@ -1,6 +1,7 @@
import base64 import base64
import hashlib import hashlib
import itertools import itertools
import pytest
import random import random
import re import re
import select import select
@@ -21,9 +22,6 @@ class TestApplicationWebsocket(TestApplicationProto):
OP_PONG = 0x0A OP_PONG = 0x0A
CLOSE_CODES = [1000, 1001, 1002, 1003, 1007, 1008, 1009, 1010, 1011] CLOSE_CODES = [1000, 1001, 1002, 1003, 1007, 1008, 1009, 1010, 1011]
def __init__(self, preinit=False):
self.preinit = preinit
def key(self): def key(self):
raw_key = bytes(random.getrandbits(8) for _ in range(16)) raw_key = bytes(random.getrandbits(8) for _ in range(16))
return base64.b64encode(raw_key).decode() return base64.b64encode(raw_key).decode()
@@ -56,7 +54,7 @@ class TestApplicationWebsocket(TestApplicationProto):
while True: while True:
rlist = select.select([sock], [], [], 60)[0] rlist = select.select([sock], [], [], 60)[0]
if not rlist: if not rlist:
self.fail('Can\'t read response from server.') pytest.fail('Can\'t read response from server.')
resp += sock.recv(4096).decode() resp += sock.recv(4096).decode()
@@ -84,7 +82,7 @@ class TestApplicationWebsocket(TestApplicationProto):
# For all current cases if the "read_timeout" was changed # For all current cases if the "read_timeout" was changed
# than test do not expect to get a response from server. # than test do not expect to get a response from server.
if read_timeout == 60: if read_timeout == 60:
self.fail('Can\'t read response from server.') pytest.fail('Can\'t read response from server.')
break break
data += sock.recv(bytes - len(data)) data += sock.recv(bytes - len(data))
@@ -130,19 +128,19 @@ class TestApplicationWebsocket(TestApplicationProto):
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):
self.fail('Invalid status code') pytest.fail('Invalid status code')
frame['code'] = code frame['code'] = code
frame['reason'] = reason frame['reason'] = reason
elif length == 0: elif length == 0:
frame['code'] = 1005 frame['code'] = 1005
frame['reason'] = '' frame['reason'] = ''
else: else:
self.fail('Close frame too short') pytest.fail('Close frame too short')
frame['data'] = data frame['data'] = data
if frame['mask']: if frame['mask']:
self.fail('Received frame with mask') pytest.fail('Received frame with mask')
return frame return frame

View File

@@ -53,7 +53,7 @@ class TestControl(TestHTTP):
args = { args = {
'url': url, 'url': url,
'sock_type': 'unix', 'sock_type': 'unix',
'addr': self.testdir + '/control.unit.sock', 'addr': self.temp_dir + '/control.unit.sock',
} }
if conf is not None: if conf is not None:

View File

@@ -13,7 +13,7 @@ from unit.applications.proto import TestApplicationProto
class TestFeatureIsolation(TestApplicationProto): class TestFeatureIsolation(TestApplicationProto):
allns = ['pid', 'mnt', 'ipc', 'uts', 'cgroup', 'net'] allns = ['pid', 'mnt', 'ipc', 'uts', 'cgroup', 'net']
def check(self, available, testdir): def check(self, available, temp_dir):
test_conf = {"namespaces": {"credential": True}} test_conf = {"namespaces": {"credential": True}}
module = '' module = ''
@@ -45,7 +45,7 @@ class TestFeatureIsolation(TestApplicationProto):
if not module: if not module:
return return
module.testdir = testdir module.temp_dir = temp_dir
module.load(app) module.load(app)
resp = module.conf(test_conf, 'applications/' + app + '/isolation') resp = module.conf(test_conf, 'applications/' + app + '/isolation')

View File

@@ -2,12 +2,14 @@ import binascii
import io import io
import json import json
import os import os
import pytest
import re import re
import select import select
import socket import socket
import time import time
from unit.main import TestUnit from unit.main import TestUnit
from conftest import option
class TestHTTP(TestUnit): class TestHTTP(TestUnit):
@@ -56,7 +58,7 @@ class TestHTTP(TestUnit):
sock.connect(connect_args) sock.connect(connect_args)
except ConnectionRefusedError: except ConnectionRefusedError:
sock.close() sock.close()
self.fail('Client can\'t connect to the server.') pytest.fail('Client can\'t connect to the server.')
else: else:
sock = kwargs['sock'] sock = kwargs['sock']
@@ -128,7 +130,7 @@ class TestHTTP(TestUnit):
return (resp, sock) return (resp, sock)
def log_out(self, log, encoding): def log_out(self, log, encoding):
if TestUnit.detailed: if option.detailed:
print('>>>') print('>>>')
log = self.log_truncate(log) log = self.log_truncate(log)
try: try:
@@ -137,7 +139,7 @@ class TestHTTP(TestUnit):
print(log) print(log)
def log_in(self, log): def log_in(self, log):
if TestUnit.detailed: if option.detailed:
print('<<<') print('<<<')
log = self.log_truncate(log) log = self.log_truncate(log)
try: try:
@@ -190,7 +192,7 @@ class TestHTTP(TestUnit):
# For all current cases if the "read_timeout" was changed # For all current cases if the "read_timeout" was changed
# than test do not expect to get a response from server. # than test do not expect to get a response from server.
if timeout == timeout_default: if timeout == timeout_default:
self.fail('Can\'t read response from server.') pytest.fail('Can\'t read response from server.')
break break
try: try:
@@ -243,28 +245,28 @@ class TestHTTP(TestUnit):
chunks = raw_body.split(crlf) chunks = raw_body.split(crlf)
if len(chunks) < 3: if len(chunks) < 3:
self.fail('Invalid chunked body') pytest.fail('Invalid chunked body')
if chunks.pop() != b'': if chunks.pop() != b'':
self.fail('No CRLF at the end of the body') pytest.fail('No CRLF at the end of the body')
try: try:
last_size = int(chunks[-2], 16) last_size = int(chunks[-2], 16)
except: except:
self.fail('Invalid zero size chunk') pytest.fail('Invalid zero size chunk')
if last_size != 0 or chunks[-1] != b'': if last_size != 0 or chunks[-1] != b'':
self.fail('Incomplete body') pytest.fail('Incomplete body')
body = b'' body = b''
while len(chunks) >= 2: while len(chunks) >= 2:
try: try:
size = int(chunks.pop(0), 16) size = int(chunks.pop(0), 16)
except: except:
self.fail('Invalid chunk size %s' % str(size)) pytest.fail('Invalid chunk size %s' % str(size))
if size == 0: if size == 0:
self.assertEqual(len(chunks), 1, 'last zero size') assert len(chunks) == 1, 'last zero size'
break break
temp_body = crlf.join(chunks) temp_body = crlf.join(chunks)
@@ -280,8 +282,8 @@ class TestHTTP(TestUnit):
def _parse_json(self, resp): def _parse_json(self, resp):
headers = resp['headers'] headers = resp['headers']
self.assertIn('Content-Type', headers) assert 'Content-Type' in headers
self.assertEqual(headers['Content-Type'], 'application/json') assert headers['Content-Type'] == 'application/json'
resp['body'] = json.loads(resp['body']) resp['body'] = json.loads(resp['body'])
@@ -305,7 +307,7 @@ class TestHTTP(TestUnit):
sock.close() sock.close()
self.assertTrue(ret, 'socket connected') assert ret, 'socket connected'
def form_encode(self, fields): def form_encode(self, fields):
is_multipart = False is_multipart = False
@@ -345,7 +347,7 @@ class TestHTTP(TestUnit):
datatype = value['type'] datatype = value['type']
if not isinstance(value['data'], io.IOBase): if not isinstance(value['data'], io.IOBase):
self.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()
@@ -353,7 +355,7 @@ class TestHTTP(TestUnit):
data = value data = value
else: else:
self.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\""

View File

@@ -1,8 +1,8 @@
import argparse import argparse
import atexit import atexit
import fcntl
import os import os
import platform import platform
import pytest
import re import re
import shutil import shutil
import signal import signal
@@ -11,80 +11,19 @@ import subprocess
import sys import sys
import tempfile import tempfile
import time import time
import unittest from conftest import option, public_dir, waitforfiles, _check_alerts, _print_log
from multiprocessing import Process from multiprocessing import Process
class TestUnit(unittest.TestCase): class TestUnit():
current_dir = os.path.abspath(
os.path.join(os.path.dirname(__file__), os.pardir)
)
pardir = os.path.abspath( pardir = os.path.abspath(
os.path.join(os.path.dirname(__file__), os.pardir, os.pardir) os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)
) )
is_su = os.geteuid() == 0
uid = os.geteuid()
gid = os.getegid()
architecture = platform.architecture()[0]
system = platform.system()
maxDiff = None
detailed = False
save_log = False
print_log = False
unsafe = False
def __init__(self, methodName='runTest'):
super().__init__(methodName)
if re.match(r'.*\/run\.py$', sys.argv[0]):
args, rest = TestUnit._parse_args()
TestUnit._set_args(args)
def run(self, result=None):
if not hasattr(self, 'application_type'):
return super().run(result)
# rerun test for each available module version
type = self.application_type
for module in self.prerequisites['modules']:
if module in self.available['modules']:
prereq_version = self.prerequisites['modules'][module]
available_versions = self.available['modules'][module]
if prereq_version == 'all':
for version in available_versions:
self.application_type = type + ' ' + version
super().run(result)
elif prereq_version == 'any':
self.application_type = type + ' ' + available_versions[0]
super().run(result)
else:
for version in available_versions:
if version.startswith(prereq_version):
self.application_type = type + ' ' + version
super().run(result)
@classmethod @classmethod
def main(cls): def setup_class(cls, complete_check=True):
args, rest = TestUnit._parse_args() cls.available = option.available
for i, arg in enumerate(rest):
if arg[:5] == 'test_':
rest[i] = cls.__name__ + '.' + arg
sys.argv = sys.argv[:1] + rest
TestUnit._set_args(args)
unittest.main()
@classmethod
def setUpClass(cls, complete_check=True):
cls.available = {'modules': {}, 'features': {}}
unit = TestUnit() unit = TestUnit()
unit._run() unit._run()
@@ -92,7 +31,7 @@ class TestUnit(unittest.TestCase):
# read unit.log # read unit.log
for i in range(50): for i in range(50):
with open(unit.testdir + '/unit.log', 'r') as f: with open(unit.temp_dir + '/unit.log', 'r') as f:
log = f.read() log = f.read()
m = re.search('controller started', log) m = re.search('controller started', log)
@@ -102,7 +41,7 @@ class TestUnit(unittest.TestCase):
break break
if m is None: if m is None:
unit._print_log() _print_log()
exit("Unit is writing log too long") exit("Unit is writing log too long")
# discover available modules from unit.log # discover available modules from unit.log
@@ -128,8 +67,7 @@ class TestUnit(unittest.TestCase):
missed.append(module) missed.append(module)
if missed: if missed:
print('Unit has no ' + ', '.join(missed) + ' module(s)') pytest.skip('Unit has no ' + ', '.join(missed) + ' module(s)')
raise unittest.SkipTest()
# check features # check features
@@ -143,13 +81,12 @@ class TestUnit(unittest.TestCase):
missed.append(feature) missed.append(feature)
if missed: if missed:
print(', '.join(missed) + ' feature(s) not supported') pytest.skip(', '.join(missed) + ' feature(s) not supported')
raise unittest.SkipTest()
def destroy(): def destroy():
unit.stop() unit.stop()
unit._check_alerts(log) _check_alerts(log)
shutil.rmtree(unit.testdir) shutil.rmtree(unit.temp_dir)
def complete(): def complete():
destroy() destroy()
@@ -161,7 +98,7 @@ class TestUnit(unittest.TestCase):
unit.complete = complete unit.complete = complete
return unit return unit
def setUp(self): def setup_method(self):
self._run() self._run()
def _run(self): def _run(self):
@@ -171,82 +108,56 @@ class TestUnit(unittest.TestCase):
if not os.path.isfile(self.unitd): if not os.path.isfile(self.unitd):
exit("Could not find unit") exit("Could not find unit")
self.testdir = tempfile.mkdtemp(prefix='unit-test-') self.temp_dir = tempfile.mkdtemp(prefix='unit-test-')
self.public_dir(self.testdir) public_dir(self.temp_dir)
if oct(stat.S_IMODE(os.stat(build_dir).st_mode)) != '0o777': if oct(stat.S_IMODE(os.stat(build_dir).st_mode)) != '0o777':
self.public_dir(build_dir) public_dir(build_dir)
os.mkdir(self.testdir + '/state') os.mkdir(self.temp_dir + '/state')
with open(self.testdir + '/unit.log', 'w') as log: with open(self.temp_dir + '/unit.log', 'w') as log:
self._p = subprocess.Popen( self._p = subprocess.Popen(
[ [
self.unitd, self.unitd,
'--no-daemon', '--no-daemon',
'--modules', self.pardir + '/build', '--modules', self.pardir + '/build',
'--state', self.testdir + '/state', '--state', self.temp_dir + '/state',
'--pid', self.testdir + '/unit.pid', '--pid', self.temp_dir + '/unit.pid',
'--log', self.testdir + '/unit.log', '--log', self.temp_dir + '/unit.log',
'--control', 'unix:' + self.testdir + '/control.unit.sock', '--control', 'unix:' + self.temp_dir + '/control.unit.sock',
'--tmp', self.testdir, '--tmp', self.temp_dir,
], ],
stderr=log, stderr=log,
) )
atexit.register(self.stop) atexit.register(self.stop)
if not self.waitforfiles(self.testdir + '/control.unit.sock'): if not waitforfiles(self.temp_dir + '/control.unit.sock'):
self._print_log() _print_log()
exit("Could not start unit") exit("Could not start unit")
self._started = True self._started = True
self.skip_alerts = [ def teardown_method(self):
r'read signalfd\(4\) failed',
r'sendmsg.+failed',
r'recvmsg.+failed',
]
self.skip_sanitizer = False
def tearDown(self):
self.stop() self.stop()
# detect errors and failures for current test
def list2reason(exc_list):
if exc_list and exc_list[-1][0] is self:
return exc_list[-1][1]
if hasattr(self, '_outcome'):
result = self.defaultTestResult()
self._feedErrorsToResult(result, self._outcome.errors)
else:
result = getattr(
self, '_outcomeForDoCleanups', self._resultForDoCleanups
)
success = not list2reason(result.errors) and not list2reason(
result.failures
)
# check unit.log for alerts # check unit.log for alerts
unit_log = self.testdir + '/unit.log' unit_log = self.temp_dir + '/unit.log'
with open(unit_log, 'r', encoding='utf-8', errors='ignore') as f: with open(unit_log, 'r', encoding='utf-8', errors='ignore') as f:
self._check_alerts(f.read()) _check_alerts(f.read())
# remove unit.log # remove unit.log
if not TestUnit.save_log and success: if not option.save_log:
shutil.rmtree(self.testdir) shutil.rmtree(self.temp_dir)
else: else:
self._print_log() _print_log()
self.assertListEqual(self.stop_errors, [None, None], 'stop errors') assert self.stop_errors == [None, None], 'stop errors'
def stop(self): def stop(self):
if not self._started: if not self._started:
@@ -301,121 +212,3 @@ class TestUnit(unittest.TestCase):
if fail: if fail:
return 'Fail to stop process' return 'Fail to stop process'
def waitforfiles(self, *files):
for i in range(50):
wait = False
ret = False
for f in files:
if not os.path.exists(f):
wait = True
break
if wait:
time.sleep(0.1)
else:
ret = True
break
return ret
def public_dir(self, path):
os.chmod(path, 0o777)
for root, dirs, files in os.walk(path):
for d in dirs:
os.chmod(os.path.join(root, d), 0o777)
for f in files:
os.chmod(os.path.join(root, f), 0o777)
def _check_alerts(self, log):
found = False
alerts = re.findall('.+\[alert\].+', log)
if alerts:
print('All alerts/sanitizer errors found in log:')
[print(alert) for alert in alerts]
found = True
if self.skip_alerts:
for skip in self.skip_alerts:
alerts = [al for al in alerts if re.search(skip, al) is None]
if alerts:
self._print_log(log)
self.assertFalse(alerts, 'alert(s)')
if not self.skip_sanitizer:
sanitizer_errors = re.findall('.+Sanitizer.+', log)
if sanitizer_errors:
self._print_log(log)
self.assertFalse(sanitizer_errors, 'sanitizer error(s)')
if found:
print('skipped.')
@staticmethod
def _parse_args():
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument(
'-d',
'--detailed',
dest='detailed',
action='store_true',
help='Detailed output for tests',
)
parser.add_argument(
'-l',
'--log',
dest='save_log',
action='store_true',
help='Save unit.log after the test execution',
)
parser.add_argument(
'-r',
'--reprint_log',
dest='print_log',
action='store_true',
help='Print unit.log to stdout in case of errors',
)
parser.add_argument(
'-u',
'--unsafe',
dest='unsafe',
action='store_true',
help='Run unsafe tests',
)
return parser.parse_known_args()
@staticmethod
def _set_args(args):
TestUnit.detailed = args.detailed
TestUnit.save_log = args.save_log
TestUnit.print_log = args.print_log
TestUnit.unsafe = args.unsafe
# set stdout to non-blocking
if TestUnit.detailed or TestUnit.print_log:
fcntl.fcntl(sys.stdout.fileno(), fcntl.F_SETFL, 0)
def _print_log(self, data=None):
path = self.testdir + '/unit.log'
print('Path to unit.log:\n' + path + '\n')
if TestUnit.print_log:
os.set_blocking(sys.stdout.fileno(), True)
sys.stdout.flush()
if data is None:
with open(path, 'r', encoding='utf-8', errors='ignore') as f:
shutil.copyfileobj(f, sys.stdout)
else:
sys.stdout.write(data)