"""Hier-Replace-Diagnose-1 c5 — corpus_variant dispatcher 단위 테스트. 가드: 1. _resolve_corpus_variant — slug→view, unknown ValueError, None→None 2. CORPUS_VARIANT_MAP — 3 slug 1:1 3. _VALID_CHUNKS_TABLE — 측정 뷰 3종 허용 + junk/injection 거부 """ from __future__ import annotations import logging import os import sys import pytest # logs/llm_gate.log root 소유 → import 시 PermissionError safe-wrap (test_query_rewriter 패턴) _orig = logging.FileHandler logging.FileHandler = lambda f, *a, **k: (_orig(f, *a, **k) if _try(f) else logging.NullHandler()) # type: ignore def _try(f): try: open(f, "a").close() return True except Exception: return False os.environ.setdefault("DATABASE_URL", "postgresql+asyncpg://test:test@localhost:5432/test") sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "app")) from services.search import retrieval_service as rs def test_resolve_valid_slugs(): assert rs._resolve_corpus_variant("prehier") == "corpus_chunks_prehier" assert rs._resolve_corpus_variant("hier_sim_raw") == "corpus_chunks_hier_sim_raw" assert rs._resolve_corpus_variant("hier_sim_clean") == "corpus_chunks_hier_sim_clean" def test_resolve_none(): assert rs._resolve_corpus_variant(None) is None @pytest.mark.parametrize("bad", ["", "hier", "corpus_chunks", "prehier; DROP TABLE", "hier_sim", "HIER_SIM_CLEAN"]) def test_resolve_unknown_raises(bad): with pytest.raises(ValueError, match="unknown_corpus_variant"): rs._resolve_corpus_variant(bad) def test_variant_map_keys(): assert set(rs.CORPUS_VARIANT_MAP) == {"prehier", "hier_sim_raw", "hier_sim_clean"} @pytest.mark.parametrize("view", [ "corpus_chunks_prehier", "corpus_chunks_hier_sim_raw", "corpus_chunks_hier_sim_clean", "document_chunks", "corpus_chunks", "document_chunks_cand_me5", ]) def test_valid_chunks_table_allows(view): assert rs._VALID_CHUNKS_TABLE.match(view) @pytest.mark.parametrize("bad", [ "corpus_chunks_prehier; DROP TABLE x", "corpus_chunks_hier_sim", "documents", "corpus_chunks_evil", "'; DELETE--", "corpus_chunks_hier_sim_cleanX", ]) def test_valid_chunks_table_rejects(bad): assert not rs._VALID_CHUNKS_TABLE.match(bad) def test_all_mapped_views_pass_allowlist(): # resolver 가 내놓는 모든 뷰는 SQL interpolation gate 통과해야 함 for v in rs.CORPUS_VARIANT_MAP.values(): assert rs._VALID_CHUNKS_TABLE.match(v)