diff options
Diffstat (limited to 'test/test_configuration.py')
-rw-r--r-- | test/test_configuration.py | 754 |
1 files changed, 390 insertions, 364 deletions
diff --git a/test/test_configuration.py b/test/test_configuration.py index e3ddc891..19a2a1a5 100644 --- a/test/test_configuration.py +++ b/test/test_configuration.py @@ -1,439 +1,465 @@ import socket import pytest -from unit.control import TestControl -from unit.option import option +from unit.control import Control +prerequisites = {'modules': {'python': 'any'}} -class TestConfiguration(TestControl): - prerequisites = {'modules': {'python': 'any'}} +client = Control() - 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' +def try_addr(addr): + return client.conf( + { + "listeners": {addr: {"pass": "routes"}}, + "routes": [{"action": {"return": 200}}], + "applications": {}, + } + ) - def test_json_leading_zero(self): - assert 'error' in self.conf('00'), 'leading zero' - def test_json_unicode(self): - assert 'success' in self.conf( - u""" - { - "ap\u0070": { - "type": "\u0070ython", - "processes": { "spare": 0 }, - "path": "\u002Fapp", - "module": "wsgi" - } +def test_json_empty(): + assert 'error' in client.conf(''), 'empty' + + +def test_json_leading_zero(): + assert 'error' in client.conf('00'), 'leading zero' + + +def test_json_unicode(): + assert 'success' in client.conf( + """ + { + "ap\u0070": { + "type": "\u0070ython", + "processes": { "spare": 0 }, + "path": "\u002Fapp", + "module": "wsgi" } - """, - 'applications', - ), 'unicode' + } + """, + 'applications', + ), 'unicode' + + assert client.conf_get('applications') == { + "app": { + "type": "python", + "processes": {"spare": 0}, + "path": "/app", + "module": "wsgi", + } + }, 'unicode get' - assert self.conf_get('applications') == { - "app": { + +def test_json_unicode_2(): + assert 'success' in client.conf( + { + "приложение": { "type": "python", "processes": {"spare": 0}, "path": "/app", "module": "wsgi", } - }, 'unicode get' + }, + 'applications', + ), 'unicode 2' - def test_json_unicode_2(self): - assert 'success' in self.conf( - { - "приложение": { - "type": "python", - "processes": {"spare": 0}, - "path": "/app", - "module": "wsgi", - } - }, - 'applications', - ), 'unicode 2' + assert 'приложение' in client.conf_get('applications') - assert 'приложение' in self.conf_get('applications'), 'unicode 2 get' - def test_json_unicode_number(self): - assert 'success' in self.conf( - u""" - { - "app": { - "type": "python", - "processes": { "spare": \u0030 }, - "path": "/app", - "module": "wsgi" - } +def test_json_unicode_number(): + assert 'success' in client.conf( + """ + { + "app": { + "type": "python", + "processes": { "spare": \u0030 }, + "path": "/app", + "module": "wsgi" } - """, - 'applications', - ), 'unicode number' + } + """, + 'applications', + ), 'unicode number' - def test_json_utf8_bom(self): - assert 'success' in self.conf( - b"""\xEF\xBB\xBF - { - "app": { - "type": "python", - "processes": {"spare": 0}, - "path": "/app", - "module": "wsgi" - } - } - """, - 'applications', - ), 'UTF-8 BOM' - - def test_json_comment_single_line(self): - assert 'success' in self.conf( - b""" - // this is bridge - { - "//app": { - "type": "python", // end line - "processes": {"spare": 0}, - // inside of block - "path": "/app", - "module": "wsgi" - } - // double // + +def test_json_utf8_bom(): + assert 'success' in client.conf( + b"""\xEF\xBB\xBF + { + "app": { + "type": "python", + "processes": {"spare": 0}, + "path": "/app", + "module": "wsgi" } - // end of json \xEF\t - """, - 'applications', - ), 'single line comments' - - def test_json_comment_multi_line(self): - assert 'success' in self.conf( - b""" - /* this is bridge */ - { - "/*app": { - /** - * multiple lines - **/ - "type": "python", - "processes": /* inline */ {"spare": 0}, - "path": "/app", - "module": "wsgi" - /* - // end of block */ - } - /* blah * / blah /* blah */ + } + """, + 'applications', + ), 'UTF-8 BOM' + + +def test_json_comment_single_line(): + assert 'success' in client.conf( + b""" + // this is bridge + { + "//app": { + "type": "python", // end line + "processes": {"spare": 0}, + // inside of block + "path": "/app", + "module": "wsgi" } - /* end of json \xEF\t\b */ - """, - 'applications', - ), 'multi line comments' - - def test_json_comment_invalid(self): - assert 'error' in self.conf(b'/{}', 'applications'), 'slash' - assert 'error' in self.conf(b'//{}', 'applications'), 'comment' - assert 'error' in self.conf(b'{} /', 'applications'), 'slash end' - assert 'error' in self.conf(b'/*{}', 'applications'), 'slash star' - assert 'error' in self.conf(b'{} /*', 'applications'), 'slash star end' - - def test_applications_open_brace(self): - assert 'error' in self.conf('{', 'applications'), 'open brace' - - def test_applications_string(self): - assert 'error' in self.conf('"{}"', 'applications'), 'string' - - @pytest.mark.skip('not yet, unsafe') - def test_applications_type_only(self): - assert 'error' in self.conf( - {"app": {"type": "python"}}, 'applications' - ), 'type only' - - def test_applications_miss_quote(self): - assert 'error' in self.conf( - """ - { - app": { - "type": "python", - "processes": { "spare": 0 }, - "path": "/app", - "module": "wsgi" - } + // double // + } + // end of json \xEF\t + """, + 'applications', + ), 'single line comments' + + +def test_json_comment_multi_line(): + assert 'success' in client.conf( + b""" + /* this is bridge */ + { + "/*app": { + /** + * multiple lines + **/ + "type": "python", + "processes": /* inline */ {"spare": 0}, + "path": "/app", + "module": "wsgi" + /* + // end of block */ } - """, - 'applications', - ), 'miss quote' + /* blah * / blah /* blah */ + } + /* end of json \xEF\t\b */ + """, + 'applications', + ), 'multi line comments' - def test_applications_miss_colon(self): - assert 'error' in self.conf( - """ - { - "app" { - "type": "python", - "processes": { "spare": 0 }, - "path": "/app", - "module": "wsgi" - } + +def test_json_comment_invalid(): + assert 'error' in client.conf(b'/{}', 'applications'), 'slash' + assert 'error' in client.conf(b'//{}', 'applications'), 'comment' + assert 'error' in client.conf(b'{} /', 'applications'), 'slash end' + assert 'error' in client.conf(b'/*{}', 'applications'), 'slash star' + assert 'error' in client.conf(b'{} /*', 'applications'), 'slash star end' + + +def test_applications_open_brace(): + assert 'error' in client.conf('{', 'applications'), 'open brace' + + +def test_applications_string(): + assert 'error' in client.conf('"{}"', 'applications'), 'string' + + +@pytest.mark.skip('not yet, unsafe') +def test_applications_type_only(): + assert 'error' in client.conf( + {"app": {"type": "python"}}, 'applications' + ), 'type only' + + +def test_applications_miss_quote(): + assert 'error' in client.conf( + """ + { + app": { + "type": "python", + "processes": { "spare": 0 }, + "path": "/app", + "module": "wsgi" } - """, - 'applications', - ), 'miss colon' + } + """, + 'applications', + ), 'miss quote' - def test_applications_miss_comma(self): - assert 'error' in self.conf( - """ - { - "app": { - "type": "python" - "processes": { "spare": 0 }, - "path": "/app", - "module": "wsgi" - } + +def test_applications_miss_colon(): + assert 'error' in client.conf( + """ + { + "app" { + "type": "python", + "processes": { "spare": 0 }, + "path": "/app", + "module": "wsgi" } - """, - 'applications', - ), 'miss comma' + } + """, + 'applications', + ), 'miss colon' - def test_applications_skip_spaces(self): - assert 'success' in self.conf( - b'{ \n\r\t}', 'applications' - ), 'skip spaces' - def test_applications_relative_path(self): - assert 'success' in self.conf( - { - "app": { - "type": "python", - "processes": {"spare": 0}, - "path": "../app", - "module": "wsgi", - } - }, - 'applications', - ), 'relative path' +def test_applications_miss_comma(): + assert 'error' in client.conf( + """ + { + "app": { + "type": "python" + "processes": { "spare": 0 }, + "path": "/app", + "module": "wsgi" + } + } + """, + 'applications', + ), 'miss comma' - @pytest.mark.skip('not yet, unsafe') - def test_listeners_empty(self): - assert 'error' in self.conf( - {"*:7080": {}}, 'listeners' - ), 'listener empty' - def test_listeners_no_app(self): - assert 'error' in self.conf( - {"*:7080": {"pass": "applications/app"}}, 'listeners' - ), 'listeners no app' +def test_applications_skip_spaces(): + assert 'success' in client.conf(b'{ \n\r\t}', 'applications'), 'skip spaces' - def test_listeners_unix_abstract(self): - if option.system != 'Linux': - assert 'error' in self.try_addr("unix:@sock"), 'abstract at' - pytest.skip('not yet') +def test_applications_relative_path(): + assert 'success' in client.conf( + { + "app": { + "type": "python", + "processes": {"spare": 0}, + "path": "../app", + "module": "wsgi", + } + }, + 'applications', + ), 'relative path' - assert 'error' in self.try_addr("unix:\0soc"), 'abstract \0' - assert 'error' in self.try_addr("unix:\u0000soc"), 'abstract \0 unicode' - 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' +@pytest.mark.skip('not yet, unsafe') +def test_listeners_empty(): + assert 'error' in client.conf({"*:7080": {}}, 'listeners'), 'listener empty' - def test_listeners_addr_error(self): - assert 'error' in self.try_addr("127.0.0.1"), 'no port' - def test_listeners_addr_error_2(self, skip_alert): - skip_alert(r'bind.*failed', r'failed to apply new conf') +def test_listeners_no_app(): + assert 'error' in client.conf( + {"*:7080": {"pass": "applications/app"}}, 'listeners' + ), 'listeners no app' - 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): - fail = False - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) +def test_listeners_unix_abstract(system): + if system != 'Linux': + assert 'error' in try_addr("unix:@sock"), 'abstract at' - self.conf( - { - "listeners": {"127.0.0.1:7080": {"pass": "routes"}}, - "routes": [], - } - ) + pytest.skip('not yet') - resp = self.conf({"listeners": {}, "applications": {}}) + assert 'error' in try_addr("unix:\0soc"), 'abstract \0' + assert 'error' in try_addr("unix:\u0000soc"), 'abstract \0 unicode' - try: - s.bind(('127.0.0.1', 7080)) - s.listen() - except OSError: - fail = True +def test_listeners_addr(): + assert 'success' in try_addr("*:7080"), 'wildcard' + assert 'success' in try_addr("127.0.0.1:7081"), 'explicit' + assert 'success' in try_addr("[::1]:7082"), 'explicit ipv6' - if fail: - pytest.fail('cannot bind or listen to the address') - assert 'success' in resp, 'port release' +def test_listeners_addr_error(): + assert 'error' in try_addr("127.0.0.1"), 'no port' - def test_json_application_name_large(self): - name = "X" * 1024 * 1024 - assert 'success' in self.conf( - { - "listeners": {"*:7080": {"pass": f"applications/{name}"}}, - "applications": { - name: { - "type": "python", - "processes": {"spare": 0}, - "path": "/app", - "module": "wsgi", - } - }, - } - ) +def test_listeners_addr_error_2(skip_alert): + skip_alert(r'bind.*failed', r'failed to apply new conf') - @pytest.mark.skip('not yet') - def test_json_application_many(self): - apps = 999 + assert 'error' in try_addr("[f607:7403:1e4b:6c66:33b2:843f:2517:da27]:7080") - conf = { - "applications": { - f"app-{a}": { - "type": "python", - "processes": {"spare": 0}, - "path": "/app", - "module": "wsgi", - } - for a in range(apps) - }, - "listeners": { - f"*:{(7000 + a)}": {"pass": f"applications/app-{a}"} - for a in range(apps) - }, - } - assert 'success' in self.conf(conf) +def test_listeners_port_release(): + for _ in range(10): + fail = False + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - def test_json_application_python_prefix(self): - conf = { - "applications": { - "sub-app": { - "type": "python", - "processes": {"spare": 0}, - "path": "/app", - "module": "wsgi", - "prefix": "/app", - } - }, - "listeners": {"*:7080": {"pass": "routes"}}, - "routes": [ + client.conf( { - "match": {"uri": "/app/*"}, - "action": {"pass": "applications/sub-app"}, + "listeners": {"127.0.0.1:7080": {"pass": "routes"}}, + "routes": [], } - ], - } + ) - assert 'success' in self.conf(conf) + resp = client.conf({"listeners": {}, "applications": {}}) - def test_json_application_prefix_target(self): - conf = { - "applications": { - "sub-app": { - "type": "python", - "processes": {"spare": 0}, - "path": "/app", - "targets": { - "foo": {"module": "foo.wsgi", "prefix": "/app"}, - "bar": { - "module": "bar.wsgi", - "callable": "bar", - "prefix": "/api", - }, - }, - } - }, - "listeners": {"*:7080": {"pass": "routes"}}, - "routes": [ - { - "match": {"uri": "/app/*"}, - "action": {"pass": "applications/sub-app/foo"}, - }, - { - "match": {"uri": "/api/*"}, - "action": {"pass": "applications/sub-app/bar"}, - }, - ], - } + try: + s.bind(('127.0.0.1', 7080)) + s.listen() - assert 'success' in self.conf(conf) + except OSError: + fail = True - def test_json_application_invalid_python_prefix(self): - conf = { - "applications": { - "sub-app": { - "type": "python", - "processes": {"spare": 0}, - "path": "/app", - "module": "wsgi", - "prefix": "app", - } - }, - "listeners": {"*:7080": {"pass": "applications/sub-app"}}, - } + if fail: + pytest.fail('cannot bind or listen to the address') - assert 'error' in self.conf(conf) + assert 'success' in resp, 'port release' - def test_json_application_empty_python_prefix(self): - conf = { - "applications": { - "sub-app": { - "type": "python", - "processes": {"spare": 0}, - "path": "/app", - "module": "wsgi", - "prefix": "", - } - }, - "listeners": {"*:7080": {"pass": "applications/sub-app"}}, - } - assert 'error' in self.conf(conf) +def test_json_application_name_large(): + name = "X" * 1024 * 1024 - def test_json_application_many2(self): - conf = { + assert 'success' in client.conf( + { + "listeners": {"*:7080": {"pass": f"applications/{name}"}}, "applications": { - f"app-{a}": { + name: { "type": "python", "processes": {"spare": 0}, "path": "/app", "module": "wsgi", } - # Larger number of applications can cause test fail with default - # open files limit due to the lack of file descriptors. - for a in range(100) }, - "listeners": {"*:7080": {"pass": "applications/app-1"}}, } + ) + + +@pytest.mark.skip('not yet') +def test_json_application_many(): + apps = 999 + + conf = { + "applications": { + f"app-{a}": { + "type": "python", + "processes": {"spare": 0}, + "path": "/app", + "module": "wsgi", + } + for a in range(apps) + }, + "listeners": { + f"*:{(7000 + a)}": {"pass": f"applications/app-{a}"} + for a in range(apps) + }, + } - assert 'success' in self.conf(conf) + assert 'success' in client.conf(conf) - def test_unprivileged_user_error(self, is_su, skip_alert): - skip_alert(r'cannot set user "root"', r'failed to apply new conf') - if is_su: - pytest.skip('unprivileged tests') - assert 'error' in self.conf( +def test_json_application_python_prefix(): + conf = { + "applications": { + "sub-app": { + "type": "python", + "processes": {"spare": 0}, + "path": "/app", + "module": "wsgi", + "prefix": "/app", + } + }, + "listeners": {"*:7080": {"pass": "routes"}}, + "routes": [ { - "app": { - "type": "external", - "processes": 1, - "executable": "/app", - "user": "root", - } + "match": {"uri": "/app/*"}, + "action": {"pass": "applications/sub-app"}, + } + ], + } + + assert 'success' in client.conf(conf) + + +def test_json_application_prefix_target(): + conf = { + "applications": { + "sub-app": { + "type": "python", + "processes": {"spare": 0}, + "path": "/app", + "targets": { + "foo": {"module": "foo.wsgi", "prefix": "/app"}, + "bar": { + "module": "bar.wsgi", + "callable": "bar", + "prefix": "/api", + }, + }, + } + }, + "listeners": {"*:7080": {"pass": "routes"}}, + "routes": [ + { + "match": {"uri": "/app/*"}, + "action": {"pass": "applications/sub-app/foo"}, + }, + { + "match": {"uri": "/api/*"}, + "action": {"pass": "applications/sub-app/bar"}, }, - 'applications', - ), 'setting user' + ], + } + + assert 'success' in client.conf(conf) + + +def test_json_application_invalid_python_prefix(): + conf = { + "applications": { + "sub-app": { + "type": "python", + "processes": {"spare": 0}, + "path": "/app", + "module": "wsgi", + "prefix": "app", + } + }, + "listeners": {"*:7080": {"pass": "applications/sub-app"}}, + } + + assert 'error' in client.conf(conf) + + +def test_json_application_empty_python_prefix(): + conf = { + "applications": { + "sub-app": { + "type": "python", + "processes": {"spare": 0}, + "path": "/app", + "module": "wsgi", + "prefix": "", + } + }, + "listeners": {"*:7080": {"pass": "applications/sub-app"}}, + } + + assert 'error' in client.conf(conf) + + +def test_json_application_many2(): + conf = { + "applications": { + f"app-{a}": { + "type": "python", + "processes": {"spare": 0}, + "path": "/app", + "module": "wsgi", + } + # Larger number of applications can cause test fail with default + # open files limit due to the lack of file descriptors. + for a in range(100) + }, + "listeners": {"*:7080": {"pass": "applications/app-1"}}, + } + + assert 'success' in client.conf(conf) + + +def test_unprivileged_user_error(require, skip_alert): + require({'privileged_user': False}) + + skip_alert(r'cannot set user "root"', r'failed to apply new conf') + + assert 'error' in client.conf( + { + "app": { + "type": "external", + "processes": 1, + "executable": "/app", + "user": "root", + } + }, + 'applications', + ), 'setting user' |