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:
@@ -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"),
|
||||||
))
|
))
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user