Skip to content

Commit

Permalink
add file.save tests, fix bugs in response and file
Browse files Browse the repository at this point in the history
  • Loading branch information
voidZXL committed Oct 31, 2024
1 parent 75c4d6e commit cfa6a48
Show file tree
Hide file tree
Showing 28 changed files with 647 additions and 211 deletions.
6 changes: 2 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,8 @@ __pycache__/
*/.obsidian

/docs/build

/tests/service/media/*
/tests/service/deploy/run/
/tests/service/deploy/log/
/tests/server/tmp/*
/tests/server/tmp
/tests/test_7_ops/utilmeta_site/__default_db
/tests/test_7_ops/utilmeta_site/operations_db
/tests/test_7_ops/flask_site/__default_db
Expand Down
1 change: 0 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,6 @@ def service_process(service: UtilMeta):
cmd.append('--sync')
cmd.append(f'--port={str(service.port)}')
server = subprocess.Popen(cmd, env=os.environ.copy(), cwd=os.getcwd())
print('CMD:', cmd)
else:
import multiprocessing
server = multiprocessing.Process(target=run_server, args=(backend, service.port, service.asynchronous))
Expand Down
43 changes: 41 additions & 2 deletions tests/server/api.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import os.path

from utilmeta.core import api, response, request, file
from utype.types import *
import utype
Expand Down Expand Up @@ -207,12 +209,48 @@ class MultiFormData(utype.Schema):

@api.post
def multipart(self, data: MultiFormData = request.Body):
paths = []
for i, image in enumerate(data.images):
name = f'{data.name}-{i}'
path = image.save(os.path.join(os.path.dirname(__file__), 'tmp'), name)
paths.append(path)
return [
data.name,
len(data.images),
sum([f.size for f in data.images])
sum([f.size for f in data.images]),
paths
]

@api.post
@awaitable(multipart)
async def multipart(self, data: MultiFormData = request.Body):
paths = []
for i, image in enumerate(data.images):
name = f'{data.name}-{i}'
path = await image.asave(os.path.join(os.path.dirname(__file__), 'tmp'), name)
paths.append(path)
return [
data.name,
len(data.images),
sum([f.size for f in data.images]),
paths
]

@api.get('files/{path}')
def get_file(self, path: str = request.FilePathParam):
if not os.path.exists(path):
raise exceptions.NotFound
# from utilmeta import service
return self.response(file=open(path, 'r'))

@api.delete('files/{path}')
def delete_file(self, path: str = request.FilePathParam):
if not os.path.exists(path):
raise exceptions.NotFound
# from utilmeta import service
os.remove(path)
return self.response(status=204)

@api.put
def batch(self, data: List[DataSchema] = request.Body) -> List[DataSchema]:
return data
Expand Down Expand Up @@ -308,7 +346,8 @@ def upload(self, data: file.File = request.Body):
# for i, f in enumerate(data.files):
# f.save(f'/tmp/{data.name}-{i}')
# print('UPLOAD!!!!', data.size)
return data.read()
return data
# test file return

@api.get
def backend(self):
Expand Down
1 change: 1 addition & 0 deletions tests/server/test.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test-content
72 changes: 70 additions & 2 deletions tests/test_3_api/params.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import os.path

from utilmeta.core.file.backends.django import DjangoFileAdaptor # noqa
from io import BytesIO
from utilmeta.core.response import Response
from pathlib import Path


# image = UploadedFile(BytesIO(b"image"), content_type="image/png", size=6)
def get_requests(backend: str = None, asynchronous: bool = False):
image = BytesIO(b"image")
# files = [
Expand All @@ -17,6 +19,10 @@ def get_requests(backend: str = None, asynchronous: bool = False):
BytesIO(b"f3"),
]

# from utilmeta import service
base_dir = Path(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'server'))
# os.removedirs(base_dir)

backend_requests = [('get', 'backend', {}, None, {}, backend, 200)] if backend else []
if asynchronous:
# test async plugins
Expand Down Expand Up @@ -181,15 +187,73 @@ def get_requests(backend: str = None, asynchronous: bool = False):
b'text',
200,
),
(
"post",
"upload",
{},
open(base_dir / 'test.txt', 'r'),
{},
b'test-content',
200,
),
(
"post",
"multipart",
{},
{"name": "test", "images": files},
{},
['test', 3, 6],
['test', 3, 6, [
str(base_dir / 'tmp/test-0'),
str(base_dir / 'tmp/test-1'),
str(base_dir / 'tmp/test-2'),
]],
200,
),
(
"get",
"files/" + str(base_dir / 'tmp/test-0'),
{},
None,
{},
b'f1',
200,
),
(
"delete",
"files/" + str(base_dir / 'tmp/test-0'),
{},
None,
{},
...,
204,
),
(
"delete",
"files/" + str(base_dir / 'tmp/test-1'),
{},
None,
{},
...,
204,
),
(
"delete",
"files/" + str(base_dir / 'tmp/test-2'),
{},
None,
{},
...,
204,
),
(
"get",
"files/" + str(base_dir / 'tmp/test-0'),
{},
{"name": "test", "images": files},
{},
...,
404,
),
(
"put",
"batch",
Expand Down Expand Up @@ -308,4 +372,8 @@ def do_live_api_tests(service):
if callable(result):
result(content)
else:
if isinstance(result, bytes):
result = result.decode()
if isinstance(content, bytes):
content = content.decode()
assert content == result, f"{method} {path} failed with {repr(content)}, {repr(result)} expected"
25 changes: 16 additions & 9 deletions tests/test_3_api/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +122,19 @@ def test_api_features(self):
headers=h,
)
)()
assert isinstance(resp, response.Response), f'invalid response: {resp}'
content = resp.data
assert resp.status == status, \
f"{method} {path} failed with {content}, {status} expected, got {resp.status}"
if result is not ...:
if callable(result):
result(content)
else:
assert content == result, f"{method} {path} failed with {content}"
try:
assert isinstance(resp, response.Response), f'invalid response: {resp}'
content = resp.data
assert resp.status == status, \
f"{method} {path} failed with {content}, {status} expected, got {resp.status}"
if result is not ...:
if callable(result):
result(content)
else:
if isinstance(result, bytes):
result = result.decode()
if isinstance(content, bytes):
content = content.decode()
assert content == result, f"{method} {path} failed with {content}"
finally:
resp.close()
28 changes: 23 additions & 5 deletions utilmeta/core/api/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -502,11 +502,29 @@ def _resolve(self) -> APIRoute:
# not endpoint
# further API mount
return route
# elif route.method == self.request.method and not self.request.is_options:
# # options/cross-origin need to collect methods to generate Allow-Methods
# return route
# first match is 1st priority
method_routes.setdefault(route.method, route)
else:
# elif route.method == self.request.method and not self.request.is_options:
# # options/cross-origin need to collect methods to generate Allow-Methods
# return route
# first match is 1st priority
method_routes.setdefault(route.method, route)

# if matched_route:
# # elif route.method == self.request.method and not self.request.is_options:
# # # options/cross-origin need to collect methods to generate Allow-Methods
# # return route
# # first match is 1st priority
# allowed_methods = []
# allowed_headers = []
# for route in self._routes:
# if route.method and route.route == matched_route.route:
# distinct_add(allowed_methods, [route.method])
# distinct_add(allowed_headers, route.header_names)
# # if route.method not in allowed_methods:
# # allowed_methods.append(route.method)
#
# method_routes.setdefault(route.method, route)

if method_routes:
allow_methods = var.allow_methods.setup(self.request)
allow_headers = var.allow_headers.setup(self.request)
Expand Down
11 changes: 8 additions & 3 deletions utilmeta/core/api/route.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,9 +365,14 @@ def match_route(self, request: Request):
# only set path params if route is API
# endpoints need to match for multiple methods
route_attr.set(pop(group, '_', ''))
# set path params for endpoint and API in every match
path_params.update(group)
path_params_attr.set(path_params)

if not self.method or self.method == request.method:
# set path params for endpoint and API in every match
for key, value in group.items():
# setdefault instead of [setitem]
# because the first match
path_params.setdefault(key, value)
path_params_attr.set(path_params)
return True
return False

Expand Down
Loading

0 comments on commit cfa6a48

Please sign in to comment.