diff options
Diffstat (limited to '')
-rw-r--r-- | test/unit/applications/lang/go.py | 1 | ||||
-rw-r--r-- | test/unit/applications/lang/node.py | 21 | ||||
-rw-r--r-- | test/unit/applications/lang/python.py | 11 | ||||
-rw-r--r-- | test/unit/applications/proto.py | 18 | ||||
-rw-r--r-- | test/unit/applications/tls.py | 42 | ||||
-rw-r--r-- | test/unit/applications/websockets.py | 18 | ||||
-rw-r--r-- | test/unit/check/chroot.py | 32 | ||||
-rw-r--r-- | test/unit/check/go.py | 1 | ||||
-rw-r--r-- | test/unit/check/isolation.py | 4 | ||||
-rw-r--r-- | test/unit/check/node.py | 13 | ||||
-rw-r--r-- | test/unit/http.py | 26 | ||||
-rw-r--r-- | test/unit/log.py | 23 | ||||
-rw-r--r-- | test/unit/option.py | 3 | ||||
-rw-r--r-- | test/unit/utils.py | 11 |
14 files changed, 180 insertions, 44 deletions
diff --git a/test/unit/applications/lang/go.py b/test/unit/applications/lang/go.py index a17b1af4..6be1667b 100644 --- a/test/unit/applications/lang/go.py +++ b/test/unit/applications/lang/go.py @@ -13,6 +13,7 @@ class TestApplicationGo(TestApplicationProto): env = os.environ.copy() env['GOPATH'] = option.current_dir + '/build/go' env['GOCACHE'] = option.cache_dir + '/go' + env['GO111MODULE'] = 'auto' if static: args = [ diff --git a/test/unit/applications/lang/node.py b/test/unit/applications/lang/node.py index cc6d06ef..5d05c70c 100644 --- a/test/unit/applications/lang/node.py +++ b/test/unit/applications/lang/node.py @@ -7,15 +7,16 @@ from unit.utils import public_dir class TestApplicationNode(TestApplicationProto): + application_type = "node" + es_modules = False + def prepare_env(self, script): # copy application - shutil.copytree( option.test_dir + '/node/' + script, option.temp_dir + '/node' ) # copy modules - shutil.copytree( option.current_dir + '/node/node_modules', option.temp_dir + '/node/node_modules', @@ -26,6 +27,19 @@ class TestApplicationNode(TestApplicationProto): def load(self, script, name='app.js', **kwargs): self.prepare_env(script) + if self.es_modules: + arguments = [ + "node", + "--loader", + "unit-http/loader.mjs", + "--require", + "unit-http/loader", + name, + ] + + else: + arguments = ["node", "--require", "unit-http/loader", name] + self._load_conf( { "listeners": { @@ -36,7 +50,8 @@ class TestApplicationNode(TestApplicationProto): "type": "external", "processes": {"spare": 0}, "working_directory": option.temp_dir + '/node', - "executable": name, + "executable": '/usr/bin/env', + "arguments": arguments, } }, }, 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) diff --git a/test/unit/applications/proto.py b/test/unit/applications/proto.py index 5c400621..92754c03 100644 --- a/test/unit/applications/proto.py +++ b/test/unit/applications/proto.py @@ -4,6 +4,7 @@ import time from unit.control import TestControl from unit.option import option +from unit.log import Log class TestApplicationProto(TestControl): @@ -15,18 +16,23 @@ class TestApplicationProto(TestControl): def date_to_sec_epoch(self, date, template='%a, %d %b %Y %H:%M:%S %Z'): return time.mktime(time.strptime(date, template)) + def findall(self, pattern, name='unit.log'): + with Log.open(name) as f: + return re.findall(pattern, f.read()) + def search_in_log(self, pattern, name='unit.log'): - with open(option.temp_dir + '/' + name, 'r', errors='ignore') as f: + with Log.open(name) as f: return re.search(pattern, f.read()) def wait_for_record(self, pattern, name='unit.log', wait=150): - for i in range(wait): - found = self.search_in_log(pattern, name) + with Log.open(name) as f: + for i in range(wait): + found = re.search(pattern, f.read()) - if found is not None: - break + if found is not None: + break - time.sleep(0.1) + time.sleep(0.1) return found diff --git a/test/unit/applications/tls.py b/test/unit/applications/tls.py index b0cd5abb..583b618f 100644 --- a/test/unit/applications/tls.py +++ b/test/unit/applications/tls.py @@ -21,10 +21,14 @@ class TestApplicationTLS(TestApplicationProto): 'req', '-x509', '-new', - '-subj', '/CN=' + name + '/', - '-config', option.temp_dir + '/openssl.conf', - '-out', option.temp_dir + '/' + name + '.crt', - '-keyout', option.temp_dir + '/' + name + '.key', + '-subj', + '/CN=' + name + '/', + '-config', + option.temp_dir + '/openssl.conf', + '-out', + option.temp_dir + '/' + name + '.crt', + '-keyout', + option.temp_dir + '/' + name + '.key', ], stderr=subprocess.STDOUT, ) @@ -63,19 +67,43 @@ class TestApplicationTLS(TestApplicationProto): return ssl.get_server_certificate(addr, ssl_version=ssl_version) - def openssl_conf(self): + def openssl_conf(self, rewrite=False, alt_names=[]): conf_path = option.temp_dir + '/openssl.conf' - if os.path.exists(conf_path): + if not rewrite and os.path.exists(conf_path): return + # Generates alt_names section with dns names + a_names = "[alt_names]\n" + for i, k in enumerate(alt_names, 1): + k = k.split('|') + + if k[0] == 'IP': + a_names += "IP.%d = %s\n" % (i, k[1]) + else: + a_names += "DNS.%d = %s\n" % (i, k[0]) + + # Generates section for sign request extension + a_sec = """req_extensions = myca_req_extensions + +[ myca_req_extensions ] +subjectAltName = @alt_names + +{a_names}""".format( + a_names=a_names + ) + with open(conf_path, 'w') as f: f.write( """[ req ] default_bits = 2048 encrypt_key = no distinguished_name = req_distinguished_name -[ req_distinguished_name ]""" + +{a_sec} +[ req_distinguished_name ]""".format( + a_sec=a_sec if alt_names else "" + ) ) def load(self, script, name=None): diff --git a/test/unit/applications/websockets.py b/test/unit/applications/websockets.py index cc720a98..aa83339c 100644 --- a/test/unit/applications/websockets.py +++ b/test/unit/applications/websockets.py @@ -43,11 +43,7 @@ class TestApplicationWebsocket(TestApplicationProto): 'Sec-WebSocket-Version': 13, } - _, sock = self.get( - headers=headers, - no_recv=True, - start=True, - ) + _, sock = self.get(headers=headers, no_recv=True, start=True,) resp = '' while True: @@ -57,7 +53,7 @@ class TestApplicationWebsocket(TestApplicationProto): resp += sock.recv(4096).decode() - if (resp.startswith('HTTP/') and '\r\n\r\n' in resp): + if resp.startswith('HTTP/') and '\r\n\r\n' in resp: resp = self._resp_to_dict(resp) break @@ -90,8 +86,8 @@ class TestApplicationWebsocket(TestApplicationProto): frame = {} - head1, = struct.unpack('!B', recv_bytes(sock, 1)) - head2, = struct.unpack('!B', recv_bytes(sock, 1)) + (head1,) = struct.unpack('!B', recv_bytes(sock, 1)) + (head2,) = struct.unpack('!B', recv_bytes(sock, 1)) frame['fin'] = bool(head1 & 0b10000000) frame['rsv1'] = bool(head1 & 0b01000000) @@ -103,10 +99,10 @@ class TestApplicationWebsocket(TestApplicationProto): length = head2 & 0b01111111 if length == 126: data = recv_bytes(sock, 2) - length, = struct.unpack('!H', data) + (length,) = struct.unpack('!H', data) elif length == 127: data = recv_bytes(sock, 8) - length, = struct.unpack('!Q', data) + (length,) = struct.unpack('!Q', data) if frame['mask']: mask_bits = recv_bytes(sock, 4) @@ -121,7 +117,7 @@ class TestApplicationWebsocket(TestApplicationProto): if frame['opcode'] == self.OP_CLOSE: if length >= 2: - code, = struct.unpack('!H', data[:2]) + (code,) = struct.unpack('!H', data[:2]) reason = data[2:].decode('utf-8') if not (code in self.CLOSE_CODES or 3000 <= code < 5000): pytest.fail('Invalid status code') diff --git a/test/unit/check/chroot.py b/test/unit/check/chroot.py new file mode 100644 index 00000000..40b75058 --- /dev/null +++ b/test/unit/check/chroot.py @@ -0,0 +1,32 @@ +import json + +from unit.http import TestHTTP +from unit.option import option + +http = TestHTTP() + + +def check_chroot(): + available = option.available + + resp = http.put( + url='/config', + sock_type='unix', + addr=option.temp_dir + '/control.unit.sock', + body=json.dumps( + { + "listeners": {"*:7080": {"pass": "routes"}}, + "routes": [ + { + "action": { + "share": option.temp_dir, + "chroot": option.temp_dir, + } + } + ], + } + ), + ) + + if 'success' in resp['body']: + available['features']['chroot'] = True diff --git a/test/unit/check/go.py b/test/unit/check/go.py index 35b0c2d5..309091c0 100644 --- a/test/unit/check/go.py +++ b/test/unit/check/go.py @@ -8,6 +8,7 @@ def check_go(current_dir, temp_dir, test_dir): env = os.environ.copy() env['GOPATH'] = current_dir + '/build/go' + env['GO111MODULE'] = 'auto' try: process = subprocess.Popen( diff --git a/test/unit/check/isolation.py b/test/unit/check/isolation.py index fe5a41f8..7c83ae35 100644 --- a/test/unit/check/isolation.py +++ b/test/unit/check/isolation.py @@ -12,6 +12,7 @@ from unit.utils import getns allns = ['pid', 'mnt', 'ipc', 'uts', 'cgroup', 'net'] http = TestHTTP() + def check_isolation(): test_conf = {"namespaces": {"credential": True}} available = option.available @@ -117,8 +118,7 @@ def check_isolation(): "body_empty": { "type": "perl", "processes": {"spare": 0}, - "working_directory": option.test_dir - + "/perl/body_empty", + "working_directory": option.test_dir + "/perl/body_empty", "script": option.test_dir + "/perl/body_empty/psgi.pl", "isolation": {"namespaces": {"credential": True}}, } diff --git a/test/unit/check/node.py b/test/unit/check/node.py index 236ba7b5..e053a749 100644 --- a/test/unit/check/node.py +++ b/test/unit/check/node.py @@ -1,6 +1,15 @@ import os +import subprocess def check_node(current_dir): - if os.path.exists(current_dir + '/node/node_modules'): - return True + if not os.path.exists(current_dir + '/node/node_modules'): + return None + + try: + v_bytes = subprocess.check_output(['/usr/bin/env', 'node', '-v']) + + return [str(v_bytes, 'utf-8').lstrip('v').rstrip()] + + except subprocess.CalledProcessError: + return None diff --git a/test/unit/http.py b/test/unit/http.py index 57e6ed3a..797b7681 100644 --- a/test/unit/http.py +++ b/test/unit/http.py @@ -10,15 +10,16 @@ import pytest from unit.option import option -class TestHTTP(): +class TestHTTP: def http(self, start_str, **kwargs): sock_type = kwargs.get('sock_type', 'ipv4') port = kwargs.get('port', 7080) url = kwargs.get('url', '/') http = 'HTTP/1.0' if 'http_10' in kwargs else 'HTTP/1.1' - headers = kwargs.get('headers', - {'Host': 'localhost', 'Connection': 'close'}) + headers = kwargs.get( + 'headers', {'Host': 'localhost', 'Connection': 'close'} + ) body = kwargs.get('body', b'') crlf = '\r\n' @@ -44,7 +45,8 @@ class TestHTTP(): sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) if 'wrapper' in kwargs: - sock = kwargs['wrapper'](sock) + server_hostname = headers.get('Host', 'localhost') + sock = kwargs['wrapper'](sock, server_hostname=server_hostname) connect_args = addr if sock_type == 'unix' else (addr, port) try: @@ -304,8 +306,9 @@ class TestHTTP(): return body, content_type def form_url_encode(self, fields): - data = "&".join("%s=%s" % (name, value) - for name, value in fields.items()).encode() + data = "&".join( + "%s=%s" % (name, value) for name, value in fields.items() + ).encode() return data, 'application/x-www-form-urlencoded' def multipart_encode(self, fields): @@ -325,7 +328,9 @@ class TestHTTP(): datatype = value['type'] if not isinstance(value['data'], io.IOBase): - pytest.fail('multipart encoding of file requires a stream.') + pytest.fail( + 'multipart encoding of file requires a stream.' + ) data = value['data'].read() @@ -335,9 +340,10 @@ class TestHTTP(): else: pytest.fail('multipart requires a string or stream data') - body += ( - "--%s\r\nContent-Disposition: form-data; name=\"%s\"" - ) % (boundary, field) + body += ("--%s\r\nContent-Disposition: form-data; name=\"%s\"") % ( + boundary, + field, + ) if filename != '': body += "; filename=\"%s\"" % filename diff --git a/test/unit/log.py b/test/unit/log.py new file mode 100644 index 00000000..7263443d --- /dev/null +++ b/test/unit/log.py @@ -0,0 +1,23 @@ +UNIT_LOG = 'unit.log' + + +class Log: + temp_dir = None + pos = {} + + def open(name=UNIT_LOG, encoding=None): + f = open(Log.get_path(name), 'r', encoding=encoding, errors='ignore') + f.seek(Log.pos.get(name, 0)) + + return f + + def set_pos(pos, name=UNIT_LOG): + Log.pos[name] = pos + + def swap(name): + pos = Log.pos.get(UNIT_LOG, 0) + Log.pos[UNIT_LOG] = Log.pos.get(name, 0) + Log.pos[name] = pos + + def get_path(name=UNIT_LOG): + return Log.temp_dir + '/' + name diff --git a/test/unit/option.py b/test/unit/option.py index 677d806e..cb3803dc 100644 --- a/test/unit/option.py +++ b/test/unit/option.py @@ -1,4 +1,4 @@ -class Options(): +class Options: _options = { 'skip_alerts': [], 'skip_sanitizer': False, @@ -13,4 +13,5 @@ class Options(): raise AttributeError + option = Options() diff --git a/test/unit/utils.py b/test/unit/utils.py index e80fc469..a627e9f5 100644 --- a/test/unit/utils.py +++ b/test/unit/utils.py @@ -61,6 +61,17 @@ def findmnt(): return out +def sysctl(): + try: + out = subprocess.check_output( + ['sysctl', '-a'], stderr=subprocess.STDOUT + ).decode() + except FileNotFoundError: + pytest.skip('requires sysctl') + + return out + + def waitformount(template, wait=50): for i in range(wait): if findmnt().find(template) != -1: |