Publish documentation / publish (push) Successful in 1m27s
Add PostgreSQL-backed memory, editable evidence with source review and activation, and human-approved archive repairs across the harness, API, and UI. Include migrations, deployment support, regression coverage, and validation documentation. Refresh permissions from validated session roles so existing administrator logins can access newly deployed archive management features.
131 lines
4.2 KiB
Python
131 lines
4.2 KiB
Python
import pytest
|
|
|
|
from tht.evidence.canonical import (
|
|
CuratedEvidence,
|
|
ManualEvidenceProvenance,
|
|
dump_curated_markdown,
|
|
parse_curated_markdown,
|
|
)
|
|
|
|
|
|
def unit(kind="domain", payload=None):
|
|
return CuratedEvidence.model_validate(
|
|
{
|
|
"schema_version": 4,
|
|
"id": "evidence:example",
|
|
"title": "Example",
|
|
"kind": kind,
|
|
"purposes": ["sql_generation"],
|
|
"language": "en",
|
|
"provenance": {"kind": "manual", "declared_by": "curator"},
|
|
"payload": payload or {"rule": "Count orders once, using their complete key."},
|
|
}
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("kind", "payload"),
|
|
[
|
|
("domain", {"rule": "A rule.\n\n### Explanation\n\n- First line\n- Second line"}),
|
|
(
|
|
"glossary",
|
|
{
|
|
"definition": "A patient.",
|
|
"synonyms": ["person", "a\nmultiline synonym"],
|
|
"variants": [],
|
|
},
|
|
),
|
|
(
|
|
"enum",
|
|
{
|
|
"column": "sales.orders.status",
|
|
"values": {"": "Unknown", "A": "Active\n\nwith details"},
|
|
},
|
|
),
|
|
("example", {"question": "How many?", "interpretation": "Count distinct orders."}),
|
|
(
|
|
"mapping",
|
|
{"concept": "Orders", "tables": ["sales.orders"], "columns": ["sales.orders.id"]},
|
|
),
|
|
("normalization", {"input": "A", "output": "active", "rule": "Expand the abbreviation."}),
|
|
("formula", {"concept": "Total", "columns": ["sales.lines.amount"], "sql": "sum(amount)"}),
|
|
(
|
|
"reference",
|
|
{"url": "https://example.test/rule", "label": "Policy", "description": "Rule source."},
|
|
),
|
|
],
|
|
)
|
|
def test_editable_round_trip_preserves_every_typed_payload(kind, payload):
|
|
original = unit(kind, payload)
|
|
rendered = dump_curated_markdown(original)
|
|
assert parse_curated_markdown(rendered) == original
|
|
assert "<!-- tht:" not in rendered
|
|
assert "payload:" not in rendered
|
|
|
|
|
|
def test_visible_edit_is_the_only_authoritative_text():
|
|
original = unit()
|
|
edited = dump_curated_markdown(original).replace(
|
|
"Count orders once, using their complete key.", "Use order ID and year."
|
|
)
|
|
parsed = parse_curated_markdown(edited)
|
|
assert parsed.payload.rule == "Use order ID and year."
|
|
assert parsed.id == original.id
|
|
|
|
|
|
def test_manual_file_needs_no_source_document_hash_or_encoded_metadata():
|
|
text = """---
|
|
schema_version: 4
|
|
id: evidence:manual
|
|
kind: domain
|
|
language: it
|
|
purposes: [sql_generation]
|
|
applies_to:
|
|
tables: [sales.orders]
|
|
---
|
|
|
|
# Una regola manuale
|
|
|
|
## Regola
|
|
|
|
Le righe vengono collegate tramite numero ordine ed esercizio.
|
|
"""
|
|
parsed = parse_curated_markdown(text)
|
|
assert isinstance(parsed.provenance, ManualEvidenceProvenance)
|
|
assert parsed.provenance.original is None
|
|
assert parsed.payload.rule.startswith("Le righe")
|
|
|
|
|
|
def test_code_fences_cannot_inject_structural_sections():
|
|
original = unit(
|
|
"normalization",
|
|
{
|
|
"input": "A",
|
|
"output": "active",
|
|
"rule": "Example:\n\n```markdown\n## Input\nDo not parse this heading.\n```",
|
|
},
|
|
)
|
|
assert parse_curated_markdown(dump_curated_markdown(original)) == original
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"mutation",
|
|
[
|
|
lambda text: text.replace("## Rule", "## Missing"),
|
|
lambda text: text + "\n## Rule\n\nAnother rule.\n",
|
|
lambda text: text.replace("kind: domain", "kind: domain\npayload: {rule: hidden}"),
|
|
lambda text: text.replace("kind: domain", "kind: glossary\nkind: domain"),
|
|
lambda text: text.replace("kind: domain", "kind: ["),
|
|
lambda text: text.replace("- sql_generation", ""),
|
|
lambda text: text.replace("language: en", "language: ''"),
|
|
],
|
|
)
|
|
def test_invalid_edit_is_rejected_with_a_correctable_error(mutation):
|
|
with pytest.raises(ValueError):
|
|
parse_curated_markdown(mutation(dump_curated_markdown(unit())))
|
|
|
|
|
|
def test_unrepresentable_legacy_content_is_reported_instead_of_silently_trimmed():
|
|
with pytest.raises(ValueError, match="losslessly"):
|
|
dump_curated_markdown(unit(payload={"rule": " significant indentation"}))
|