feat(music-lab): YouTube OAuth + resumable 업로드
This commit is contained in:
88
music-lab/tests/test_youtube_upload.py
Normal file
88
music-lab/tests/test_youtube_upload.py
Normal file
@@ -0,0 +1,88 @@
|
||||
import pytest
|
||||
from unittest.mock import patch, MagicMock
|
||||
from app.pipeline import youtube
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def fresh_db(monkeypatch, tmp_path):
|
||||
from app import db
|
||||
monkeypatch.setattr(db, "DB_PATH", str(tmp_path / "music.db"))
|
||||
db.init_db()
|
||||
return db
|
||||
|
||||
|
||||
def _setup_token(db_module):
|
||||
db_module.upsert_oauth_token(
|
||||
channel_id="UC1", channel_title="t", avatar_url=None,
|
||||
refresh_token="r1", access_token="a1", expires_at="2099-01-01T00:00:00",
|
||||
)
|
||||
|
||||
|
||||
@patch("app.pipeline.youtube._build_youtube_client")
|
||||
def test_upload_succeeds_after_resumable(mock_client, fresh_db, tmp_path, monkeypatch):
|
||||
monkeypatch.setenv("YOUTUBE_OAUTH_CLIENT_ID", "cid")
|
||||
monkeypatch.setenv("YOUTUBE_OAUTH_CLIENT_SECRET", "sec")
|
||||
_setup_token(fresh_db)
|
||||
|
||||
yt = MagicMock()
|
||||
insert = MagicMock()
|
||||
# next_chunk: first call returns (None, None), second returns (None, response with id)
|
||||
insert.next_chunk.side_effect = [(None, None), (None, {"id": "VID123"})]
|
||||
yt.videos().insert.return_value = insert
|
||||
mock_client.return_value = yt
|
||||
|
||||
video_path = tmp_path / "v.mp4"
|
||||
video_path.write_bytes(b"\x00" * 100)
|
||||
out = youtube.upload_video(
|
||||
video_path=str(video_path),
|
||||
thumbnail_path=None,
|
||||
metadata={"title": "T", "description": "D", "tags": ["x"], "category_id": 10},
|
||||
privacy="private",
|
||||
)
|
||||
assert out["video_id"] == "VID123"
|
||||
|
||||
|
||||
def test_upload_no_token_raises(fresh_db, tmp_path):
|
||||
video_path = tmp_path / "v.mp4"
|
||||
video_path.write_bytes(b"\x00")
|
||||
with pytest.raises(youtube.NotAuthenticatedError):
|
||||
youtube.upload_video(
|
||||
video_path=str(video_path), thumbnail_path=None,
|
||||
metadata={"title":"T","description":"D","tags":[],"category_id":10},
|
||||
privacy="private",
|
||||
)
|
||||
|
||||
|
||||
@patch("app.pipeline.youtube._build_youtube_client")
|
||||
def test_upload_quota_exceeded_marks_quota(mock_client, fresh_db, tmp_path, monkeypatch):
|
||||
from googleapiclient.errors import HttpError
|
||||
monkeypatch.setenv("YOUTUBE_OAUTH_CLIENT_ID", "cid")
|
||||
monkeypatch.setenv("YOUTUBE_OAUTH_CLIENT_SECRET", "sec")
|
||||
_setup_token(fresh_db)
|
||||
|
||||
yt = MagicMock()
|
||||
err = HttpError(MagicMock(status=403), b'{"error":{"errors":[{"reason":"quotaExceeded"}]}}')
|
||||
insert_call = MagicMock()
|
||||
insert_call.next_chunk.side_effect = err
|
||||
yt.videos().insert.return_value = insert_call
|
||||
mock_client.return_value = yt
|
||||
|
||||
video_path = tmp_path / "v.mp4"
|
||||
video_path.write_bytes(b"\x00")
|
||||
with pytest.raises(youtube.QuotaExceededError):
|
||||
youtube.upload_video(
|
||||
video_path=str(video_path), thumbnail_path=None,
|
||||
metadata={"title":"T","description":"D","tags":[],"category_id":10},
|
||||
privacy="private",
|
||||
)
|
||||
|
||||
|
||||
def test_get_status_returns_none_when_not_connected(fresh_db):
|
||||
assert youtube.get_status() is None
|
||||
|
||||
|
||||
def test_get_status_returns_channel_info(fresh_db):
|
||||
_setup_token(fresh_db)
|
||||
s = youtube.get_status()
|
||||
assert s["channel_id"] == "UC1"
|
||||
assert s["channel_title"] == "t"
|
||||
Reference in New Issue
Block a user