diff options
author | Andrei Zeliankou <zelenkov@nginx.com> | 2022-09-05 23:06:16 +0100 |
---|---|---|
committer | Andrei Zeliankou <zelenkov@nginx.com> | 2022-09-05 23:06:16 +0100 |
commit | 6915ce1d1ca08ee72de1bafba1514a458b72116c (patch) | |
tree | 2e45cf7e1ca04fd7216d9c31cbc9cd91f6af1c3f | |
parent | 3ea113fcb7261a0be3b9dc8d32c402da1bcfadaa (diff) | |
download | unit-6915ce1d1ca08ee72de1bafba1514a458b72116c.tar.gz unit-6915ce1d1ca08ee72de1bafba1514a458b72116c.tar.bz2 |
Tests: added tests for basic statistics.
-rw-r--r-- | test/conftest.py | 3 | ||||
-rw-r--r-- | test/test_status.py | 223 | ||||
-rw-r--r-- | test/test_status_tls.py | 30 | ||||
-rw-r--r-- | test/unit/status.py | 45 |
4 files changed, 301 insertions, 0 deletions
diff --git a/test/conftest.py b/test/conftest.py index ed19838c..18851baa 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -24,6 +24,7 @@ from unit.check.unix_abstract import check_unix_abstract from unit.http import TestHTTP from unit.log import Log from unit.option import option +from unit.status import Status from unit.utils import public_dir from unit.utils import waitforfiles @@ -429,6 +430,8 @@ def unit_run(state_dir=None): controller['pid'] = pid_by_name(controller['name']) controller['fds'] = _count_fds(controller['pid']) + Status._check_zeros() + return unit_instance diff --git a/test/test_status.py b/test/test_status.py new file mode 100644 index 00000000..214072d4 --- /dev/null +++ b/test/test_status.py @@ -0,0 +1,223 @@ +import time + +import pytest +from unit.applications.lang.python import TestApplicationPython +from unit.option import option +from unit.status import Status + + +class TestStatus(TestApplicationPython): + prerequisites = {'modules': {'python': 'any'}} + + def test_status(self): + assert 'error' in self.conf_delete('/status'), 'DELETE method' + + def test_status_requests(self, skip_alert): + skip_alert(r'Python failed to import module "blah"') + + assert 'success' in self.conf( + { + "listeners": { + "*:7080": {"pass": "routes"}, + "*:7081": {"pass": "applications/empty"}, + "*:7082": {"pass": "applications/blah"}, + }, + "routes": [{"action": {"return": 200}}], + "applications": { + "empty": { + "type": self.get_application_type(), + "processes": {"spare": 0}, + "path": option.test_dir + '/python/empty', + "working_directory": option.test_dir + '/python/empty', + "module": "wsgi", + }, + "blah": { + "type": self.get_application_type(), + "processes": {"spare": 0}, + "module": "blah", + }, + }, + }, + ) + + Status.init() + + assert self.get()['status'] == 200 + assert Status.get('/requests/total') == 1, '2xx' + + assert self.get(port=7081)['status'] == 200 + assert Status.get('/requests/total') == 2, '2xx app' + + assert ( + self.get(headers={'Host': '/', 'Connection': 'close'})['status'] + == 400 + ) + assert Status.get('/requests/total') == 3, '4xx' + + assert self.get(port=7082)['status'] == 503 + assert Status.get('/requests/total') == 4, '5xx' + + self.http( + b"""GET / HTTP/1.1 +Host: localhost + +GET / HTTP/1.1 +Host: localhost +Connection: close + +""", + raw=True, + ) + assert Status.get('/requests/total') == 6, 'pipeline' + + (_, sock) = self.get(port=7081, no_recv=True, start=True) + + time.sleep(1) + + assert Status.get('/requests/total') == 7, 'no receive' + + sock.close() + + def test_status_connections(self): + def check_connections(accepted, active, idle, closed): + Status.get('/connections') == { + 'accepted': accepted, + 'active': active, + 'idle': idle, + 'closed': closed, + } + + assert 'success' in self.conf( + { + "listeners": { + "*:7080": {"pass": "routes"}, + "*:7081": {"pass": "applications/delayed"}, + }, + "routes": [{"action": {"return": 200}}], + "applications": { + "delayed": { + "type": self.get_application_type(), + "processes": {"spare": 0}, + "path": option.test_dir + "/python/delayed", + "working_directory": option.test_dir + + "/python/delayed", + "module": "wsgi", + }, + }, + }, + ) + + Status.init() + + # accepted, closed + + assert self.get()['status'] == 200 + check_connections(1, 0, 0, 1) + + # idle + + _, sock = self.http(b'', start=True, raw=True, no_recv=True) + check_connections(2, 0, 1, 1) + + self.get(sock=sock) + check_connections(2, 0, 0, 2) + + # active + + (_, sock) = self.get( + headers={ + 'Host': 'localhost', + 'X-Delay': '2', + 'Connection': 'close', + }, + port=7081, + start=True, + read_timeout=1, + ) + check_connections(3, 1, 0, 2) + + self.get(sock=sock) + check_connections(3, 0, 0, 3) + + def test_status_applications(self): + def check_applications(expert): + apps = list(self.conf_get('/status/applications').keys()).sort() + assert apps == expert.sort() + + def check_application(name, running, starting, idle, active): + Status.get('/applications/' + name) == { + 'processes': { + 'running': running, + 'starting': starting, + 'idle': idle, + }, + 'requests': {'active': active}, + } + + self.load('delayed') + Status.init() + + check_applications(['delayed']) + check_application('delayed', 0, 0, 0, 0) + + # idle + + assert self.get()['status'] == 200 + check_application('delayed', 1, 0, 1, 0) + + assert 'success' in self.conf('4', 'applications/delayed/processes') + check_application('delayed', 4, 0, 4, 0) + + # active + + (_, sock) = self.get( + headers={ + 'Host': 'localhost', + 'X-Delay': '2', + 'Connection': 'close', + }, + start=True, + read_timeout=1, + ) + check_application('delayed', 4, 0, 3, 1) + sock.close() + + # starting + + assert 'success' in self.conf( + { + "listeners": { + "*:7080": {"pass": "applications/restart"}, + "*:7081": {"pass": "applications/delayed"}, + }, + "routes": [], + "applications": { + "restart": { + "type": self.get_application_type(), + "processes": {"spare": 0}, + "path": option.test_dir + "/python/restart", + "working_directory": option.test_dir + + "/python/restart", + "module": "longstart", + }, + "delayed": { + "type": self.get_application_type(), + "processes": {"spare": 0}, + "path": option.test_dir + "/python/delayed", + "working_directory": option.test_dir + + "/python/delayed", + "module": "wsgi", + }, + }, + }, + ) + Status.init() + + check_applications(['delayed', 'restart']) + check_application('restart', 0, 0, 0, 0) + check_application('delayed', 0, 0, 0, 0) + + self.get(read_timeout=1) + + check_application('restart', 0, 1, 0, 1) + check_application('delayed', 0, 0, 0, 0) diff --git a/test/test_status_tls.py b/test/test_status_tls.py new file mode 100644 index 00000000..dc3d68da --- /dev/null +++ b/test/test_status_tls.py @@ -0,0 +1,30 @@ +from unit.applications.tls import TestApplicationTLS +from unit.status import Status + + +class TestStatusTLS(TestApplicationTLS): + prerequisites = {'modules': {'openssl': 'any'}} + + def test_status_tls_requests(self): + self.certificate() + + assert 'success' in self.conf( + { + "listeners": { + "*:7080": {"pass": "routes"}, + "*:7081": { + "pass": "routes", + "tls": {"certificate": "default"}, + }, + }, + "routes": [{"action": {"return": 200}}], + "applications": {}, + } + ) + + Status.init() + + assert self.get()['status'] == 200 + assert self.get_ssl(port=7081)['status'] == 200 + + assert Status.get('/requests/total') == 2 diff --git a/test/unit/status.py b/test/unit/status.py new file mode 100644 index 00000000..17416f17 --- /dev/null +++ b/test/unit/status.py @@ -0,0 +1,45 @@ +from unit.control import TestControl + + +class Status: + _status = None + control = TestControl() + + def _check_zeros(): + assert Status.control.conf_get('/status') == { + 'connections': { + 'accepted': 0, + 'active': 0, + 'idle': 0, + 'closed': 0, + }, + 'requests': {'total': 0}, + 'applications': {}, + } + + def init(status=None): + Status._status = ( + status if status is not None else Status.control.conf_get('/status') + ) + + def diff(): + def find_diffs(d1, d2): + if isinstance(d1, dict) and isinstance(d2, dict): + return { + k: find_diffs(d1.get(k, 0), d2.get(k, 0)) + for k in d1 + if k in d2 + } + else: + return d1 - d2 + + return find_diffs(Status.control.conf_get('/status'), Status._status) + + def get(path='/'): + path = path.split('/')[1:] + diff = Status.diff() + + for p in path: + diff = diff[p] + + return diff |