From 4cc802ed9567286c95e56918ab87191210b14b7e Mon Sep 17 00:00:00 2001 From: gahusb Date: Tue, 7 Apr 2026 01:02:40 +0900 Subject: [PATCH] =?UTF-8?q?test(blog-lab):=204=EB=8B=A8=EA=B3=84=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=B4=ED=94=84=EB=9D=BC=EC=9D=B8=20=ED=86=B5?= =?UTF-8?q?=ED=95=A9=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 --- blog-lab/tests/test_pipeline_integration.py | 146 ++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 blog-lab/tests/test_pipeline_integration.py diff --git a/blog-lab/tests/test_pipeline_integration.py b/blog-lab/tests/test_pipeline_integration.py new file mode 100644 index 0000000..ab7b380 --- /dev/null +++ b/blog-lab/tests/test_pipeline_integration.py @@ -0,0 +1,146 @@ +"""4단계 파이프라인 통합 테스트.""" +import os +import pytest +from unittest.mock import patch +from fastapi.testclient import TestClient + + +@pytest.fixture(autouse=True) +def setup_db(tmp_path): + test_db = str(tmp_path / "test.db") + import app.config as config + config.DB_PATH = test_db + from app import db + db.DB_PATH = test_db + db.init_db() + yield + + +@pytest.fixture +def client(): + from app.main import app + return TestClient(app) + + +def test_full_pipeline_status_flow(client): + """draft → marketed → reviewed → published 상태 흐름.""" + from app import db + + # 1. 키워드 분석 결과 직접 삽입 + analysis = db.add_keyword_analysis({ + "keyword": "무선 이어폰", + "blog_total": 1000, + "shop_total": 500, + "competition": 45, + "opportunity": 60, + "top_products": [{"title": "에어팟", "lprice": 200000, "mallName": "애플"}], + "top_blogs": [{"title": "리뷰", "link": "https://blog.naver.com/user/123", "content": "본문"}], + }) + + # 2. 브랜드 링크 등록 + resp = client.post("/api/blog-marketing/links", json={ + "keyword_id": analysis["id"], + "url": "https://link.coupang.com/abc", + "product_name": "삼성 버즈3", + "description": "노이즈캔슬링", + }) + assert resp.status_code == 201 + + # 3. 포스트 직접 생성 (generate는 Claude API 필요) + post = db.add_post({ + "keyword_id": analysis["id"], + "title": "무선 이어폰 추천", + "body": "

초안 본문

", + "excerpt": "요약", + "tags": ["이어폰"], + "status": "draft", + }) + db.link_brand_links_to_post(keyword_id=analysis["id"], post_id=post["id"]) + + # 4. 상태 확인: draft + resp = client.get(f"/api/blog-marketing/posts/{post['id']}") + assert resp.json()["status"] == "draft" + + # 5. marketed 상태 + db.update_post(post["id"], {"status": "marketed", "body": "

마케팅된 본문

"}) + resp = client.get(f"/api/blog-marketing/posts/{post['id']}") + assert resp.json()["status"] == "marketed" + + # 6. reviewed 상태 (점수 48/60 = 통과) + db.update_post(post["id"], { + "status": "reviewed", + "review_score": 48, + "review_detail": { + "scores": {"empathy": 8, "click_appeal": 8, "conversion": 8, "seo": 8, "format": 8, "link_natural": 8}, + "total": 48, "pass": True, "feedback": "우수" + }, + }) + resp = client.get(f"/api/blog-marketing/posts/{post['id']}") + assert resp.json()["status"] == "reviewed" + assert resp.json()["review_score"] == 48 + + # 7. 발행 + resp = client.post(f"/api/blog-marketing/posts/{post['id']}/publish", json={ + "naver_url": "https://blog.naver.com/mypost/123", + }) + assert resp.json()["status"] == "published" + + +def test_links_associated_with_post(client): + """keyword_id로 등록한 링크가 post 생성 후 post_id로도 조회 가능.""" + from app import db + + analysis = db.add_keyword_analysis({"keyword": "테스트", "blog_total": 10, "shop_total": 5}) + client.post("/api/blog-marketing/links", json={ + "keyword_id": analysis["id"], + "url": "https://link.com/1", + "product_name": "상품1", + }) + + post = db.add_post({"keyword_id": analysis["id"], "title": "제목", "body": "본문", "status": "draft"}) + db.link_brand_links_to_post(keyword_id=analysis["id"], post_id=post["id"]) + + resp = client.get(f"/api/blog-marketing/links?post_id={post['id']}") + links = resp.json()["links"] + assert len(links) == 1 + assert links[0]["product_name"] == "상품1" + + +@patch("app.main.ANTHROPIC_API_KEY", "fake-key-for-test") +def test_market_endpoint_returns_404_for_missing_post(client): + """존재하지 않는 post_id로 마케터 호출 시 404.""" + resp = client.post("/api/blog-marketing/market/9999") + assert resp.status_code == 404 + + +@patch("app.main.ANTHROPIC_API_KEY", "fake-key-for-test") +def test_review_endpoint_returns_404_for_missing_post(client): + """존재하지 않는 post_id로 리뷰 호출 시 404.""" + resp = client.post("/api/blog-marketing/review/9999") + assert resp.status_code == 404 + + +def test_multiple_links_per_keyword(client): + """하나의 키워드에 복수 링크 등록 가능.""" + from app import db + analysis = db.add_keyword_analysis({"keyword": "테스트", "blog_total": 10, "shop_total": 5}) + + for i in range(3): + resp = client.post("/api/blog-marketing/links", json={ + "keyword_id": analysis["id"], + "url": f"https://link.com/{i}", + "product_name": f"상품{i}", + }) + assert resp.status_code == 201 + + resp = client.get(f"/api/blog-marketing/links?keyword_id={analysis['id']}") + assert len(resp.json()["links"]) == 3 + + +def test_dashboard_still_works(client): + """대시보드 API가 여전히 정상 작동.""" + resp = client.get("/api/blog-marketing/dashboard") + assert resp.status_code == 200 + data = resp.json() + assert "total_posts" in data + assert "published_posts" in data