transfer: 新增 list_files/download (流式+进度+Content-Disposition 文件名); CLI --list/--download/--out; 测试 57 项

This commit is contained in:
lou
2026-08-10 00:46:08 +08:00
parent 29f885fd48
commit c972948d94
3 changed files with 154 additions and 5 deletions
+63 -1
View File
@@ -11,6 +11,7 @@ import sys
import tempfile
import threading
import unittest
import urllib.parse
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
@@ -33,6 +34,7 @@ class ProtocolServer:
self.transfer_id = "T-1"
self.completed = False
self.file_id = "F-42"
self.files: list[dict] = [] # ls/下载测试: [{"file_id","file_name","size","data"}]
def make_handler(self):
srv = self
@@ -74,6 +76,33 @@ class ProtocolServer:
if m:
self._json(200, {"received": sorted(srv.received)})
return
if self.path == "/api/files":
# 列表只含元信息, 不含 data (bytes 不可 JSON 序列化)
self._json(200, {
"files": [
{k: v for k, v in f.items() if k != "data"}
for f in srv.files
]
})
return
m = re.fullmatch(r"/api/files/([^/]+)", self.path)
if m:
fid = m.group(1)
rec = next((f for f in srv.files if f["file_id"] == fid), None)
if rec is None:
self._json(404, {"error": "文件不存在"})
return
body = rec["data"]
self.send_response(200)
self.send_header("Content-Type", "application/octet-stream")
self.send_header("Content-Length", str(len(body)))
self.send_header(
"Content-Disposition",
f"attachment; filename*=utf-8''{urllib.parse.quote(rec['file_name'])}",
)
self.end_headers()
self.wfile.write(body)
return
self._json(404, {"error": "not found"})
def do_PUT(self):
@@ -109,6 +138,7 @@ class TransferTestBase(unittest.TestCase):
)
self._td = tempfile.TemporaryDirectory(prefix='hermes-test-')
self.addCleanup(self._td.cleanup)
self.tmp = Path(self._td.name)
self.chunk_files: dict[int, Path] = {}
for i in range(1, CHUNK_COUNT + 1):
p = Path(self._td.name) / f"chunk{i}.bin"
@@ -174,5 +204,37 @@ class TestProtocolErrors(TransferTestBase):
self.assertEqual(payload["status"], "incomplete")
if __name__ == '__main__':
class TestListDownload(TransferTestBase):
"""ls 列表 + 下载 (真实 HTTP 服务器)"""
def setUp(self):
super().setUp()
self.srv.files = [
{"file_id": "F-1", "file_name": "报告.pdf", "size": 6, "data": b"hello"},
]
def test_list_files(self):
files = self.client.list_files()
self.assertEqual(len(files), 1)
self.assertEqual(files[0]["file_id"], "F-1")
self.assertEqual(files[0]["file_name"], "报告.pdf")
def test_download_to_path(self):
dest = self.tmp / "out.bin"
got = self.client.download("F-1", dest)
self.assertEqual(got, dest)
self.assertEqual(dest.read_bytes(), b"hello")
def test_download_uses_server_filename(self):
got = self.client.download("F-1")
self.assertEqual(got.name, "报告.pdf")
self.assertEqual(got.read_bytes(), b"hello")
got.unlink()
def test_download_unknown_404(self):
with self.assertRaises(TransferError):
self.client.download("NOPE")
if __name__ == "__main__":
unittest.main()