logic-engine / tests /pipeline_engine /test_context.py
ghostdrive1's picture
Upload folder using huggingface_hub
116524e verified
Raw
History Blame Contribute Delete
12.5 kB
"""Unit tests for pipeline.StepContext.
Tests cover the generic base class (sample + metadata) and the subclassing
pattern that consuming applications use to add domain fields.
"""
from __future__ import annotations
import dataclasses
from dataclasses import dataclass
from types import MappingProxyType
from typing import Any
import pytest
from pipeline import StepContext
# ---------------------------------------------------------------------------
# Test-local subclass — validates the subclassing pattern
# ---------------------------------------------------------------------------
@dataclass(frozen=True)
class DomainContext(StepContext):
"""Minimal subclass used only in these tests."""
output: Any = None
score: float = 0.0
# ---------------------------------------------------------------------------
# Base class defaults
# ---------------------------------------------------------------------------
@pytest.mark.unit
class TestStepContextDefaults:
def test_sample_defaults_to_none(self):
ctx = StepContext()
assert ctx.sample is None
def test_metadata_defaults_to_empty_mappingproxy(self):
ctx = StepContext(sample="s")
assert ctx.metadata == MappingProxyType({})
assert isinstance(ctx.metadata, MappingProxyType)
def test_only_two_fields_on_base_class(self):
field_names = {f.name for f in dataclasses.fields(StepContext)}
assert field_names == {"sample", "metadata"}
# ---------------------------------------------------------------------------
# Immutability
# ---------------------------------------------------------------------------
@pytest.mark.unit
class TestStepContextImmutability:
def test_setting_sample_raises(self):
ctx = StepContext(sample="s")
with pytest.raises(
(dataclasses.FrozenInstanceError, AttributeError, TypeError)
):
ctx.sample = "other" # type: ignore[misc]
def test_setting_metadata_raises(self):
ctx = StepContext(sample="s")
with pytest.raises(
(dataclasses.FrozenInstanceError, AttributeError, TypeError)
):
ctx.metadata = MappingProxyType({"x": 1}) # type: ignore[misc]
def test_metadata_mappingproxy_is_not_mutable(self):
ctx = StepContext(sample="s", metadata={"k": "v"})
with pytest.raises(TypeError):
ctx.metadata["k"] = "overwrite" # type: ignore[index]
# ---------------------------------------------------------------------------
# Coercion
# ---------------------------------------------------------------------------
@pytest.mark.unit
class TestStepContextCoercion:
def test_plain_dict_metadata_coerced_to_mappingproxy(self):
ctx = StepContext(sample="s", metadata={"x": 1})
assert isinstance(ctx.metadata, MappingProxyType)
assert ctx.metadata["x"] == 1
def test_existing_mappingproxy_not_double_wrapped(self):
mp = MappingProxyType({"x": 1})
ctx = StepContext(sample="s", metadata=mp)
assert ctx.metadata is mp
# ---------------------------------------------------------------------------
# replace()
# ---------------------------------------------------------------------------
@pytest.mark.unit
class TestStepContextReplace:
def test_replace_returns_new_object(self):
ctx = StepContext(sample="s")
ctx2 = ctx.replace(sample="t")
assert ctx2 is not ctx
def test_replace_does_not_mutate_original(self):
ctx = StepContext(sample="s")
ctx.replace(sample="t")
assert ctx.sample == "s"
def test_replace_updates_target_field(self):
ctx = StepContext(sample="s")
ctx2 = ctx.replace(sample="t")
assert ctx2.sample == "t"
def test_replace_preserves_other_fields(self):
ctx = StepContext(sample="s", metadata={"k": 1})
ctx2 = ctx.replace(sample="t")
assert ctx2.metadata["k"] == 1
def test_replace_metadata_immutable_pattern(self):
ctx = StepContext(sample="s", metadata={"x": 1})
ctx2 = ctx.replace(metadata=MappingProxyType({**ctx.metadata, "y": 2}))
assert ctx2.metadata["x"] == 1
assert ctx2.metadata["y"] == 2
assert "y" not in ctx.metadata # original unchanged
# ---------------------------------------------------------------------------
# Equality
# ---------------------------------------------------------------------------
@pytest.mark.unit
class TestStepContextEquality:
def test_equal_contexts(self):
ctx1 = StepContext(sample="s")
ctx2 = StepContext(sample="s")
assert ctx1 == ctx2
def test_different_sample_not_equal(self):
assert StepContext(sample="a") != StepContext(sample="b")
def test_context_not_hashable(self):
"""StepContext is frozen but NOT hashable: MappingProxyType wraps a dict,
which is unhashable, so Python cannot derive a hash for the dataclass."""
ctx = StepContext(sample="s")
with pytest.raises(TypeError):
hash(ctx)
# ---------------------------------------------------------------------------
# Subclassing pattern
# ---------------------------------------------------------------------------
@pytest.mark.unit
class TestStepContextSubclassing:
def test_subclass_has_base_fields(self):
ctx = DomainContext(sample="s")
assert ctx.sample == "s"
assert ctx.metadata == MappingProxyType({})
def test_subclass_has_domain_fields(self):
ctx = DomainContext(sample="s", output="answer", score=0.95)
assert ctx.output == "answer"
assert ctx.score == 0.95
def test_subclass_defaults(self):
ctx = DomainContext(sample="s")
assert ctx.output is None
assert ctx.score == 0.0
def test_subclass_is_frozen(self):
ctx = DomainContext(sample="s", output="x")
with pytest.raises(
(dataclasses.FrozenInstanceError, AttributeError, TypeError)
):
ctx.output = "y" # type: ignore[misc]
def test_subclass_replace_returns_same_type(self):
ctx = DomainContext(sample="s")
ctx2 = ctx.replace(output="answer")
assert isinstance(ctx2, DomainContext)
assert ctx2.output == "answer"
def test_subclass_replace_preserves_base_fields(self):
ctx = DomainContext(sample="s", metadata={"k": 1})
ctx2 = ctx.replace(output="x")
assert ctx2.sample == "s"
assert ctx2.metadata["k"] == 1
def test_subclass_replace_preserves_domain_fields(self):
ctx = DomainContext(sample="s", output="a", score=0.9)
ctx2 = ctx.replace(sample="t")
assert ctx2.output == "a"
assert ctx2.score == 0.9
def test_subclass_metadata_coercion(self):
ctx = DomainContext(sample="s", metadata={"x": 1})
assert isinstance(ctx.metadata, MappingProxyType)
def test_subclass_isinstance_of_step_context(self):
ctx = DomainContext(sample="s")
assert isinstance(ctx, StepContext)
def test_subclass_equality(self):
a = DomainContext(sample="s", output="x")
b = DomainContext(sample="s", output="x")
assert a == b
def test_subclass_inequality_on_domain_field(self):
a = DomainContext(sample="s", output="x")
b = DomainContext(sample="s", output="y")
assert a != b
# ---------------------------------------------------------------------------
# Multi-level subclassing (SubSubContext)
# ---------------------------------------------------------------------------
@dataclass(frozen=True)
class ExtendedContext(DomainContext):
"""Two-level subclass: StepContext → DomainContext → ExtendedContext."""
label: str = ""
@pytest.mark.unit
class TestMultiLevelSubclassing:
def test_has_all_ancestor_fields(self):
ctx = ExtendedContext(sample="s", output="x", score=0.5, label="L")
assert ctx.sample == "s"
assert ctx.metadata == MappingProxyType({})
assert ctx.output == "x"
assert ctx.score == 0.5
assert ctx.label == "L"
def test_defaults_from_all_levels(self):
ctx = ExtendedContext(sample="s")
assert ctx.output is None # from DomainContext
assert ctx.score == 0.0 # from DomainContext
assert ctx.label == "" # from ExtendedContext
def test_replace_returns_correct_type(self):
ctx = ExtendedContext(sample="s")
ctx2 = ctx.replace(label="new")
assert isinstance(ctx2, ExtendedContext)
assert ctx2.label == "new"
def test_replace_preserves_all_levels(self):
ctx = ExtendedContext(sample="s", output="x", score=0.9, label="L")
ctx2 = ctx.replace(sample="t")
assert ctx2.output == "x"
assert ctx2.score == 0.9
assert ctx2.label == "L"
def test_isinstance_chain(self):
ctx = ExtendedContext(sample="s")
assert isinstance(ctx, StepContext)
assert isinstance(ctx, DomainContext)
assert isinstance(ctx, ExtendedContext)
def test_is_frozen(self):
ctx = ExtendedContext(sample="s", label="L")
with pytest.raises(
(dataclasses.FrozenInstanceError, AttributeError, TypeError)
):
ctx.label = "new" # type: ignore[misc]
def test_metadata_coercion(self):
ctx = ExtendedContext(sample="s", metadata={"k": 1})
assert isinstance(ctx.metadata, MappingProxyType)
def test_field_count(self):
field_names = {f.name for f in dataclasses.fields(ExtendedContext)}
assert field_names == {"sample", "metadata", "output", "score", "label"}
# ---------------------------------------------------------------------------
# Multi-field replace
# ---------------------------------------------------------------------------
@pytest.mark.unit
class TestMultiFieldReplace:
def test_replace_multiple_fields_at_once(self):
ctx = DomainContext(sample="s", output="a", score=0.1)
ctx2 = ctx.replace(sample="t", output="b", score=0.9)
assert ctx2.sample == "t"
assert ctx2.output == "b"
assert ctx2.score == 0.9
def test_replace_base_and_domain_fields_together(self):
ctx = DomainContext(sample="s", metadata={"k": 1}, output="a")
ctx2 = ctx.replace(
sample="t",
metadata=MappingProxyType({"k": 2}),
output="b",
)
assert ctx2.sample == "t"
assert ctx2.metadata["k"] == 2
assert ctx2.output == "b"
def test_replace_all_fields_on_multi_level_subclass(self):
ctx = ExtendedContext(sample="s", output="a", score=0.1, label="L")
ctx2 = ctx.replace(sample="t", output="b", score=0.9, label="M")
assert isinstance(ctx2, ExtendedContext)
assert ctx2.sample == "t"
assert ctx2.output == "b"
assert ctx2.score == 0.9
assert ctx2.label == "M"
def test_original_unchanged_after_multi_field_replace(self):
ctx = DomainContext(sample="s", output="a", score=0.1)
ctx.replace(sample="t", output="b", score=0.9)
assert ctx.sample == "s"
assert ctx.output == "a"
assert ctx.score == 0.1
# ---------------------------------------------------------------------------
# Cross-type equality
# ---------------------------------------------------------------------------
@dataclass(frozen=True)
class OtherContext(StepContext):
"""A different subclass with the same field name as DomainContext."""
output: Any = None
@pytest.mark.unit
class TestCrossTypeEquality:
def test_different_subclass_types_not_equal(self):
a = DomainContext(sample="s", output="x")
b = OtherContext(sample="s", output="x")
assert a != b
def test_base_not_equal_to_subclass(self):
base = StepContext(sample="s")
sub = DomainContext(sample="s")
assert base != sub
def test_subclass_not_equal_to_sub_subclass(self):
parent = DomainContext(sample="s")
child = ExtendedContext(sample="s")
assert parent != child