Files
Random-image-api/backend/tests/test_hydrate_persists_popularity_fields.py
2026-02-22 18:58:55 +08:00

190 lines
6.8 KiB
Python

from __future__ import annotations
import asyncio
import json
from pathlib import Path
import httpx
import sqlalchemy as sa
from cryptography.fernet import Fernet
from fastapi.testclient import TestClient
from app.core.crypto import FieldEncryptor
from app.db.models.base import Base
from app.db.models.images import Image
from app.db.models.jobs import JobRow
from app.db.models.pixiv_tokens import PixivToken
from app.db.session import create_sessionmaker
from app.jobs.claim import claim_next_job
from app.jobs.dispatch import JobDispatcher
from app.jobs.executor import execute_claimed_job
from app.jobs.handlers.hydrate_metadata import build_hydrate_metadata_handler
from app.main import create_app
def test_hydrate_persists_popularity_fields_and_public_api_outputs(tmp_path: Path, monkeypatch) -> None:
db_path = tmp_path / "hydrate_popularity.db"
db_url = "sqlite+aiosqlite:///" + db_path.as_posix()
field_key = Fernet.generate_key().decode("ascii")
encryptor = FieldEncryptor.from_key(field_key)
monkeypatch.setenv("APP_ENV", "dev")
monkeypatch.setenv("DATABASE_URL", db_url)
monkeypatch.setenv("FIELD_ENCRYPTION_KEY", field_key)
monkeypatch.setenv("PIXIV_OAUTH_CLIENT_ID", "cid_test")
monkeypatch.setenv("PIXIV_OAUTH_CLIENT_SECRET", "csec_test")
monkeypatch.setenv("PIXIV_OAUTH_HASH_SECRET", "hsec_test")
refresh_token = "rt_test"
access_token = "at_test"
def handler(req: httpx.Request) -> httpx.Response:
if str(req.url) == "https://oauth.secure.pixiv.net/auth/token":
body = (req.content or b"").decode("utf-8")
assert f"refresh_token={refresh_token}" in body
return httpx.Response(
200,
json={
"response": {
"access_token": access_token,
"token_type": "bearer",
"expires_in": 3600,
"refresh_token": refresh_token,
"scope": "",
"user": {"id": 123},
}
},
)
if str(req.url).startswith("https://app-api.pixiv.net/v1/illust/detail"):
assert req.headers.get("Authorization") == f"Bearer {access_token}"
return httpx.Response(
200,
json={
"illust": {
"id": 111,
"title": "title_test",
"user": {"id": 999, "name": "user_test"},
"x_restrict": 0,
"illust_type": 0,
"illust_ai_type": 1,
"width": 1200,
"height": 800,
"create_date": "2020-01-01T00:00:00+00:00",
"page_count": 2,
"total_bookmarks": 123,
"total_view": 456,
"total_comments": 7,
"tags": [{"name": "tag1"}],
"meta_pages": [
{
"image_urls": {
"original": "https://i.pximg.net/img-original/img/2020/01/01/00/00/00/111_p0.jpg"
}
},
{
"image_urls": {
"original": "https://i.pximg.net/img-original/img/2020/01/01/00/00/00/111_p1.png"
}
},
],
}
},
)
return httpx.Response(500, text="unexpected")
transport = httpx.MockTransport(handler)
app = create_app()
image0_id: int | None = None
async def _run() -> None:
async with app.state.engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
Session = create_sessionmaker(app.state.engine)
async with Session() as session:
token_row = PixivToken(
label="acc1",
enabled=1,
refresh_token_enc=encryptor.encrypt_text(refresh_token),
refresh_token_masked="***",
weight=1.0,
)
session.add(token_row)
job = JobRow(
type="hydrate_metadata",
status="pending",
payload_json=json.dumps({"illust_id": 111}, ensure_ascii=False, separators=(",", ":")),
)
session.add(job)
await session.commit()
dispatcher = JobDispatcher()
dispatcher.register("hydrate_metadata", build_hydrate_metadata_handler(app.state.engine, transport=transport))
claimed = await claim_next_job(app.state.engine, worker_id="w1")
assert claimed is not None
transition = await execute_claimed_job(app.state.engine, dispatcher, job_row=claimed, worker_id="w1")
assert transition is not None
assert transition.status.value == "completed"
async with Session() as session:
images = (
(
await session.execute(
sa.select(Image).where(Image.illust_id == 111).order_by(Image.page_index.asc())
)
)
.scalars()
.all()
)
assert len(images) == 2
assert images[0].page_index == 0
assert images[1].page_index == 1
for img in images:
assert img.bookmark_count == 123
assert img.view_count == 456
assert img.comment_count == 7
assert img.illust_type == 0
nonlocal image0_id
image0_id = int(images[0].id)
await app.state.engine.dispose()
asyncio.run(_run())
assert image0_id is not None
with TestClient(app) as client:
resp = client.get("/images", params={"r18": 2}, headers={"X-Request-Id": "req_test"})
assert resp.status_code == 200
body = resp.json()
assert body["ok"] is True
assert body["request_id"] == "req_test"
found = [
it
for it in body["items"]
if int(it["illust_id"]) == 111 and int(it["page_index"]) == 0
]
assert found
item0 = found[0]
assert item0["bookmark_count"] == 123
assert item0["view_count"] == 456
assert item0["comment_count"] == 7
resp2 = client.get(f"/images/{image0_id}", headers={"X-Request-Id": "req_test2"})
assert resp2.status_code == 200
body2 = resp2.json()
assert body2["ok"] is True
assert body2["request_id"] == "req_test2"
item = body2["item"]["image"]
assert item["bookmark_count"] == 123
assert item["view_count"] == 456
assert item["comment_count"] == 7