"""엔티티 위키 페이지 검증 (수정사항 8번) + 외부 호출 필터.""" from __future__ import annotations import pytest from fastapi.testclient import TestClient from config.settings import settings from index.db import connect from wiki_out.entities import write_entity_wiki from wiki_out.merge import get_scalar, split_frontmatter from wiki_out.okf_writer import write_program_wiki from wiki_out.validate import validate PAYLOAD = { "MAIN_PROGRAM": "ZENT_A", "DESCRIPTION": "엔티티 테스트", "INCLUDE_PROGRAM": [{"INCLUDE": "ZENT_A", "SOURCE_CODE": ( "REPORT zent_a.\n" "START-OF-SELECTION.\n" " PERFORM load.\n" "FORM load.\n" " SELECT * FROM t001 INTO TABLE gt_t001.\n" " INSERT zfit_log FROM ls_log.\n" " CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT'\n" " EXPORTING input = lv_in\n" " IMPORTING output = lv_out.\n" " CALL FUNCTION 'Z_REMOTE_POST' DESTINATION 'RFCDEST'\n" " EXPORTING i_x = lv_x.\n" " CALL SCREEN 100.\n" " lv_style = cl_gui_alv_grid=>mc_style_enabled.\n" "ENDFORM." )}], } @pytest.fixture() def wiki(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, "wiki_dir", tmp_path / "wiki") monkeypatch.setattr(settings, "llm_base_url", "") c = TestClient(api.app) assert c.post("/ingest", json=PAYLOAD).json()["status"] == "loaded" con = connect() write_program_wiki("ZENT_A", con=con, wiki_dir=tmp_path / "wiki") stats = write_entity_wiki(con, tmp_path / "wiki") con.close() return tmp_path / "wiki", stats def test_table_pages_split_read_and_write(wiki): root, stats = wiki assert stats["tables"] >= 2 read_page = (root / "tables" / "T001.md").read_text(encoding="utf-8") fm, body = split_frontmatter(read_page) assert get_scalar(fm, "type") == "abap-table" assert get_scalar(fm, "x-read-programs") == "1" assert get_scalar(fm, "x-write-programs") == "0" assert "[ZENT_A](/programs/ZENT_A.md)" in body write_page = (root / "tables" / "ZFIT_LOG.md").read_text(encoding="utf-8") fm2, body2 = split_frontmatter(write_page) assert get_scalar(fm2, "x-write-programs") == "1" assert "## 쓰기 (1개 프로그램)" in body2 def test_function_pages_list_callers_and_mark_rfc(wiki): root, stats = wiki assert stats["functions"] >= 2 fm_page = (root / "functions" / "CONVERSION_EXIT_ALPHA_INPUT.md").read_text(encoding="utf-8") fm, body = split_frontmatter(fm_page) assert get_scalar(fm, "type") == "abap-function" assert get_scalar(fm, "x-caller-programs") == "1" assert "[ZENT_A](/programs/ZENT_A.md)" in body rfc_page = (root / "functions" / "Z_REMOTE_POST.md").read_text(encoding="utf-8") fm_rfc, body_rfc = split_frontmatter(rfc_page) assert "RFC" in (get_scalar(fm_rfc, "tags") or "") assert "call_function_rfc" in body_rfc def test_constants_and_screen_numbers_are_not_function_pages(wiki): """`CL_GUI_ALV_GRID=>MC_STYLE_ENABLED`(상수)와 `CALL SCREEN 100`(화면번호)은 호출이 아니다.""" root, _ = wiki names = {p.stem for p in (root / "functions").glob("*.md")} assert not any("MC_STYLE" in n for n in names), names assert "100" not in names, names def test_program_x_calls_excludes_constants_and_screens(wiki): root, _ = wiki fm, _ = split_frontmatter((root / "programs" / "ZENT_A.md").read_text(encoding="utf-8")) calls = get_scalar(fm, "x-calls") or "" assert "CONVERSION_EXIT_ALPHA_INPUT" in calls and "Z_REMOTE_POST" in calls assert "MC_STYLE_ENABLED" not in calls, calls assert '"100"' not in calls, calls def test_manifest_lists_entities_and_hierarchy(wiki): root, _ = wiki text = (root / "index.md").read_text(encoding="utf-8") assert "[tables/](/tables/)" in text and "[functions/](/functions/)" in text assert "## 계층 — 모듈 → 패키지 → 프로그램" in text assert "[ZENT_A](/programs/ZENT_A.md)" in text def test_entity_pages_pass_okf_validation(wiki): root, _ = wiki assert validate(root) == [] def test_entity_pages_are_idempotent(wiki): """재실행해도 사람 교정이 없으면 같은 내용(생성 시각 제외)이어야 한다.""" root, _ = wiki page = root / "tables" / "T001.md" before = page.read_text(encoding="utf-8") con = connect() write_entity_wiki(con, root) con.close() after = page.read_text(encoding="utf-8") strip = lambda t: "\n".join(l for l in t.splitlines() if not l.startswith("generated:")) assert strip(before) == strip(after)