core/tests/test_construction_affordances.py

185 lines
6.1 KiB
Python

from __future__ import annotations
import pytest
from generate.construction_affordances import (
ConstructionProposal,
all_diagnostic_families,
lookup_by_organ,
lookup_by_relation_type,
lookup_family,
make_proposal,
propose_construction,
)
from generate.kernel_facts import SourceSpan
def test_catalog_entries_are_diagnostic_only_and_serving_forbidden() -> None:
families = all_diagnostic_families()
assert len(families) == 4
for family in families:
assert family.diagnostic_only is True
assert family.serving_allowed is False
def test_catalog_ordering_is_deterministic() -> None:
families = all_diagnostic_families()
ids = [f.family_id for f in families]
assert ids == sorted(ids)
assert ids == [
"binding.quantity_entity",
"partition.percent_partition",
"proportional_change.decrease_to_fraction",
"state_change.unary_delta",
]
def test_lookups_correctness() -> None:
quantity_entity = lookup_family("binding.quantity_entity")
assert quantity_entity is not None
assert lookup_by_organ("quantity_entity_binding") is quantity_entity
assert lookup_by_relation_type("quantity_entity") is quantity_entity
fraction_family = lookup_family("proportional_change.decrease_to_fraction")
assert fraction_family is not None
assert lookup_by_organ("fraction_decrease") is fraction_family
assert lookup_by_relation_type("decrease_to_fraction") is fraction_family
partition_family = lookup_family("partition.percent_partition")
assert partition_family is not None
assert lookup_by_organ("percent_partition") is partition_family
assert lookup_by_relation_type("percent_of") is partition_family
unary_delta = lookup_family("state_change.unary_delta")
assert unary_delta is not None
assert lookup_by_organ("unary_delta_transition") is unary_delta
assert lookup_by_relation_type("unary_delta") is unary_delta
assert lookup_family("invalid_family_id") is None
assert lookup_by_organ("invalid_organ") is None
assert lookup_by_relation_type("invalid_relation") is None
@pytest.mark.parametrize(
("family_id", "relation_type", "candidate_organ", "required_roles"),
(
(
"binding.quantity_entity",
"quantity_entity",
"quantity_entity_binding",
{
"quantity",
"entity",
"quantity_kind",
"provenance_span",
"local_binding_relation",
},
),
(
"proportional_change.decrease_to_fraction",
"decrease_to_fraction",
"fraction_decrease",
{"base_quantity", "scale", "state_entity", "transition"},
),
(
"partition.percent_partition",
"percent_of",
"percent_partition",
{"whole", "part", "scale"},
),
(
"state_change.unary_delta",
"unary_delta",
"unary_delta_transition",
{"action_cue", "delta_quantity", "changed_object", "direction"},
),
),
)
def test_propose_construction_is_preassessment_and_catalog_backed(
family_id: str,
relation_type: str,
candidate_organ: str,
required_roles: set[str],
) -> None:
span = SourceSpan("surface cue", 0, 11)
proposal = propose_construction(family_id, (span,))
assert proposal.family_id == family_id
assert proposal.relation_type == relation_type
assert proposal.candidate_organ == candidate_organ
assert proposal.evidence_spans == (span,)
assert proposal.status == "proposed"
assert proposal.missing_roles == ()
assert proposal.active_hazards == ()
assert proposal.diagnostic_only is True
assert proposal.serving_allowed is False
assert {role.role for role in proposal.role_obligations if role.required} == required_roles
@pytest.mark.parametrize(
"family_id",
(
"binding.quantity_entity",
"proportional_change.decrease_to_fraction",
"partition.percent_partition",
"state_change.unary_delta",
),
)
def test_make_proposal_rejects_migrated_proposal_first_families(
family_id: str,
) -> None:
span = SourceSpan("surface cue", 0, 11)
with pytest.raises(
ValueError,
match=rf"{family_id} is proposal-first; use propose_construction before assessment",
):
make_proposal(
family_id=family_id,
evidence_spans=(span,),
assessment_runnable=True,
missing_roles=(),
active_hazards=(),
)
def test_make_proposal_still_rejects_unknown_family_ids() -> None:
span = SourceSpan("surface cue", 0, 11)
with pytest.raises(KeyError):
propose_construction("invalid_family_id", (span,))
def test_construction_proposal_cannot_carry_assessment_authority() -> None:
span = SourceSpan("test text", 0, 9)
ConstructionProposal(
family_id="proportional_change.decrease_to_fraction",
relation_type="decrease_to_fraction",
candidate_organ="fraction_decrease",
evidence_spans=(span,),
status="proposed",
)
for status in ("partial", "closed", "refused"):
with pytest.raises(ValueError, match="must remain 'proposed'"):
ConstructionProposal(
family_id="proportional_change.decrease_to_fraction",
relation_type="decrease_to_fraction",
candidate_organ="fraction_decrease",
evidence_spans=(span,),
status=status, # type: ignore[arg-type]
)
assert "missing_roles" not in ConstructionProposal.__dataclass_fields__
assert "active_hazards" not in ConstructionProposal.__dataclass_fields__
with pytest.raises(TypeError, match="unexpected keyword argument 'missing_roles'"):
ConstructionProposal(
family_id="proportional_change.decrease_to_fraction",
relation_type="decrease_to_fraction",
candidate_organ="fraction_decrease",
evidence_spans=(span,),
status="proposed",
missing_roles=("base_quantity_unbound",),
)