feat: import F95 screenshot galleries with AVIF support
This commit is contained in:
1 parent
5b911d1e3b
commit
69f71cb836
21 files changed
+513
-33
No files matched your search
@@ -0,0 +1,75 @@
|
||||
import asyncio
|
||||
import importlib
|
||||
import io
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from contextlib import asynccontextmanager
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import patch
|
||||
|
||||
from alembic.config import Config
|
||||
from alembic.migration import MigrationContext
|
||||
from alembic.operations import Operations
|
||||
from alembic.script import ScriptDirectory
|
||||
|
||||
from app import gc
|
||||
from app.blobs import blob_path, put_bytes
|
||||
from app.config import get_settings
|
||||
from app.models import Game
|
||||
|
||||
|
||||
def test_gallery_migration_has_one_head_and_postgres_default_for_existing_games():
|
||||
output = io.StringIO()
|
||||
context = MigrationContext.configure(dialect_name="postgresql", opts={"as_sql": True, "output_buffer": output})
|
||||
operations = Operations(context)
|
||||
migration = importlib.import_module("migrations.versions.0006_game_gallery")
|
||||
with patch("alembic.op.add_column", operations.add_column):
|
||||
migration.upgrade()
|
||||
sql = output.getvalue()
|
||||
assert "gallery_hashes VARCHAR(80)[] DEFAULT '{}' NOT NULL" in sql
|
||||
assert ScriptDirectory.from_config(Config("alembic.ini")).get_heads() == ["0006"]
|
||||
|
||||
|
||||
def test_gc_retains_gallery_images_without_cover_or_version_references(tmp_path, monkeypatch):
|
||||
monkeypatch.setenv("DATA_DIR", str(tmp_path))
|
||||
get_settings.cache_clear()
|
||||
live = "a" * 64 + ".webp"
|
||||
orphan = "b" * 64 + ".webp"
|
||||
for value in (live, orphan):
|
||||
path = blob_path(value)
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_bytes(b"image")
|
||||
|
||||
async def scalars(statement):
|
||||
expr = statement.column_descriptions[0]["expr"]
|
||||
return SimpleNamespace(all=lambda: [[live]] if expr is Game.gallery_hashes else [])
|
||||
|
||||
@asynccontextmanager
|
||||
async def session():
|
||||
yield SimpleNamespace(scalars=scalars)
|
||||
|
||||
@asynccontextmanager
|
||||
async def access(**_):
|
||||
yield
|
||||
|
||||
monkeypatch.setattr(gc, "SessionLocal", session)
|
||||
monkeypatch.setattr(gc, "storage_access", access)
|
||||
try:
|
||||
report = asyncio.run(gc.collect())
|
||||
assert report["retainedBlobs"] == 1
|
||||
assert report["files"] == 1
|
||||
assert blob_path(live).exists()
|
||||
finally:
|
||||
get_settings.cache_clear()
|
||||
|
||||
|
||||
def test_identical_parallel_gallery_images_share_a_blob(tmp_path, monkeypatch):
|
||||
monkeypatch.setenv("DATA_DIR", str(tmp_path))
|
||||
get_settings.cache_clear()
|
||||
try:
|
||||
with ThreadPoolExecutor(max_workers=8) as executor:
|
||||
results = list(executor.map(lambda _: put_bytes(b"same image", ".webp"), range(32)))
|
||||
assert len({value[0] for value in results}) == 1
|
||||
assert blob_path(results[0][0]).read_bytes() == b"same image"
|
||||
assert not list((tmp_path / "blobs/tmp").iterdir())
|
||||
finally:
|
||||
get_settings.cache_clear()
|
||||
Reference in new issue
Block a user