2026-05-19 cutover(렌더를 Windows insta-render 워커로 이관)에서 card_assets 등록 단계가 새 설계에 누락됨. 구 card_renderer.render_slate가 NAS DB에 등록하던 것을, webhook은 task/slate status만 갱신하도록 만들어 card_assets가 영구 빈 상태 → /assets 404, /package 409, get_slate assets=0.
insta_update가 succeeded 시 워커 출력 디렉토리를 스캔해 실제 PNG만 card_assets에 등록(_register_rendered_assets). CARDS_DIR/{id}, INSTA_DATA_PATH/{id} 두 후보를 순서대로 스캔 → 경로 정합 전환기에도 견고. 신규 테스트 2건(등록 성공 / 파일 없으면 미등록).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
133 lines
4.3 KiB
Python
133 lines
4.3 KiB
Python
"""POST /api/internal/insta/update — Windows worker webhook."""
|
|
import os
|
|
import pytest
|
|
from fastapi import FastAPI
|
|
from fastapi.testclient import TestClient
|
|
from app.internal_router import router
|
|
from app import db
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _set_key(monkeypatch):
|
|
monkeypatch.setenv("INTERNAL_API_KEY", "test-secret")
|
|
|
|
|
|
@pytest.fixture
|
|
def client(tmp_path, monkeypatch):
|
|
# SQLite in-memory test
|
|
monkeypatch.setenv("INSTA_DATA_PATH", str(tmp_path))
|
|
db.init_db()
|
|
app = FastAPI()
|
|
app.include_router(router)
|
|
return TestClient(app)
|
|
|
|
|
|
def _make_task():
|
|
return db.create_task("slate_render", {"slate_id": 42})
|
|
|
|
|
|
def test_update_with_valid_key_updates_db(client):
|
|
tid = _make_task()
|
|
r = client.post(
|
|
"/api/internal/insta/update",
|
|
headers={"X-Internal-Key": "test-secret"},
|
|
json={"task_id": tid, "status": "processing", "progress": 30},
|
|
)
|
|
assert r.status_code == 200
|
|
task = db.get_task(tid)
|
|
assert task["status"] == "processing"
|
|
assert task["progress"] == 30
|
|
|
|
|
|
def test_update_with_invalid_key_returns_401(client):
|
|
tid = _make_task()
|
|
r = client.post(
|
|
"/api/internal/insta/update",
|
|
headers={"X-Internal-Key": "wrong"},
|
|
json={"task_id": tid, "status": "processing", "progress": 30},
|
|
)
|
|
assert r.status_code == 401
|
|
|
|
|
|
def test_update_succeeded_sets_result_path(client):
|
|
tid = _make_task()
|
|
r = client.post(
|
|
"/api/internal/insta/update",
|
|
headers={"X-Internal-Key": "test-secret"},
|
|
json={
|
|
"task_id": tid,
|
|
"status": "succeeded",
|
|
"progress": 100,
|
|
"result_path": "/media/insta/42/01.png",
|
|
},
|
|
)
|
|
assert r.status_code == 200
|
|
task = db.get_task(tid)
|
|
assert task["status"] == "succeeded"
|
|
assert task["result_id"] is not None # slate_id from input_data
|
|
|
|
|
|
def test_update_failed_records_error(client):
|
|
tid = _make_task()
|
|
r = client.post(
|
|
"/api/internal/insta/update",
|
|
headers={"X-Internal-Key": "test-secret"},
|
|
json={"task_id": tid, "status": "failed", "progress": 0, "error": "Chromium crashed"},
|
|
)
|
|
assert r.status_code == 200
|
|
task = db.get_task(tid)
|
|
assert task["status"] == "failed"
|
|
assert "Chromium" in (task.get("error") or "")
|
|
|
|
|
|
def test_succeeded_registers_card_assets(client, tmp_path, monkeypatch):
|
|
"""succeeded 시 워커가 쓴 PNG들을 card_assets로 등록 (cutover 후 누락된 단계)."""
|
|
from app import config
|
|
|
|
# FK 충족용 실제 슬레이트
|
|
sid = db.add_card_slate({"keyword": "금리", "category": "economy"})
|
|
# 워커가 PNG 10장을 쓴 디렉토리 시뮬 (CARDS_DIR/{sid})
|
|
cards_root = tmp_path / "insta_cards"
|
|
sdir = cards_root / str(sid)
|
|
sdir.mkdir(parents=True)
|
|
for p in range(1, 11):
|
|
(sdir / f"{p:02d}.png").write_bytes(b"\x89PNG\r\n\x1a\n" + b"x" * 100)
|
|
monkeypatch.setattr(config, "CARDS_DIR", str(cards_root))
|
|
monkeypatch.setattr(config, "INSTA_DATA_PATH", str(tmp_path))
|
|
|
|
tid = db.create_task("slate_render", {"slate_id": sid})
|
|
r = client.post(
|
|
"/api/internal/insta/update",
|
|
headers={"X-Internal-Key": "test-secret"},
|
|
json={
|
|
"task_id": tid,
|
|
"status": "succeeded",
|
|
"progress": 100,
|
|
"result_path": f"/media/insta/{sid}/01.png",
|
|
},
|
|
)
|
|
assert r.status_code == 200
|
|
assets = db.list_card_assets(sid)
|
|
assert len(assets) == 10
|
|
assert assets[0]["page_index"] == 1
|
|
assert assets[0]["file_path"].endswith("01.png")
|
|
assert db.get_card_slate(sid)["status"] == "rendered"
|
|
|
|
|
|
def test_succeeded_no_files_registers_nothing(client, tmp_path, monkeypatch):
|
|
"""워커 출력이 없으면(파일 미존재) 잘못된 asset 등록 금지 — 200은 유지."""
|
|
from app import config
|
|
|
|
sid = db.add_card_slate({"keyword": "환율", "category": "economy"})
|
|
monkeypatch.setattr(config, "CARDS_DIR", str(tmp_path / "insta_cards"))
|
|
monkeypatch.setattr(config, "INSTA_DATA_PATH", str(tmp_path))
|
|
|
|
tid = db.create_task("slate_render", {"slate_id": sid})
|
|
r = client.post(
|
|
"/api/internal/insta/update",
|
|
headers={"X-Internal-Key": "test-secret"},
|
|
json={"task_id": tid, "status": "succeeded", "progress": 100},
|
|
)
|
|
assert r.status_code == 200
|
|
assert db.list_card_assets(sid) == []
|