fix(fc5): ruff (E712, ASYNC230/240, I001) + Credential.credential_type + FileStorage in api_migrate test

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-22 13:20:42 -04:00
parent 26fd3f9f2c
commit e5885f85c5
4 changed files with 36 additions and 30 deletions
+1 -1
View File
@@ -115,7 +115,7 @@ async def migrate_async(
encrypted = fc_crypto.encrypt(cred["plaintext"]) encrypted = fc_crypto.encrypt(cred["plaintext"])
db.add(Credential( db.add(Credential(
platform=cred["platform"], platform=cred["platform"],
kind=cred.get("credential_type") or "cookies", credential_type=cred.get("credential_type") or "cookies",
encrypted_blob=encrypted, encrypted_blob=encrypted,
expires_at=cred.get("expires_at"), expires_at=cred.get("expires_at"),
)) ))
+6 -3
View File
@@ -18,7 +18,7 @@ async def verify_async(db: AsyncSession, *, expected: dict | None = None) -> dic
checks = { checks = {
"artist_subscriptions": ( "artist_subscriptions": (
select(func.count(Artist.id)).where(Artist.is_subscription == True) select(func.count(Artist.id)).where(Artist.is_subscription.is_(True))
), ),
"source_count": select(func.count(Source.id)), "source_count": select(func.count(Source.id)),
"credential_count": select(func.count(Credential.id)), "credential_count": select(func.count(Credential.id)),
@@ -51,12 +51,15 @@ async def verify_sha256_sample(
samples: list[dict] = [] samples: list[dict] = []
for img_id, path, expected_sha in rows: for img_id, path, expected_sha in rows:
p = Path(path) p = Path(path)
if not p.exists(): # Sync stdlib filesystem ops are intentional: this verify pass runs
# inside a Celery task under asyncio.run; no other awaitables compete
# for the loop. Same pattern as download_service.py.
if not p.exists(): # noqa: ASYNC240
missing += 1 missing += 1
samples.append({"id": img_id, "path": path, "result": "missing"}) samples.append({"id": img_id, "path": path, "result": "missing"})
continue continue
h = hashlib.sha256() h = hashlib.sha256()
with p.open("rb") as f: with p.open("rb") as f: # noqa: ASYNC230
for chunk in iter(lambda: f.read(65536), b""): for chunk in iter(lambda: f.read(65536), b""):
h.update(chunk) h.update(chunk)
if h.hexdigest() == expected_sha: if h.hexdigest() == expected_sha:
+4 -9
View File
@@ -20,15 +20,10 @@ from ..celery_app import celery
from ..config import get_config from ..config import get_config
from ..models import MigrationRun from ..models import MigrationRun
from ..services.credential_crypto import CredentialCrypto from ..services.credential_crypto import CredentialCrypto
from ..services.migrators import ( from ..services.migrators import backup as backup_mod
backup as backup_mod, from ..services.migrators import gs_ingest, ir_ingest, ml_queue
gs_ingest, from ..services.migrators import rollback as rollback_mod
ir_ingest, from ..services.migrators import tag_apply, verify
ml_queue,
rollback as rollback_mod,
tag_apply,
verify,
)
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
+25 -17
View File
@@ -3,15 +3,23 @@ import io
import json import json
import pytest import pytest
from werkzeug.datastructures import FileStorage
import backend.app.tasks.migration # noqa: F401 — register celery task import backend.app.tasks.migration # noqa: F401 — register celery task
from backend.app import create_app from backend.app import create_app
from backend.app.celery_app import celery from backend.app.celery_app import celery
pytestmark = pytest.mark.integration pytestmark = pytest.mark.integration
def _file_storage(payload: dict, filename: str = "export.json") -> FileStorage:
return FileStorage(
stream=io.BytesIO(json.dumps(payload).encode("utf-8")),
filename=filename,
content_type="application/json",
)
@pytest.fixture @pytest.fixture
async def app(): async def app():
return create_app() return create_app()
@@ -46,7 +54,7 @@ async def test_post_rejects_unknown_kind(client):
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_post_ingest_rejects_missing_file(client): async def test_post_ingest_rejects_missing_file(client):
resp = await client.post("/api/migrate/gs_ingest", data={}) resp = await client.post("/api/migrate/gs_ingest", form={})
assert resp.status_code == 400 assert resp.status_code == 400
body = await resp.get_json() body = await resp.get_json()
assert body["error"] == "missing_export_file" assert body["error"] == "missing_export_file"
@@ -67,11 +75,11 @@ async def test_post_ingest_accepts_multipart_file(client, monkeypatch):
"source_app": "gallerysubscriber", "schema_version": 1, "source_app": "gallerysubscriber", "schema_version": 1,
"subscriptions": [], "credentials": [], "subscriptions": [], "credentials": [],
} }
data = { resp = await client.post(
"export_file": (io.BytesIO(json.dumps(export).encode()), "gs-export.json"), "/api/migrate/gs_ingest",
"dry_run": "false", form={"dry_run": "false"},
} files={"export_file": _file_storage(export, "gs-export.json")},
resp = await client.post("/api/migrate/gs_ingest", files=data) )
assert resp.status_code == 202 assert resp.status_code == 202
@@ -85,11 +93,11 @@ async def test_post_apply_without_backup_rejected(client, monkeypatch):
"source_app": "gallerysubscriber", "schema_version": 1, "source_app": "gallerysubscriber", "schema_version": 1,
"subscriptions": [], "credentials": [], "subscriptions": [], "credentials": [],
} }
data = { resp = await client.post(
"export_file": (io.BytesIO(json.dumps(export).encode()), "gs-export.json"), "/api/migrate/gs_ingest",
"dry_run": "false", form={"dry_run": "false"},
} files={"export_file": _file_storage(export, "gs-export.json")},
resp = await client.post("/api/migrate/gs_ingest", files=data) )
assert resp.status_code == 400 assert resp.status_code == 400
body = await resp.get_json() body = await resp.get_json()
assert body["error"] == "no_backup" assert body["error"] == "no_backup"
@@ -109,11 +117,11 @@ async def test_post_dry_run_allowed_without_backup(client, monkeypatch):
"source_app": "gallerysubscriber", "schema_version": 1, "source_app": "gallerysubscriber", "schema_version": 1,
"subscriptions": [], "credentials": [], "subscriptions": [], "credentials": [],
} }
data = { resp = await client.post(
"export_file": (io.BytesIO(json.dumps(export).encode()), "gs-export.json"), "/api/migrate/gs_ingest",
"dry_run": "true", form={"dry_run": "true"},
} files={"export_file": _file_storage(export, "gs-export.json")},
resp = await client.post("/api/migrate/gs_ingest", files=data) )
assert resp.status_code == 202 assert resp.status_code == 202