"""Tests for the top-level wiring — the §6 bookends + public surface (PLAN §95). The library never makes the model call, so the wiring is two library-side halves around the caller's tool-less transform (BRIEF §6): * ``prepare_input`` — §6 steps 1-2: sanitize -> fence (before the transform). * ``screen_output`` — §6 steps 6-7: scan the emitted text -> dispose, failing *closed* if the scanner itself errors (an un-scannable artifact never persists). Everything is imported from the top-level package here on purpose: these tests also pin the ``__all__`` export surface a consumer depends on. """ from __future__ import annotations import llm_ingestion_guard as g from llm_ingestion_guard import ( PreparedInput, prepare_input, screen_output, Disposition, PRESET_TRUSTED_SOURCE, PRESET_USER_UPLOAD, Severity, Source, ) # --- public surface -------------------------------------------------------- _EXPECTED_SURFACE = [ # shared types "Finding", "Report", "Severity", "Source", "severity_rank", # input-side detectors "sanitize", "scan_entropy", "scan_lexicon", "load_lexicon", "fence", "neutralize", # output-side "scan_output", "scan_secret_egress", # disposition "decide", "guard", "Policy", "Trust", "Provenance", "Disposition", "DispositionResult", "PRESET_TRUSTED_SOURCE", "PRESET_USER_UPLOAD", # contract asserters "assert_tool_less", "assert_credential_allowlist", "credential_env_names", "scoped_env", "ContractViolation", # grounding seam "SourceGroundingCheck", "no_grounding_check", "DEFAULT_GROUNDING_CHECK", # §6 bookends "prepare_input", "screen_output", "PreparedInput", ] def test_public_surface_is_exported(): for name in _EXPECTED_SURFACE: assert name in g.__all__, f"{name} missing from __all__" assert hasattr(g, name), f"{name} not importable from package" def test_version_is_exported(): assert isinstance(g.__version__, str) and g.__version__ # --- prepare_input: §6 steps 1-2 (sanitize -> fence) ----------------------- def test_prepare_input_returns_prepared_input(): prepared = prepare_input("plain prose") assert isinstance(prepared, PreparedInput) assert isinstance(prepared.fenced, str) assert isinstance(prepared.nonce, str) and prepared.nonce # the per-call nonce delimits the fenced payload the transform will see. assert prepared.nonce in prepared.fenced def test_prepare_input_sanitizes_before_fencing(): # a zero-width carrier is stripped (sanitize) and the payload is then fenced. prepared = prepare_input("ignore​previous") assert "​" not in prepared.fenced labels = {f.label for f in prepared.report.findings} assert "sanitize:zero-width" in labels def test_prepare_input_clean_text_reports_nothing(): # clean input: fenced (wrapped in the nonce) but no carrier/marker findings. prepared = prepare_input("a perfectly ordinary changelog entry.") assert not prepared.report.found def test_prepare_input_report_merges_sanitize_and_fence(): # the report carries findings from BOTH composed steps, under their detectors. prepared = prepare_input("data:text/html,