MAS-WP-0005-T09: generator destination transforms with round-trip refusal; example profiles
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 252470@bnt-lap001 Assistant-Session: ad628c8f-4be2-4979-94c9-76c7ed6f8241
This commit is contained in:
parent
eb10f54fd2
commit
c3e2f05ee5
5 changed files with 200 additions and 5 deletions
78
tests/test_destination.py
Normal file
78
tests/test_destination.py
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
"""v0.2 destination transforms (spec v0.2 section 12.1)."""
|
||||
import pytest
|
||||
|
||||
from mas_conformance import statute_document
|
||||
from mas_core.generator import DESTINATION_PROFILES, CannotExpress, for_destination
|
||||
from mas_core.statute import build_statute
|
||||
|
||||
|
||||
def statute(resource=True, interaction=True, id_case="insensitive", version="0.2"):
|
||||
doc = statute_document("valid-great-service.json")
|
||||
doc["mas_version"] = version
|
||||
if version == "0.2":
|
||||
doc["alternate_syntax"] = {"resource": resource, "interaction": interaction}
|
||||
doc["equivalence"]["resource_id_case"] = id_case
|
||||
return build_statute(doc, "great-service.example")
|
||||
|
||||
|
||||
V2 = statute()
|
||||
IONOS = DESTINATION_PROFILES["ionos-forward"]
|
||||
|
||||
|
||||
def test_canonical_when_nothing_is_needed():
|
||||
r = for_destination("ticket=GS-1+comments", V2, ())
|
||||
assert (r.local_part, r.applied) == ("ticket=GS-1+comments", ())
|
||||
|
||||
|
||||
def test_ionos_forward_profile():
|
||||
# the MAS-WP-0003 case: IONOS refuses '=' and forces lower case
|
||||
r = for_destination("ticket=GS-85QJ8WC09P+comments", V2, IONOS)
|
||||
assert r.local_part == "ticket--gs-85qj8wc09p+comments"
|
||||
assert r.applied == ("resource-alternate", "lowercase")
|
||||
|
||||
|
||||
def test_transforms_apply_only_where_they_change_something():
|
||||
assert for_destination("support", V2, IONOS).applied == ()
|
||||
assert for_destination("ticket=gs-1", V2, IONOS).applied == ("resource-alternate",)
|
||||
|
||||
|
||||
def test_interaction_alternate():
|
||||
assert for_destination("ticket!callback", V2, ("interaction-alternate",)).local_part == "ticket---callback"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("st, code", [
|
||||
(statute(resource=False), "transform-not-permitted"), # no '--' switch
|
||||
(statute(version="0.1"), "transform-not-permitted"), # v0.1 statute
|
||||
(statute(id_case="exact"), "would-be-misread"), # lower-casing changes the id
|
||||
])
|
||||
def test_refuses_what_the_namespace_would_misread(st, code):
|
||||
with pytest.raises(CannotExpress) as e:
|
||||
for_destination("ticket=GS-1", st, IONOS)
|
||||
assert e.value.code == code
|
||||
|
||||
|
||||
def test_refuses_id_containing_equals():
|
||||
with pytest.raises(CannotExpress) as e:
|
||||
for_destination("ticket=RE=2026-4711", V2, ("resource-alternate",))
|
||||
assert e.value.code == "cannot-express"
|
||||
|
||||
|
||||
def test_refuses_case_change_in_facet():
|
||||
with pytest.raises(CannotExpress) as e:
|
||||
for_destination("ticket=gs-1+Comments", V2, ("lowercase",))
|
||||
assert e.value.code == "would-be-misread"
|
||||
|
||||
|
||||
def test_length_is_checked_after_transforms():
|
||||
lp = "ticket=" + "a" * 57 # 64 octets canonical, 65 with '--'
|
||||
assert len(lp) == 64
|
||||
with pytest.raises(CannotExpress) as e:
|
||||
for_destination(lp, V2, ("resource-alternate",))
|
||||
assert e.value.code == "too-long"
|
||||
|
||||
|
||||
def test_lossy_transforms_need_explicit_permission():
|
||||
with pytest.raises(CannotExpress):
|
||||
for_destination("ticket=gs-1+comments", V2, ("drop-facet",))
|
||||
r = for_destination("ticket!callback", V2, ("drop-interaction",), allow_lossy=True)
|
||||
assert r.local_part == "ticket" and r.warnings
|
||||
Loading…
Add table
Add a link
Reference in a new issue