import json import socket from pathlib import Path import pytest from mas_conformance import address_fixtures, property_fixtures from mas_conformance.runner import check_address_fixture, run_parser_corpus from mas_core.generator import generate_local_part, resource_local_part from mas_core.model import RequiredProfile from mas_core.parser import ( MasSyntaxError, parse_address, parse_local_part, validate_profile, ) ROOT = Path(__file__).resolve().parents[1] FIXTURES = address_fixtures() + property_fixtures() def test_vendored_corpus_matches_spec_bundle(): spec = json.loads((ROOT / "mas-v0.1-spec/conformance/addresses.json").read_text()) assert spec == address_fixtures() def test_vendored_schema_matches_spec_bundle(): a = (ROOT / "mas-v0.1-spec/schema/mas-statute-v0.1.schema.json").read_bytes() b = (ROOT / "mas_core/schema/mas-statute-v0.1.schema.json").read_bytes() assert a == b @pytest.mark.parametrize("fixture", FIXTURES, ids=[f["id"] for f in FIXTURES]) def test_fixture(fixture): assert check_address_fixture(fixture) is None def test_whole_corpus_passes(): assert run_parser_corpus().ok @pytest.fixture def no_network(monkeypatch): def deny(*a, **k): raise AssertionError("parser attempted network I/O") monkeypatch.setattr(socket, "socket", deny) monkeypatch.setattr(socket, "create_connection", deny) monkeypatch.setattr(socket, "getaddrinfo", deny) def test_parser_performs_no_io(no_network, monkeypatch): monkeypatch.setattr("builtins.open", lambda *a, **k: (_ for _ in ()).throw(AssertionError("file I/O"))) for f in FIXTURES: try: parse_local_part(f["local_part"]) except MasSyntaxError: pass parse_address("anna.mueller@acme.coulomb.social") def test_maximal_parser_reports_required_profile(): p = parse_local_part("ticket!callback") assert p.required_profile is RequiredProfile.P2 assert not validate_profile(p, "p1") assert validate_profile(p, "P2") assert parse_local_part("ticket=PR42+comments").required_profile is RequiredProfile.P1 def test_combined_form_model(): p = parse_local_part("ticket=PR42+comments!callback") assert p.target.resource_type == "ticket" and p.target.resource_id == "PR42" assert p.facet.value == "comments" and p.interaction.mode == "callback" assert p.required_profile is RequiredProfile.P2 assert not p.generator_allowed def test_no_kind_inference_from_punctuation(): for lp in ("bernd.worsch", "research-companion", "invoice_ingest"): d = parse_local_part(lp).to_dict() assert d["target_kind"] == "opaque" assert not {"kind", "person", "actor", "function"} & set(d) def test_facet_loss_degrades_to_base(): p = parse_local_part("ticket=GS-41872+comments") assert p.base == "ticket=GS-41872" stripped = p.without_facet() assert stripped.raw == "ticket=GS-41872" and stripped.target == p.target def test_parse_address_domain(): a = parse_address("ticket=GS-41872@Great-Service.Example") assert a.domain == "great-service.example" with pytest.raises(MasSyntaxError): parse_address("no-domain") def test_generator_rules(): assert generate_local_part("ticket", interaction="callback") == "ticket!callback" assert resource_local_part("ticket", "GS-41872") == "ticket=GS-41872" with pytest.raises(MasSyntaxError): generate_local_part("ticket=PR42", facet="comments", interaction="callback") with pytest.raises(MasSyntaxError): generate_local_part("ticket", facet="a+b") with pytest.raises(MasSyntaxError): generate_local_part("ticket", facet="") with pytest.raises(MasSyntaxError): resource_local_part("ticket", "x" * 60) for f in FIXTURES: if f["valid"] and f.get("generator_allowed") is False: p = parse_local_part(f["local_part"]) with pytest.raises(MasSyntaxError): generate_local_part(str(p.target), facet=p.facet.value, interaction=p.interaction.mode)