"""/search/logic, /chunks/{id}, /programs/{name}/chunks — 로직 조각 검색·조회 API.""" from __future__ import annotations import pytest from fastapi.testclient import TestClient from config.settings import settings import summarize.runner as runner PAYLOAD = { "MAIN_PROGRAM": "ZLOGIC_T1", "DESCRIPTION": "로직 검색 테스트", "INCLUDE_PROGRAM": [{ "INCLUDE": "ZLOGIC_T1", "SOURCE_CODE": ( "REPORT zlogic_t1.\n" "START-OF-SELECTION.\n" " PERFORM get_gr.\n" "FORM get_gr.\n" " SELECT mblnr FROM mseg INTO TABLE gt_mseg WHERE bwart = '101'.\n" " SORT gt_mseg BY mblnr.\n" "ENDFORM." ), }], } class StubLLM: usage = {"calls": 0, "prompt_tokens": 0, "completion_tokens": 0, "cost_usd": 0.0} def complete_json(self, system: str, user: str) -> dict: if "[작업] program_summary" in user: return {"program": "x", "business_purpose_ko": "구매오더 입고 자재문서 조회", "confidence": 0.8} if "name: GET_GR" in user: return {"unit_purpose_ko": "입고 자재문서 조회", "chunks": [{ "line_start": 5, "line_end": 6, "first_line": "SELECT mblnr FROM mseg INTO TABLE gt_mseg WHERE bwart = '101'.", "kind": "sql_select", "purpose_ko": "이동유형 101(입고) 자재문서를 MSEG 에서 조회", "purpose_en": "Read goods receipt material documents (movement type 101) from MSEG", "keywords_ko": ["입고", "자재문서", "이동유형 101"], "keywords_en": ["goods receipt", "GR"], "sap_objects": ["MSEG", "BWART"], "confidence": 0.9}]} return {"unit_purpose_ko": "요약", "chunks": []} @pytest.fixture() def client(tmp_path, monkeypatch): import query.api as api monkeypatch.setattr(settings, "database_url", f"sqlite:///{tmp_path / 'index.db'}") monkeypatch.setattr(settings, "data_normalized", tmp_path / "normalized") monkeypatch.setattr(settings, "data_parsed", tmp_path / "parsed") monkeypatch.setattr(settings, "llm_base_url", "") monkeypatch.setattr(runner, "create_llm", lambda fake=False, **kw: StubLLM()) c = TestClient(api.app) assert c.post("/ingest", json=PAYLOAD).json()["status"] == "loaded" runner.summarize_units("ZLOGIC_T1", None, fake=False, dry_run=False, trigger="test") return c def test_search_logic_groups_by_program(client): r = client.get("/search/logic", params={"q": "입고 처리하는 로직"}).json() assert r["total"] >= 1 g = r["programs"][0] assert g["program"] == "ZLOGIC_T1" assert g["purpose"].startswith("구매오더 입고") c = g["chunks"][0] assert c["unit"] == "GET_GR" and c["kind"] == "sql_select" assert (c["line_start"], c["line_end"]) == (5, 6) assert c["tables_read"] == ["MSEG"] # 영어 · 객체명으로도 맞는다 assert client.get("/search/logic", params={"q": "goods receipt"}).json()["total"] >= 1 assert client.get("/search/logic", params={"q": "MSEG"}).json()["total"] >= 1 assert client.get("/search/logic", params={"q": "MSEG", "kind": "db_write"}).json()["total"] == 0 def test_chunk_code_and_program_chunks(client): from urllib.parse import quote chunk_id = client.get("/search/logic", params={"q": "입고"}).json()["programs"][0]["chunks"][0]["chunk_id"] r = client.get(f"/chunks/{quote(chunk_id, safe='')}").json() # chunk_id 의 '#' 은 URL 인코딩 필요 assert r["code"].startswith(" SELECT mblnr FROM mseg") assert r["unit_lines"] == "4-7" assert client.get("/chunks/NOPE#C9").status_code == 404 lst = client.get("/programs/ZLOGIC_T1/chunks").json()["chunks"] assert len(lst) == 1 and lst[0]["chunk_id"] == chunk_id s = client.get("/programs/ZLOGIC_T1/summary").json() assert s["structure"]["logic_chunks"][0]["chunk_id"] == chunk_id assert s["summary"]["business_purpose_ko"].startswith("구매오더") u = client.get("/programs/ZLOGIC_T1/units/GET_GR/code").json() assert u["chunks"][0]["chunk_id"] == chunk_id assert client.get("/health").json()["logic_chunks"] == 1 st = client.get("/summaries/status").json() assert st["programs"][0]["chunks"] == 1 and st["programs"][0]["program_summary"] == "done"