Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
128 lines
4.8 KiB
Python
128 lines
4.8 KiB
Python
"""엔티티 위키 페이지 검증 (수정사항 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)
|