diff options
Diffstat (limited to '')
-rw-r--r-- | test/python/lifespan/empty/asgi.py | 16 | ||||
-rw-r--r-- | test/python/targets/asgi.py | 54 | ||||
-rw-r--r-- | test/python/targets/wsgi.py | 8 | ||||
-rw-r--r-- | test/test_asgi_lifespan.py | 95 | ||||
-rw-r--r-- | test/test_asgi_targets.py | 92 | ||||
-rw-r--r-- | test/test_python_targets.py | 51 | ||||
-rw-r--r-- | test/unit/applications/lang/python.py | 11 |
7 files changed, 295 insertions, 32 deletions
diff --git a/test/python/lifespan/empty/asgi.py b/test/python/lifespan/empty/asgi.py index ea43af13..8ceecc2f 100644 --- a/test/python/lifespan/empty/asgi.py +++ b/test/python/lifespan/empty/asgi.py @@ -1,19 +1,19 @@ import os -async def application(scope, receive, send): +async def handler(prefix, scope, receive, send): if scope['type'] == 'lifespan': - with open('version', 'w+') as f: + with open(prefix + 'version', 'w+') as f: f.write( scope['asgi']['version'] + ' ' + scope['asgi']['spec_version'] ) while True: message = await receive() if message['type'] == 'lifespan.startup': - os.remove('startup') + os.remove(prefix + 'startup') await send({'type': 'lifespan.startup.complete'}) elif message['type'] == 'lifespan.shutdown': - os.remove('shutdown') + os.remove(prefix + 'shutdown') await send({'type': 'lifespan.shutdown.complete'}) return @@ -25,3 +25,11 @@ async def application(scope, receive, send): 'headers': [(b'content-length', b'0'),], } ) + + +async def application(scope, receive, send): + return await handler('', scope, receive, send) + + +async def application2(scope, receive, send): + return await handler('app2_', scope, receive, send) diff --git a/test/python/targets/asgi.py b/test/python/targets/asgi.py new file mode 100644 index 00000000..b51f3964 --- /dev/null +++ b/test/python/targets/asgi.py @@ -0,0 +1,54 @@ +async def application_201(scope, receive, send): + assert scope['type'] == 'http' + + await send( + { + 'type': 'http.response.start', + 'status': 201, + 'headers': [(b'content-length', b'0')], + } + ) + + +async def application_200(scope, receive, send): + assert scope['type'] == 'http' + + await send( + { + 'type': 'http.response.start', + 'status': 200, + 'headers': [(b'content-length', b'0')], + } + ) + + +def legacy_application_200(scope): + assert scope['type'] == 'http' + + return legacy_app_http_200 + + +async def legacy_app_http_200(receive, send): + await send( + { + 'type': 'http.response.start', + 'status': 200, + 'headers': [(b'content-length', b'0')], + } + ) + + +def legacy_application_201(scope, receive=None, send=None): + assert scope['type'] == 'http' + + return legacy_app_http_201 + + +async def legacy_app_http_201(receive, send): + await send( + { + 'type': 'http.response.start', + 'status': 201, + 'headers': [(b'content-length', b'0')], + } + ) diff --git a/test/python/targets/wsgi.py b/test/python/targets/wsgi.py new file mode 100644 index 00000000..fa17ab87 --- /dev/null +++ b/test/python/targets/wsgi.py @@ -0,0 +1,8 @@ +def wsgi_target_a(env, start_response): + start_response('200', [('Content-Length', '1')]) + return [b'1'] + + +def wsgi_target_b(env, start_response): + start_response('200', [('Content-Length', '1')]) + return [b'2'] diff --git a/test/test_asgi_lifespan.py b/test/test_asgi_lifespan.py index 43286e22..90866ec3 100644 --- a/test/test_asgi_lifespan.py +++ b/test/test_asgi_lifespan.py @@ -14,45 +14,88 @@ class TestASGILifespan(TestApplicationPython): } load_module = 'asgi' - def test_asgi_lifespan(self): - self.load('lifespan/empty') + def setup_cookies(self, prefix): + base_dir = option.test_dir + '/python/lifespan/empty' - startup_path = option.test_dir + '/python/lifespan/empty/startup' - shutdown_path = option.test_dir + '/python/lifespan/empty/shutdown' - version_path = option.test_dir + '/python/lifespan/empty/version' + os.chmod(base_dir, 0o777) - os.chmod(option.test_dir + '/python/lifespan/empty', 0o777) + for name in ['startup', 'shutdown', 'version']: + path = option.test_dir + '/python/lifespan/empty/' + prefix + name + open(path, 'a').close() + os.chmod(path, 0o777) - open(startup_path, 'a').close() - os.chmod(startup_path, 0o777) + def assert_cookies(self, prefix): + for name in ['startup', 'shutdown']: + path = option.test_dir + '/python/lifespan/empty/' + prefix + name + exists = os.path.isfile(path) + if exists: + os.remove(path) - open(shutdown_path, 'a').close() - os.chmod(shutdown_path, 0o777) + assert not exists, name - open(version_path, 'a').close() - os.chmod(version_path, 0o777) + path = option.test_dir + '/python/lifespan/empty/' + prefix + 'version' - assert self.get()['status'] == 204 + with open(path, 'r') as f: + version = f.read() - unit_stop() + os.remove(path) - is_startup = os.path.isfile(startup_path) - is_shutdown = os.path.isfile(shutdown_path) + assert version == '3.0 2.0', 'version' - if is_startup: - os.remove(startup_path) + def test_asgi_lifespan(self): + self.load('lifespan/empty') - if is_shutdown: - os.remove(shutdown_path) + self.setup_cookies('') - with open(version_path, 'r') as f: - version = f.read() + assert self.get()['status'] == 204 - os.remove(version_path) + unit_stop() - assert not is_startup, 'startup' - assert not is_shutdown, 'shutdown' - assert version == '3.0 2.0', 'version' + self.assert_cookies('') + + def test_asgi_lifespan_targets(self): + assert 'success' in self.conf( + { + "listeners": {"*:7080": {"pass": "routes"}}, + "routes": [ + { + "match": {"uri": "/1"}, + "action": {"pass": "applications/targets/1"}, + }, + { + "match": {"uri": "/2"}, + "action": {"pass": "applications/targets/2"}, + }, + ], + "applications": { + "targets": { + "type": "python", + "processes": {"spare": 0}, + "working_directory": option.test_dir + + "/python/lifespan/empty", + "path": option.test_dir + '/python/lifespan/empty', + "targets": { + "1": {"module": "asgi", "callable": "application"}, + "2": { + "module": "asgi", + "callable": "application2", + }, + }, + } + }, + } + ) + + self.setup_cookies('') + self.setup_cookies('app2_') + + assert self.get(url="/1")['status'] == 204 + assert self.get(url="/2")['status'] == 204 + + unit_stop() + + self.assert_cookies('') + self.assert_cookies('app2_') def test_asgi_lifespan_failed(self): self.load('lifespan/failed') diff --git a/test/test_asgi_targets.py b/test/test_asgi_targets.py new file mode 100644 index 00000000..a0eb1f84 --- /dev/null +++ b/test/test_asgi_targets.py @@ -0,0 +1,92 @@ +from distutils.version import LooseVersion + +import pytest + +from unit.applications.lang.python import TestApplicationPython +from unit.option import option + + +class TestASGITargets(TestApplicationPython): + prerequisites = { + 'modules': {'python': lambda v: LooseVersion(v) >= LooseVersion('3.5')} + } + load_module = 'asgi' + + @pytest.fixture(autouse=True) + def setup_method_fixture(self): + assert 'success' in self.conf( + { + "listeners": {"*:7080": {"pass": "routes"}}, + "routes": [ + { + "match": {"uri": "/1"}, + "action": {"pass": "applications/targets/1"}, + }, + { + "match": {"uri": "/2"}, + "action": {"pass": "applications/targets/2"}, + }, + ], + "applications": { + "targets": { + "type": "python", + "processes": {"spare": 0}, + "working_directory": option.test_dir + + "/python/targets/", + "path": option.test_dir + '/python/targets/', + "protocol": "asgi", + "targets": { + "1": { + "module": "asgi", + "callable": "application_200", + }, + "2": { + "module": "asgi", + "callable": "application_201", + }, + }, + } + }, + } + ) + + def conf_targets(self, targets): + assert 'success' in self.conf(targets, 'applications/targets/targets') + + def test_asgi_targets(self): + assert self.get(url='/1')['status'] == 200 + assert self.get(url='/2')['status'] == 201 + + def test_asgi_targets_legacy(self): + self.conf_targets( + { + "1": {"module": "asgi", "callable": "legacy_application_200"}, + "2": {"module": "asgi", "callable": "legacy_application_201"}, + } + ) + + assert self.get(url='/1')['status'] == 200 + assert self.get(url='/2')['status'] == 201 + + def test_asgi_targets_mix(self): + self.conf_targets( + { + "1": {"module": "asgi", "callable": "application_200"}, + "2": {"module": "asgi", "callable": "legacy_application_201"}, + } + ) + + assert self.get(url='/1')['status'] == 200 + assert self.get(url='/2')['status'] == 201 + + def test_asgi_targets_broken(self, skip_alert): + skip_alert(r'Python failed to get "blah" from module') + + self.conf_targets( + { + "1": {"module": "asgi", "callable": "application_200"}, + "2": {"module": "asgi", "callable": "blah"}, + } + ) + + assert self.get(url='/1')['status'] != 200 diff --git a/test/test_python_targets.py b/test/test_python_targets.py new file mode 100644 index 00000000..ca736c0d --- /dev/null +++ b/test/test_python_targets.py @@ -0,0 +1,51 @@ +import pytest + +from unit.applications.lang.python import TestApplicationPython +from unit.option import option + + +class TestPythonTargets(TestApplicationPython): + prerequisites = {'modules': {'python': 'all'}} + + def test_python_targets(self): + assert 'success' in self.conf( + { + "listeners": {"*:7080": {"pass": "routes"}}, + "routes": [ + { + "match": {"uri": "/1"}, + "action": {"pass": "applications/targets/1"}, + }, + { + "match": {"uri": "/2"}, + "action": {"pass": "applications/targets/2"}, + }, + ], + "applications": { + "targets": { + "type": "python", + "working_directory": option.test_dir + + "/python/targets/", + "path": option.test_dir + '/python/targets/', + "targets": { + "1": { + "module": "wsgi", + "callable": "wsgi_target_a", + }, + "2": { + "module": "wsgi", + "callable": "wsgi_target_b", + }, + }, + } + }, + } + ) + + resp = self.get(url='/1') + assert resp['status'] == 200 + assert resp['body'] == '1' + + resp = self.get(url='/2') + assert resp['status'] == 200 + assert resp['body'] == '2' diff --git a/test/unit/applications/lang/python.py b/test/unit/applications/lang/python.py index 287d23f0..b399dffd 100644 --- a/test/unit/applications/lang/python.py +++ b/test/unit/applications/lang/python.py @@ -42,8 +42,15 @@ class TestApplicationPython(TestApplicationProto): "module": module, } - for attr in ('callable', 'home', 'limits', 'path', 'protocol', - 'threads'): + for attr in ( + 'callable', + 'home', + 'limits', + 'path', + 'protocol', + 'targets', + 'threads', + ): if attr in kwargs: app[attr] = kwargs.pop(attr) |