summaryrefslogtreecommitdiffhomepage
path: root/test
diff options
context:
space:
mode:
authorAndrei Belov <defan@nginx.com>2021-08-19 18:17:12 +0300
committerAndrei Belov <defan@nginx.com>2021-08-19 18:17:12 +0300
commitdb442f1be7e713e6a219621ff97a51046590dbd6 (patch)
tree913734275bc890ec175e51fcb0f36b01a3c52c24 /test
parenta1d2ced6fc2317d36bc917c5d0ac339bc647dc34 (diff)
parent13c0025dfa6e041563d0ad5dd81679b44522694c (diff)
downloadunit-1.25.0-1.tar.gz
unit-1.25.0-1.tar.bz2
Merged with the default branch.1.25.0-1
Diffstat (limited to '')
-rw-r--r--test/conftest.py169
-rw-r--r--test/python/client_ip/wsgi.py4
-rw-r--r--test/python/restart/longstart.py10
-rw-r--r--test/python/restart/v1.py7
-rw-r--r--test/python/restart/v2.py7
-rw-r--r--test/ruby/hooks/config.ru7
-rw-r--r--test/ruby/hooks/eval.rb3
-rw-r--r--test/ruby/hooks/multiple.rb13
-rw-r--r--test/ruby/hooks/on_thread_boot.rb9
-rw-r--r--test/ruby/hooks/on_thread_shutdown.rb9
-rw-r--r--test/ruby/hooks/on_worker_boot.rb5
-rw-r--r--test/ruby/hooks/on_worker_shutdown.rb5
-rw-r--r--test/test_client_ip.py129
-rw-r--r--test/test_configuration.py82
-rw-r--r--test/test_node_es_modules.py3
-rw-r--r--test/test_python_procman.py79
-rw-r--r--test/test_respawn.py15
-rw-r--r--test/test_routing.py4
-rw-r--r--test/test_ruby_hooks.py98
-rw-r--r--test/test_ruby_isolation.py7
-rw-r--r--test/test_static_chroot.py (renamed from test/test_share_chroot.py)22
-rw-r--r--test/test_static_fallback.py (renamed from test/test_share_fallback.py)37
-rw-r--r--test/test_static_mount.py (renamed from test/test_share_mount.py)18
-rw-r--r--test/test_static_symlink.py (renamed from test/test_share_symlink.py)16
-rw-r--r--test/test_static_types.py (renamed from test/test_share_types.py)21
-rw-r--r--test/test_tls.py13
-rw-r--r--test/test_tls_sni.py20
-rw-r--r--test/test_variables.py19
-rw-r--r--test/unit/applications/lang/java.py2
-rw-r--r--test/unit/applications/lang/python.py1
-rw-r--r--test/unit/applications/lang/ruby.py38
-rw-r--r--test/unit/applications/proto.py16
-rw-r--r--test/unit/check/isolation.py7
-rw-r--r--test/unit/http.py2
-rw-r--r--test/unit/utils.py28
35 files changed, 692 insertions, 233 deletions
diff --git a/test/conftest.py b/test/conftest.py
index 5ea4e49d..4d46e2fc 100644
--- a/test/conftest.py
+++ b/test/conftest.py
@@ -22,8 +22,8 @@ from unit.check.node import check_node
from unit.check.regex import check_regex
from unit.check.tls import check_openssl
from unit.http import TestHTTP
-from unit.option import option
from unit.log import Log
+from unit.option import option
from unit.utils import public_dir
from unit.utils import waitforfiles
@@ -74,7 +74,7 @@ def pytest_addoption(parser):
unit_instance = {}
_processes = []
-_fds_check = {
+_fds_info = {
'main': {'fds': 0, 'skip': False},
'router': {'name': 'unit: router', 'pid': -1, 'fds': 0, 'skip': False},
'controller': {
@@ -115,6 +115,17 @@ def pytest_configure(config):
fcntl.fcntl(sys.stdout.fileno(), fcntl.F_SETFL, 0)
+def print_log_on_assert(func):
+ def inner_function(*args, **kwargs):
+ try:
+ func(*args, **kwargs)
+ except AssertionError as e:
+ _print_log(kwargs.get('log', None))
+ raise e
+
+ return inner_function
+
+
def pytest_generate_tests(metafunc):
cls = metafunc.cls
if (
@@ -275,9 +286,9 @@ def run(request):
]
option.skip_sanitizer = False
- _fds_check['main']['skip'] = False
- _fds_check['router']['skip'] = False
- _fds_check['controller']['skip'] = False
+ _fds_info['main']['skip'] = False
+ _fds_info['router']['skip'] = False
+ _fds_info['controller']['skip'] = False
yield
@@ -299,7 +310,7 @@ def run(request):
# clean temp_dir before the next test
if not option.restart:
- _clear_conf(unit['temp_dir'] + '/control.unit.sock', log)
+ _clear_conf(unit['temp_dir'] + '/control.unit.sock', log=log)
for item in os.listdir(unit['temp_dir']):
if item not in [
@@ -317,53 +328,18 @@ def run(request):
):
os.remove(path)
else:
- shutil.rmtree(path)
-
- # check descriptors (wait for some time before check)
-
- def waitforfds(diff):
- for i in range(600):
- fds_diff = diff()
-
- if fds_diff <= option.fds_threshold:
- break
-
- time.sleep(0.1)
-
- return fds_diff
-
- ps = _fds_check['main']
- if not ps['skip']:
- fds_diff = waitforfds(
- lambda: _count_fds(unit_instance['pid']) - ps['fds']
- )
- ps['fds'] += fds_diff
-
- assert (
- fds_diff <= option.fds_threshold
- ), 'descriptors leak main process'
-
- else:
- ps['fds'] = _count_fds(unit_instance['pid'])
-
- for name in ['controller', 'router']:
- ps = _fds_check[name]
- ps_pid = ps['pid']
- ps['pid'] = pid_by_name(ps['name'])
-
- if not ps['skip']:
- fds_diff = waitforfds(lambda: _count_fds(ps['pid']) - ps['fds'])
- ps['fds'] += fds_diff
-
- if not option.restart:
- assert ps['pid'] == ps_pid, 'same pid %s' % name
+ for attempt in range(10):
+ try:
+ shutil.rmtree(path)
+ break
+ except OSError as err:
+ if err.errno != 16:
+ raise
+ time.sleep(1)
- assert fds_diff <= option.fds_threshold, (
- 'descriptors leak %s' % name
- )
+ # check descriptors
- else:
- ps['fds'] = _count_fds(ps['pid'])
+ _check_fds(log=log)
# print unit.log in case of error
@@ -424,6 +400,8 @@ def unit_run():
with open(temp_dir + '/unit.log', 'w') as log:
unit_instance['process'] = subprocess.Popen(unitd_args, stderr=log)
+ Log.temp_dir = temp_dir
+
if not waitforfiles(temp_dir + '/control.unit.sock'):
_print_log()
exit('Could not start unit')
@@ -433,20 +411,19 @@ def unit_run():
unit_instance['unitd'] = unitd
option.temp_dir = temp_dir
- Log.temp_dir = temp_dir
with open(temp_dir + '/unit.pid', 'r') as f:
unit_instance['pid'] = f.read().rstrip()
_clear_conf(unit_instance['temp_dir'] + '/control.unit.sock')
- _fds_check['main']['fds'] = _count_fds(unit_instance['pid'])
+ _fds_info['main']['fds'] = _count_fds(unit_instance['pid'])
- router = _fds_check['router']
+ router = _fds_info['router']
router['pid'] = pid_by_name(router['name'])
router['fds'] = _count_fds(router['pid'])
- controller = _fds_check['controller']
+ controller = _fds_info['controller']
controller['pid'] = pid_by_name(controller['name'])
controller['fds'] = _count_fds(controller['pid'])
@@ -481,7 +458,8 @@ def unit_stop():
return 'Could not terminate unit'
-def _check_alerts(log=None):
+@print_log_on_assert
+def _check_alerts(*, log=None):
if log is None:
with Log.open(encoding='utf-8') as f:
log = f.read()
@@ -499,22 +477,18 @@ def _check_alerts(log=None):
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)'
+ 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)'
+ assert not sanitizer_errors, 'sanitizer error(s)'
if found:
print('skipped.')
-def _print_log(data=None):
+def _print_log(log=None):
path = Log.get_path()
print('Path to unit.log:\n' + path + '\n')
@@ -523,19 +497,15 @@ def _print_log(data=None):
os.set_blocking(sys.stdout.fileno(), True)
sys.stdout.flush()
- if data is None:
+ if log is None:
with open(path, 'r', encoding='utf-8', errors='ignore') as f:
shutil.copyfileobj(f, sys.stdout)
else:
- sys.stdout.write(data)
+ sys.stdout.write(log)
-def _clear_conf(sock, log=None):
- def check_success(resp):
- if 'success' not in resp:
- _print_log(log)
- assert 'success' in resp
-
+@print_log_on_assert
+def _clear_conf(sock, *, log=None):
resp = http.put(
url='/config',
sock_type='unix',
@@ -543,7 +513,7 @@ def _clear_conf(sock, log=None):
body=json.dumps({"listeners": {}, "applications": {}}),
)['body']
- check_success(resp)
+ assert 'success' in resp, 'clear conf'
if 'openssl' not in option.available['modules']:
return
@@ -561,7 +531,54 @@ def _clear_conf(sock, log=None):
url='/certificates/' + cert, sock_type='unix', addr=sock,
)['body']
- check_success(resp)
+ assert 'success' in resp, 'remove certificate'
+
+
+@print_log_on_assert
+def _check_fds(*, log=None):
+ def waitforfds(diff):
+ for i in range(600):
+ fds_diff = diff()
+
+ if fds_diff <= option.fds_threshold:
+ break
+
+ time.sleep(0.1)
+
+ return fds_diff
+
+ ps = _fds_info['main']
+ if not ps['skip']:
+ fds_diff = waitforfds(
+ lambda: _count_fds(unit_instance['pid']) - ps['fds']
+ )
+ ps['fds'] += fds_diff
+
+ assert (
+ fds_diff <= option.fds_threshold
+ ), 'descriptors leak main process'
+
+ else:
+ ps['fds'] = _count_fds(unit_instance['pid'])
+
+ for name in ['controller', 'router']:
+ ps = _fds_info[name]
+ ps_pid = ps['pid']
+ ps['pid'] = pid_by_name(ps['name'])
+
+ if not ps['skip']:
+ fds_diff = waitforfds(lambda: _count_fds(ps['pid']) - ps['fds'])
+ ps['fds'] += fds_diff
+
+ if not option.restart:
+ assert ps['pid'] == ps_pid, 'same pid %s' % name
+
+ assert fds_diff <= option.fds_threshold, (
+ 'descriptors leak %s' % name
+ )
+
+ else:
+ ps['fds'] = _count_fds(ps['pid'])
def _count_fds(pid):
@@ -639,9 +656,9 @@ def skip_alert():
@pytest.fixture()
def skip_fds_check():
def _skip(main=False, router=False, controller=False):
- _fds_check['main']['skip'] = main
- _fds_check['router']['skip'] = router
- _fds_check['controller']['skip'] = controller
+ _fds_info['main']['skip'] = main
+ _fds_info['router']['skip'] = router
+ _fds_info['controller']['skip'] = controller
return _skip
diff --git a/test/python/client_ip/wsgi.py b/test/python/client_ip/wsgi.py
new file mode 100644
index 00000000..0e12db0a
--- /dev/null
+++ b/test/python/client_ip/wsgi.py
@@ -0,0 +1,4 @@
+def application(env, start_response):
+ ip = env['REMOTE_ADDR'].encode()
+ start_response('200', [('Content-Length', str(len(ip)))])
+ return ip
diff --git a/test/python/restart/longstart.py b/test/python/restart/longstart.py
new file mode 100644
index 00000000..777398ac
--- /dev/null
+++ b/test/python/restart/longstart.py
@@ -0,0 +1,10 @@
+import os
+import time
+
+time.sleep(2)
+
+def application(environ, start_response):
+ body = str(os.getpid()).encode()
+
+ start_response('200', [('Content-Length', str(len(body)))])
+ return [body]
diff --git a/test/python/restart/v1.py b/test/python/restart/v1.py
new file mode 100644
index 00000000..2e45b269
--- /dev/null
+++ b/test/python/restart/v1.py
@@ -0,0 +1,7 @@
+import os
+
+def application(environ, start_response):
+ body = "v1".encode()
+
+ start_response('200', [('Content-Length', str(len(body)))])
+ return [body]
diff --git a/test/python/restart/v2.py b/test/python/restart/v2.py
new file mode 100644
index 00000000..59e3d30f
--- /dev/null
+++ b/test/python/restart/v2.py
@@ -0,0 +1,7 @@
+import os
+
+def application(environ, start_response):
+ body = "v2".encode()
+
+ start_response('200', [('Content-Length', str(len(body)))])
+ return [body]
diff --git a/test/ruby/hooks/config.ru b/test/ruby/hooks/config.ru
new file mode 100644
index 00000000..f3069558
--- /dev/null
+++ b/test/ruby/hooks/config.ru
@@ -0,0 +1,7 @@
+app = Proc.new do |env|
+ ['200', {
+ 'Content-Length' => '0'
+ }, ['']]
+end
+
+run app
diff --git a/test/ruby/hooks/eval.rb b/test/ruby/hooks/eval.rb
new file mode 100644
index 00000000..ce7329c1
--- /dev/null
+++ b/test/ruby/hooks/eval.rb
@@ -0,0 +1,3 @@
+require 'securerandom'
+
+File.write("./cookie_eval.#{SecureRandom.hex}", "evaluated")
diff --git a/test/ruby/hooks/multiple.rb b/test/ruby/hooks/multiple.rb
new file mode 100644
index 00000000..b1b659a5
--- /dev/null
+++ b/test/ruby/hooks/multiple.rb
@@ -0,0 +1,13 @@
+require 'securerandom'
+
+@mutex = Mutex.new
+
+on_worker_boot do
+ File.write("./cookie_worker_boot.#{SecureRandom.hex}", "worker booted")
+end
+
+on_thread_boot do
+ @mutex.synchronize do
+ File.write("./cookie_thread_boot.#{SecureRandom.hex}", "thread booted")
+ end
+end
diff --git a/test/ruby/hooks/on_thread_boot.rb b/test/ruby/hooks/on_thread_boot.rb
new file mode 100644
index 00000000..4f88424e
--- /dev/null
+++ b/test/ruby/hooks/on_thread_boot.rb
@@ -0,0 +1,9 @@
+require 'securerandom'
+
+@mutex = Mutex.new
+
+on_thread_boot do
+ @mutex.synchronize do
+ File.write("./cookie_thread_boot.#{SecureRandom.hex}", "booted")
+ end
+end
diff --git a/test/ruby/hooks/on_thread_shutdown.rb b/test/ruby/hooks/on_thread_shutdown.rb
new file mode 100644
index 00000000..d953b8b7
--- /dev/null
+++ b/test/ruby/hooks/on_thread_shutdown.rb
@@ -0,0 +1,9 @@
+require 'securerandom'
+
+@mutex = Mutex.new
+
+on_thread_shutdown do
+ @mutex.synchronize do
+ File.write("./cookie_thread_shutdown.#{SecureRandom.hex}", "shutdown")
+ end
+end
diff --git a/test/ruby/hooks/on_worker_boot.rb b/test/ruby/hooks/on_worker_boot.rb
new file mode 100644
index 00000000..b6529f60
--- /dev/null
+++ b/test/ruby/hooks/on_worker_boot.rb
@@ -0,0 +1,5 @@
+require 'securerandom'
+
+on_worker_boot do
+ File.write("./cookie_worker_boot.#{SecureRandom.hex}", "booted")
+end
diff --git a/test/ruby/hooks/on_worker_shutdown.rb b/test/ruby/hooks/on_worker_shutdown.rb
new file mode 100644
index 00000000..9ffaad93
--- /dev/null
+++ b/test/ruby/hooks/on_worker_shutdown.rb
@@ -0,0 +1,5 @@
+require 'securerandom'
+
+on_worker_shutdown do
+ File.write("./cookie_worker_shutdown.#{SecureRandom.hex}", "shutdown")
+end
diff --git a/test/test_client_ip.py b/test/test_client_ip.py
new file mode 100644
index 00000000..0084574e
--- /dev/null
+++ b/test/test_client_ip.py
@@ -0,0 +1,129 @@
+import pytest
+
+from unit.applications.lang.python import TestApplicationPython
+
+
+class TestClientIP(TestApplicationPython):
+ prerequisites = {'modules': {'python': 'any'}}
+
+ def client_ip(self, options):
+ assert 'success' in self.conf(
+ {
+ "127.0.0.1:7081":
+ {"client_ip": options, "pass": "applications/client_ip"},
+ "[::1]:7082":
+ {"client_ip": options, "pass": "applications/client_ip"},
+ },
+ 'listeners',
+ ), 'listeners configure'
+
+ def get_xff(self, xff, sock_type='ipv4'):
+ port = 7081 if sock_type == 'ipv4' else 7082
+
+ return self.get(
+ sock_type=sock_type,
+ port=port,
+ headers={'Connection': 'close', 'X-Forwarded-For': xff},
+ )['body']
+
+ def setup_method(self):
+ self.load('client_ip')
+
+ def test_settings_client_ip_single_ip(self):
+ self.client_ip(
+ {'header': 'X-Forwarded-For', 'source': '123.123.123.123'}
+ )
+
+ assert self.get(port=7081)['body'] == '127.0.0.1', 'ipv4 default'
+ assert (
+ self.get(sock_type='ipv6', port=7082)['body'] == '::1'
+ ), 'ipv6 default'
+ assert self.get_xff('1.1.1.1') == '127.0.0.1', 'bad source'
+ assert self.get_xff('blah') == '127.0.0.1', 'bad header'
+ assert self.get_xff('1.1.1.1', 'ipv6') == '::1', 'bad source ipv6'
+
+ self.client_ip({'header': 'X-Forwarded-For', 'source': '127.0.0.1'})
+
+ assert self.get(port=7081)['body'] == '127.0.0.1', 'ipv4 default 2'
+ assert (
+ self.get(sock_type='ipv6', port=7082)['body'] == '::1'
+ ), 'ipv6 default 2'
+ assert self.get_xff('1.1.1.1') == '1.1.1.1', 'replace'
+ assert self.get_xff('blah') == '127.0.0.1', 'bad header 2'
+ assert (
+ self.get_xff('1.1.1.1', 'ipv6') == '::1'
+ ), 'bad source ipv6 2'
+
+ self.client_ip({'header': 'X-Forwarded-For', 'source': '!127.0.0.1'})
+
+ assert self.get_xff('1.1.1.1') == '127.0.0.1', 'bad source 3'
+ assert self.get_xff('1.1.1.1', 'ipv6') == '1.1.1.1', 'replace 2'
+
+ def test_settings_client_ip_ipv4(self):
+ self.client_ip({'header': 'X-Forwarded-For', 'source': '127.0.0.1'})
+
+ assert (
+ self.get_xff('8.8.8.8, 84.23.23.11') == '84.23.23.11'
+ ), 'xff replace'
+ assert (
+ self.get_xff('8.8.8.8, 84.23.23.11, 127.0.0.1') == '127.0.0.1'
+ ), 'xff replace 2'
+ assert (
+ self.get_xff(['8.8.8.8', '127.0.0.1, 10.0.1.1']) == '10.0.1.1'
+ ), 'xff replace multi'
+
+ def test_settings_client_ip_ipv6(self):
+ self.client_ip({'header': 'X-Forwarded-For', 'source': '::1'})
+
+ assert self.get_xff('1.1.1.1') == '127.0.0.1', 'bad source ipv4'
+
+ for ip in [
+ 'f607:7403:1e4b:6c66:33b2:843f:2517:da27',
+ '2001:db8:3c4d:15::1a2f:1a2b',
+ '2001::3c4d:15:1a2f:1a2b',
+ '::11.22.33.44',
+ ]:
+ assert self.get_xff(ip, 'ipv6') == ip, 'replace'
+
+ def test_settings_client_ip_recursive(self):
+ self.client_ip(
+ {
+ 'header': 'X-Forwarded-For',
+ 'recursive': True,
+ 'source': ['127.0.0.1', '10.50.0.17', '10.5.2.1'],
+ }
+ )
+
+ assert self.get_xff('1.1.1.1') == '1.1.1.1', 'xff chain'
+ assert self.get_xff('1.1.1.1, 10.5.2.1') == '1.1.1.1', 'xff chain 2'
+ assert (
+ self.get_xff('8.8.8.8, 1.1.1.1, 10.5.2.1') == '1.1.1.1'
+ ), 'xff chain 3'
+ assert (
+ self.get_xff('10.50.0.17, 10.5.2.1, 10.5.2.1') == '10.50.0.17'
+ ), 'xff chain 4'
+ assert (
+ self.get_xff(['8.8.8.8', '1.1.1.1, 127.0.0.1']) == '1.1.1.1'
+ ), 'xff replace multi'
+ assert (
+ self.get_xff(['8.8.8.8', '1.1.1.1, 127.0.0.1', '10.5.2.1'])
+ == '1.1.1.1'
+ ), 'xff replace multi 2'
+ assert (
+ self.get_xff(['10.5.2.1', '10.50.0.17, 1.1.1.1', '10.5.2.1'])
+ == '1.1.1.1'
+ ), 'xff replace multi 3'
+ assert (
+ self.get_xff('8.8.8.8, 2001:db8:3c4d:15::1a2f:1a2b, 127.0.0.1')
+ == '2001:db8:3c4d:15::1a2f:1a2b'
+ ), 'xff chain ipv6'
+
+ def test_settings_client_ip_invalid(self):
+ assert 'error' in self.conf(
+ {"http": {"client_ip": {'header': 'X-Forwarded-For', 'source': []}}},
+ 'settings',
+ ), 'empty array source'
+ assert 'error' in self.conf(
+ {"http":{"client_ip": {'header': 'X-Forwarded-For', 'source': 'a'}}},
+ 'settings',
+ ), 'empty source invalid'
diff --git a/test/test_configuration.py b/test/test_configuration.py
index 880aef6c..8655968f 100644
--- a/test/test_configuration.py
+++ b/test/test_configuration.py
@@ -1,12 +1,22 @@
+import socket
+
import pytest
-import socket
from unit.control import TestControl
class TestConfiguration(TestControl):
prerequisites = {'modules': {'python': 'any'}}
+ def try_addr(self, addr):
+ return self.conf(
+ {
+ "listeners": {addr: {"pass": "routes"}},
+ "routes": [{"action": {"return": 200}}],
+ "applications": {},
+ }
+ )
+
def test_json_empty(self):
assert 'error' in self.conf(''), 'empty'
@@ -217,50 +227,20 @@ class TestConfiguration(TestControl):
{"*:7080": {"pass": "applications/app"}}, 'listeners'
), 'listeners no app'
- def test_listeners_wildcard(self):
- assert 'success' in self.conf(
- {
- "listeners": {"*:7080": {"pass": "applications/app"}},
- "applications": {
- "app": {
- "type": "python",
- "processes": {"spare": 0},
- "path": "/app",
- "module": "wsgi",
- }
- },
- }
- ), 'listeners wildcard'
+ def test_listeners_addr(self):
+ assert 'success' in self.try_addr("*:7080"), 'wildcard'
+ assert 'success' in self.try_addr("127.0.0.1:7081"), 'explicit'
+ assert 'success' in self.try_addr("[::1]:7082"), 'explicit ipv6'
- def test_listeners_explicit(self):
- assert 'success' in self.conf(
- {
- "listeners": {"127.0.0.1:7080": {"pass": "applications/app"}},
- "applications": {
- "app": {
- "type": "python",
- "processes": {"spare": 0},
- "path": "/app",
- "module": "wsgi",
- }
- },
- }
- ), 'explicit'
+ def test_listeners_addr_error(self):
+ assert 'error' in self.try_addr("127.0.0.1"), 'no port'
- def test_listeners_explicit_ipv6(self):
- assert 'success' in self.conf(
- {
- "listeners": {"[::1]:7080": {"pass": "applications/app"}},
- "applications": {
- "app": {
- "type": "python",
- "processes": {"spare": 0},
- "path": "/app",
- "module": "wsgi",
- }
- },
- }
- ), 'explicit ipv6'
+ def test_listeners_addr_error_2(self, skip_alert):
+ skip_alert(r'bind.*failed', r'failed to apply new conf')
+
+ assert 'error' in self.try_addr(
+ "[f607:7403:1e4b:6c66:33b2:843f:2517:da27]:7080"
+ )
def test_listeners_port_release(self):
for i in range(10):
@@ -289,22 +269,6 @@ class TestConfiguration(TestControl):
assert 'success' in resp, 'port release'
- @pytest.mark.skip('not yet, unsafe')
- def test_listeners_no_port(self):
- assert 'error' in self.conf(
- {
- "listeners": {"127.0.0.1": {"pass": "applications/app"}},
- "applications": {
- "app": {
- "type": "python",
- "processes": {"spare": 0},
- "path": "/app",
- "module": "wsgi",
- }
- },
- }
- ), 'no port'
-
def test_json_application_name_large(self):
name = "X" * 1024 * 1024
diff --git a/test/test_node_es_modules.py b/test/test_node_es_modules.py
index 0945a967..5464d4a6 100644
--- a/test/test_node_es_modules.py
+++ b/test/test_node_es_modules.py
@@ -1,6 +1,7 @@
+from distutils.version import LooseVersion
+
import pytest
-from distutils.version import LooseVersion
from unit.applications.lang.node import TestApplicationNode
from unit.applications.websockets import TestApplicationWebsocket
diff --git a/test/test_python_procman.py b/test/test_python_procman.py
index b0d0f5af..a95c5680 100644
--- a/test/test_python_procman.py
+++ b/test/test_python_procman.py
@@ -1,4 +1,5 @@
import re
+import shutil
import subprocess
import time
@@ -201,3 +202,81 @@ class TestPythonProcman(TestApplicationPython):
assert 'success' in self.conf({"listeners": {}, "applications": {}})
assert len(self.pids_for_process()) == 0, 'stop all'
+
+ def test_python_restart(self, temp_dir):
+ shutil.copyfile(
+ option.test_dir + '/python/restart/v1.py', temp_dir + '/wsgi.py'
+ )
+
+ self.load(
+ temp_dir,
+ name=self.app_name,
+ processes=1,
+ environment={'PYTHONDONTWRITEBYTECODE': '1'},
+ )
+
+ b = self.get()['body']
+ assert b == "v1", 'process started'
+
+ shutil.copyfile(
+ option.test_dir + '/python/restart/v2.py', temp_dir + '/wsgi.py'
+ )
+
+ b = self.get()['body']
+ assert b == "v1", 'still old process'
+
+ assert 'success' in self.conf_get(
+ '/control/applications/' + self.app_name + '/restart'
+ ), 'restart processes'
+
+ b = self.get()['body']
+ assert b == "v2", 'new process started'
+
+ assert 'error' in self.conf_get(
+ '/control/applications/blah/restart'
+ ), 'application incorrect'
+
+ assert 'error' in self.conf_delete(
+ '/control/applications/' + self.app_name + '/restart'
+ ), 'method incorrect'
+
+ def test_python_restart_multi(self):
+ self.conf_proc('2')
+
+ pids = self.pids_for_process()
+ assert len(pids) == 2, 'restart 2 started'
+
+ assert 'success' in self.conf_get(
+ '/control/applications/' + self.app_name + '/restart'
+ ), 'restart processes'
+
+ new_pids = self.pids_for_process()
+ assert len(new_pids) == 2, 'restart still 2'
+
+ assert len(new_pids.intersection(pids)) == 0, 'restart all new'
+
+ def test_python_restart_longstart(self):
+ self.load(
+ 'restart',
+ name=self.app_name,
+ module="longstart",
+ processes={"spare": 1, "max": 2, "idle_timeout": 5},
+ )
+
+ assert len(self.pids_for_process()) == 1, 'longstarts == 1'
+
+ pid = self.get()['body']
+ pids = self.pids_for_process()
+ assert len(pids) == 2, 'longstarts == 2'
+
+ assert 'success' in self.conf_get(
+ '/control/applications/' + self.app_name + '/restart'
+ ), 'restart processes'
+
+ # wait for longstarted app
+ time.sleep(2)
+
+ new_pids = self.pids_for_process()
+ assert len(new_pids) == 1, 'restart 1'
+
+ assert len(new_pids.intersection(pids)) == 0, 'restart all new'
diff --git a/test/test_respawn.py b/test/test_respawn.py
index edbfa2a8..5a5d6126 100644
--- a/test/test_respawn.py
+++ b/test/test_respawn.py
@@ -44,11 +44,16 @@ class TestRespawn(TestApplicationPython):
return re.findall(str(ppid) + r'.*' + name, ps_output)
def smoke_test(self, unit_pid):
- for _ in range(5):
- assert 'success' in self.conf(
- '1', 'applications/' + self.app_name + '/processes'
- )
- assert self.get()['status'] == 200
+ for _ in range(10):
+ r = self.conf('1', 'applications/' + self.app_name + '/processes')
+
+ if 'success' in r:
+ break
+
+ time.sleep(0.1)
+
+ assert 'success' in r
+ assert self.get()['status'] == 200
# Check if the only one router, controller,
# and application processes running.
diff --git a/test/test_routing.py b/test/test_routing.py
index eaa0a134..ef5622c2 100644
--- a/test/test_routing.py
+++ b/test/test_routing.py
@@ -1751,6 +1751,10 @@ class TestRouting(TestApplicationProto):
self.route_match_invalid({"source": "*:1-a"})
self.route_match_invalid({"source": "*:65536"})
+ def test_routes_match_source_none(self):
+ self.route_match({"source": []})
+ assert self.get()['status'] == 404, 'source none'
+
def test_routes_match_destination(self):
assert 'success' in self.conf(
{"*:7080": {"pass": "routes"}, "*:7081": {"pass": "routes"}},
diff --git a/test/test_ruby_hooks.py b/test/test_ruby_hooks.py
new file mode 100644
index 00000000..af8ce337
--- /dev/null
+++ b/test/test_ruby_hooks.py
@@ -0,0 +1,98 @@
+import os
+import time
+from pathlib import Path
+
+import pytest
+
+from conftest import unit_stop
+from unit.applications.lang.ruby import TestApplicationRuby
+from unit.option import option
+from unit.utils import waitforglob
+
+
+class TestRubyHooks(TestApplicationRuby):
+ prerequisites = {'modules': {'ruby': 'all'}}
+
+ def _wait_cookie(self, pattern, count):
+ return waitforglob(
+ option.temp_dir + '/ruby/hooks/cookie_' + pattern, count
+ )
+
+ def test_ruby_hooks_eval(self):
+ processes = 2
+
+ self.load('hooks', processes=processes, hooks='eval.rb')
+
+ hooked = self._wait_cookie('eval.*', processes)
+
+ assert hooked, 'hooks evaluated'
+
+ def test_ruby_hooks_on_worker_boot(self):
+ processes = 2
+
+ self.load('hooks', processes=processes, hooks='on_worker_boot.rb')
+
+ hooked = self._wait_cookie('worker_boot.*', processes)
+
+ assert hooked, 'on_worker_boot called'
+
+ def test_ruby_hooks_on_worker_shutdown(self):
+ processes = 2
+
+ self.load('hooks', processes=processes, hooks='on_worker_shutdown.rb')
+
+ assert self.get()['status'] == 200, 'app response'
+
+ self.load('empty')
+
+ hooked = self._wait_cookie('worker_shutdown.*', processes)
+
+ assert hooked, 'on_worker_shutdown called'
+
+ def test_ruby_hooks_on_thread_boot(self):
+ processes = 1
+ threads = 2
+
+ self.load(
+ 'hooks',
+ processes=processes,
+ threads=threads,
+ hooks='on_thread_boot.rb',
+ )
+
+ hooked = self._wait_cookie('thread_boot.*', processes * threads)
+
+ assert hooked, 'on_thread_boot called'
+
+ def test_ruby_hooks_on_thread_shutdown(self):
+ processes = 1
+ threads = 2
+
+ self.load(
+ 'hooks',
+ processes=processes,
+ threads=threads,
+ hooks='on_thread_shutdown.rb',
+ )
+
+ assert self.get()['status'] == 200, 'app response'
+
+ self.load('empty')
+
+ hooked = self._wait_cookie('thread_shutdown.*', processes * threads)
+
+ assert hooked, 'on_thread_shutdown called'
+
+ def test_ruby_hooks_multiple(self):
+ processes = 1
+ threads = 1
+
+ self.load(
+ 'hooks', processes=processes, threads=threads, hooks='multiple.rb',
+ )
+
+ hooked = self._wait_cookie('worker_boot.*', processes)
+ assert hooked, 'on_worker_boot called'
+
+ hooked = self._wait_cookie('thread_boot.*', threads)
+ assert hooked, 'on_thread_boot called'
diff --git a/test/test_ruby_isolation.py b/test/test_ruby_isolation.py
index 8443d857..f414d610 100644
--- a/test/test_ruby_isolation.py
+++ b/test/test_ruby_isolation.py
@@ -35,13 +35,6 @@ class TestRubyIsolation(TestApplicationRuby):
'pid': True,
}
- os.mkdir(option.temp_dir + '/ruby')
-
- shutil.copytree(
- option.test_dir + '/ruby/status_int',
- option.temp_dir + '/ruby/status_int',
- )
-
self.load('status_int', isolation=isolation)
assert 'success' in self.conf(
diff --git a/test/test_share_chroot.py b/test/test_static_chroot.py
index 7e53d3f7..f9bc93a8 100644
--- a/test/test_share_chroot.py
+++ b/test/test_static_chroot.py
@@ -6,17 +6,14 @@ import pytest
from unit.applications.proto import TestApplicationProto
-class TestShareChroot(TestApplicationProto):
+class TestStaticChroot(TestApplicationProto):
prerequisites = {'features': ['chroot']}
@pytest.fixture(autouse=True)
def setup_method_fixture(self, temp_dir):
os.makedirs(temp_dir + '/assets/dir')
- with open(temp_dir + '/assets/index.html', 'w') as index, open(
- temp_dir + '/assets/dir/file', 'w'
- ) as file:
- index.write('0123456789')
- file.write('blah')
+ Path(temp_dir + '/assets/index.html').write_text('0123456789')
+ Path(temp_dir + '/assets/dir/file').write_text('blah')
test = Path(__file__)
self.test_path = '/' + test.parent.name + '/' + test.name
@@ -28,7 +25,7 @@ class TestShareChroot(TestApplicationProto):
}
)
- def test_share_chroot(self, temp_dir):
+ def test_static_chroot(self, temp_dir):
assert self.get(url='/dir/file')['status'] == 200, 'default chroot'
assert self.get(url='/index.html')['status'] == 200, 'default chroot 2'
@@ -44,7 +41,10 @@ class TestShareChroot(TestApplicationProto):
assert self.get(url='/index.html')['status'] == 403, 'chroot 403 2'
assert self.get(url='/file')['status'] == 403, 'chroot 403'
- def test_share_chroot_permission(self, temp_dir):
+ def test_static_chroot_permission(self, is_su, temp_dir):
+ if is_su:
+ pytest.skip('does\'t work under root')
+
os.chmod(temp_dir + '/assets/dir', 0o100)
assert 'success' in self.conf(
@@ -57,7 +57,7 @@ class TestShareChroot(TestApplicationProto):
assert self.get(url='/dir/file')['status'] == 200, 'chroot'
- def test_share_chroot_empty(self, temp_dir):
+ def test_static_chroot_empty(self, temp_dir):
assert 'success' in self.conf(
{"share": temp_dir + "/assets", "chroot": ""}, 'routes/0/action',
), 'configure chroot empty absolute'
@@ -74,7 +74,7 @@ class TestShareChroot(TestApplicationProto):
self.get(url=self.test_path)['status'] == 200
), 'chroot empty relative'
- def test_share_chroot_relative(self, is_su, temp_dir):
+ def test_static_chroot_relative(self, is_su, temp_dir):
if is_su:
pytest.skip('does\'t work under root')
@@ -96,7 +96,7 @@ class TestShareChroot(TestApplicationProto):
assert self.get(url=self.test_path)['status'] == 200, 'relative'
- def test_share_chroot_invalid(self, temp_dir):
+ def test_static_chroot_invalid(self, temp_dir):
assert 'error' in self.conf(
{"share": temp_dir, "chroot": True}, 'routes/0/action',
), 'configure chroot error'
diff --git a/test/test_share_fallback.py b/test/test_static_fallback.py
index 0b1c270e..dc9056b9 100644
--- a/test/test_share_fallback.py
+++ b/test/test_static_fallback.py
@@ -1,21 +1,21 @@
import os
+from pathlib import Path
import pytest
from unit.applications.proto import TestApplicationProto
-from unit.option import option
-class TestStatic(TestApplicationProto):
+class TestStaticFallback(TestApplicationProto):
prerequisites = {}
- def setup_method(self):
- os.makedirs(option.temp_dir + '/assets/dir')
- with open(option.temp_dir + '/assets/index.html', 'w') as index:
- index.write('0123456789')
+ @pytest.fixture(autouse=True)
+ def setup_method_fixture(self, temp_dir):
+ os.makedirs(temp_dir + '/assets/dir')
+ Path(temp_dir + '/assets/index.html').write_text('0123456789')
- os.makedirs(option.temp_dir + '/assets/403')
- os.chmod(option.temp_dir + '/assets/403', 0o000)
+ os.makedirs(temp_dir + '/assets/403')
+ os.chmod(temp_dir + '/assets/403', 0o000)
self._load_conf(
{
@@ -23,21 +23,22 @@ class TestStatic(TestApplicationProto):
"*:7080": {"pass": "routes"},
"*:7081": {"pass": "routes"},
},
- "routes": [{"action": {"share": option.temp_dir + "/assets"}}],
+ "routes": [{"action": {"share": temp_dir + "/assets"}}],
"applications": {},
}
)
- def teardown_method(self):
+ yield
+
try:
- os.chmod(option.temp_dir + '/assets/403', 0o777)
+ os.chmod(temp_dir + '/assets/403', 0o777)
except FileNotFoundError:
pass
def action_update(self, conf):
assert 'success' in self.conf(conf, 'routes/0/action')
- def test_fallback(self):
+ def test_static_fallback(self):
self.action_update({"share": "/blah"})
assert self.get()['status'] == 404, 'bad path no fallback'
@@ -47,7 +48,7 @@ class TestStatic(TestApplicationProto):
assert resp['status'] == 200, 'bad path fallback status'
assert resp['body'] == '', 'bad path fallback'
- def test_fallback_valid_path(self, temp_dir):
+ def test_static_fallback_valid_path(self, temp_dir):
self.action_update(
{"share": temp_dir + "/assets", "fallback": {"return": 200}}
)
@@ -65,7 +66,7 @@ class TestStatic(TestApplicationProto):
assert self.get(url='/dir')['status'] == 301, 'fallback status 301'
- def test_fallback_nested(self):
+ def test_static_fallback_nested(self):
self.action_update(
{
"share": "/blah",
@@ -80,7 +81,7 @@ class TestStatic(TestApplicationProto):
assert resp['status'] == 200, 'fallback nested status'
assert resp['body'] == '', 'fallback nested'
- def test_fallback_share(self, temp_dir):
+ def test_static_fallback_share(self, temp_dir):
self.action_update(
{"share": "/blah", "fallback": {"share": temp_dir + "/assets"},}
)
@@ -97,7 +98,7 @@ class TestStatic(TestApplicationProto):
self.get(url='/dir')['status'] == 301
), 'fallback share status 301'
- def test_fallback_proxy(self):
+ def test_static_fallback_proxy(self):
assert 'success' in self.conf(
[
{
@@ -119,7 +120,7 @@ class TestStatic(TestApplicationProto):
assert resp['body'] == '', 'fallback proxy'
@pytest.mark.skip('not yet')
- def test_fallback_proxy_loop(self, skip_alert):
+ def test_static_fallback_proxy_loop(self, skip_alert):
skip_alert(
r'open.*/blah/index.html.*failed',
r'accept.*failed',
@@ -135,7 +136,7 @@ class TestStatic(TestApplicationProto):
assert 'success' in self.conf_delete('listeners/*:7081')
self.get(read_timeout=1)
- def test_fallback_invalid(self):
+ def test_static_fallback_invalid(self):
def check_error(conf):
assert 'error' in self.conf(conf, 'routes/0/action')
diff --git a/test/test_share_mount.py b/test/test_static_mount.py
index f22fbe75..570f6439 100644
--- a/test/test_share_mount.py
+++ b/test/test_static_mount.py
@@ -1,12 +1,13 @@
import os
import subprocess
+from pathlib import Path
import pytest
from unit.applications.proto import TestApplicationProto
-class TestShareMount(TestApplicationProto):
+class TestStaticMount(TestApplicationProto):
prerequisites = {'features': ['chroot']}
@pytest.fixture(autouse=True)
@@ -17,12 +18,9 @@ class TestShareMount(TestApplicationProto):
os.makedirs(temp_dir + '/assets/dir/mount')
os.makedirs(temp_dir + '/assets/dir/dir')
os.makedirs(temp_dir + '/assets/mount')
- with open(temp_dir + '/assets/index.html', 'w') as index, open(
- temp_dir + '/assets/dir/dir/file', 'w'
- ) as file, open(temp_dir + '/assets/mount/index.html', 'w') as mount:
- index.write('index')
- file.write('file')
- mount.write('mount')
+ Path(temp_dir + '/assets/index.html').write_text('index')
+ Path(temp_dir + '/assets/dir/dir/file').write_text('file')
+ Path(temp_dir + '/assets/mount/index.html').write_text('mount')
try:
process = subprocess.Popen(
@@ -66,7 +64,7 @@ class TestShareMount(TestApplicationProto):
except:
pytest.fail('Can\'t run umount process.')
- def test_share_mount(self, temp_dir, skip_alert):
+ def test_static_mount(self, temp_dir, skip_alert):
skip_alert(r'opening.*failed')
resp = self.get(url='/mount/')
@@ -89,7 +87,7 @@ class TestShareMount(TestApplicationProto):
assert resp['status'] == 200
assert resp['body'] == 'mount'
- def test_share_mount_two_blocks(self, temp_dir, skip_alert):
+ def test_static_mount_two_blocks(self, temp_dir, skip_alert):
skip_alert(r'opening.*failed')
os.symlink(temp_dir + '/assets/dir', temp_dir + '/assets/link')
@@ -117,7 +115,7 @@ class TestShareMount(TestApplicationProto):
assert self.get(url='/mount/')['status'] == 200, 'block enabled'
assert self.head(url='/mount/')['status'] == 403, 'block disabled'
- def test_share_mount_chroot(self, temp_dir, skip_alert):
+ def test_static_mount_chroot(self, temp_dir, skip_alert):
skip_alert(r'opening.*failed')
assert 'success' in self.conf(
diff --git a/test/test_share_symlink.py b/test/test_static_symlink.py
index 3970b605..35eb402a 100644
--- a/test/test_share_symlink.py
+++ b/test/test_static_symlink.py
@@ -1,21 +1,19 @@
import os
+from pathlib import Path
import pytest
from unit.applications.proto import TestApplicationProto
-class TestShareSymlink(TestApplicationProto):
+class TestStaticSymlink(TestApplicationProto):
prerequisites = {'features': ['chroot']}
@pytest.fixture(autouse=True)
def setup_method_fixture(self, temp_dir):
os.makedirs(temp_dir + '/assets/dir/dir')
- with open(temp_dir + '/assets/index.html', 'w') as index, open(
- temp_dir + '/assets/dir/file', 'w'
- ) as file:
- index.write('0123456789')
- file.write('blah')
+ Path(temp_dir + '/assets/index.html').write_text('0123456789')
+ Path(temp_dir + '/assets/dir/file').write_text('blah')
self._load_conf(
{
@@ -24,7 +22,7 @@ class TestShareSymlink(TestApplicationProto):
}
)
- def test_share_symlink(self, temp_dir, skip_alert):
+ def test_static_symlink(self, temp_dir, skip_alert):
skip_alert(r'opening.*failed')
os.symlink(temp_dir + '/assets/dir', temp_dir + '/assets/link')
@@ -48,7 +46,7 @@ class TestShareSymlink(TestApplicationProto):
assert self.get(url='/link/file')['status'] == 200, 'symlink enabled'
- def test_share_symlink_two_blocks(self, temp_dir, skip_alert):
+ def test_static_symlink_two_blocks(self, temp_dir, skip_alert):
skip_alert(r'opening.*failed')
os.symlink(temp_dir + '/assets/dir', temp_dir + '/assets/link')
@@ -76,7 +74,7 @@ class TestShareSymlink(TestApplicationProto):
assert self.get(url='/link/file')['status'] == 200, 'block enabled'
assert self.head(url='/link/file')['status'] == 403, 'block disabled'
- def test_share_symlink_chroot(self, temp_dir, skip_alert):
+ def test_static_symlink_chroot(self, temp_dir, skip_alert):
skip_alert(r'opening.*failed')
os.symlink(
diff --git a/test/test_share_types.py b/test/test_static_types.py
index b5ed97a0..20defddf 100644
--- a/test/test_share_types.py
+++ b/test/test_static_types.py
@@ -1,12 +1,11 @@
-import os
from pathlib import Path
import pytest
+
from unit.applications.proto import TestApplicationProto
-from unit.option import option
-class TestShareTypes(TestApplicationProto):
+class TestStaticTypes(TestApplicationProto):
prerequisites = {}
@pytest.fixture(autouse=True)
@@ -36,7 +35,7 @@ class TestShareTypes(TestApplicationProto):
assert resp['status'] == 200, 'status'
assert resp['body'] == body, 'body'
- def test_share_types_basic(self, temp_dir):
+ def test_static_types_basic(self, temp_dir):
self.action_update({"share": temp_dir + "/assets"})
self.check_body('/index.html', 'index')
self.check_body('/file.xml', '.xml')
@@ -54,7 +53,7 @@ class TestShareTypes(TestApplicationProto):
self.action_update({"share": temp_dir + "/assets", "types": [""]})
assert self.get(url='/file.xml')['status'] == 403, 'no mtype'
- def test_share_types_wildcard(self, temp_dir):
+ def test_static_types_wildcard(self, temp_dir):
self.action_update(
{"share": temp_dir + "/assets", "types": ["application/*"]}
)
@@ -67,7 +66,7 @@ class TestShareTypes(TestApplicationProto):
assert self.get(url='/file.xml')['status'] == 403, 'video * mtype xml'
self.check_body('/file.mp4', '.mp4')
- def test_share_types_negation(self, temp_dir):
+ def test_static_types_negation(self, temp_dir):
self.action_update(
{"share": temp_dir + "/assets", "types": ["!application/xml"]}
)
@@ -85,7 +84,7 @@ class TestShareTypes(TestApplicationProto):
self.check_body('/file.png', '.png')
assert self.get(url='/file.jpg')['status'] == 403, 'negation sort jpg'
- def test_share_types_regex(self, temp_dir):
+ def test_static_types_regex(self, temp_dir):
self.action_update(
{"share": temp_dir + "/assets", "types": ["~text/(html|plain)"]}
)
@@ -93,7 +92,7 @@ class TestShareTypes(TestApplicationProto):
self.check_body('/file.html', '.html')
self.check_body('/file.txt', '.txt')
- def test_share_types_case(self, temp_dir):
+ def test_static_types_case(self, temp_dir):
self.action_update(
{"share": temp_dir + "/assets", "types": ["!APpliCaTiOn/xMl"]}
)
@@ -118,7 +117,7 @@ class TestShareTypes(TestApplicationProto):
self.get(url='/file.xml')['status'] == 403
), 'mixed case video * negation'
- def test_share_types_fallback(self, temp_dir):
+ def test_static_types_fallback(self, temp_dir):
assert 'success' in self.conf(
[
{
@@ -139,7 +138,7 @@ class TestShareTypes(TestApplicationProto):
self.check_body('/file.php', '')
self.check_body('/file.mp4', '.mp4')
- def test_share_types_index(self, temp_dir):
+ def test_static_types_index(self, temp_dir):
self.action_update(
{"share": temp_dir + "/assets", "types": "application/xml"}
)
@@ -147,7 +146,7 @@ class TestShareTypes(TestApplicationProto):
self.check_body('/file.xml', '.xml')
assert self.get(url='/file.mp4')['status'] == 403, 'forbidden mtype'
- def test_share_types_custom_mime(self, temp_dir):
+ def test_static_types_custom_mime(self, temp_dir):
self._load_conf(
{
"listeners": {"*:7080": {"pass": "routes"}},
diff --git a/test/test_tls.py b/test/test_tls.py
index 0cfeaded..546f0f89 100644
--- a/test/test_tls.py
+++ b/test/test_tls.py
@@ -665,3 +665,16 @@ basicConstraints = critical,CA:TRUE"""
)
assert res['status'] == 200, 'status ok'
assert res['body'] == filename + data
+
+ def test_tls_multi_listener(self):
+ self.load('empty')
+
+ self.certificate()
+
+ self.add_tls()
+ self.add_tls(port=7081)
+
+ assert self.get_ssl()['status'] == 200, 'listener #1'
+
+ assert self.get_ssl(port=7081)['status'] == 200, 'listener #2'
+
diff --git a/test/test_tls_sni.py b/test/test_tls_sni.py
index 2e5424e2..eba6140a 100644
--- a/test/test_tls_sni.py
+++ b/test/test_tls_sni.py
@@ -168,6 +168,26 @@ basicConstraints = critical,CA:TRUE"""
self.check_cert('alt2.example.com', bundles['example.com']['subj'])
self.check_cert('blah', bundles['default']['subj'])
+ def test_tls_sni_no_hostname(self):
+ bundles = {
+ "localhost.com": {"subj": "localhost.com", "alt_names": []},
+ "example.com": {
+ "subj": "example.com",
+ "alt_names": ["example.com"],
+ },
+ }
+ self.config_bundles(bundles)
+ self.add_tls(["localhost.com", "example.com"])
+
+ resp, sock = self.get_ssl(
+ headers={'Content-Length': '0', 'Connection': 'close'}, start=True,
+ )
+ assert resp['status'] == 200
+ assert (
+ sock.getpeercert()['subject'][0][0][1]
+ == bundles['localhost.com']['subj']
+ )
+
def test_tls_sni_upper_case(self):
bundles = {
"localhost.com": {"subj": "LOCALHOST.COM", "alt_names": []},
diff --git a/test/test_variables.py b/test/test_variables.py
index 139d867e..d8547b7b 100644
--- a/test/test_variables.py
+++ b/test/test_variables.py
@@ -100,6 +100,25 @@ class TestVariables(TestApplicationProto):
assert self.get(url='/1')['status'] == 200
assert self.get(url='/2')['status'] == 404
+ def test_variables_empty(self):
+ def update_pass(prefix):
+ assert 'success' in self.conf(
+ {
+ "listeners": {
+ "*:7080": {"pass": prefix + "/$method"},
+ },
+ },
+ ), 'variables empty'
+
+ update_pass("routes");
+ assert self.get(url='/1')['status'] == 404
+
+ update_pass("upstreams");
+ assert self.get(url='/2')['status'] == 404
+
+ update_pass("applications");
+ assert self.get(url='/3')['status'] == 404
+
def test_variables_invalid(self):
def check_variables(routes):
assert 'error' in self.conf(
diff --git a/test/unit/applications/lang/java.py b/test/unit/applications/lang/java.py
index c9c2095e..53b27b07 100644
--- a/test/unit/applications/lang/java.py
+++ b/test/unit/applications/lang/java.py
@@ -52,7 +52,7 @@ class TestApplicationJava(TestApplicationProto):
os.makedirs(classes_path)
classpath = (
- option.current_dir + '/build/tomcat-servlet-api-9.0.44.jar'
+ option.current_dir + '/build/tomcat-servlet-api-9.0.52.jar'
)
ws_jars = glob.glob(
diff --git a/test/unit/applications/lang/python.py b/test/unit/applications/lang/python.py
index b399dffd..215aa332 100644
--- a/test/unit/applications/lang/python.py
+++ b/test/unit/applications/lang/python.py
@@ -44,6 +44,7 @@ class TestApplicationPython(TestApplicationProto):
for attr in (
'callable',
+ 'environment',
'home',
'limits',
'path',
diff --git a/test/unit/applications/lang/ruby.py b/test/unit/applications/lang/ruby.py
index 02644584..61d50558 100644
--- a/test/unit/applications/lang/ruby.py
+++ b/test/unit/applications/lang/ruby.py
@@ -1,24 +1,44 @@
+import os
+import shutil
+
from unit.applications.proto import TestApplicationProto
from unit.option import option
+from unit.utils import public_dir
class TestApplicationRuby(TestApplicationProto):
application_type = "ruby"
+ def prepare_env(self, script):
+ shutil.copytree(
+ option.test_dir + '/ruby/' + script,
+ option.temp_dir + '/ruby/' + script,
+ )
+
+ public_dir(option.temp_dir + '/ruby/' + script)
+
def load(self, script, name='config.ru', **kwargs):
- script_path = option.test_dir + '/ruby/' + script
+ self.prepare_env(script)
+
+ script_path = option.temp_dir + '/ruby/' + script
+
+ app = {
+ "type": self.get_application_type(),
+ "processes": {"spare": 0},
+ "working_directory": script_path,
+ "script": script_path + '/' + name,
+ }
+
+ for key in [
+ 'hooks',
+ ]:
+ if key in kwargs:
+ app[key] = kwargs[key]
self._load_conf(
{
"listeners": {"*:7080": {"pass": "applications/" + script}},
- "applications": {
- script: {
- "type": self.get_application_type(),
- "processes": {"spare": 0},
- "working_directory": script_path,
- "script": script_path + '/' + name,
- }
- },
+ "applications": {script: app},
},
**kwargs
)
diff --git a/test/unit/applications/proto.py b/test/unit/applications/proto.py
index 92754c03..e30d21ff 100644
--- a/test/unit/applications/proto.py
+++ b/test/unit/applications/proto.py
@@ -47,13 +47,15 @@ class TestApplicationProto(TestControl):
if 'applications' in conf:
for app in conf['applications'].keys():
app_conf = conf['applications'][app]
- if 'user' in kwargs:
- app_conf['user'] = kwargs['user']
- if 'group' in kwargs:
- app_conf['group'] = kwargs['group']
-
- if 'isolation' in kwargs:
- app_conf['isolation'] = kwargs['isolation']
+ for key in [
+ 'user',
+ 'group',
+ 'isolation',
+ 'processes',
+ 'threads',
+ ]:
+ if key in kwargs:
+ app_conf[key] = kwargs[key]
assert 'success' in self.conf(conf), 'load application configuration'
diff --git a/test/unit/check/isolation.py b/test/unit/check/isolation.py
index 7c83ae35..43c8842f 100644
--- a/test/unit/check/isolation.py
+++ b/test/unit/check/isolation.py
@@ -3,6 +3,7 @@ import os
from unit.applications.lang.go import TestApplicationGo
from unit.applications.lang.java import TestApplicationJava
+from unit.applications.lang.ruby import TestApplicationRuby
from unit.applications.lang.node import TestApplicationNode
from unit.applications.proto import TestApplicationProto
from unit.http import TestHTTP
@@ -65,14 +66,16 @@ def check_isolation():
}
elif 'ruby' in available['modules']:
+ TestApplicationRuby().prepare_env('empty')
+
conf = {
"listeners": {"*:7080": {"pass": "applications/empty"}},
"applications": {
"empty": {
"type": "ruby",
"processes": {"spare": 0},
- "working_directory": option.test_dir + "/ruby/empty",
- "script": option.test_dir + "/ruby/empty/config.ru",
+ "working_directory": option.temp_dir + "/ruby/empty",
+ "script": option.temp_dir + "/ruby/empty/config.ru",
"isolation": {"namespaces": {"credential": True}},
}
},
diff --git a/test/unit/http.py b/test/unit/http.py
index 797b7681..dcfcd232 100644
--- a/test/unit/http.py
+++ b/test/unit/http.py
@@ -45,7 +45,7 @@ class TestHTTP:
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
if 'wrapper' in kwargs:
- server_hostname = headers.get('Host', 'localhost')
+ server_hostname = headers.get('Host', None)
sock = kwargs['wrapper'](sock, server_hostname=server_hostname)
connect_args = addr if sock_type == 'unix' else (addr, port)
diff --git a/test/unit/utils.py b/test/unit/utils.py
index a627e9f5..43aaa81b 100644
--- a/test/unit/utils.py
+++ b/test/unit/utils.py
@@ -1,3 +1,4 @@
+import glob
import os
import socket
import subprocess
@@ -16,8 +17,8 @@ def public_dir(path):
os.chmod(os.path.join(root, f), 0o777)
-def waitforfiles(*files):
- for i in range(50):
+def waitforfiles(*files, timeout=50):
+ for i in range(timeout):
wait = False
for f in files:
@@ -33,6 +34,21 @@ def waitforfiles(*files):
return False
+def waitforglob(pattern, count=1, timeout=50):
+ for i in range(timeout):
+ n = 0
+
+ for f in glob.glob(pattern):
+ n += 1
+
+ if n == count:
+ return True
+
+ time.sleep(0.1)
+
+ return False
+
+
def waitforsocket(port):
for i in range(50):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
@@ -72,8 +88,8 @@ def sysctl():
return out
-def waitformount(template, wait=50):
- for i in range(wait):
+def waitformount(template, timeout=50):
+ for i in range(timeout):
if findmnt().find(template) != -1:
return True
@@ -82,8 +98,8 @@ def waitformount(template, wait=50):
return False
-def waitforunmount(template, wait=50):
- for i in range(wait):
+def waitforunmount(template, timeout=50):
+ for i in range(timeout):
if findmnt().find(template) == -1:
return True