feat(upload): 支持 Agent 本地图片安全上传
This commit is contained in:
@@ -8,8 +8,11 @@ import contextlib
|
||||
import importlib.util
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import threading
|
||||
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
@@ -36,6 +39,19 @@ def make_plan(operation: str) -> dict:
|
||||
return value
|
||||
|
||||
|
||||
def make_file_plan(images: list[dict]) -> dict:
|
||||
value = {
|
||||
"schema_version": 2,
|
||||
"operation": "create_work",
|
||||
"base_url": "http://example.invalid",
|
||||
"target": IDENTITY,
|
||||
"external_id": "agent-file-001",
|
||||
"content": {**CONTENT, "image_source": "file", "images": images},
|
||||
}
|
||||
value["confirmation_code"] = MODULE.confirmation_code(value)
|
||||
return value
|
||||
|
||||
|
||||
def write_plan(folder: Path, value: dict) -> Path:
|
||||
path = folder / "plan.json"
|
||||
path.write_text(json.dumps(value, ensure_ascii=False), encoding="utf-8")
|
||||
@@ -56,6 +72,7 @@ def apply_silently(path: Path, code: str) -> dict:
|
||||
def main() -> None:
|
||||
original_exact_project = MODULE.exact_project
|
||||
original_request_json = MODULE.request_json
|
||||
original_request_multipart = MODULE.request_multipart
|
||||
original_get_work = MODULE.get_work
|
||||
MODULE.exact_project = lambda _base, _project_id: PROJECT
|
||||
try:
|
||||
@@ -88,6 +105,82 @@ def main() -> None:
|
||||
assert result["work_id"] == 88 and result["image_count"] == 1
|
||||
assert [method for method, _path in create_calls].count("POST") == 1
|
||||
|
||||
local_image = folder / "generated.png"
|
||||
local_image.write_bytes(b"local-image-content")
|
||||
descriptors = MODULE.describe_image_files([str(local_image)])
|
||||
captured: dict = {}
|
||||
|
||||
class Handler(BaseHTTPRequestHandler):
|
||||
def do_POST(self):
|
||||
captured["authorization"] = self.headers.get("Authorization")
|
||||
captured["content_type"] = self.headers.get("Content-Type")
|
||||
captured["body"] = self.rfile.read(int(self.headers["Content-Length"]))
|
||||
response = json.dumps({"id": 88}).encode()
|
||||
self.send_response(201)
|
||||
self.send_header("Content-Type", "application/json")
|
||||
self.send_header("Content-Length", str(len(response)))
|
||||
self.end_headers()
|
||||
self.wfile.write(response)
|
||||
|
||||
def log_message(self, _format, *_args):
|
||||
return
|
||||
|
||||
server = ThreadingHTTPServer(("127.0.0.1", 0), Handler)
|
||||
thread = threading.Thread(target=server.serve_forever, daemon=True)
|
||||
previous_key = os.environ.get("DELIVERY_DESK_API_KEY")
|
||||
os.environ["DELIVERY_DESK_API_KEY"] = "dd_test"
|
||||
thread.start()
|
||||
try:
|
||||
status, payload = MODULE.request_multipart(
|
||||
f"http://127.0.0.1:{server.server_port}",
|
||||
"/api/projects/12/works",
|
||||
fields={"title": "作品标题", "description": "正文", "tags": "[]"},
|
||||
files=descriptors,
|
||||
)
|
||||
local_image.write_bytes(b"x" * len(b"local-image-content"))
|
||||
try:
|
||||
MODULE.request_multipart(
|
||||
f"http://127.0.0.1:{server.server_port}",
|
||||
"/api/projects/12/works",
|
||||
fields={"title": "作品标题", "description": "正文", "tags": "[]"},
|
||||
files=descriptors,
|
||||
)
|
||||
raise AssertionError("同尺寸但哈希变化的文件未被阻止")
|
||||
except MODULE.UploadError as error:
|
||||
assert error.action == "revise" and "发生变化" in str(error)
|
||||
local_image.write_bytes(b"local-image-content")
|
||||
finally:
|
||||
server.shutdown()
|
||||
server.server_close()
|
||||
thread.join()
|
||||
if previous_key is None:
|
||||
os.environ.pop("DELIVERY_DESK_API_KEY", None)
|
||||
else:
|
||||
os.environ["DELIVERY_DESK_API_KEY"] = previous_key
|
||||
assert status == 201 and payload["id"] == 88
|
||||
assert captured["authorization"] == "Bearer dd_test"
|
||||
assert "multipart/form-data" in captured["content_type"]
|
||||
assert b"local-image-content" in captured["body"] and b'name="images"' in captured["body"]
|
||||
|
||||
file_plan = make_file_plan(descriptors)
|
||||
file_path = write_plan(folder, file_plan)
|
||||
MODULE.request_json = lambda *_args, **_kwargs: (200, [])
|
||||
multipart_calls: list[tuple] = []
|
||||
MODULE.request_multipart = lambda *_args, **_kwargs: (multipart_calls.append((_args, _kwargs)) or (201, {"id": 88}))
|
||||
MODULE.get_work = lambda *_args, **_kwargs: {**detail(1, image_url="https://cos.example.com/originals/generated.png"), "external_id": "agent-file-001"}
|
||||
result = apply_silently(file_path, file_plan["confirmation_code"])
|
||||
assert result["work_id"] == 88 and len(multipart_calls) == 1
|
||||
assert multipart_calls[0][1]["files"][0]["sha256"] == descriptors[0]["sha256"]
|
||||
|
||||
local_image.write_bytes(b"changed-after-confirmation")
|
||||
multipart_calls.clear()
|
||||
try:
|
||||
apply_silently(file_path, file_plan["confirmation_code"])
|
||||
raise AssertionError("确认后变化的文件未被阻止")
|
||||
except MODULE.UploadError as error:
|
||||
assert error.action == "revise" and "发生变化" in str(error)
|
||||
assert not multipart_calls
|
||||
|
||||
round_plan = make_plan("create_round")
|
||||
round_path = write_plan(folder, round_plan)
|
||||
MODULE.get_work = lambda *_args, **_kwargs: detail(3, work_id=34)
|
||||
@@ -126,6 +219,7 @@ def main() -> None:
|
||||
finally:
|
||||
MODULE.exact_project = original_exact_project
|
||||
MODULE.request_json = original_request_json
|
||||
MODULE.request_multipart = original_request_multipart
|
||||
MODULE.get_work = original_get_work
|
||||
print("Upload Skill regression tests passed")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user