Files
web-page-backend/agent-office/tests/test_pipeline_polling.py
gahusb 7552ce4263 feat(agent-office): youtube_publisher 에이전트 + 30s 폴링
- 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)
2026-05-07 17:20:21 +09:00

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)