feat: download - fase 02
This commit is contained in:
@@ -4,6 +4,8 @@ import asyncio
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
import zipfile
|
||||
from io import BytesIO
|
||||
from pathlib import Path
|
||||
|
||||
import httpx
|
||||
@@ -55,6 +57,75 @@ class DownloadApiGoldenTest(unittest.TestCase):
|
||||
|
||||
def test_download_directory_type_conflict(self) -> None:
|
||||
(self.root / "docs").mkdir()
|
||||
(self.root / "docs" / "a.txt").write_text("a", encoding="utf-8")
|
||||
|
||||
response = self._get("/api/files/download?path=storage1/docs")
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn('attachment; filename="docs.zip"', response.headers.get("content-disposition", ""))
|
||||
with zipfile.ZipFile(BytesIO(response.content)) as archive:
|
||||
self.assertIn("docs/", archive.namelist())
|
||||
self.assertIn("docs/a.txt", archive.namelist())
|
||||
self.assertEqual(archive.read("docs/a.txt"), b"a")
|
||||
|
||||
def test_download_multi_file_selection_as_zip(self) -> None:
|
||||
(self.root / "a.txt").write_text("A", encoding="utf-8")
|
||||
(self.root / "b.txt").write_text("B", encoding="utf-8")
|
||||
|
||||
response = self._get("/api/files/download?path=storage1/a.txt&path=storage1/b.txt")
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertRegex(
|
||||
response.headers.get("content-disposition", ""),
|
||||
r'attachment; filename="kodidownload-\d{8}-\d{6}\.zip"',
|
||||
)
|
||||
with zipfile.ZipFile(BytesIO(response.content)) as archive:
|
||||
self.assertIn("a.txt", archive.namelist())
|
||||
self.assertIn("b.txt", archive.namelist())
|
||||
self.assertEqual(archive.read("a.txt"), b"A")
|
||||
self.assertEqual(archive.read("b.txt"), b"B")
|
||||
|
||||
def test_download_multi_directory_selection_as_zip(self) -> None:
|
||||
(self.root / "dir1" / "sub").mkdir(parents=True)
|
||||
(self.root / "dir2").mkdir()
|
||||
(self.root / "dir1" / "sub" / "a.txt").write_text("A", encoding="utf-8")
|
||||
(self.root / "dir2" / "b.txt").write_text("B", encoding="utf-8")
|
||||
|
||||
response = self._get("/api/files/download?path=storage1/dir1&path=storage1/dir2")
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertRegex(
|
||||
response.headers.get("content-disposition", ""),
|
||||
r'attachment; filename="kodidownload-\d{8}-\d{6}\.zip"',
|
||||
)
|
||||
with zipfile.ZipFile(BytesIO(response.content)) as archive:
|
||||
self.assertIn("dir1/", archive.namelist())
|
||||
self.assertIn("dir1/sub/", archive.namelist())
|
||||
self.assertIn("dir1/sub/a.txt", archive.namelist())
|
||||
self.assertIn("dir2/b.txt", archive.namelist())
|
||||
|
||||
def test_download_mixed_file_and_directory_selection_as_zip(self) -> None:
|
||||
(self.root / "readme.txt").write_text("R", encoding="utf-8")
|
||||
(self.root / "photos" / "nested").mkdir(parents=True)
|
||||
(self.root / "photos" / "nested" / "img.txt").write_text("P", encoding="utf-8")
|
||||
|
||||
response = self._get("/api/files/download?path=storage1/readme.txt&path=storage1/photos")
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertRegex(
|
||||
response.headers.get("content-disposition", ""),
|
||||
r'attachment; filename="kodidownload-\d{8}-\d{6}\.zip"',
|
||||
)
|
||||
with zipfile.ZipFile(BytesIO(response.content)) as archive:
|
||||
self.assertIn("readme.txt", archive.namelist())
|
||||
self.assertIn("photos/", archive.namelist())
|
||||
self.assertIn("photos/nested/img.txt", archive.namelist())
|
||||
|
||||
def test_download_directory_with_symlink_rejected(self) -> None:
|
||||
target = self.root / "real.txt"
|
||||
target.write_text("x", encoding="utf-8")
|
||||
(self.root / "docs").mkdir()
|
||||
(self.root / "docs" / "link.txt").symlink_to(target)
|
||||
|
||||
response = self._get("/api/files/download?path=storage1/docs")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user