116 lines
4.5 KiB
Python
116 lines
4.5 KiB
Python
"""일정 생성은 **언제** 걸리나.
|
|
|
|
★ 에디터 요청 안에서 생성하지 않는다. 두 기간 합쳐 50~100초다 —
|
|
지역 이야기가 잡으로 도는 이유와 같다(local_content_service._ensure_region_stories L444).
|
|
캔버스는 잡을 넣고, 이번 응답에는 안 실린다. **다음에 열 때** 보인다.
|
|
|
|
★ 가드가 "이야기가 다 찼나" 만 보면 안 된다. 이미 이야기가 찬 업장은 일정을 영영 못 받는다 —
|
|
story_service 가 has_stories 하나로 판단하다 daily 를 못 받던 것과 같은 함정이다.
|
|
"""
|
|
import uuid
|
|
|
|
from services import story_service
|
|
from services.llm import perplexity
|
|
from services.local_content_service import LocalContentService
|
|
|
|
|
|
class _Place:
|
|
"""`_ensure_region_stories` 와 `run_local_sync` 가 getattr 로만 읽는 최소 객체."""
|
|
|
|
def __init__(self):
|
|
self.place_id = uuid.uuid4()
|
|
self.region_code = "52군산시"
|
|
self.name = "스테이,머뭄"
|
|
self.road_address = "전북특별자치도 군산시 절골길 18"
|
|
self.address = self.road_address
|
|
|
|
|
|
class _SyncResult:
|
|
"""`sync_place_by_id` 가 돌려주는 모양(ResSyncPlace)의 최소 대역."""
|
|
|
|
class result:
|
|
success = True
|
|
|
|
msg = ""
|
|
festivals = attractions = restaurants = 0
|
|
|
|
|
|
async def test_local_sync_job_generates_itineraries(monkeypatch, db_engine):
|
|
"""검증: LOCAL_SYNC 잡이 일정 생성을 부른다.
|
|
기대결과: place_id 로 ensure_generated_by_id 를 부르고 결과를 잡 결과에 싣는다.
|
|
|
|
★ 이야기 블록은 키가 없으면(APP_ENV=test 는 .env 를 안 읽는다) 조기 반환한다 —
|
|
그래서 일정 블록이 **이야기보다 앞**에 있어야 이 테스트가 통과한다. 그게 의도다."""
|
|
called: list = []
|
|
|
|
async def fake_ensure(place_id):
|
|
called.append(place_id)
|
|
return {"place_id": str(place_id), "counts": {"1박 2일": 5, "2박 3일": 5}, "notes": []}
|
|
|
|
async def fake_sync(self, place_id):
|
|
return _SyncResult()
|
|
|
|
monkeypatch.setattr("services.itinerary_llm_service.ensure_generated_by_id", fake_ensure)
|
|
monkeypatch.setattr(LocalContentService, "sync_place_by_id", fake_sync)
|
|
|
|
pid = uuid.uuid4()
|
|
out = await story_service.run_local_sync({
|
|
"payload": {"place_id": str(pid), "region_code": "52군산시",
|
|
"region_label": "전북특별자치도 군산시"},
|
|
})
|
|
|
|
assert called == [pid]
|
|
assert out["itineraries"]["counts"] == {"1박 2일": 5, "2박 3일": 5}
|
|
|
|
|
|
async def test_canvas_enqueues_job_when_only_itineraries_are_missing(monkeypatch, db_engine):
|
|
"""검증: 이야기는 다 찼지만 일정이 없는 업장이 캔버스를 연다.
|
|
기대결과: 잡을 넣는다 — 가드가 일정 누락도 보기 때문이다.
|
|
★ 이 테스트가 없으면 "이야기가 찬 업장은 일정을 영영 못 받는" 함정이 조용히 살아난다."""
|
|
enqueued: list = []
|
|
|
|
async def fake_enqueue(place):
|
|
enqueued.append(place.place_id)
|
|
return "job-1"
|
|
|
|
async def no_missing_kinds(region_code):
|
|
return []
|
|
|
|
async def two_missing_durations(place_id):
|
|
return ["1박 2일", "2박 3일"]
|
|
|
|
monkeypatch.setattr(perplexity, "is_configured", lambda: True)
|
|
monkeypatch.setattr(story_service, "missing_kinds", no_missing_kinds)
|
|
monkeypatch.setattr(story_service, "enqueue_region_job", fake_enqueue)
|
|
monkeypatch.setattr("services.itinerary_llm_service.missing_durations", two_missing_durations)
|
|
|
|
place = _Place()
|
|
await LocalContentService()._ensure_region_stories(place)
|
|
|
|
assert enqueued == [place.place_id]
|
|
|
|
|
|
async def test_canvas_does_not_enqueue_when_everything_is_filled(monkeypatch, db_engine):
|
|
"""검증: 이야기도 일정도 다 있는 업장.
|
|
기대결과: 잡을 넣지 않는다 — 유료 호출을 다시 걸지 않는다(요금 가드)."""
|
|
enqueued: list = []
|
|
|
|
async def fake_enqueue(place):
|
|
enqueued.append(place.place_id)
|
|
return "job-1"
|
|
|
|
async def no_missing_kinds(region_code):
|
|
return []
|
|
|
|
async def no_missing_durations(place_id):
|
|
return []
|
|
|
|
monkeypatch.setattr(perplexity, "is_configured", lambda: True)
|
|
monkeypatch.setattr(story_service, "missing_kinds", no_missing_kinds)
|
|
monkeypatch.setattr(story_service, "enqueue_region_job", fake_enqueue)
|
|
monkeypatch.setattr("services.itinerary_llm_service.missing_durations", no_missing_durations)
|
|
|
|
await LocalContentService()._ensure_region_stories(_Place())
|
|
|
|
assert enqueued == []
|