response unit tests
This commit is contained in:
@@ -128,7 +128,6 @@ class Response():
|
|||||||
def __init__(self, body='', status_code=200, headers=None):
|
def __init__(self, body='', status_code=200, headers=None):
|
||||||
self.status_code = status_code
|
self.status_code = status_code
|
||||||
self.headers = headers or {}
|
self.headers = headers or {}
|
||||||
self.cookies = []
|
|
||||||
if isinstance(body, (dict, list)):
|
if isinstance(body, (dict, list)):
|
||||||
self.body = json.dumps(body).encode()
|
self.body = json.dumps(body).encode()
|
||||||
self.headers['Content-Type'] = 'application/json'
|
self.headers['Content-Type'] = 'application/json'
|
||||||
@@ -154,7 +153,7 @@ class Response():
|
|||||||
if secure:
|
if secure:
|
||||||
http_cookie += '; Secure'
|
http_cookie += '; Secure'
|
||||||
if http_only:
|
if http_only:
|
||||||
http_cookie += '; httpOnly'
|
http_cookie += '; HttpOnly'
|
||||||
if 'Set-Cookie' in self.headers:
|
if 'Set-Cookie' in self.headers:
|
||||||
self.headers['Set-Cookie'].append(http_cookie)
|
self.headers['Set-Cookie'].append(http_cookie)
|
||||||
else:
|
else:
|
||||||
@@ -203,8 +202,10 @@ class Response():
|
|||||||
else:
|
else:
|
||||||
content_type = 'application/octet-stream'
|
content_type = 'application/octet-stream'
|
||||||
with open(filename) as f:
|
with open(filename) as f:
|
||||||
return Response(body=f.read(), status_code=status_code,
|
body = f.read()
|
||||||
headers={'Content-Type': content_type})
|
return Response(body=body, status_code=status_code,
|
||||||
|
headers={'Content-Type': content_type,
|
||||||
|
'Content-Length': str(len(body))})
|
||||||
|
|
||||||
|
|
||||||
class URLPattern():
|
class URLPattern():
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import sys
|
import sys
|
||||||
sys.path.append('tests')
|
sys.path.append('tests/libs')
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|||||||
1
tests/files/test.bin
Normal file
1
tests/files/test.bin
Normal file
@@ -0,0 +1 @@
|
|||||||
|
foo
|
||||||
1
tests/files/test.css
Normal file
1
tests/files/test.css
Normal file
@@ -0,0 +1 @@
|
|||||||
|
foo
|
||||||
1
tests/files/test.gif
Normal file
1
tests/files/test.gif
Normal file
@@ -0,0 +1 @@
|
|||||||
|
foo
|
||||||
1
tests/files/test.html
Normal file
1
tests/files/test.html
Normal file
@@ -0,0 +1 @@
|
|||||||
|
foo
|
||||||
1
tests/files/test.jpg
Normal file
1
tests/files/test.jpg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
foo
|
||||||
1
tests/files/test.js
Normal file
1
tests/files/test.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
foo
|
||||||
1
tests/files/test.json
Normal file
1
tests/files/test.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
foo
|
||||||
1
tests/files/test.png
Normal file
1
tests/files/test.png
Normal file
@@ -0,0 +1 @@
|
|||||||
|
foo
|
||||||
1
tests/files/test.txt
Normal file
1
tests/files/test.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
foo
|
||||||
2045
tests/libs/datetime.py
Normal file
2045
tests/libs/datetime.py
Normal file
File diff suppressed because it is too large
Load Diff
46
tests/libs/ffilib.py
Normal file
46
tests/libs/ffilib.py
Normal 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
76
tests/libs/time.py
Normal 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
|
||||||
@@ -1,6 +1,161 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
try:
|
||||||
|
import uio as io
|
||||||
|
except ImportError:
|
||||||
|
import io
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
|
from microdot import Response
|
||||||
|
|
||||||
|
|
||||||
class TestResponse(unittest.TestCase):
|
class TestResponse(unittest.TestCase):
|
||||||
def test_foo(self):
|
def test_create_from_string(self):
|
||||||
pass
|
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')
|
||||||
|
|||||||
Reference in New Issue
Block a user