Files
Orchestrator/bahn/teamlandkarte-mcp/tests/test_format_rationale_pbt.py
T
ankn a5f8fb49ab Migrate all repos into monorepo context folders
Bahn: aisupport, Analyse-O2C-C2S, awesome-bahn-mcp-servers, beam-mcp,
      Confluence_Bot, db-planet-mcp-server, O2C-Harness, project-audit,
      Projekt-KIQ-HP, teamlandkarte-mcp
Dhive: Jury-Voting
Privat: CV, NoteGraph (NOTE: NoteGraph needs complete redo after consolidation)
Shared: AI-Orchestrator, OrgMyLife, power_skills_and_more
Shared/references: symphony (read-only)

Bahn repos remain available as independent remotes - this monorepo
pulls them in via subtree, the originals are untouched.
2026-06-30 20:39:52 +02:00

113 lines
4.2 KiB
Python

# Feature: llm-fulltext-matching, Property 7: Tabellen-Rationale ist gültiges Markdown und längenbegrenzt
"""Property-based tests for ``_format_rationale_for_table``.
For any input string ``R``, the helper
:func:`teamlandkarte_mcp.mcp_server._format_rationale_for_table` must:
1. Produce output of at most 280 characters.
2. Never emit a literal pipe ``|`` (so the Markdown table stays valid).
3. Never emit ``\\n`` or ``\\r`` (whitespace is collapsed to spaces).
4. Match the canonical normalized form when the normalized form fits in
280 characters; otherwise end with the ellipsis character ``…``
(U+2026) and equal ``normalized[:279].rstrip() + "…"``.
The "normalized" form is defined as
``" ".join(R.replace("|", "/").replace("\\r", " ").replace("\\n", " ").split())``.
**Validates: Requirements 8.4, 8.5**
"""
from __future__ import annotations
from hypothesis import given, settings
from hypothesis import strategies as st
from teamlandkarte_mcp.mcp_server import _format_rationale_for_table
# Surrogate code points (Cs) are intentionally excluded; Hypothesis can
# otherwise produce lone surrogates that would break the assertions on
# string equality. Control characters (``\\n``, ``\\r``, ``\\t``...) are
# kept on purpose: the helper is supposed to handle them explicitly.
_TEXT_STRATEGY = st.text(
alphabet=st.characters(blacklist_categories=("Cs",)),
max_size=600,
)
def _normalize(text: str) -> str:
"""Replicate the helper's normalization (without truncation)."""
cleaned = text.replace("|", "/").replace("\r", " ").replace("\n", " ")
return " ".join(cleaned.split())
@settings(max_examples=100, deadline=None)
@given(text=_TEXT_STRATEGY)
def test_format_rationale_is_bounded_safe_and_canonical(text: str) -> None:
"""Property 7: bounded length, no pipes/newlines, deterministic shape."""
out = _format_rationale_for_table(text)
# 1. Length is bounded by 280.
assert len(out) <= 280, (
f"output longer than 280 chars: {len(out)} for input {text!r}"
)
# 2. Pipe characters are translated.
assert "|" not in out, f"pipe leaked into output: {out!r}"
# 3. Newlines/carriage returns are translated.
assert "\n" not in out, f"newline leaked into output: {out!r}"
assert "\r" not in out, f"carriage return leaked into output: {out!r}"
# 4. Match canonical form, with truncation behaviour.
normalized = _normalize(text)
if len(normalized) <= 280:
assert out == normalized, (
f"expected verbatim normalized form, got {out!r} "
f"(normalized={normalized!r})"
)
# Note: ``out`` may legitimately end with ``…`` if the input itself
# contained an ellipsis. The contract is that the helper does not
# *append* an ellipsis when the input fits, which is implied by
# ``out == normalized``.
else:
expected = normalized[:279].rstrip() + "\u2026"
assert out == expected, (
f"expected truncated form {expected!r}, got {out!r}"
)
assert out.endswith("\u2026"), (
"truncated output must end with the ellipsis character"
)
@settings(max_examples=100, deadline=None)
@given(
text=st.text(
alphabet=st.characters(
blacklist_categories=("Cs",),
blacklist_characters="|\r\n",
),
min_size=281,
max_size=600,
)
)
def test_format_rationale_truncates_when_normalized_too_long(text: str) -> None:
"""Property 7 (forced truncation): outputs longer than 280 are shortened.
Generates text without pipes/newlines so the normalized form preserves
most of the input length and almost certainly exceeds 280 characters
(after whitespace collapsing). When that happens, the output must end
with ``…`` and equal ``normalized[:279].rstrip() + "…"``.
"""
out = _format_rationale_for_table(text)
normalized = _normalize(text)
assert len(out) <= 280
if len(normalized) > 280:
assert out.endswith("\u2026")
assert out == normalized[:279].rstrip() + "\u2026"
else:
# Whitespace collapse may rarely shrink below 281; in that case
# the helper returns the verbatim normalized form.
assert out == normalized