Initial project import
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from unittest import mock
|
||||
|
||||
from PIL import Image
|
||||
|
||||
from services.image_storage_service import ImageStorageService
|
||||
|
||||
|
||||
def png_bytes() -> bytes:
|
||||
path = Path(tempfile.gettempdir()) / "chatgpt2api-test-image.png"
|
||||
Image.new("RGB", (2, 2), color=(255, 0, 0)).save(path, format="PNG")
|
||||
return path.read_bytes()
|
||||
|
||||
|
||||
class FakeWebDAVClient:
|
||||
uploaded: dict[str, bytes] = {}
|
||||
deleted: list[str] = []
|
||||
|
||||
def __init__(self, _settings):
|
||||
pass
|
||||
|
||||
def put(self, rel: str, payload: bytes) -> str:
|
||||
self.uploaded[rel] = payload
|
||||
return f"https://dav.example.test/{rel}"
|
||||
|
||||
def get(self, rel: str) -> bytes:
|
||||
return self.uploaded[rel]
|
||||
|
||||
def delete(self, rel: str) -> bool:
|
||||
self.deleted.append(rel)
|
||||
self.uploaded.pop(rel, None)
|
||||
return True
|
||||
|
||||
def test(self) -> dict[str, object]:
|
||||
self.put(".chatgpt2api_webdav_test.txt", b"chatgpt2api webdav test\n")
|
||||
self.delete(".chatgpt2api_webdav_test.txt")
|
||||
return {"ok": True, "status": 200, "error": None}
|
||||
|
||||
|
||||
class ImageStorageServiceTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.tmp = tempfile.TemporaryDirectory()
|
||||
self.addCleanup(self.tmp.cleanup)
|
||||
self.data_dir = Path(self.tmp.name)
|
||||
self.images_dir = self.data_dir / "images"
|
||||
self.settings = {
|
||||
"enabled": False,
|
||||
"mode": "local",
|
||||
"webdav_url": "",
|
||||
"webdav_username": "",
|
||||
"webdav_password": "",
|
||||
"webdav_root_path": "chatgpt2api/images",
|
||||
"public_base_url": "",
|
||||
}
|
||||
self.config_patcher = mock.patch("services.image_storage_service.config")
|
||||
self.mock_config = self.config_patcher.start()
|
||||
self.addCleanup(self.config_patcher.stop)
|
||||
self.mock_config.images_dir = self.images_dir
|
||||
self.mock_config.base_url = "http://app.test"
|
||||
self.mock_config.cleanup_old_images.return_value = 0
|
||||
self.mock_config.get_image_storage_settings.side_effect = lambda: dict(self.settings)
|
||||
FakeWebDAVClient.uploaded = {}
|
||||
FakeWebDAVClient.deleted = []
|
||||
|
||||
def service(self) -> ImageStorageService:
|
||||
return ImageStorageService(self.data_dir / "image_index.json")
|
||||
|
||||
def test_local_mode_saves_to_local_directory(self):
|
||||
stored = self.service().save(png_bytes(), "http://app.test")
|
||||
|
||||
self.assertEqual(stored.storage, "local")
|
||||
self.assertTrue((self.images_dir / stored.rel).is_file())
|
||||
self.assertEqual(stored.url, f"http://app.test/images/{stored.rel}")
|
||||
|
||||
def test_webdav_mode_uploads_without_local_file(self):
|
||||
self.settings.update({
|
||||
"enabled": True,
|
||||
"mode": "webdav",
|
||||
"webdav_url": "https://dav.example.test",
|
||||
"webdav_password": "secret",
|
||||
})
|
||||
with mock.patch("services.image_storage_service.WebDAVClient", FakeWebDAVClient):
|
||||
stored = self.service().save(png_bytes(), "http://app.test")
|
||||
payload = self.service().get_bytes(stored.rel)
|
||||
|
||||
self.assertEqual(stored.storage, "webdav")
|
||||
self.assertFalse((self.images_dir / stored.rel).exists())
|
||||
self.assertIn(stored.rel, FakeWebDAVClient.uploaded)
|
||||
self.assertEqual(payload, FakeWebDAVClient.uploaded[stored.rel])
|
||||
|
||||
def test_list_items_ignores_non_image_files(self):
|
||||
image = png_bytes()
|
||||
image_path = self.images_dir / "2026" / "05" / "07" / "sample.png"
|
||||
image_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
image_path.write_bytes(image)
|
||||
(self.images_dir / ".DS_Store").write_text("not an image", encoding="utf-8")
|
||||
(self.images_dir / "2026" / ".DS_Store").write_text("not an image", encoding="utf-8")
|
||||
|
||||
items = self.service().list_items("http://app.test")
|
||||
|
||||
self.assertEqual([item["rel"] for item in items], ["2026/05/07/sample.png"])
|
||||
self.assertEqual(items[0]["storage"], "local")
|
||||
|
||||
def test_both_mode_saves_to_local_and_webdav(self):
|
||||
self.settings.update({
|
||||
"enabled": True,
|
||||
"mode": "both",
|
||||
"webdav_url": "https://dav.example.test",
|
||||
"webdav_password": "secret",
|
||||
"public_base_url": "https://cdn.example.test/images",
|
||||
})
|
||||
with mock.patch("services.image_storage_service.WebDAVClient", FakeWebDAVClient):
|
||||
stored = self.service().save(png_bytes(), "http://app.test")
|
||||
|
||||
self.assertEqual(stored.storage, "both")
|
||||
self.assertTrue((self.images_dir / stored.rel).is_file())
|
||||
self.assertIn(stored.rel, FakeWebDAVClient.uploaded)
|
||||
self.assertEqual(stored.url, f"https://cdn.example.test/images/{stored.rel}")
|
||||
|
||||
def test_test_webdav_writes_and_deletes_probe_file(self):
|
||||
self.settings.update({
|
||||
"enabled": True,
|
||||
"mode": "webdav",
|
||||
"webdav_url": "https://dav.example.test",
|
||||
"webdav_password": "secret",
|
||||
})
|
||||
with mock.patch("services.image_storage_service.WebDAVClient", FakeWebDAVClient):
|
||||
result = self.service().test_webdav()
|
||||
|
||||
self.assertTrue(result["ok"])
|
||||
self.assertIn(".chatgpt2api_webdav_test.txt", FakeWebDAVClient.deleted)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user