Tests: reworked TestUnitHTTP.

This commit is contained in:
Andrey Zelenkov
2018-01-30 16:17:01 +03:00
parent 9f48f2b3e7
commit afa0fd9a71
3 changed files with 143 additions and 103 deletions

View File

@@ -52,14 +52,14 @@ def application(environ, start_response):
body = 'Test body string.' body = 'Test body string.'
r = unit.TestUnitHTTP.post(headers={ resp = self.post(headers={
'Host': 'localhost', 'Host': 'localhost',
'Content-Type': 'text/html', 'Content-Type': 'text/html',
'Custom-Header': 'blah' 'Custom-Header': 'blah'
}, data=body) }, body=body)
self.assertEqual(r.status_code, 200, 'status') self.assertEqual(resp['status'], 200, 'status')
headers = dict(r.headers) headers = resp['headers']
self.assertRegex(headers.pop('Server'), r'unit/[\d\.]+', self.assertRegex(headers.pop('Server'), r'unit/[\d\.]+',
'server header') 'server header')
self.assertDictEqual(headers, { self.assertDictEqual(headers, {
@@ -71,7 +71,7 @@ def application(environ, start_response):
'Server-Protocol': 'HTTP/1.1', 'Server-Protocol': 'HTTP/1.1',
'Custom-Header': 'blah' 'Custom-Header': 'blah'
}, 'headers') }, 'headers')
self.assertEqual(r.content, body.encode(), 'body') self.assertEqual(resp['body'], body, 'body')
def test_python_application_query_string(self): def test_python_application_query_string(self):
code, name = """ code, name = """
@@ -89,12 +89,10 @@ def application(environ, start_response):
self.python_application(name, code) self.python_application(name, code)
self.conf_with_name(name) self.conf_with_name(name)
r = unit.TestUnitHTTP.get(uri='/?var1=val1&var2=val2', headers={ resp = self.get(url='/?var1=val1&var2=val2')
'Host': 'localhost'
})
self.assertEqual(r.status_code, 200, 'status') self.assertEqual(resp['status'], 200, 'status')
headers = dict(r.headers) headers = resp['headers']
headers.pop('Server') headers.pop('Server')
self.assertDictEqual(headers, { self.assertDictEqual(headers, {
'Content-Length': '0', 'Content-Length': '0',
@@ -118,9 +116,7 @@ def application(environ, start_response):
self.python_application(name, code) self.python_application(name, code)
self.conf_with_name(name) self.conf_with_name(name)
r = unit.TestUnitHTTP.get(headers={'Host': 'localhost'}) self.assertEqual(self.get()['headers']['Server-Port'], '7080',
self.assertEqual(r.headers.pop('Server-Port'), '7080',
'Server-Port header') 'Server-Port header')
@unittest.expectedFailure @unittest.expectedFailure
@@ -137,8 +133,7 @@ def application(environ, start_response):
self.python_application(name, code) self.python_application(name, code)
self.conf_with_name(name) self.conf_with_name(name)
r = unit.TestUnitHTTP.get(headers={'Host': 'localhost'}) self.assertNotIn('Transfer-Encoding', self.get()['headers'],
self.assertNotIn('Transfer-Encoding', r.headers,
'204 header transfer encoding') '204 header transfer encoding')
if __name__ == '__main__': if __name__ == '__main__':

View File

@@ -44,7 +44,7 @@ def application(env, start_response):
} }
}) })
unit.TestUnitHTTP.get() self.get()
self.conf({ self.conf({
"listeners": {}, "listeners": {},

View File

@@ -5,9 +5,9 @@ import json
import time import time
import shutil import shutil
import socket import socket
import select
import tempfile import tempfile
import unittest import unittest
from requests import Request, Session
from subprocess import call from subprocess import call
from multiprocessing import Process from multiprocessing import Process
@@ -144,7 +144,121 @@ class TestUnit(unittest.TestCase):
return ret return ret
class TestUnitControl(TestUnit): class TestUnitHTTP(TestUnit):
def http(self, start_str, **kwargs):
sock_type = 'ipv4' if 'sock_type' not in kwargs else kwargs['sock_type']
port = 7080 if 'port' not in kwargs else kwargs['port']
url = '/' if 'url' not in kwargs else kwargs['url']
http = 'HTTP/1.0' if 'http_10' in kwargs else 'HTTP/1.1'
headers = {'Host': 'localhost'} if 'headers' not in kwargs else kwargs['headers']
body = b'' if 'body' not in kwargs else kwargs['body']
crlf = '\r\n'
if 'addr' not in kwargs:
addr = '::1' if sock_type == 'ipv6' else '127.0.0.1'
else:
addr = kwargs['addr']
sock_types = {
'ipv4': socket.AF_INET,
'ipv6': socket.AF_INET6,
'unix': socket.AF_UNIX
}
if 'sock' not in kwargs:
sock = socket.socket(sock_types[sock_type], socket.SOCK_STREAM)
if sock_type == 'unix':
sock.connect(addr)
else:
sock.connect((addr, port))
else:
sock = kwargs['sock']
sock.setblocking(False)
if 'raw' not in kwargs:
req = ' '.join([start_str, url, http]) + crlf
if body is not b'':
if isinstance(body, str):
body = body.encode()
if 'Content-Length' not in headers:
headers['Content-Length'] = len(body)
for header, value in headers.items():
req += header + ': ' + str(value) + crlf
req = (req + crlf).encode() + body
else:
req = start_str
sock.sendall(req)
if '--verbose' in sys.argv:
print('>>>', req, sep='\n')
resp = self._recvall(sock)
if '--verbose' in sys.argv:
print('<<<', resp, sep='\n')
if 'raw_resp' not in kwargs:
resp = self._resp_to_dict(resp)
if 'start' not in kwargs:
sock.close()
return resp
return (resp, sock)
def delete(self, **kwargs):
return self.http('DELETE', **kwargs)
def get(self, **kwargs):
return self.http('GET', **kwargs)
def post(self, **kwargs):
return self.http('POST', **kwargs)
def put(self, **kwargs):
return self.http('PUT', **kwargs)
def _recvall(self, sock, buff_size=4096):
data = ''
while select.select([sock], [], [], 1)[0]:
part = sock.recv(buff_size).decode()
data += part
if part is '':
break
return data
def _resp_to_dict(self, resp):
m = re.search('(.*?\x0d\x0a?)\x0d\x0a?(.*)', resp, re.M | re.S)
headers_text, body = m.group(1), m.group(2)
p = re.compile('(.*?)\x0d\x0a?', re.M | re.S)
headers_lines = p.findall(headers_text)
status = re.search('^HTTP\/\d\.\d\s(\d+)|$', headers_lines.pop(0)).group(1)
headers = {}
for line in headers_lines:
m = re.search('(.*)\:\s(.*)', line)
headers[m.group(1)] = m.group(2)
return {
'status': int(status),
'headers': headers,
'body': body
}
class TestUnitControl(TestUnitHTTP):
# TODO socket reuse # TODO socket reuse
# TODO http client # TODO http client
@@ -153,92 +267,23 @@ class TestUnitControl(TestUnit):
if isinstance(conf, dict): if isinstance(conf, dict):
conf = json.dumps(conf) conf = json.dumps(conf)
return self._body_json(self.put(path, conf)) return json.loads(self.put(
url=path,
body=conf,
sock_type='unix',
addr=self.testdir + '/control.unit.sock'
)['body'])
def conf_get(self, path='/'): def conf_get(self, path='/'):
return self._body_json(self.get(path)) return json.loads(self.get(
url=path,
sock_type='unix',
addr=self.testdir + '/control.unit.sock'
)['body'])
def conf_delete(self, path='/'): def conf_delete(self, path='/'):
return self._body_json(self.delete(path)) return json.loads(self.delete(
url=path,
def http(self, req): sock_type='unix',
with self._control_sock() as sock: addr=self.testdir + '/control.unit.sock'
sock.sendall(req) )['body'])
if '--verbose' in sys.argv:
print('>>>', req, sep='\n')
resp = self._recvall(sock)
if '--verbose' in sys.argv:
print('<<<', resp, sep='\n')
return resp
def get(self, path='/'):
resp = self.http(('GET ' + path
+ ' HTTP/1.1\r\nHost: localhost\r\n\r\n').encode())
return resp
def delete(self, path='/'):
resp = self.http(('DELETE ' + path
+ ' HTTP/1.1\r\nHost: localhost\r\n\r\n').encode())
return resp
def put(self, path='/', data=''):
if isinstance(data, str):
data = data.encode()
resp = self.http(('PUT ' + path + ' HTTP/1.1\nHost: localhost\n'
+ 'Content-Length: ' + str(len(data))
+ '\r\n\r\n').encode() + data)
return resp
def _control_sock(self):
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect(self.testdir + '/control.unit.sock')
return sock
def _recvall(self, sock, buff_size=4096):
data = ''
while True:
part = sock.recv(buff_size).decode()
data += part
if len(part) < buff_size:
break
return data
def _body_json(self, resp):
m = re.search('.*?\x0d\x0a?\x0d\x0a?(.*)', resp, re.M | re.S)
return json.loads(m.group(1))
class TestUnitHTTP():
@classmethod
def http(self, method, **kwargs):
host = '127.0.0.1:7080' if 'host' not in kwargs else kwargs['host']
uri = '/' if 'uri' not in kwargs else kwargs['uri']
sess = Session() if 'sess' not in kwargs else kwargs['sess']
data = None if 'data' not in kwargs else kwargs['data']
headers = None if 'headers' not in kwargs else kwargs['headers']
req = Request(method, 'http://' + host + uri, data=data,
headers=headers)
r = sess.send(req.prepare())
if 'keep' not in kwargs:
sess.close()
return r
return (r, sess)
def get(**kwargs):
return TestUnitHTTP.http('GET', **kwargs)
def post(**kwargs):
return TestUnitHTTP.http('POST', **kwargs)