fix(insta-render): fonts.ready 대기 + PNG 비어있음 검증 (렌더 known-issue 해결)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-06 12:53:07 +09:00
parent 8bfc8e153f
commit 9241b5cd90
2 changed files with 28 additions and 0 deletions

View File

@@ -151,8 +151,11 @@ async def _render_slate_locked(slate: dict, slate_id: int, template: str) -> Lis
html_path = f.name
try:
await page.goto(f"file://{html_path}", wait_until="networkidle")
await page.evaluate("document.fonts.ready") # 웹폰트 로딩 완료까지 대기
out_path = os.path.join(out_dir, f"{spec['page_no']:02d}.png")
await page.screenshot(path=out_path, full_page=False, omit_background=False)
if os.path.getsize(out_path) < 1000: # 빈/깨진 PNG 방어
raise RuntimeError(f"rendered PNG too small: {out_path}")
paths.append(out_path)
finally:
try:

View File

@@ -1,10 +1,13 @@
"""worker.py — Redis BLPOP + webhook 단위 테스트."""
import json
import os
from pathlib import Path
import pytest
import httpx
from unittest.mock import AsyncMock, patch
import worker
from card_renderer import render_slate, init_browser, shutdown_browser
@pytest.fixture
@@ -179,6 +182,28 @@ async def test_poll_once_calls_fail_on_exception(monkeypatch):
fake_queue.ack.assert_not_awaited()
@pytest.mark.asyncio
async def test_render_produces_nonempty_1080x1350(tmp_path, monkeypatch):
"""Phase 2 — fonts.ready 대기 + PNG 비어있음 검증: 10장 모두 > 1000 bytes."""
import card_renderer as _cr
templates_dir = str(Path(__file__).resolve().parent.parent / "templates")
monkeypatch.setattr(_cr, "CARD_TEMPLATE_DIR", templates_dir)
monkeypatch.setattr(_cr, "INSTA_MEDIA_ROOT", str(tmp_path))
await init_browser()
try:
slate = {
"cover_copy": {"headline": "헤드라인", "body": "서브", "accent_color": "#0F62FE"},
"body_copies": [{"headline": f"포인트{i}", "body": "본문"} for i in range(8)],
"cta_copy": {"headline": "요약", "body": "마무리", "cta": "팔로우"},
}
paths = await render_slate(slate, slate_id=99999)
assert len(paths) == 10
for p in paths:
assert os.path.getsize(p) > 1000 # 비어있지 않음
finally:
await shutdown_browser()
@pytest.mark.asyncio
async def test_poll_once_returns_false_on_timeout(monkeypatch):
"""F6 — dequeue가 None 반환(타임아웃)이면 False 리턴, ack/fail 안 부름."""