import pytest from unittest.mock import patch, AsyncMock from fastapi.testclient import TestClient from app.main import app from app import db as db_module @pytest.fixture(autouse=True) def fresh_db(monkeypatch, tmp_path): db_file = tmp_path / "test_saju.db" monkeypatch.setattr(db_module, "DB_PATH", str(db_file)) db_module.init_db() yield try: if db_file.exists(): db_file.unlink() except PermissionError: pass def _interpret_result(interp_json=None): if interp_json is None: interp_json = {"items": [], "summary": "...", "advice": "...", "warning": None, "confidence": "medium"} return { "interpretation_json": interp_json, "model": "claude-sonnet-4-6", "tokens_in": 200, "tokens_out": 400, "cost_usd": 0.01, "latency_ms": 1200, "reroll_count": 0, } def test_health(): with TestClient(app) as c: r = c.get("/health") assert r.status_code == 200 assert r.json() == {"ok": True} def test_saju_interpret_endpoint(monkeypatch): """saju interpret이 pipeline mock으로 동작.""" async def fake_interpret(*args, **kwargs): return _interpret_result() # interpret_saju를 mock from app.routers import saju as saju_router monkeypatch.setattr(saju_router.pipeline, "interpret_saju", fake_interpret) with TestClient(app) as c: r = c.post("/api/saju/interpret", json={ "year": 1990, "month": 5, "day": 15, "hour": 14, "gender": "male", "calendar_type": "solar" }) assert r.status_code == 200, r.text data = r.json() assert "saju" in data assert "analysis" in data assert "daeun" in data assert "reading_id" in data assert data["reading_id"] > 0 def test_saju_list_get_cycle(monkeypatch): async def fake_interpret(*args, **kwargs): return _interpret_result() from app.routers import saju as saju_router monkeypatch.setattr(saju_router.pipeline, "interpret_saju", fake_interpret) with TestClient(app) as c: # save rid = c.post("/api/saju/interpret", json={ "year": 1990, "month": 5, "day": 15, "hour": 14, "gender": "male", "calendar_type": "solar" }).json()["reading_id"] # list assert c.get("/api/saju/readings").json()["total"] == 1 # get r = c.get(f"/api/saju/readings/{rid}") assert r.status_code == 200 assert r.json()["birth_year"] == 1990 def test_saju_patch_and_delete(monkeypatch): async def fake_interpret(*args, **kwargs): return _interpret_result() from app.routers import saju as saju_router monkeypatch.setattr(saju_router.pipeline, "interpret_saju", fake_interpret) with TestClient(app) as c: rid = c.post("/api/saju/interpret", json={ "year": 1990, "month": 5, "day": 15, "hour": 14, "gender": "male", "calendar_type": "solar" }).json()["reading_id"] r = c.patch(f"/api/saju/readings/{rid}", json={"favorite": True, "memo": "메모"}) assert r.status_code == 200 row = c.get(f"/api/saju/readings/{rid}").json() assert row["favorite"] == 1 assert row["memo"] == "메모" r = c.delete(f"/api/saju/readings/{rid}") assert r.status_code == 200 assert c.get(f"/api/saju/readings/{rid}").status_code == 404 def test_saju_get_404(): with TestClient(app) as c: assert c.get("/api/saju/readings/9999").status_code == 404 def test_saju_current_fortune(monkeypatch): async def fake_interpret(*args, **kwargs): return _interpret_result() from app.routers import saju as saju_router monkeypatch.setattr(saju_router.pipeline, "interpret_saju", fake_interpret) with TestClient(app) as c: rid = c.post("/api/saju/interpret", json={ "year": 1990, "month": 5, "day": 15, "hour": 14, "gender": "male", "calendar_type": "solar" }).json()["reading_id"] r = c.get(f"/api/saju/current-fortune?reading_id={rid}") assert r.status_code == 200 data = r.json() assert "seun" in data assert data["seun"]["stem"] def test_compat_interpret(monkeypatch): async def fake_compat(*args, **kwargs): return { "interpretation_json": {"summary": "...", "strengths": [ {"title": "A", "explanation": "B", "evidence": "C"}, {"title": "A2", "explanation": "B2", "evidence": "C2"}, ], "challenges": [ {"title": "D", "explanation": "E", "evidence": "F"}, {"title": "D2", "explanation": "E2", "evidence": "F2"}, ], "advice": "...", "warning": None, "confidence": "high"}, "model": "claude-sonnet-4-6", "tokens_in": 300, "tokens_out": 500, "cost_usd": 0.015, "latency_ms": 1500, "reroll_count": 0, } from app.routers import compat as compat_router monkeypatch.setattr(compat_router.pipeline, "interpret_compat", fake_compat) with TestClient(app) as c: r = c.post("/api/saju/compat/interpret", json={ "person_a": {"year": 1990, "month": 5, "day": 15, "hour": 14, "gender": "male", "calendar_type": "solar"}, "person_b": {"year": 1992, "month": 8, "day": 8, "hour": 18, "gender": "female", "calendar_type": "solar"}, }) assert r.status_code == 200, r.text data = r.json() assert "score" in data assert "saju_a" in data assert "saju_b" in data def test_compat_list_get_cycle(monkeypatch): async def fake_compat(*args, **kwargs): return { "interpretation_json": {"summary": "...", "strengths": [ {"title": "a", "explanation": "b", "evidence": "c"}, {"title": "a", "explanation": "b", "evidence": "c"}, ], "challenges": [ {"title": "d", "explanation": "e", "evidence": "f"}, {"title": "d", "explanation": "e", "evidence": "f"}, ], "advice": "", "warning": None, "confidence": "medium"}, "model": "x", "tokens_in": 0, "tokens_out": 0, "cost_usd": 0.0, "latency_ms": 0, "reroll_count": 0, } from app.routers import compat as compat_router monkeypatch.setattr(compat_router.pipeline, "interpret_compat", fake_compat) with TestClient(app) as c: rid = c.post("/api/saju/compat/interpret", json={ "person_a": {"year": 1990, "month": 5, "day": 15, "hour": 14, "gender": "male", "calendar_type": "solar"}, "person_b": {"year": 1992, "month": 8, "day": 8, "hour": 18, "gender": "female", "calendar_type": "solar"}, }).json()["reading_id"] assert c.get("/api/saju/compat/readings").json()["total"] == 1 r = c.get(f"/api/saju/compat/readings/{rid}") assert r.status_code == 200 assert r.json()["score"] >= 0 def test_compat_patch_and_delete(monkeypatch): async def fake_compat(*args, **kwargs): return { "interpretation_json": {"summary": "...", "strengths": [ {"title": "a", "explanation": "b", "evidence": "c"}, {"title": "a", "explanation": "b", "evidence": "c"}, ], "challenges": [ {"title": "d", "explanation": "e", "evidence": "f"}, {"title": "d", "explanation": "e", "evidence": "f"}, ], "advice": "", "warning": None, "confidence": "medium"}, "model": "x", "tokens_in": 0, "tokens_out": 0, "cost_usd": 0.0, "latency_ms": 0, "reroll_count": 0, } from app.routers import compat as compat_router monkeypatch.setattr(compat_router.pipeline, "interpret_compat", fake_compat) with TestClient(app) as c: rid = c.post("/api/saju/compat/interpret", json={ "person_a": {"year": 1990, "month": 5, "day": 15, "hour": 14, "gender": "male", "calendar_type": "solar"}, "person_b": {"year": 1992, "month": 8, "day": 8, "hour": 18, "gender": "female", "calendar_type": "solar"}, }).json()["reading_id"] r = c.patch(f"/api/saju/compat/readings/{rid}", json={"favorite": True, "memo": "좋은 궁합"}) assert r.status_code == 200 row = c.get(f"/api/saju/compat/readings/{rid}").json() assert row["favorite"] == 1 r = c.delete(f"/api/saju/compat/readings/{rid}") assert r.status_code == 200 assert c.get(f"/api/saju/compat/readings/{rid}").status_code == 404 def test_compat_get_404(): with TestClient(app) as c: assert c.get("/api/saju/compat/readings/9999").status_code == 404