summaryrefslogtreecommitdiffhomepage
path: root/test
diff options
context:
space:
mode:
authorAndrei Belov <defan@nginx.com>2020-03-12 18:40:48 +0300
committerAndrei Belov <defan@nginx.com>2020-03-12 18:40:48 +0300
commit4b7ca39903178e20ec7381205694cb01f0dec6bc (patch)
tree51afb9c7003b5927183e7ddecd766eb19e421233 /test
parent8414897527ed1616ea39a0cae4d1b8ee170d5cb8 (diff)
parentb3c8a7b33a29208e75dfe4f670cf81dac7b99ccc (diff)
downloadunit-4b7ca39903178e20ec7381205694cb01f0dec6bc.tar.gz
unit-4b7ca39903178e20ec7381205694cb01f0dec6bc.tar.bz2
Merged with the default branch.1.16.0-1
Diffstat (limited to '')
-rw-r--r--test/php/cwd/index.php19
-rw-r--r--test/php/cwd/subdir/index.php1
-rw-r--r--test/php/open/index.php7
-rw-r--r--test/php/open/test.txt1
-rw-r--r--test/python/input_iter/wsgi.py17
-rw-r--r--test/python/input_readline/wsgi.py20
-rw-r--r--test/python/input_readline_size/wsgi.py16
-rw-r--r--test/python/input_readlines/wsgi.py5
-rw-r--r--test/python/upstreams/0/wsgi.py8
-rw-r--r--test/python/upstreams/1/wsgi.py8
-rw-r--r--test/python/upstreams/2/wsgi.py8
-rwxr-xr-xtest/run.py2
-rw-r--r--test/test_access_log.py1
-rw-r--r--test/test_php_application.py165
-rw-r--r--test/test_proxy.py7
-rw-r--r--test/test_python_application.py73
-rw-r--r--test/test_routing.py110
-rw-r--r--test/test_settings.py25
-rw-r--r--test/test_share_fallback.py212
-rw-r--r--test/test_static.py30
-rw-r--r--test/test_tls.py18
-rw-r--r--test/test_upstreams_rr.py465
-rw-r--r--test/test_usr1.py41
-rw-r--r--test/unit/applications/lang/go.py5
-rw-r--r--test/unit/applications/lang/java.py4
-rw-r--r--test/unit/applications/lang/php.py4
-rw-r--r--test/unit/applications/tls.py3
-rw-r--r--test/unit/http.py45
-rw-r--r--test/unit/main.py91
29 files changed, 1306 insertions, 105 deletions
diff --git a/test/php/cwd/index.php b/test/php/cwd/index.php
new file mode 100644
index 00000000..24ae3a21
--- /dev/null
+++ b/test/php/cwd/index.php
@@ -0,0 +1,19 @@
+<?php
+
+if (isset($_GET['chdir']) && $_GET['chdir'] != "") {
+ if (!chdir($_GET['chdir'])) {
+ echo "failure to chdir(" . $_GET['chdir'] . ")\n";
+ exit;
+ }
+}
+
+$opcache = -1;
+
+if (function_exists('opcache_get_status')) {
+ $opcache = opcache_get_status()->opcache_enabled;
+}
+
+header('X-OPcache: ' . $opcache);
+
+print(getcwd());
+?>
diff --git a/test/php/cwd/subdir/index.php b/test/php/cwd/subdir/index.php
new file mode 100644
index 00000000..597bcac4
--- /dev/null
+++ b/test/php/cwd/subdir/index.php
@@ -0,0 +1 @@
+<?php print(getcwd()); ?>
diff --git a/test/php/open/index.php b/test/php/open/index.php
new file mode 100644
index 00000000..a5bebc54
--- /dev/null
+++ b/test/php/open/index.php
@@ -0,0 +1,7 @@
+<?php
+if (isset($_GET['chdir'])) {
+ chdir($_GET['chdir']);
+}
+
+echo file_get_contents('test.txt');
+?>
diff --git a/test/php/open/test.txt b/test/php/open/test.txt
new file mode 100644
index 00000000..30d74d25
--- /dev/null
+++ b/test/php/open/test.txt
@@ -0,0 +1 @@
+test \ No newline at end of file
diff --git a/test/python/input_iter/wsgi.py b/test/python/input_iter/wsgi.py
index d3bf437f..04a6a06c 100644
--- a/test/python/input_iter/wsgi.py
+++ b/test/python/input_iter/wsgi.py
@@ -1,5 +1,16 @@
def application(environ, start_response):
- body = bytes(environ['wsgi.input'].__iter__())
+ body = []
+ content_length = 0
- start_response('200', [('Content-Length', str(len(body)))])
- return [body]
+ for l in environ['wsgi.input'].__iter__():
+ body.append(l)
+ content_length += len(l)
+
+ start_response(
+ '200',
+ [
+ ('Content-Length', str(content_length)),
+ ('X-Lines-Count', str(len(body))),
+ ],
+ )
+ return body
diff --git a/test/python/input_readline/wsgi.py b/test/python/input_readline/wsgi.py
new file mode 100644
index 00000000..ec42e0c8
--- /dev/null
+++ b/test/python/input_readline/wsgi.py
@@ -0,0 +1,20 @@
+def application(environ, start_response):
+ body = []
+ content_length = 0
+
+ while True:
+ l = environ['wsgi.input'].readline()
+ if not l:
+ break
+
+ body.append(l)
+ content_length += len(l)
+
+ start_response(
+ '200',
+ [
+ ('Content-Length', str(content_length)),
+ ('X-Lines-Count', str(len(body))),
+ ],
+ )
+ return body
diff --git a/test/python/input_readline_size/wsgi.py b/test/python/input_readline_size/wsgi.py
new file mode 100644
index 00000000..36cf07b0
--- /dev/null
+++ b/test/python/input_readline_size/wsgi.py
@@ -0,0 +1,16 @@
+def application(environ, start_response):
+ body = []
+
+ while True:
+ l = environ['wsgi.input'].readline(9)
+ if not l:
+ break
+
+ body.append(l)
+
+ if len(l) > 9:
+ body.append(b'len(l) > 9: ' + l)
+ break
+
+ start_response('200', [('X-Lines-Count', str(len(body)))])
+ return body
diff --git a/test/python/input_readlines/wsgi.py b/test/python/input_readlines/wsgi.py
new file mode 100644
index 00000000..64b03d79
--- /dev/null
+++ b/test/python/input_readlines/wsgi.py
@@ -0,0 +1,5 @@
+def application(environ, start_response):
+ body = environ['wsgi.input'].readlines()
+
+ start_response('200', [('X-Lines-Count', str(len(body)))])
+ return body
diff --git a/test/python/upstreams/0/wsgi.py b/test/python/upstreams/0/wsgi.py
new file mode 100644
index 00000000..2c88979b
--- /dev/null
+++ b/test/python/upstreams/0/wsgi.py
@@ -0,0 +1,8 @@
+import time
+
+def application(env, start_response):
+ delay = int(env.get('HTTP_X_DELAY', 0))
+
+ start_response('200', [('Content-Length', '0'), ('X-Upstream', '0')])
+ time.sleep(delay)
+ return []
diff --git a/test/python/upstreams/1/wsgi.py b/test/python/upstreams/1/wsgi.py
new file mode 100644
index 00000000..5077bdb1
--- /dev/null
+++ b/test/python/upstreams/1/wsgi.py
@@ -0,0 +1,8 @@
+import time
+
+def application(env, start_response):
+ delay = int(env.get('HTTP_X_DELAY', 0))
+
+ start_response('200', [('Content-Length', '0'), ('X-Upstream', '1')])
+ time.sleep(delay)
+ return []
diff --git a/test/python/upstreams/2/wsgi.py b/test/python/upstreams/2/wsgi.py
new file mode 100644
index 00000000..bb0ce797
--- /dev/null
+++ b/test/python/upstreams/2/wsgi.py
@@ -0,0 +1,8 @@
+import time
+
+def application(env, start_response):
+ delay = int(env.get('HTTP_X_DELAY', 0))
+
+ start_response('200', [('Content-Length', '0'), ('X-Upstream', '2')])
+ time.sleep(delay)
+ return []
diff --git a/test/run.py b/test/run.py
index b79d0484..59e06bcb 100755
--- a/test/run.py
+++ b/test/run.py
@@ -12,7 +12,7 @@ if __name__ == '__main__':
tests = loader.discover(start_dir=this_dir)
suite.addTests(tests)
- runner = unittest.TextTestRunner(verbosity=3)
+ runner = unittest.TextTestRunner(stream=sys.stdout, verbosity=3)
result = runner.run(suite)
ret = not (len(result.failures) == len(result.errors) == 0)
diff --git a/test/test_access_log.py b/test/test_access_log.py
index 94f6e7bf..898d8b24 100644
--- a/test/test_access_log.py
+++ b/test/test_access_log.py
@@ -2,7 +2,6 @@ import os
import re
import time
import unittest
-from subprocess import call
from unit.applications.lang.python import TestApplicationPython
diff --git a/test/test_php_application.py b/test/test_php_application.py
index 837181e6..c3645a99 100644
--- a/test/test_php_application.py
+++ b/test/test_php_application.py
@@ -1,4 +1,6 @@
+import os
import re
+import shutil
import unittest
from unit.applications.lang.php import TestApplicationPHP
@@ -11,6 +13,28 @@ class TestPHPApplication(TestApplicationPHP):
self.assertRegex(body, r'time: \d+', 'disable_functions before time')
self.assertRegex(body, r'exec: \/\w+', 'disable_functions before exec')
+ def set_opcache(self, app, val):
+ self.assertIn(
+ 'success',
+ self.conf(
+ {
+ "admin": {
+ "opcache.enable": val,
+ "opcache.enable_cli": val,
+ },
+ },
+ 'applications/' + app + '/options',
+ ),
+ )
+
+ opcache = self.get()['headers']['X-OPcache']
+
+ if not opcache or opcache == '-1':
+ print('opcache is not supported')
+ raise unittest.SkipTest()
+
+ self.assertEqual(opcache, val, 'opcache value')
+
def test_php_application_variables(self):
self.load('variables')
@@ -464,7 +488,8 @@ class TestPHPApplication(TestApplicationPHP):
def test_php_application_script(self):
self.assertIn(
- 'success', self.conf(
+ 'success',
+ self.conf(
{
"listeners": {"*:7080": {"pass": "applications/script"}},
"applications": {
@@ -476,7 +501,8 @@ class TestPHPApplication(TestApplicationPHP):
}
},
}
- ), 'configure script'
+ ),
+ 'configure script',
)
resp = self.get()
@@ -486,7 +512,8 @@ class TestPHPApplication(TestApplicationPHP):
def test_php_application_index_default(self):
self.assertIn(
- 'success', self.conf(
+ 'success',
+ self.conf(
{
"listeners": {"*:7080": {"pass": "applications/phpinfo"}},
"applications": {
@@ -497,7 +524,8 @@ class TestPHPApplication(TestApplicationPHP):
}
},
}
- ), 'configure index default'
+ ),
+ 'configure index default',
)
resp = self.get()
@@ -512,5 +540,134 @@ class TestPHPApplication(TestApplicationPHP):
self.get(url='/index.wrong')['status'], 200, 'status'
)
+ new_root = self.testdir + "/php"
+ os.mkdir(new_root)
+ shutil.copy(self.current_dir + '/php/phpinfo/index.wrong', new_root)
+
+ self.assertIn(
+ 'success',
+ self.conf(
+ {
+ "listeners": {"*:7080": {"pass": "applications/phpinfo"}},
+ "applications": {
+ "phpinfo": {
+ "type": "php",
+ "processes": {"spare": 0},
+ "root": new_root,
+ "working_directory": new_root,
+ }
+ },
+ }
+ ),
+ 'configure new root',
+ )
+
+ resp = self.get()
+ self.assertNotEqual(
+ str(resp['status']) + resp['body'], '200', 'status new root'
+ )
+
+ def run_php_application_cwd_root_tests(self):
+ self.assertIn(
+ 'success', self.conf_delete('applications/cwd/working_directory')
+ )
+
+ script_cwd = self.current_dir + '/php/cwd'
+
+ resp = self.get()
+ self.assertEqual(resp['status'], 200, 'status ok')
+ self.assertEqual(resp['body'], script_cwd, 'default cwd')
+
+ self.assertIn(
+ 'success',
+ self.conf(
+ '"' + self.current_dir + '"',
+ 'applications/cwd/working_directory',
+ ),
+ )
+
+ resp = self.get()
+ self.assertEqual(resp['status'], 200, 'status ok')
+ self.assertEqual(resp['body'], script_cwd, 'wdir cwd')
+
+ resp = self.get(url='/?chdir=/')
+ self.assertEqual(resp['status'], 200, 'status ok')
+ self.assertEqual(resp['body'], '/', 'cwd after chdir')
+
+ # cwd must be restored
+
+ resp = self.get()
+ self.assertEqual(resp['status'], 200, 'status ok')
+ self.assertEqual(resp['body'], script_cwd, 'cwd restored')
+
+ resp = self.get(url='/subdir/')
+ self.assertEqual(
+ resp['body'], script_cwd + '/subdir', 'cwd subdir',
+ )
+
+ def test_php_application_cwd_root(self):
+ self.load('cwd')
+ self.run_php_application_cwd_root_tests()
+
+ def test_php_application_cwd_opcache_disabled(self):
+ self.load('cwd')
+ self.set_opcache('cwd', '0')
+ self.run_php_application_cwd_root_tests()
+
+ def test_php_application_cwd_opcache_enabled(self):
+ self.load('cwd')
+ self.set_opcache('cwd', '1')
+ self.run_php_application_cwd_root_tests()
+
+ def run_php_application_cwd_script_tests(self):
+ self.load('cwd')
+
+ script_cwd = self.current_dir + '/php/cwd'
+
+ self.assertIn(
+ 'success', self.conf_delete('applications/cwd/working_directory')
+ )
+
+ self.assertIn(
+ 'success', self.conf('"index.php"', 'applications/cwd/script')
+ )
+
+ self.assertEqual(
+ self.get()['body'], script_cwd, 'default cwd',
+ )
+
+ self.assertEqual(
+ self.get(url='/?chdir=/')['body'], '/', 'cwd after chdir',
+ )
+
+ # cwd must be restored
+ self.assertEqual(self.get()['body'], script_cwd, 'cwd restored')
+
+ def test_php_application_cwd_script(self):
+ self.load('cwd')
+ self.run_php_application_cwd_script_tests()
+
+ def test_php_application_cwd_script_opcache_disabled(self):
+ self.load('cwd')
+ self.set_opcache('cwd', '0')
+ self.run_php_application_cwd_script_tests()
+
+ def test_php_application_cwd_script_opcache_enabled(self):
+ self.load('cwd')
+ self.set_opcache('cwd', '1')
+ self.run_php_application_cwd_script_tests()
+
+ def test_php_application_path_relative(self):
+ self.load('open')
+
+ self.assertEqual(self.get()['body'], 'test', 'relative path')
+
+ self.assertNotEqual(
+ self.get(url='/?chdir=/')['body'], 'test', 'relative path w/ chdir'
+ )
+
+ self.assertEqual(self.get()['body'], 'test', 'relative path 2')
+
+
if __name__ == '__main__':
TestPHPApplication.main()
diff --git a/test/test_proxy.py b/test/test_proxy.py
index 5d158285..74bd0873 100644
--- a/test/test_proxy.py
+++ b/test/test_proxy.py
@@ -188,6 +188,13 @@ Content-Length: 10
self.assertEqual(resp['status'], 200, 'status')
self.assertEqual(resp['body'], payload, 'body')
+ self.conf({'http': {'max_body_size': 32 * 1024 * 1024}}, 'settings')
+
+ payload = '0123456789abcdef' * 32 * 64 * 1024
+ resp = self.post_http10(body=payload, read_buffer_size=1024 * 1024)
+ self.assertEqual(resp['status'], 200, 'status')
+ self.assertEqual(resp['body'], payload, 'body')
+
def test_proxy_parallel(self):
payload = 'X' * 4096 * 257
buff_size = 4096 * 258
diff --git a/test/test_python_application.py b/test/test_python_application.py
index 818816d0..460cc804 100644
--- a/test/test_python_application.py
+++ b/test/test_python_application.py
@@ -384,13 +384,80 @@ Connection: close
self.assertEqual(self.get()['status'], 500, 'start response exit')
- @unittest.skip('not yet')
def test_python_application_input_iter(self):
self.load('input_iter')
- body = '0123456789'
+ body = '''0123456789
+next line
+
+last line'''
+
+ resp = self.post(body=body)
+ self.assertEqual(resp['body'], body, 'input iter')
+ self.assertEqual(
+ resp['headers']['X-Lines-Count'], '4', 'input iter lines'
+ )
+
+ def test_python_application_input_readline(self):
+ self.load('input_readline')
+
+ body = '''0123456789
+next line
+
+last line'''
+
+ resp = self.post(body=body)
+ self.assertEqual(resp['body'], body, 'input readline')
+ self.assertEqual(
+ resp['headers']['X-Lines-Count'], '4', 'input readline lines'
+ )
+
+ def test_python_application_input_readline_size(self):
+ self.load('input_readline_size')
+
+ body = '''0123456789
+next line
+
+last line'''
+
+ self.assertEqual(
+ self.post(body=body)['body'], body, 'input readline size'
+ )
+ self.assertEqual(
+ self.post(body='0123')['body'], '0123', 'input readline size less'
+ )
+
+ def test_python_application_input_readlines(self):
+ self.load('input_readlines')
+
+ body = '''0123456789
+next line
+
+last line'''
+
+ resp = self.post(body=body)
+ self.assertEqual(resp['body'], body, 'input readlines')
+ self.assertEqual(
+ resp['headers']['X-Lines-Count'], '4', 'input readlines lines'
+ )
+
+ def test_python_application_input_readlines_huge(self):
+ self.load('input_readlines')
+
+ body = (
+ '''0123456789 abcdefghi
+next line: 0123456789 abcdefghi
- self.assertEqual(self.post(body=body)['body'], body, 'input iter')
+last line: 987654321
+'''
+ * 512
+ )
+
+ self.assertEqual(
+ self.post(body=body, read_buffer_size=16384)['body'],
+ body,
+ 'input readlines huge',
+ )
def test_python_application_input_read_length(self):
self.load('input_read_length')
diff --git a/test/test_routing.py b/test/test_routing.py
index eb7b2fd8..950923d6 100644
--- a/test/test_routing.py
+++ b/test/test_routing.py
@@ -288,14 +288,62 @@ class TestRouting(TestApplicationProto):
)
def test_routes_route_pass_absent(self):
- self.skip_alerts.append(r'failed to apply new conf')
-
self.assertIn(
'error',
self.conf([{"match": {"method": "GET"}, "action": {}}], 'routes'),
'route pass absent configure',
)
+ def test_routes_action_unique(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ {
+ "listeners": {
+ "*:7080": {"pass": "routes"},
+ "*:7081": {"pass": "applications/app"},
+ },
+ "routes": [{"action": {"proxy": "http://127.0.0.1:7081"}}],
+ "applications": {
+ "app": {
+ "type": "python",
+ "processes": {"spare": 0},
+ "path": "/app",
+ "module": "wsgi",
+ }
+ },
+ }
+ ),
+ )
+
+ self.assertIn(
+ 'error',
+ self.conf(
+ {"proxy": "http://127.0.0.1:7081", "share": self.testdir},
+ 'routes/0/action',
+ ),
+ 'proxy share',
+ )
+ self.assertIn(
+ 'error',
+ self.conf(
+ {
+ "proxy": "http://127.0.0.1:7081",
+ "pass": "applications/app",
+ },
+ 'routes/0/action',
+ ),
+ 'proxy pass',
+ )
+ self.assertIn(
+ 'error',
+ self.conf(
+ {"share": self.testdir, "pass": "applications/app"},
+ 'routes/0/action',
+ ),
+ 'share pass',
+ )
+
def test_routes_rules_two(self):
self.assertIn(
'success',
@@ -1364,6 +1412,13 @@ class TestRouting(TestApplicationProto):
sock, port = sock_port()
sock2, port2 = sock_port()
+ self.route_match({"source": ["*:" + str(port), "!127.0.0.1"]})
+ self.assertEqual(self.get(sock=sock)['status'], 404, 'negative 3')
+ self.assertEqual(self.get(sock=sock2)['status'], 404, 'negative 4')
+
+ sock, port = sock_port()
+ sock2, port2 = sock_port()
+
self.route_match(
{"source": "127.0.0.1:" + str(port) + "-" + str(port)}
)
@@ -1678,6 +1733,7 @@ class TestRouting(TestApplicationProto):
self.route_match_invalid({"source": "127"})
self.route_match_invalid({"source": "256.0.0.1"})
self.route_match_invalid({"source": "127.0.0."})
+ self.route_match_invalid({"source": " 127.0.0.1"})
self.route_match_invalid({"source": "127.0.0.1:"})
self.route_match_invalid({"source": "127.0.0.1/"})
self.route_match_invalid({"source": "11.0.0.0/33"})
@@ -1690,6 +1746,7 @@ class TestRouting(TestApplicationProto):
self.route_match_invalid({"source": "2001::/129"})
self.route_match_invalid({"source": "::FFFFF"})
self.route_match_invalid({"source": "[::1]:"})
+ self.route_match_invalid({"source": "[:::]:7080"})
self.route_match_invalid({"source": "*:"})
self.route_match_invalid({"source": "*:1-a"})
self.route_match_invalid({"source": "*:65536"})
@@ -1716,6 +1773,55 @@ class TestRouting(TestApplicationProto):
self.assertEqual(self.get()['status'], 404, 'dest neg')
self.assertEqual(self.get(port=7081)['status'], 200, 'dest neg 2')
+ self.route_match({"destination": ['!*:7080', '!*:7081']})
+ self.assertEqual(self.get()['status'], 404, 'dest neg 3')
+ self.assertEqual(self.get(port=7081)['status'], 404, 'dest neg 4')
+
+ self.route_match({"destination": ['!*:7081', '!*:7082']})
+ self.assertEqual(self.get()['status'], 200, 'dest neg 5')
+
+ self.route_match({"destination": ['*:7080', '!*:7080']})
+ self.assertEqual(self.get()['status'], 404, 'dest neg 6')
+
+ self.route_match(
+ {"destination": ['127.0.0.1:7080', '*:7081', '!*:7080']}
+ )
+ self.assertEqual(self.get()['status'], 404, 'dest neg 7')
+ self.assertEqual(self.get(port=7081)['status'], 200, 'dest neg 8')
+
+ self.route_match({"destination": ['!*:7081', '!*:7082', '*:7083']})
+ self.assertEqual(self.get()['status'], 404, 'dest neg 9')
+
+ self.route_match(
+ {"destination": ['*:7081', '!127.0.0.1:7080', '*:7080']}
+ )
+ self.assertEqual(self.get()['status'], 404, 'dest neg 10')
+ self.assertEqual(self.get(port=7081)['status'], 200, 'dest neg 11')
+
+ self.assertIn(
+ 'success',
+ self.conf_delete('routes/0/match/destination/0'),
+ 'remove destination rule',
+ )
+ self.assertEqual(self.get()['status'], 404, 'dest neg 12')
+ self.assertEqual(self.get(port=7081)['status'], 404, 'dest neg 13')
+
+ self.assertIn(
+ 'success',
+ self.conf_delete('routes/0/match/destination/0'),
+ 'remove destination rule 2',
+ )
+ self.assertEqual(self.get()['status'], 200, 'dest neg 14')
+ self.assertEqual(self.get(port=7081)['status'], 404, 'dest neg 15')
+
+ self.assertIn(
+ 'success',
+ self.conf_post("\"!127.0.0.1\"", 'routes/0/match/destination'),
+ 'add destination rule',
+ )
+ self.assertEqual(self.get()['status'], 404, 'dest neg 16')
+ self.assertEqual(self.get(port=7081)['status'], 404, 'dest neg 17')
+
def test_routes_match_destination_proxy(self):
self.assertIn(
'success',
diff --git a/test/test_settings.py b/test/test_settings.py
index 6b849558..9de3a928 100644
--- a/test/test_settings.py
+++ b/test/test_settings.py
@@ -215,6 +215,31 @@ Connection: close
self.post(body='012345')['status'], 413, 'status size max'
)
+ def test_settings_max_body_size_large(self):
+ self.load('mirror')
+
+ self.conf({'http': {'max_body_size': 32 * 1024 * 1024}}, 'settings')
+
+ body = '0123456789abcdef' * 4 * 64 * 1024
+ resp = self.post(body=body, read_buffer_size=1024 * 1024)
+ self.assertEqual(resp['status'], 200, 'status size 4')
+ self.assertEqual(resp['body'], body, 'status body 4')
+
+ body = '0123456789abcdef' * 8 * 64 * 1024
+ resp = self.post(body=body, read_buffer_size=1024 * 1024)
+ self.assertEqual(resp['status'], 200, 'status size 8')
+ self.assertEqual(resp['body'], body, 'status body 8')
+
+ body = '0123456789abcdef' * 16 * 64 * 1024
+ resp = self.post(body=body, read_buffer_size=1024 * 1024)
+ self.assertEqual(resp['status'], 200, 'status size 16')
+ self.assertEqual(resp['body'], body, 'status body 16')
+
+ body = '0123456789abcdef' * 32 * 64 * 1024
+ resp = self.post(body=body, read_buffer_size=1024 * 1024)
+ self.assertEqual(resp['status'], 200, 'status size 32')
+ self.assertEqual(resp['body'], body, 'status body 32')
+
@unittest.skip('not yet')
def test_settings_negative_value(self):
self.assertIn(
diff --git a/test/test_share_fallback.py b/test/test_share_fallback.py
new file mode 100644
index 00000000..8c45793e
--- /dev/null
+++ b/test/test_share_fallback.py
@@ -0,0 +1,212 @@
+import os
+import unittest
+from unit.applications.proto import TestApplicationProto
+
+
+class TestStatic(TestApplicationProto):
+ prerequisites = {}
+
+ def setUp(self):
+ super().setUp()
+
+ os.makedirs(self.testdir + '/assets/dir')
+ with open(self.testdir + '/assets/index.html', 'w') as index:
+ index.write('0123456789')
+
+ os.makedirs(self.testdir + '/assets/403')
+ os.chmod(self.testdir + '/assets/403', 0o000)
+
+ self._load_conf(
+ {
+ "listeners": {
+ "*:7080": {"pass": "routes"},
+ "*:7081": {"pass": "applications/empty"},
+ },
+ "routes": [{"action": {"share": self.testdir + "/assets"}}],
+ "applications": {
+ "empty": {
+ "type": "python",
+ "processes": {"spare": 0},
+ "path": self.current_dir + "/python/empty",
+ "working_directory": self.current_dir
+ + "/python/empty",
+ "module": "wsgi",
+ }
+ },
+ }
+ )
+
+ def tearDown(self):
+ os.chmod(self.testdir + '/assets/403', 0o777)
+
+ super().tearDown()
+
+ def test_fallback(self):
+ self.assertIn(
+ 'success',
+ self.conf({"share": "/blah"}, 'routes/0/action'),
+ 'configure bad path no fallback',
+ )
+ self.assertEqual(self.get()['status'], 404, 'bad path no fallback')
+
+ self.assertIn(
+ 'success',
+ self.conf(
+ {"share": "/blah", "fallback": {"pass": "applications/empty"}},
+ 'routes/0/action',
+ ),
+ 'configure bad path fallback',
+ )
+ resp = self.get()
+ self.assertEqual(resp['status'], 200, 'bad path fallback status')
+ self.assertEqual(resp['body'], '', 'bad path fallback')
+
+ def test_fallback_valid_path(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ {
+ "share": self.testdir + "/assets",
+ "fallback": {"pass": "applications/empty"},
+ },
+ 'routes/0/action',
+ ),
+ 'configure fallback',
+ )
+ resp = self.get()
+ self.assertEqual(resp['status'], 200, 'fallback status')
+ self.assertEqual(resp['body'], '0123456789', 'fallback')
+
+ resp = self.get(url='/403/')
+ self.assertEqual(resp['status'], 200, 'fallback status 403')
+ self.assertEqual(resp['body'], '', 'fallback 403')
+
+ resp = self.post()
+ self.assertEqual(resp['status'], 200, 'fallback status 405')
+ self.assertEqual(resp['body'], '', 'fallback 405')
+
+ self.assertEqual(
+ self.get(url='/dir')['status'], 301, 'fallback status 301'
+ )
+
+ def test_fallback_nested(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ {
+ "share": "/blah",
+ "fallback": {
+ "share": "/blah/blah",
+ "fallback": {"pass": "applications/empty"},
+ },
+ },
+ 'routes/0/action',
+ ),
+ 'configure fallback nested',
+ )
+ resp = self.get()
+ self.assertEqual(resp['status'], 200, 'fallback nested status')
+ self.assertEqual(resp['body'], '', 'fallback nested')
+
+ def test_fallback_share(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ {
+ "share": "/blah",
+ "fallback": {"share": self.testdir + "/assets"},
+ },
+ 'routes/0/action',
+ ),
+ 'configure fallback share',
+ )
+ resp = self.get()
+ self.assertEqual(resp['status'], 200, 'fallback share status')
+ self.assertEqual(resp['body'], '0123456789', 'fallback share')
+
+ resp = self.head()
+ self.assertEqual(resp['status'], 200, 'fallback share status HEAD')
+ self.assertEqual(resp['body'], '', 'fallback share HEAD')
+
+ self.assertEqual(
+ self.get(url='/dir')['status'], 301, 'fallback share status 301'
+ )
+
+ def test_fallback_proxy(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ {
+ "share": "/blah",
+ "fallback": {"proxy": "http://127.0.0.1:7081"},
+ },
+ 'routes/0/action',
+ ),
+ 'configure fallback proxy',
+ )
+ resp = self.get()
+ self.assertEqual(resp['status'], 200, 'fallback proxy status')
+ self.assertEqual(resp['body'], '', 'fallback proxy')
+
+ @unittest.skip('not yet')
+ def test_fallback_proxy_cycle(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ {
+ "share": "/blah",
+ "fallback": {"proxy": "http://127.0.0.1:7080"},
+ },
+ 'routes/0/action',
+ ),
+ 'configure fallback cycle',
+ )
+ self.assertNotEqual(self.get()['status'], 200, 'fallback cycle')
+
+ self.assertIn(
+ 'success', self.conf_delete('listeners/*:7081'), 'delete listener'
+ )
+ self.assertNotEqual(self.get()['status'], 200, 'fallback cycle 2')
+
+ def test_fallback_invalid(self):
+ self.assertIn(
+ 'error',
+ self.conf({"share": "/blah", "fallback": {}}, 'routes/0/action'),
+ 'configure fallback empty',
+ )
+ self.assertIn(
+ 'error',
+ self.conf({"share": "/blah", "fallback": ""}, 'routes/0/action'),
+ 'configure fallback not object',
+ )
+ self.assertIn(
+ 'error',
+ self.conf(
+ {
+ "proxy": "http://127.0.0.1:7081",
+ "fallback": {"share": "/blah"},
+ },
+ 'routes/0/action',
+ ),
+ 'configure fallback proxy invalid',
+ )
+ self.assertIn(
+ 'error',
+ self.conf(
+ {
+ "pass": "applications/empty",
+ "fallback": {"share": "/blah"},
+ },
+ 'routes/0/action',
+ ),
+ 'configure fallback pass invalid',
+ )
+ self.assertIn(
+ 'error',
+ self.conf({"fallback": {"share": "/blah"}}, 'routes/0/action'),
+ 'configure fallback only',
+ )
+
+
+if __name__ == '__main__':
+ TestStatic.main()
diff --git a/test/test_static.py b/test/test_static.py
index f9dcb7dd..b2489aa0 100644
--- a/test/test_static.py
+++ b/test/test_static.py
@@ -39,6 +39,9 @@ class TestStatic(TestApplicationProto):
self.get(url='/index.html')['body'], '0123456789', 'index'
)
self.assertEqual(self.get(url='/')['body'], '0123456789', 'index 2')
+ self.assertEqual(self.get(url='//')['body'], '0123456789', 'index 3')
+ self.assertEqual(self.get(url='/.')['body'], '0123456789', 'index 4')
+ self.assertEqual(self.get(url='/./')['body'], '0123456789', 'index 5')
self.assertEqual(
self.get(url='/?blah')['body'], '0123456789', 'index vars'
)
@@ -199,10 +202,29 @@ class TestStatic(TestApplicationProto):
self.get(url='/link/file')['status'], 200, 'symlink file'
)
- def test_static_head(self):
- resp = self.head(url='/')
- self.assertEqual(resp['status'], 200, 'status')
- self.assertEqual(resp['body'], '', 'empty body')
+ def test_static_method(self):
+ resp = self.head()
+ self.assertEqual(resp['status'], 200, 'HEAD status')
+ self.assertEqual(resp['body'], '', 'HEAD empty body')
+
+ self.assertEqual(self.delete()['status'], 405, 'DELETE')
+ self.assertEqual(self.post()['status'], 405, 'POST')
+ self.assertEqual(self.put()['status'], 405, 'PUT')
+
+ def test_static_path(self):
+ self.assertEqual(
+ self.get(url='/dir/../dir/file')['status'], 200, 'relative'
+ )
+
+ self.assertEqual(self.get(url='./')['status'], 400, 'path invalid')
+ self.assertEqual(self.get(url='../')['status'], 400, 'path invalid 2')
+ self.assertEqual(self.get(url='/..')['status'], 400, 'path invalid 3')
+ self.assertEqual(
+ self.get(url='../assets/')['status'], 400, 'path invalid 4'
+ )
+ self.assertEqual(
+ self.get(url='/../assets/')['status'], 400, 'path invalid 5'
+ )
def test_static_two_clients(self):
_, sock = self.get(url='/', start=True, no_recv=True)
diff --git a/test/test_tls.py b/test/test_tls.py
index 1ead111c..475e9919 100644
--- a/test/test_tls.py
+++ b/test/test_tls.py
@@ -157,7 +157,8 @@ class TestTLS(TestApplicationTLS):
'-genkey',
'-out', self.testdir + '/ec.key',
'-name', 'prime256v1',
- ]
+ ],
+ stderr=subprocess.STDOUT,
)
subprocess.call(
@@ -170,7 +171,8 @@ class TestTLS(TestApplicationTLS):
'-config', self.testdir + '/openssl.conf',
'-key', self.testdir + '/ec.key',
'-out', self.testdir + '/ec.crt',
- ]
+ ],
+ stderr=subprocess.STDOUT,
)
self.certificate_load('ec')
@@ -230,7 +232,8 @@ class TestTLS(TestApplicationTLS):
'-config', self.testdir + '/openssl.conf',
'-out', self.testdir + '/int.csr',
'-keyout', self.testdir + '/int.key',
- ]
+ ],
+ stderr=subprocess.STDOUT,
)
subprocess.call(
@@ -242,7 +245,8 @@ class TestTLS(TestApplicationTLS):
'-config', self.testdir + '/openssl.conf',
'-out', self.testdir + '/end.csr',
'-keyout', self.testdir + '/end.key',
- ]
+ ],
+ stderr=subprocess.STDOUT,
)
with open(self.testdir + '/ca.conf', 'w') as f:
@@ -288,7 +292,8 @@ basicConstraints = critical,CA:TRUE"""
'-cert', self.testdir + '/root.crt',
'-in', self.testdir + '/int.csr',
'-out', self.testdir + '/int.crt',
- ]
+ ],
+ stderr=subprocess.STDOUT,
)
subprocess.call(
@@ -302,7 +307,8 @@ basicConstraints = critical,CA:TRUE"""
'-cert', self.testdir + '/int.crt',
'-in', self.testdir + '/end.csr',
'-out', self.testdir + '/end.crt',
- ]
+ ],
+ stderr=subprocess.STDOUT,
)
crt_path = self.testdir + '/end-int.crt'
diff --git a/test/test_upstreams_rr.py b/test/test_upstreams_rr.py
new file mode 100644
index 00000000..2bc2d90a
--- /dev/null
+++ b/test/test_upstreams_rr.py
@@ -0,0 +1,465 @@
+import os
+import re
+import unittest
+from unit.applications.lang.python import TestApplicationPython
+
+
+class TestUpstreamsRR(TestApplicationPython):
+ prerequisites = {'modules': ['python']}
+
+ def setUp(self):
+ super().setUp()
+
+ self.assertIn(
+ 'success',
+ self.conf(
+ {
+ "listeners": {
+ "*:7080": {"pass": "upstreams/one"},
+ "*:7081": {"pass": "applications/ups_0"},
+ "*:7082": {"pass": "applications/ups_1"},
+ "*:7083": {"pass": "applications/ups_2"},
+ "*:7090": {"pass": "upstreams/two"},
+ },
+ "upstreams": {
+ "one": {
+ "servers": {
+ "127.0.0.1:7081": {},
+ "127.0.0.1:7082": {},
+ },
+ },
+ "two": {
+ "servers": {
+ "127.0.0.1:7081": {},
+ "127.0.0.1:7082": {},
+ },
+ },
+ },
+ "applications": {
+ "ups_0": {
+ "type": "python",
+ "processes": {"spare": 0},
+ "path": self.current_dir + "/python/upstreams/0",
+ "working_directory": self.current_dir
+ + "/python/upstreams/0",
+ "module": "wsgi",
+ },
+ "ups_1": {
+ "type": "python",
+ "processes": {"spare": 0},
+ "path": self.current_dir + "/python/upstreams/1",
+ "working_directory": self.current_dir
+ + "/python/upstreams/1",
+ "module": "wsgi",
+ },
+ "ups_2": {
+ "type": "python",
+ "processes": {"spare": 0},
+ "path": self.current_dir + "/python/upstreams/2",
+ "working_directory": self.current_dir
+ + "/python/upstreams/2",
+ "module": "wsgi",
+ },
+ },
+ },
+ ),
+ 'upstreams initial configuration',
+ )
+
+ self.cpu_count = os.cpu_count()
+
+ def get_resps(self, req=100, port=7080):
+ resps = [0]
+ for _ in range(req):
+ headers = self.get(port=port)['headers']
+ if 'X-Upstream' in headers:
+ ups = int(headers['X-Upstream'])
+
+ if ups > len(resps) - 1:
+ resps.extend([0] * (ups - len(resps) + 1))
+
+ resps[ups] += 1
+
+ return resps
+
+ def get_resps_sc(self, req=100, port=7080):
+ to_send = b"""GET / HTTP/1.1
+Host: localhost
+
+""" * (
+ req - 1
+ )
+
+ to_send += b"""GET / HTTP/1.1
+Host: localhost
+Connection: close
+
+"""
+
+ resp = self.http(to_send, raw_resp=True, raw=True, port=port)
+ ups = re.findall('X-Upstream: (\d+)', resp)
+ resps = [0] * (int(max(ups)) + 1)
+
+ for i in range(len(ups)):
+ resps[int(ups[i])] += 1
+
+ return resps
+
+ def test_upstreams_rr_no_weight(self):
+ resps = self.get_resps()
+ self.assertLessEqual(
+ abs(resps[0] - resps[1]), self.cpu_count, 'no weight'
+ )
+
+ self.assertIn(
+ 'success',
+ self.conf_delete('upstreams/one/servers/127.0.0.1:7081'),
+ 'no weight server remove',
+ )
+
+ resps = self.get_resps(req=50)
+ self.assertEqual(resps[1], 50, 'no weight 2')
+
+ self.assertIn(
+ 'success',
+ self.conf({}, 'upstreams/one/servers/127.0.0.1:7081'),
+ 'no weight server revert',
+ )
+
+ resps = self.get_resps()
+ self.assertLessEqual(
+ abs(resps[0] - resps[1]), self.cpu_count, 'no weight 3'
+ )
+
+ self.assertIn(
+ 'success',
+ self.conf({}, 'upstreams/one/servers/127.0.0.1:7083'),
+ 'no weight server new',
+ )
+
+ resps = self.get_resps()
+ self.assertLessEqual(
+ max(resps) - min(resps), self.cpu_count, 'no weight 4'
+ )
+
+ resps = self.get_resps_sc(req=30)
+ self.assertEqual(resps[0], 10, 'no weight 4 0')
+ self.assertEqual(resps[1], 10, 'no weight 4 1')
+ self.assertEqual(resps[2], 10, 'no weight 4 2')
+
+ def test_upstreams_rr_weight(self):
+ self.assertIn(
+ 'success',
+ self.conf({"weight": 3}, 'upstreams/one/servers/127.0.0.1:7081'),
+ 'configure weight',
+ )
+
+ resps = self.get_resps_sc()
+ self.assertEqual(resps[0], 75, 'weight 3 0')
+ self.assertEqual(resps[1], 25, 'weight 3 1')
+
+ self.assertIn(
+ 'success',
+ self.conf_delete('upstreams/one/servers/127.0.0.1:7081/weight'),
+ 'configure weight remove',
+ )
+ resps = self.get_resps_sc(req=10)
+ self.assertEqual(resps[0], 5, 'weight 0 0')
+ self.assertEqual(resps[1], 5, 'weight 0 1')
+
+ self.assertIn(
+ 'success',
+ self.conf('1', 'upstreams/one/servers/127.0.0.1:7081/weight'),
+ 'configure weight 1',
+ )
+
+ resps = self.get_resps_sc()
+ self.assertEqual(resps[0], 50, 'weight 1 0')
+ self.assertEqual(resps[1], 50, 'weight 1 1')
+
+ self.assertIn(
+ 'success',
+ self.conf(
+ {
+ "127.0.0.1:7081": {"weight": 3},
+ "127.0.0.1:7083": {"weight": 2},
+ },
+ 'upstreams/one/servers',
+ ),
+ 'configure weight 2',
+ )
+
+ resps = self.get_resps_sc()
+ self.assertEqual(resps[0], 60, 'weight 2 0')
+ self.assertEqual(resps[2], 40, 'weight 2 1')
+
+ def test_upstreams_rr_independent(self):
+ def sum_resps(*args):
+ sum = [0] * len(args[0])
+ for arg in args:
+ sum = [x + y for x, y in zip(sum, arg)]
+
+ return sum
+
+ resps = self.get_resps_sc(req=30, port=7090)
+ self.assertEqual(resps[0], 15, 'dep two before 0')
+ self.assertEqual(resps[1], 15, 'dep two before 1')
+
+ resps = self.get_resps_sc(req=30)
+ self.assertEqual(resps[0], 15, 'dep one before 0')
+ self.assertEqual(resps[1], 15, 'dep one before 1')
+
+ self.assertIn(
+ 'success',
+ self.conf('2', 'upstreams/two/servers/127.0.0.1:7081/weight'),
+ 'configure dep weight',
+ )
+
+ resps = self.get_resps_sc(req=30, port=7090)
+ self.assertEqual(resps[0], 20, 'dep two 0')
+ self.assertEqual(resps[1], 10, 'dep two 1')
+
+ resps = self.get_resps_sc(req=30)
+ self.assertEqual(resps[0], 15, 'dep one 0')
+ self.assertEqual(resps[1], 15, 'dep one 1')
+
+ self.assertIn(
+ 'success',
+ self.conf('1', 'upstreams/two/servers/127.0.0.1:7081/weight'),
+ 'configure dep weight 1',
+ )
+
+ r_one, r_two = [0, 0], [0, 0]
+ for _ in range(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))
+
+ self.assertLessEqual(
+ abs(r_one[0] - r_one[1]), self.cpu_count, 'dep one mix'
+ )
+ self.assertLessEqual(
+ abs(r_two[0] - r_two[1]), self.cpu_count, 'dep two mix'
+ )
+
+ def test_upstreams_rr_delay(self):
+ headers_delay_1 = {
+ 'Connection': 'close',
+ 'Host': 'localhost',
+ 'Content-Length': '0',
+ 'X-Delay': '1',
+ }
+ headers_no_delay = {
+ 'Connection': 'close',
+ 'Host': 'localhost',
+ 'Content-Length': '0',
+ }
+
+ req = 50
+
+ socks = []
+ for i in range(req):
+ headers = headers_delay_1 if i % 5 == 0 else headers_no_delay
+ _, sock = self.get(
+ headers=headers,
+ start=True,
+ no_recv=True,
+ )
+ socks.append(sock)
+
+ resps = [0, 0]
+ for i in range(req):
+ resp = self.recvall(socks[i]).decode()
+ socks[i].close()
+
+ m = re.search('X-Upstream: (\d+)', resp)
+ resps[int(m.group(1))] += 1
+
+ self.assertLessEqual(
+ abs(resps[0] - resps[1]), self.cpu_count, 'dep two mix'
+ )
+
+ def test_upstreams_rr_active_req(self):
+ conns = 5
+ socks = []
+ socks2 = []
+
+ for _ in range(conns):
+ _, sock = self.get(start=True, no_recv=True)
+ socks.append(sock)
+
+ _, sock2 = self.http(
+ b"""POST / HTTP/1.1
+Host: localhost
+Content-Length: 10
+Connection: close
+
+""",
+ start=True,
+ no_recv=True,
+ raw=True,
+ )
+ socks2.append(sock2)
+
+ # Send one more request and read response to make sure that previous
+ # requests had enough time to reach server.
+
+ self.assertEqual(self.get()['status'], 200)
+
+ self.assertIn(
+ 'success',
+ self.conf(
+ {"127.0.0.1:7083": {"weight": 2}}, 'upstreams/one/servers',
+ ),
+ 'active req new server',
+ )
+ self.assertIn(
+ 'success',
+ self.conf_delete('upstreams/one/servers/127.0.0.1:7083'),
+ 'active req server remove',
+ )
+ self.assertIn(
+ 'success', self.conf_delete('listeners/*:7080'), 'delete listener'
+ )
+ self.assertIn(
+ 'success',
+ self.conf_delete('upstreams/one'),
+ 'active req upstream remove',
+ )
+
+ for i in range(conns):
+ resp = self.recvall(socks[i]).decode()
+ socks[i].close()
+
+ self.assertRegex(resp, r'X-Upstream', 'active req GET')
+
+ resp = self.http(b"""0123456789""", sock=socks2[i], raw=True)
+ self.assertEqual(resp['status'], 200, 'active req POST')
+
+ def test_upstreams_rr_bad_server(self):
+ self.assertIn(
+ 'success',
+ self.conf({"weight": 1}, 'upstreams/one/servers/127.0.0.1:7084'),
+ 'configure bad server',
+ )
+
+ resps = self.get_resps_sc(req=30)
+ self.assertEqual(resps[0], 10, 'bad server 0')
+ self.assertEqual(resps[1], 10, 'bad server 1')
+ self.assertEqual(sum(resps), 20, 'bad server sum')
+
+ def test_upstreams_rr_pipeline(self):
+ resps = self.get_resps_sc()
+
+ self.assertEqual(resps[0], 50, 'pipeline 0')
+ self.assertEqual(resps[1], 50, 'pipeline 1')
+
+ def test_upstreams_rr_post(self):
+ resps = [0, 0]
+ for _ in range(50):
+ resps[
+ int(self.post(body='0123456789')['headers']['X-Upstream'])
+ ] += 1
+ resps[int(self.get()['headers']['X-Upstream'])] += 1
+
+ self.assertLessEqual(
+ abs(resps[0] - resps[1]), self.cpu_count, 'post'
+ )
+
+ def test_upstreams_rr_unix(self):
+ addr_0 = self.testdir + '/sock_0'
+ addr_1 = self.testdir + '/sock_1'
+
+ self.assertIn(
+ 'success',
+ self.conf(
+ {
+ "*:7080": {"pass": "upstreams/one"},
+ "unix:" + addr_0: {"pass": "applications/ups_0"},
+ "unix:" + addr_1: {"pass": "applications/ups_1"},
+ },
+ 'listeners',
+ ),
+ 'configure listeners unix',
+ )
+
+ self.assertIn(
+ 'success',
+ self.conf(
+ {"unix:" + addr_0: {}, "unix:" + addr_1: {},},
+ 'upstreams/one/servers',
+ ),
+ 'configure servers unix',
+ )
+
+ resps = self.get_resps_sc()
+
+ self.assertEqual(resps[0], 50, 'unix 0')
+ self.assertEqual(resps[1], 50, 'unix 1')
+
+ def test_upstreams_rr_ipv6(self):
+ self.assertIn(
+ 'success',
+ self.conf(
+ {
+ "*:7080": {"pass": "upstreams/one"},
+ "[::1]:7081": {"pass": "applications/ups_0"},
+ "[::1]:7082": {"pass": "applications/ups_1"},
+ },
+ 'listeners',
+ ),
+ 'configure listeners ipv6',
+ )
+
+ self.assertIn(
+ 'success',
+ self.conf(
+ {"[::1]:7081": {}, "[::1]:7082": {},}, 'upstreams/one/servers'
+ ),
+ 'configure servers ipv6',
+ )
+
+ resps = self.get_resps_sc()
+
+ self.assertEqual(resps[0], 50, 'ipv6 0')
+ self.assertEqual(resps[1], 50, 'ipv6 1')
+
+ def test_upstreams_rr_servers_empty(self):
+ self.assertIn(
+ 'success',
+ self.conf({}, 'upstreams/one/servers'),
+ 'configure servers empty',
+ )
+
+ self.assertEqual(self.get()['status'], 502, 'servers empty')
+
+ def test_upstreams_rr_invalid(self):
+ self.assertIn(
+ 'error', self.conf({}, 'upstreams'), 'upstreams empty',
+ )
+ self.assertIn(
+ 'error', self.conf({}, 'upstreams/one'), 'named upstreams empty',
+ )
+ self.assertIn(
+ 'error',
+ self.conf({}, 'upstreams/one/servers/127.0.0.1'),
+ 'invalid address',
+ )
+ self.assertIn(
+ 'error',
+ self.conf({}, 'upstreams/one/servers/127.0.0.1:7081/blah'),
+ 'invalid server option',
+ )
+ self.assertIn(
+ 'error',
+ self.conf({}, 'upstreams/one/servers/127.0.0.1:7081/weight'),
+ 'invalid weight option',
+ )
+ self.assertIn(
+ 'error',
+ self.conf('-1', 'upstreams/one/servers/127.0.0.1:7081/weight'),
+ 'invalid negative weight',
+ )
+
+
+if __name__ == '__main__':
+ TestUpstreamsRR.main()
diff --git a/test/test_usr1.py b/test/test_usr1.py
index dd9292c7..2b4f394b 100644
--- a/test/test_usr1.py
+++ b/test/test_usr1.py
@@ -10,7 +10,9 @@ class TestUSR1(TestApplicationPython):
def test_usr1_access_log(self):
self.load('empty')
- log_path = self.testdir + '/access.log'
+ log = 'access.log'
+ log_new = 'new.log'
+ log_path = self.testdir + '/' + log
self.assertIn(
'success',
@@ -20,14 +22,12 @@ class TestUSR1(TestApplicationPython):
self.assertTrue(self.waitforfiles(log_path), 'open')
- log_path_new = self.testdir + '/new.log'
+ os.rename(log_path, self.testdir + '/' + log_new)
- os.rename(log_path, log_path_new)
-
- self.get()
+ self.assertEqual(self.get()['status'], 200)
self.assertIsNotNone(
- self.wait_for_record(r'"GET / HTTP/1.1" 200 0 "-" "-"', 'new.log'),
+ self.wait_for_record(r'"GET / HTTP/1.1" 200 0 "-" "-"', log_new),
'rename new',
)
self.assertFalse(os.path.isfile(log_path), 'rename old')
@@ -39,32 +39,33 @@ class TestUSR1(TestApplicationPython):
self.assertTrue(self.waitforfiles(log_path), 'reopen')
- self.get(url='/usr1')
+ self.assertEqual(self.get(url='/usr1')['status'], 200)
+
+ self.stop()
self.assertIsNotNone(
- self.wait_for_record(
- r'"GET /usr1 HTTP/1.1" 200 0 "-" "-"', 'access.log'
- ),
+ self.wait_for_record(r'"GET /usr1 HTTP/1.1" 200 0 "-" "-"', log),
'reopen 2',
)
self.assertIsNone(
- self.search_in_log(r'/usr1', 'new.log'), 'rename new 2'
+ self.search_in_log(r'/usr1', log_new), 'rename new 2'
)
@unittest.skip('not yet')
def test_usr1_unit_log(self):
self.load('log_body')
- log_path = self.testdir + '/unit.log'
- log_path_new = self.testdir + '/new.log'
+ log_new = 'new.log'
+ log_path = self.testdir + '/' + 'unit.log'
+ log_path_new = self.testdir + '/' + log_new
os.rename(log_path, log_path_new)
body = 'body_for_a_log_new'
- self.post(body=body)
+ self.assertEqual(self.post(body=body)['status'], 200)
self.assertIsNotNone(
- self.wait_for_record(body, 'new.log'), 'rename new'
+ self.wait_for_record(body, log_new), 'rename new'
)
self.assertFalse(os.path.isfile(log_path), 'rename old')
@@ -76,16 +77,18 @@ class TestUSR1(TestApplicationPython):
self.assertTrue(self.waitforfiles(log_path), 'reopen')
body = 'body_for_a_log_unit'
- self.post(body=body)
+ self.assertEqual(self.post(body=body)['status'], 200)
+
+ self.stop()
self.assertIsNotNone(self.wait_for_record(body), 'rename new')
- self.assertIsNone(self.search_in_log(body, 'new.log'), 'rename new 2')
+ self.assertIsNone(self.search_in_log(body, log_new), 'rename new 2')
# merge two log files into unit.log to check alerts
with open(log_path, 'w') as unit_log, \
- open(log_path_new, 'r') as new_log:
- unit_log.write(new_log.read())
+ open(log_path_new, 'r') as unit_log_new:
+ unit_log.write(unit_log_new.read())
if __name__ == '__main__':
diff --git a/test/unit/applications/lang/go.py b/test/unit/applications/lang/go.py
index 7212a95c..e0f83c0a 100644
--- a/test/unit/applications/lang/go.py
+++ b/test/unit/applications/lang/go.py
@@ -1,5 +1,5 @@
import os
-from subprocess import Popen
+import subprocess
from unit.applications.proto import TestApplicationProto
@@ -26,7 +26,7 @@ class TestApplicationGo(TestApplicationProto):
env['GOPATH'] = self.pardir + '/build/go'
try:
- process = Popen(
+ process = subprocess.Popen(
[
'go',
'build',
@@ -35,6 +35,7 @@ class TestApplicationGo(TestApplicationProto):
self.current_dir + '/go/' + script + '/' + name + '.go',
],
env=env,
+ stderr=subprocess.STDOUT,
)
process.communicate()
diff --git a/test/unit/applications/lang/java.py b/test/unit/applications/lang/java.py
index a370d96b..a8a09ce5 100644
--- a/test/unit/applications/lang/java.py
+++ b/test/unit/applications/lang/java.py
@@ -1,7 +1,7 @@
import os
import glob
import shutil
-from subprocess import Popen
+import subprocess
from unit.applications.proto import TestApplicationProto
@@ -64,7 +64,7 @@ class TestApplicationJava(TestApplicationProto):
javac.extend(src)
try:
- process = Popen(javac)
+ process = subprocess.Popen(javac, stderr=subprocess.STDOUT)
process.communicate()
except:
diff --git a/test/unit/applications/lang/php.py b/test/unit/applications/lang/php.py
index 6b1677e6..e8c70c62 100644
--- a/test/unit/applications/lang/php.py
+++ b/test/unit/applications/lang/php.py
@@ -4,7 +4,7 @@ from unit.applications.proto import TestApplicationProto
class TestApplicationPHP(TestApplicationProto):
application_type = "php"
- def load(self, script, name='index.php', **kwargs):
+ def load(self, script, index='index.php', **kwargs):
script_path = self.current_dir + '/php/' + script
self._load_conf(
@@ -16,7 +16,7 @@ class TestApplicationPHP(TestApplicationProto):
"processes": {"spare": 0},
"root": script_path,
"working_directory": script_path,
- "index": name,
+ "index": index,
}
},
},
diff --git a/test/unit/applications/tls.py b/test/unit/applications/tls.py
index 1290279d..9213974a 100644
--- a/test/unit/applications/tls.py
+++ b/test/unit/applications/tls.py
@@ -47,7 +47,8 @@ class TestApplicationTLS(TestApplicationProto):
'-config', self.testdir + '/openssl.conf',
'-out', self.testdir + '/' + name + '.crt',
'-keyout', self.testdir + '/' + name + '.key',
- ]
+ ],
+ stderr=subprocess.STDOUT,
)
if load:
diff --git a/test/unit/http.py b/test/unit/http.py
index c71e8f7e..47fb48f1 100644
--- a/test/unit/http.py
+++ b/test/unit/http.py
@@ -96,12 +96,7 @@ class TestHTTP(TestUnit):
encoding = 'utf-8' if 'encoding' not in kwargs else kwargs['encoding']
- if TestUnit.detailed:
- print('>>>')
- try:
- print(req.decode(encoding, 'ignore'))
- except UnicodeEncodeError:
- print(req)
+ self.log_out(req, encoding)
resp = ''
@@ -113,12 +108,7 @@ class TestHTTP(TestUnit):
sock, read_timeout=read_timeout, buff_size=read_buffer_size
).decode(encoding)
- if TestUnit.detailed:
- print('<<<')
- try:
- print(resp)
- except UnicodeEncodeError:
- print(resp.encode())
+ self.log_in(resp)
if 'raw_resp' not in kwargs:
resp = self._resp_to_dict(resp)
@@ -138,6 +128,37 @@ class TestHTTP(TestUnit):
return (resp, sock)
+ def log_out(self, log, encoding):
+ if TestUnit.detailed:
+ print('>>>')
+ log = self.log_truncate(log)
+ try:
+ print(log.decode(encoding, 'ignore'))
+ except UnicodeEncodeError:
+ print(log)
+
+ def log_in(self, log):
+ if TestUnit.detailed:
+ print('<<<')
+ log = self.log_truncate(log)
+ try:
+ print(log)
+ except UnicodeEncodeError:
+ print(log.encode())
+
+ def log_truncate(self, log, limit=1024):
+ len_log = len(log)
+ if len_log > limit:
+ log = log[:limit]
+ appendix = '(...logged %s of %s bytes)' % (limit, len_log)
+
+ if isinstance(log, bytes):
+ appendix = appendix.encode()
+
+ log = log + appendix
+
+ return log
+
def delete(self, **kwargs):
return self.http('DELETE', **kwargs)
diff --git a/test/unit/main.py b/test/unit/main.py
index 37d01d3b..69234dcc 100644
--- a/test/unit/main.py
+++ b/test/unit/main.py
@@ -5,6 +5,7 @@ import stat
import time
import fcntl
import shutil
+import signal
import argparse
import platform
import tempfile
@@ -30,6 +31,7 @@ class TestUnit(unittest.TestCase):
detailed = False
save_log = False
+ print_log = False
unsafe = False
def __init__(self, methodName='runTest'):
@@ -183,7 +185,7 @@ class TestUnit(unittest.TestCase):
shutil.rmtree(self.testdir)
else:
- self._print_path_to_log()
+ self._print_log()
def stop(self):
if self._started:
@@ -207,9 +209,9 @@ class TestUnit(unittest.TestCase):
os.mkdir(self.testdir + '/state')
- print()
-
- self._p = Process(target=subprocess.call, args=[ [
+ with open(self.testdir + '/unit.log', 'w') as log:
+ self._p = subprocess.Popen(
+ [
self.unitd,
'--no-daemon',
'--modules', self.pardir + '/build',
@@ -217,55 +219,40 @@ class TestUnit(unittest.TestCase):
'--pid', self.testdir + '/unit.pid',
'--log', self.testdir + '/unit.log',
'--control', 'unix:' + self.testdir + '/control.unit.sock',
- ] ])
- self._p.start()
-
- if not self.waitforfiles(
- self.testdir + '/unit.pid',
- self.testdir + '/unit.log',
- self.testdir + '/control.unit.sock',
- ):
+ '--tmp', self.testdir,
+ ],
+ stderr=log,
+ )
+
+ if not self.waitforfiles(self.testdir + '/control.unit.sock'):
exit("Could not start unit")
self._started = True
self.skip_alerts = [
r'read signalfd\(4\) failed',
+ r'last message send failed',
r'sendmsg.+failed',
r'recvmsg.+failed',
]
self.skip_sanitizer = False
def _stop(self):
- with open(self.testdir + '/unit.pid', 'r') as f:
- pid = f.read().rstrip()
-
- subprocess.call(['kill', '-s', 'QUIT', pid])
-
- for i in range(150):
- if not os.path.exists(self.testdir + '/unit.pid'):
- break
- time.sleep(0.1)
-
- self._p.join(timeout=5)
-
- if self._p.is_alive():
- self._p.terminate()
- self._p.join(timeout=5)
-
- if self._p.is_alive():
- self.fail("Could not terminate process " + str(self._p.pid))
-
- if os.path.exists(self.testdir + '/unit.pid'):
- self.fail("Could not terminate unit")
+ with self._p as p:
+ p.send_signal(signal.SIGQUIT)
+
+ try:
+ retcode = p.wait(15)
+ if retcode:
+ self.fail(
+ "Child process terminated with code " + str(retcode)
+ )
+ except:
+ self.fail("Could not terminate unit")
+ p.kill()
self._started = False
- if self._p.exitcode:
- self.fail(
- "Child process terminated with code " + str(self._p.exitcode)
- )
-
def _check_alerts(self, log):
found = False
@@ -281,14 +268,14 @@ class TestUnit(unittest.TestCase):
alerts = [al for al in alerts if re.search(skip, al) is None]
if alerts:
- self._print_path_to_log()
+ 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_path_to_log()
+ self._print_log(log)
self.assertFalse(sanitizer_errors, 'sanitizer error(s)')
if found:
@@ -361,6 +348,13 @@ class TestUnit(unittest.TestCase):
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',
@@ -374,12 +368,23 @@ class TestUnit(unittest.TestCase):
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:
+ if TestUnit.detailed or TestUnit.print_log:
fcntl.fcntl(sys.stdout.fileno(), fcntl.F_SETFL, 0)
- def _print_path_to_log(self):
- print('Path to unit.log:\n' + self.testdir + '/unit.log')
+ def _print_log(self, data=None):
+ path = self.testdir + '/unit.log'
+
+ print('Path to unit.log:\n' + path + '\n')
+
+ if TestUnit.print_log:
+ if data is None:
+ with open(path, 'r', encoding='utf-8', errors='ignore') as f:
+ data = f.read()
+
+ print(data)
+