response unit tests

This commit is contained in:
Miguel Grinberg
2019-04-27 14:23:07 +01:00
parent 0b95feafc9
commit cd71986a50
17 changed files with 2340 additions and 8 deletions

View File

@@ -128,7 +128,6 @@ class Response():
def __init__(self, body='', status_code=200, headers=None):
self.status_code = status_code
self.headers = headers or {}
self.cookies = []
if isinstance(body, (dict, list)):
self.body = json.dumps(body).encode()
self.headers['Content-Type'] = 'application/json'
@@ -154,7 +153,7 @@ class Response():
if secure:
http_cookie += '; Secure'
if http_only:
http_cookie += '; httpOnly'
http_cookie += '; HttpOnly'
if 'Set-Cookie' in self.headers:
self.headers['Set-Cookie'].append(http_cookie)
else:
@@ -203,8 +202,10 @@ class Response():
else:
content_type = 'application/octet-stream'
with open(filename) as f:
return Response(body=f.read(), status_code=status_code,
headers={'Content-Type': content_type})
body = f.read()
return Response(body=body, status_code=status_code,
headers={'Content-Type': content_type,
'Content-Length': str(len(body))})
class URLPattern():

View File

@@ -1,5 +1,5 @@
import sys
sys.path.append('tests')
sys.path.append('tests/libs')
import unittest

1
tests/files/test.bin Normal file
View File

@@ -0,0 +1 @@
foo

1
tests/files/test.css Normal file
View File

@@ -0,0 +1 @@
foo

1
tests/files/test.gif Normal file
View File

@@ -0,0 +1 @@
foo

1
tests/files/test.html Normal file
View File

@@ -0,0 +1 @@
foo

1
tests/files/test.jpg Normal file
View File

@@ -0,0 +1 @@
foo

1
tests/files/test.js Normal file
View File

@@ -0,0 +1 @@
foo

1
tests/files/test.json Normal file
View File

@@ -0,0 +1 @@
foo

1
tests/files/test.png Normal file
View File

@@ -0,0 +1 @@
foo

1
tests/files/test.txt Normal file
View File

@@ -0,0 +1 @@
foo

2045
tests/libs/datetime.py Normal file

File diff suppressed because it is too large Load Diff

46
tests/libs/ffilib.py Normal file
View File

@@ -0,0 +1,46 @@
import sys
try:
import ffi
except ImportError:
ffi = None
_cache = {}
def open(name, maxver=10, extra=()):
if not ffi:
return None
try:
return _cache[name]
except KeyError:
pass
def libs():
if sys.platform == "linux":
yield '%s.so' % name
for i in range(maxver, -1, -1):
yield '%s.so.%u' % (name, i)
else:
for ext in ('dylib', 'dll'):
yield '%s.%s' % (name, ext)
for n in extra:
yield n
err = None
for n in libs():
try:
l = ffi.open(n)
_cache[name] = l
return l
except OSError as e:
err = e
raise err
def libc():
return open("libc", 6)
# Find out bitness of the platform, even if long ints are not supported
# TODO: All bitness differences should be removed from micropython-lib, and
# this snippet too.
bitness = 1
v = sys.maxsize
while v:
bitness += 1
v >>= 1

76
tests/libs/time.py Normal file
View File

@@ -0,0 +1,76 @@
from utime import *
from ucollections import namedtuple
import ustruct
import uctypes
import ffi
import ffilib
import array
libc = ffilib.libc()
# struct tm *gmtime(const time_t *timep);
# struct tm *localtime(const time_t *timep);
# size_t strftime(char *s, size_t max, const char *format,
# const struct tm *tm);
gmtime_ = libc.func("P", "gmtime", "P")
localtime_ = libc.func("P", "localtime", "P")
strftime_ = libc.func("i", "strftime", "sisP")
mktime_ = libc.func("i", "mktime", "P")
_struct_time = namedtuple("struct_time",
["tm_year", "tm_mon", "tm_mday", "tm_hour", "tm_min", "tm_sec", "tm_wday", "tm_yday", "tm_isdst"])
def _tuple_to_c_tm(t):
return ustruct.pack("@iiiiiiiii", t[5], t[4], t[3], t[2], t[1] - 1, t[0] - 1900, (t[6] + 1) % 7, t[7] - 1, t[8])
def _c_tm_to_tuple(tm):
t = ustruct.unpack("@iiiiiiiii", tm)
return _struct_time(t[5] + 1900, t[4] + 1, t[3], t[2], t[1], t[0], (t[6] - 1) % 7, t[7] + 1, t[8])
def struct_time(tm):
return _struct_time(*tm)
def strftime(format, t=None):
if t is None:
t = localtime()
buf = bytearray(32)
l = strftime_(buf, 32, format, _tuple_to_c_tm(t))
return str(buf[:l], "utf-8")
def localtime(t=None):
if t is None:
t = time()
t = int(t)
a = ustruct.pack('l', t)
tm_p = localtime_(a)
return _c_tm_to_tuple(uctypes.bytearray_at(tm_p, 36))
def gmtime(t=None):
if t is None:
t = time()
t = int(t)
a = ustruct.pack('l', t)
tm_p = gmtime_(a)
return _c_tm_to_tuple(uctypes.bytearray_at(tm_p, 36))
def mktime(tt):
return mktime_(_tuple_to_c_tm(tt))
def perf_counter():
return time()
def process_time():
return clock()
daylight = 0
timezone = 0

View File

@@ -1,6 +1,161 @@
from datetime import datetime
try:
import uio as io
except ImportError:
import io
import unittest
from microdot import Response
class TestResponse(unittest.TestCase):
def test_foo(self):
pass
def test_create_from_string(self):
res = Response('foo')
self.assertEqual(res.status_code, 200)
self.assertEqual(res.headers, {})
self.assertEqual(res.body, b'foo')
fd = io.BytesIO()
res.write(fd)
self.assertEqual(
fd.getvalue(),
b'HTTP/1.0 200 OK\r\n'
b'Content-Length: 3\r\n'
b'Content-Type: text/plain\r\n'
b'\r\n'
b'foo')
def test_create_from_string_with_content_length(self):
res = Response('foo', headers={'Content-Length': '2'})
self.assertEqual(res.status_code, 200)
self.assertEqual(res.headers, {'Content-Length': '2'})
self.assertEqual(res.body, b'foo')
fd = io.BytesIO()
res.write(fd)
self.assertEqual(
fd.getvalue(),
b'HTTP/1.0 200 OK\r\n'
b'Content-Length: 2\r\n'
b'Content-Type: text/plain\r\n'
b'\r\n'
b'foo')
def test_create_from_bytes(self):
res = Response(b'foo')
self.assertEqual(res.status_code, 200)
self.assertEqual(res.headers, {})
self.assertEqual(res.body, b'foo')
fd = io.BytesIO()
res.write(fd)
self.assertEqual(
fd.getvalue(),
b'HTTP/1.0 200 OK\r\n'
b'Content-Length: 3\r\n'
b'Content-Type: text/plain\r\n'
b'\r\n'
b'foo')
def test_create_json(self):
res = Response({'foo': 'bar'})
self.assertEqual(res.status_code, 200)
self.assertEqual(res.headers, {'Content-Type': 'application/json'})
self.assertEqual(res.body, b'{"foo": "bar"}')
fd = io.BytesIO()
res.write(fd)
self.assertEqual(
fd.getvalue(),
b'HTTP/1.0 200 OK\r\n'
b'Content-Type: application/json\r\n'
b'Content-Length: 14\r\n'
b'\r\n'
b'{"foo": "bar"}')
res = Response([1, '2'])
self.assertEqual(res.status_code, 200)
self.assertEqual(res.headers, {'Content-Type': 'application/json'})
self.assertEqual(res.body, b'[1, "2"]')
fd = io.BytesIO()
res.write(fd)
self.assertEqual(
fd.getvalue(),
b'HTTP/1.0 200 OK\r\n'
b'Content-Type: application/json\r\n'
b'Content-Length: 8\r\n'
b'\r\n'
b'[1, "2"]')
def test_create_from_other(self):
res = Response(123)
self.assertEqual(res.status_code, 200)
self.assertEqual(res.headers, {})
self.assertEqual(res.body, b'123')
def test_create_with_status_code(self):
res = Response('not found', 404)
self.assertEqual(res.status_code, 404)
self.assertEqual(res.headers, {})
self.assertEqual(res.body, b'not found')
def test_create_with_headers(self):
res = Response('foo', headers={'X-Test': 'Foo'})
self.assertEqual(res.status_code, 200)
self.assertEqual(res.headers, {'X-Test': 'Foo'})
self.assertEqual(res.body, b'foo')
def test_create_with_status_code_and_headers(self):
res = Response('foo', 202, {'X-Test': 'Foo'})
self.assertEqual(res.status_code, 202)
self.assertEqual(res.headers, {'X-Test': 'Foo'})
self.assertEqual(res.body, b'foo')
def test_cookies(self):
res = Response('ok')
res.set_cookie('foo1', 'bar1')
res.set_cookie('foo2', 'bar2', path='/')
res.set_cookie('foo3', 'bar3', domain='example.com:1234')
res.set_cookie('foo4', 'bar4',
expires=datetime(2019, 11, 5, 2, 23, 54))
res.set_cookie('foo5', 'bar5', max_age=123)
res.set_cookie('foo6', 'bar6', secure=True, http_only=True)
res.set_cookie('foo7', 'bar7', path='/foo', domain='example.com:1234',
expires=datetime(2019, 11, 5, 2, 23, 54), max_age=123,
secure=True, http_only=True)
self.assertEqual(res.headers, {'Set-Cookie': [
'foo1=bar1',
'foo2=bar2; Path=/',
'foo3=bar3; Domain=example.com:1234',
'foo4=bar4; Expires=Tue, 05 Nov 2019 02:23:54 GMT',
'foo5=bar5; Max-Age=123',
'foo6=bar6; Secure; HttpOnly',
'foo7=bar7; Path=/foo; Domain=example.com:1234; '
'Expires=Tue, 05 Nov 2019 02:23:54 GMT; Max-Age=123; Secure; '
'HttpOnly'
]})
def test_redirect(self):
res = Response.redirect('/foo')
self.assertEqual(res.status_code, 302)
self.assertEqual(res.headers['Location'], '/foo')
res = Response.redirect('/foo', status_code=301)
self.assertEqual(res.status_code, 301)
self.assertEqual(res.headers['Location'], '/foo')
def test_send_file(self):
files = [
('test.txt', 'text/plain'),
('test.gif', 'image/gif'),
('test.jpg', 'image/jpeg'),
('test.png', 'image/png'),
('test.html', 'text/html'),
('test.css', 'text/css'),
('test.js', 'application/javascript'),
('test.json', 'application/json'),
('test.bin', 'application/octet-stream'),
]
for file, content_type in files:
res = Response.send_file('tests/files/' + file)
self.assertEqual(res.status_code, 200)
self.assertEqual(res.headers['Content-Type'], content_type)
self.assertEqual(res.headers['Content-Length'], '4')
self.assertEqual(res.body, b'foo\n')

View File

@@ -19,7 +19,7 @@ basepython=
deps=
flake8
commands=
flake8 --exclude=tests/unittest.py microdot.py tests
flake8 --exclude=tests/unittest.py --exclude tests/libs microdot.py tests
[testenv:upy]
whitelist_externals=sh