Files
BEHAVE/core/tests/test_envelope.py
anti 315ca0b225 refactor: drop decnet- prefix — BEHAVE is now standalone
Rename packages and imports:
  decnet-behave-core  → behave-core
  decnet-behave-shell → behave-shell
  decnet-behave-text  → behave-text
  decnet_behave_*     → behave_*

BEHAVE is no longer a DECNET sub-project.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-10 06:20:01 -04:00

89 lines
2.4 KiB
Python

# SPDX-License-Identifier: GPL-3.0-or-later
"""Structural validation tests for the shared Observation envelope.
These tests are STRUCTURAL ONLY — window ordering, confidence bounds, schema
version, round-trip serialization, extra-field rejection. Registry-aware
validation (unknown primitives, categorical-allowed-set, numeric-min-bound,
etc.) lives in each sibling package's own test_envelope.py because the
registry IS the sibling-specific concern.
"""
from __future__ import annotations
import pytest
from pydantic import ValidationError
from behave_core.spec import OBSERVATION_SCHEMA_VERSION, Observation, Window
def _make(primitive: str = "motor.example", value="x", **kwargs) -> Observation:
base = dict(
primitive=primitive,
value=value,
confidence=0.8,
window=Window(start_ts=1.0, end_ts=2.0),
source="test/sensor",
)
base.update(kwargs)
return Observation(**base)
def test_minimal_observation_round_trips():
obs = _make()
obs2 = Observation.model_validate_json(obs.model_dump_json())
assert obs == obs2
def test_schema_version_pinned_to_one():
assert OBSERVATION_SCHEMA_VERSION == 1
obs = _make()
assert obs.v == 1
def test_window_end_must_be_after_start():
with pytest.raises(ValidationError):
Window(start_ts=2.0, end_ts=1.0)
def test_window_point_event_allowed():
w = Window(start_ts=5.0, end_ts=5.0)
assert w.start_ts == w.end_ts
def test_confidence_must_be_in_unit_interval():
with pytest.raises(ValidationError):
_make(confidence=-0.01)
with pytest.raises(ValidationError):
_make(confidence=1.01)
def test_extra_fields_forbidden():
with pytest.raises(ValidationError):
Observation(
primitive="motor.example",
value="x",
confidence=0.5,
window=Window(start_ts=1.0, end_ts=2.0),
source="test/sensor",
unknown_field="oops",
)
def test_id_and_ts_auto_default():
obs1 = _make()
obs2 = _make()
assert obs1.id != obs2.id
assert obs1.ts > 0
def test_core_envelope_is_registry_agnostic():
"""The base Observation accepts any primitive string; sibling subclasses validate."""
obs = _make(primitive="anything.goes.here", value="anything")
assert obs.primitive == "anything.goes.here"
assert obs.value == "anything"
def test_source_must_be_nonempty():
with pytest.raises(ValidationError):
_make(source="")