diff --git a/music-lab/tests/test_pipeline_flow.py b/music-lab/tests/test_pipeline_flow.py index 4e6c910..8db28f3 100644 --- a/music-lab/tests/test_pipeline_flow.py +++ b/music-lab/tests/test_pipeline_flow.py @@ -111,3 +111,65 @@ def test_pipeline_reject_and_regenerate(client): assert p["feedback_count_per_step"]["cover"] == 1 history = db.get_feedback_history(pid) assert history[0]["feedback_text"] == "더 어둡게" + + +@patch("app.pipeline.youtube.upload_video", return_value={"video_id": "MIX_VID"}) +@patch("app.pipeline.review.run_4_axis", new=AsyncMock(return_value={ + "metadata_quality": {"score": 90, "notes": ""}, + "policy_compliance": {"score": 95, "issues": []}, + "viewer_experience": {"score": 85, "notes": ""}, + "trend_alignment": {"score": 70, "matched_keywords": []}, + "weighted_total": 87.0, "verdict": "pass", "summary": "ok", + "used_fallback": False, +})) +@patch("app.pipeline.metadata.generate", new=AsyncMock(return_value={ + "title": "Mix", "description": "Track desc", + "tags": ["lofi"], "category_id": 10, + "used_fallback": False, "error": None, +})) +@patch("app.pipeline.thumb.generate", return_value={ + "url": "/media/videos/X/thumbnail.jpg", "used_fallback": False, +}) +@patch("app.pipeline.video.generate", return_value={ + "url": "/media/videos/X/video.mp4", "used_fallback": False, "duration_sec": 600, +}) +@patch("app.pipeline.cover.generate", new=AsyncMock(return_value={ + "url": "/media/videos/X/cover.jpg", "used_fallback": False, "error": None, +})) +def test_full_pipeline_compile_job_happy_path(mock_video, mock_thumb, mock_yt, client): + # compile_job 1개 추가 (succeeded) + conn = sqlite3.connect(db.DB_PATH) + cur = conn.cursor() + try: + cur.execute(""" + INSERT INTO compile_jobs (title, track_ids, crossfade_sec, output_path, + status, created_at) + VALUES ('Test Mix', '[1]', 3, '/app/data/compiles/1.mp3', 'succeeded', datetime()) + """) + except sqlite3.OperationalError: + pytest.skip("compile_jobs schema mismatch — skip integration test") + conn.commit() + cid = cur.lastrowid + conn.close() + + pid = client.post("/api/music/pipeline", json={"compile_job_id": cid}).json()["id"] + assert db.get_pipeline(pid)["state"] == "created" + assert db.get_pipeline(pid)["compile_job_id"] == cid + assert db.get_pipeline(pid)["track_id"] is None + + client.post(f"/api/music/pipeline/{pid}/start") + p = db.get_pipeline(pid) + assert p["state"] == "cover_pending" + + for step in ["cover", "video", "thumb", "meta"]: + r = client.post(f"/api/music/pipeline/{pid}/feedback", + json={"step": step, "intent": "approve"}) + assert r.status_code == 202 + + p = db.get_pipeline(pid) + assert p["state"] == "publish_pending" + + client.post(f"/api/music/pipeline/{pid}/publish") + p = db.get_pipeline(pid) + assert p["state"] == "published" + assert p["youtube_video_id"] == "MIX_VID"