173 lines
5.5 KiB
Python
173 lines
5.5 KiB
Python
from __future__ import annotations
|
|
|
|
from decimal import Decimal
|
|
|
|
import pytest
|
|
|
|
from generate.comprehension.state import (
|
|
ComprehensionState,
|
|
ComprehensionStateError,
|
|
EntityRef,
|
|
ExpectationFrame,
|
|
PartialOp,
|
|
QuantityRef,
|
|
QuestionTargetSlot,
|
|
)
|
|
|
|
|
|
def test_canonical_bytes_are_deterministic_for_equal_states() -> None:
|
|
first = ComprehensionState(
|
|
entities=(
|
|
EntityRef(
|
|
canonical_name="tina",
|
|
gender="female",
|
|
first_mention_position=0,
|
|
),
|
|
),
|
|
quantities=(
|
|
QuantityRef(
|
|
value=Decimal("12.00"),
|
|
unit="dollars",
|
|
unit_class="currency",
|
|
owner_entity="tina",
|
|
mention_position=1,
|
|
),
|
|
),
|
|
operations=(
|
|
PartialOp(
|
|
operator_kind="accumulation_verb",
|
|
subject_entity="tina",
|
|
object_entity=None,
|
|
quantity_index=0,
|
|
position=2,
|
|
),
|
|
),
|
|
question_target=QuestionTargetSlot(
|
|
kind="continuous_quantity",
|
|
entity="tina",
|
|
unit_class="currency",
|
|
position=3,
|
|
),
|
|
expectation=ExpectationFrame(
|
|
allowed_categories=("residual_modifier", "state_continuation_verb"),
|
|
reason="question frame remains open",
|
|
),
|
|
)
|
|
second = ComprehensionState(
|
|
entities=(
|
|
EntityRef(
|
|
canonical_name="tina",
|
|
gender="female",
|
|
first_mention_position=0,
|
|
),
|
|
),
|
|
quantities=(
|
|
QuantityRef(
|
|
value=Decimal("12.0"),
|
|
unit="dollars",
|
|
unit_class="currency",
|
|
owner_entity="tina",
|
|
mention_position=1,
|
|
),
|
|
),
|
|
operations=(
|
|
PartialOp(
|
|
operator_kind="accumulation_verb",
|
|
subject_entity="tina",
|
|
object_entity=None,
|
|
quantity_index=0,
|
|
position=2,
|
|
),
|
|
),
|
|
question_target=QuestionTargetSlot(
|
|
kind="continuous_quantity",
|
|
entity="tina",
|
|
unit_class="currency",
|
|
position=3,
|
|
),
|
|
expectation=ExpectationFrame(
|
|
allowed_categories=("residual_modifier", "state_continuation_verb"),
|
|
reason="question frame remains open",
|
|
),
|
|
)
|
|
|
|
assert first.canonical_bytes() == second.canonical_bytes()
|
|
assert first.canonical_hash() == second.canonical_hash()
|
|
assert (
|
|
first.canonical_bytes()
|
|
== b'{"entities":[{"canonical_name":"tina","first_mention_position":0,"gender":"female"}],"expectation":{"allowed_categories":["residual_modifier","state_continuation_verb"],"reason":"question frame remains open"},"operations":[{"object_entity":null,"operator_kind":"accumulation_verb","position":2,"quantity_index":0,"subject_entity":"tina"}],"quantities":[{"mention_position":1,"owner_entity":"tina","unit":"dollars","unit_class":"currency","value":"12"}],"question_target":{"entity":"tina","kind":"continuous_quantity","position":3,"unit_class":"currency"}}'
|
|
)
|
|
|
|
|
|
def test_optional_fields_serialize_as_null() -> None:
|
|
state = ComprehensionState(
|
|
entities=(),
|
|
quantities=(),
|
|
operations=(),
|
|
question_target=None,
|
|
expectation=None,
|
|
)
|
|
|
|
assert (
|
|
state.canonical_bytes()
|
|
== b'{"entities":[],"expectation":null,"operations":[],"quantities":[],"question_target":null}'
|
|
)
|
|
|
|
|
|
def test_entity_ref_refuses_invalid_gender() -> None:
|
|
with pytest.raises(ComprehensionStateError, match="EntityRef.gender"):
|
|
EntityRef(
|
|
canonical_name="tina",
|
|
gender="plural", # type: ignore[arg-type]
|
|
first_mention_position=0,
|
|
)
|
|
|
|
|
|
def test_quantity_ref_refuses_non_decimal_and_missing_unit_shape() -> None:
|
|
with pytest.raises(ComprehensionStateError, match="QuantityRef.value"):
|
|
QuantityRef(
|
|
value="12.0", # type: ignore[arg-type]
|
|
unit="dollars",
|
|
unit_class="currency",
|
|
owner_entity="tina",
|
|
mention_position=1,
|
|
)
|
|
with pytest.raises(
|
|
ComprehensionStateError,
|
|
match="QuantityRef.unit and QuantityRef.unit_class cannot both be None",
|
|
):
|
|
QuantityRef(
|
|
value=Decimal("12"),
|
|
unit=None,
|
|
unit_class=None,
|
|
owner_entity=None,
|
|
mention_position=1,
|
|
)
|
|
|
|
|
|
def test_expectation_frame_refuses_empty_or_unknown_categories() -> None:
|
|
with pytest.raises(
|
|
ComprehensionStateError,
|
|
match="ExpectationFrame.allowed_categories must not be empty",
|
|
):
|
|
ExpectationFrame(allowed_categories=(), reason="x")
|
|
with pytest.raises(ComprehensionStateError, match="allowed_categories"):
|
|
ExpectationFrame(
|
|
allowed_categories=("unsupported",),
|
|
reason="x",
|
|
)
|
|
|
|
|
|
def test_comprehension_state_refuses_non_tuple_and_wrong_member_types() -> None:
|
|
with pytest.raises(ComprehensionStateError, match="entities must be tuple"):
|
|
ComprehensionState(
|
|
entities=[], # type: ignore[arg-type]
|
|
quantities=(),
|
|
operations=(),
|
|
)
|
|
with pytest.raises(ComprehensionStateError, match="quantities\\[0\\]"):
|
|
ComprehensionState(
|
|
entities=(),
|
|
quantities=("bad",), # type: ignore[arg-type]
|
|
operations=(),
|
|
)
|