summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAndrei Zeliankou <zelenkov@nginx.com>2022-09-05 23:06:16 +0100
committerAndrei Zeliankou <zelenkov@nginx.com>2022-09-05 23:06:16 +0100
commit6915ce1d1ca08ee72de1bafba1514a458b72116c (patch)
tree2e45cf7e1ca04fd7216d9c31cbc9cd91f6af1c3f
parent3ea113fcb7261a0be3b9dc8d32c402da1bcfadaa (diff)
downloadunit-6915ce1d1ca08ee72de1bafba1514a458b72116c.tar.gz
unit-6915ce1d1ca08ee72de1bafba1514a458b72116c.tar.bz2
Tests: added tests for basic statistics.
-rw-r--r--test/conftest.py3
-rw-r--r--test/test_status.py223
-rw-r--r--test/test_status_tls.py30
-rw-r--r--test/unit/status.py45
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