- YoutubePublisherAgent: 음악 파이프라인의 *_pending 상태를 폴링하여 텔레그램 단일 채널로 단계별 검토 요청 발송, reply 수신 시 의도 분류 후 music-lab에 feedback POST - service_proxy: pipeline list/get/feedback/telegram-msg/lookup-by-msg 헬퍼 5종 추가 (MUSIC_LAB_URL 사용) - scheduler: 30초 interval로 poll_state_changes 실행 - telegram webhook: reply_to_message 가 파이프라인 메시지면 youtube_publisher 로 라우팅 (슬래시 명령보다 우선) - 테스트 4종 추가 (4 PASS)
111 lines
3.2 KiB
Python
111 lines
3.2 KiB
Python
import os
|
|
import sys
|
|
import tempfile
|
|
|
|
_fd, _TMP = tempfile.mkstemp(suffix=".db")
|
|
os.close(_fd)
|
|
os.unlink(_TMP)
|
|
os.environ["AGENT_OFFICE_DB_PATH"] = _TMP
|
|
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
|
|
|
|
import pytest
|
|
from unittest.mock import AsyncMock, patch
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _init_db():
|
|
import gc
|
|
gc.collect()
|
|
if os.path.exists(_TMP):
|
|
os.remove(_TMP)
|
|
from app.db import init_db
|
|
init_db()
|
|
yield
|
|
gc.collect()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_poll_notifies_once_per_state():
|
|
from app.agents.youtube_publisher import YoutubePublisherAgent
|
|
|
|
pipelines = [{
|
|
"id": 1,
|
|
"state": "cover_pending",
|
|
"cover_url": "/x.jpg",
|
|
"track_title": "Test",
|
|
}]
|
|
with patch(
|
|
"app.agents.youtube_publisher.service_proxy.list_active_pipelines",
|
|
new=AsyncMock(return_value=pipelines),
|
|
), patch(
|
|
"app.agents.youtube_publisher.send_raw",
|
|
new=AsyncMock(return_value={"ok": True, "message_id": 99}),
|
|
) as mock_send, patch(
|
|
"app.agents.youtube_publisher.service_proxy.save_pipeline_telegram_msg",
|
|
new=AsyncMock(),
|
|
):
|
|
a = YoutubePublisherAgent()
|
|
await a.poll_state_changes()
|
|
await a.poll_state_changes() # 같은 상태 — 두 번째는 알림 안 함
|
|
assert mock_send.call_count == 1
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_on_telegram_reply_approve_calls_feedback():
|
|
from app.agents.youtube_publisher import YoutubePublisherAgent
|
|
|
|
with patch(
|
|
"app.agents.youtube_publisher.service_proxy.post_pipeline_feedback",
|
|
new=AsyncMock(),
|
|
) as mock_fb, patch(
|
|
"app.agents.youtube_publisher.send_raw",
|
|
new=AsyncMock(),
|
|
):
|
|
a = YoutubePublisherAgent()
|
|
await a.on_telegram_reply(pipeline_id=42, step="cover", user_text="승인")
|
|
mock_fb.assert_called_once_with(42, "cover", "approve", None)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_on_telegram_reply_reject_with_feedback():
|
|
from app.agents.youtube_publisher import YoutubePublisherAgent
|
|
|
|
with patch(
|
|
"app.agents.youtube_publisher.service_proxy.post_pipeline_feedback",
|
|
new=AsyncMock(),
|
|
) as mock_fb, patch(
|
|
"app.agents.youtube_publisher.send_raw",
|
|
new=AsyncMock(),
|
|
):
|
|
a = YoutubePublisherAgent()
|
|
await a.on_telegram_reply(pipeline_id=43, step="meta", user_text="반려, 제목 짧게")
|
|
args = mock_fb.call_args[0]
|
|
assert args[0] == 43
|
|
assert args[1] == "meta"
|
|
assert args[2] == "reject"
|
|
assert "제목 짧게" in (args[3] or "")
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_on_telegram_reply_unclear_asks_again():
|
|
from app.agents.youtube_publisher import YoutubePublisherAgent
|
|
|
|
sent = []
|
|
|
|
async def mock_send(text=None, **kw):
|
|
sent.append(text)
|
|
return {"ok": True, "message_id": 1}
|
|
|
|
with patch(
|
|
"app.agents.youtube_publisher.send_raw",
|
|
new=mock_send,
|
|
), patch(
|
|
"app.agents.youtube_publisher.classify_intent.classify",
|
|
return_value=("unclear", None),
|
|
):
|
|
a = YoutubePublisherAgent()
|
|
await a.on_telegram_reply(pipeline_id=44, step="cover", user_text="huh?")
|
|
assert any("다시 입력" in (s or "") for s in sent)
|