"""#1714 beat 任务 scheduled_cleanup_stale_ingest_jobs 薄封装测试。

mock SessionLocal 和清理核心，验证 beat 任务正确串联
cleanup_stale_ingest_jobs → cleanup_orphan_processing_assets → revoke 消息。
"""

from __future__ import annotations

import os
import sys
from pathlib import Path
from unittest.mock import MagicMock, patch

os.environ.setdefault("JWT_SECRET_KEY", "unit-test-secret")
os.environ.setdefault("DATABASE_URL", "sqlite:///test_beat.db")

sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "apps" / "worker"))
sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "apps" / "api"))
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))

import worker_app.tasks.cleanup as cleanup  # noqa: E402


def test_beat_cleanup_calls_core_and_revokes():
    """beat 任务串联三个核心步骤，返回汇总计数。"""
    fake_session = MagicMock()

    with (
        patch("worker_app.db.SessionLocal", return_value=fake_session) as m_db,
        patch(
            "packages.application.ingest_orphan_cleanup.cleanup_stale_ingest_jobs",
            return_value=([("job-1", "cel-1"), ("job-2", "")], ["a-1"]),
        ) as m_jobs,
        patch(
            "packages.application.ingest_orphan_cleanup.cleanup_orphan_processing_assets",
            return_value=["a-2"],
        ) as m_assets,
        patch(
            "packages.shared.celery_orphan_guard.revoke_and_purge",
            return_value=1,
        ) as m_revoke,
    ):
        result = cleanup.scheduled_cleanup_stale_ingest_jobs()

    m_db.assert_called_once()
    m_jobs.assert_called_once()
    assert m_jobs.call_args.kwargs["processing_timeout_minutes"] == 60
    m_assets.assert_called_once()
    m_revoke.assert_called_once()
    # 队列名只传 transcode/celery（不传 generation）
    assert m_revoke.call_args.kwargs["queue_names"] == ("transcode", "celery")
    fake_session.close.assert_called_once()
    assert result == {"stale_jobs": 2, "assets_to_error": 2, "purged_messages": 1}


def test_beat_cleanup_no_op_when_nothing_stale():
    """无孤儿时不调 revoke，返回全 0。"""
    fake_session = MagicMock()

    with (
        patch("worker_app.db.SessionLocal", return_value=fake_session),
        patch(
            "packages.application.ingest_orphan_cleanup.cleanup_stale_ingest_jobs",
            return_value=([], []),
        ),
        patch(
            "packages.application.ingest_orphan_cleanup.cleanup_orphan_processing_assets",
            return_value=[],
        ),
        patch("packages.shared.celery_orphan_guard.revoke_and_purge") as m_revoke,
    ):
        result = cleanup.scheduled_cleanup_stale_ingest_jobs()

    m_revoke.assert_not_called()
    assert result == {"stale_jobs": 0, "assets_to_error": 0, "purged_messages": 0}
