unit/test/test_configuration.py
Andrei Zeliankou c183bd8749 Tests: get rid of classes in test files.
Class usage came from the unittest framework and it was always redundant
after migration to the pytest.  This commit removes classes from files
containing tests to make them more readable and understandable.
2023-06-14 18:20:09 +01:00

465 lines
11 KiB
Python

import socket
import pytest
from unit.control import Control
prerequisites = {'modules': {'python': 'any'}}
client = Control()
def try_addr(addr):
return client.conf(
{
"listeners": {addr: {"pass": "routes"}},
"routes": [{"action": {"return": 200}}],
"applications": {},
}
)
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'
assert client.conf_get('applications') == {
"app": {
"type": "python",
"processes": {"spare": 0},
"path": "/app",
"module": "wsgi",
}
}, 'unicode get'
def test_json_unicode_2():
assert 'success' in client.conf(
{
"приложение": {
"type": "python",
"processes": {"spare": 0},
"path": "/app",
"module": "wsgi",
}
},
'applications',
), 'unicode 2'
assert 'приложение' in client.conf_get('applications')
def test_json_unicode_number():
assert 'success' in client.conf(
"""
{
"app": {
"type": "python",
"processes": { "spare": \u0030 },
"path": "/app",
"module": "wsgi"
}
}
""",
'applications',
), 'unicode number'
def test_json_utf8_bom():
assert 'success' in client.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():
assert 'success' in client.conf(
b"""
// this is bridge
{
"//app": {
"type": "python", // end line
"processes": {"spare": 0},
// inside of block
"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 */
}
/* blah * / blah /* blah */
}
/* end of json \xEF\t\b */
""",
'applications',
), 'multi line comments'
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 quote'
def test_applications_miss_colon():
assert 'error' in client.conf(
"""
{
"app" {
"type": "python",
"processes": { "spare": 0 },
"path": "/app",
"module": "wsgi"
}
}
""",
'applications',
), 'miss colon'
def test_applications_miss_comma():
assert 'error' in client.conf(
"""
{
"app": {
"type": "python"
"processes": { "spare": 0 },
"path": "/app",
"module": "wsgi"
}
}
""",
'applications',
), 'miss comma'
def test_applications_skip_spaces():
assert 'success' in client.conf(b'{ \n\r\t}', 'applications'), 'skip spaces'
def test_applications_relative_path():
assert 'success' in client.conf(
{
"app": {
"type": "python",
"processes": {"spare": 0},
"path": "../app",
"module": "wsgi",
}
},
'applications',
), 'relative path'
@pytest.mark.skip('not yet, unsafe')
def test_listeners_empty():
assert 'error' in client.conf({"*:7080": {}}, 'listeners'), 'listener empty'
def test_listeners_no_app():
assert 'error' in client.conf(
{"*:7080": {"pass": "applications/app"}}, 'listeners'
), 'listeners no app'
def test_listeners_unix_abstract(system):
if system != 'Linux':
assert 'error' in try_addr("unix:@sock"), 'abstract at'
pytest.skip('not yet')
assert 'error' in try_addr("unix:\0soc"), 'abstract \0'
assert 'error' in try_addr("unix:\u0000soc"), 'abstract \0 unicode'
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'
def test_listeners_addr_error():
assert 'error' in try_addr("127.0.0.1"), 'no port'
def test_listeners_addr_error_2(skip_alert):
skip_alert(r'bind.*failed', r'failed to apply new conf')
assert 'error' in try_addr("[f607:7403:1e4b:6c66:33b2:843f:2517:da27]:7080")
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)
client.conf(
{
"listeners": {"127.0.0.1:7080": {"pass": "routes"}},
"routes": [],
}
)
resp = client.conf({"listeners": {}, "applications": {}})
try:
s.bind(('127.0.0.1', 7080))
s.listen()
except OSError:
fail = True
if fail:
pytest.fail('cannot bind or listen to the address')
assert 'success' in resp, 'port release'
def test_json_application_name_large():
name = "X" * 1024 * 1024
assert 'success' in client.conf(
{
"listeners": {"*:7080": {"pass": f"applications/{name}"}},
"applications": {
name: {
"type": "python",
"processes": {"spare": 0},
"path": "/app",
"module": "wsgi",
}
},
}
)
@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 client.conf(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": [
{
"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"},
},
],
}
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'