104 lines
3.4 KiB
Python
104 lines
3.4 KiB
Python
|
|
"""Plan 03-01 CONN-05 RED: structure_hv field schema validation on MemoryRecord.
|
||
|
|
|
||
|
|
MemoryRecord.structure_hv: bytes is the renamed Phase-2 hd_vector slot. It
|
||
|
|
must accept empty bytes (pre-migration sentinel) OR exactly STRUCTURE_HV_BYTES
|
||
|
|
(1250 bytes, D=10000 BSC packed) -- anything else is a constitutional schema
|
||
|
|
violation that __post_init__ rejects.
|
||
|
|
"""
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from datetime import datetime, timezone
|
||
|
|
from uuid import uuid4
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
|
||
|
|
|
||
|
|
def _kwargs(**override):
|
||
|
|
"""Build a minimal valid MemoryRecord kwargs dict; tests override fields."""
|
||
|
|
from iai_mcp.types import EMBED_DIM
|
||
|
|
|
||
|
|
base = dict(
|
||
|
|
id=uuid4(),
|
||
|
|
tier="episodic",
|
||
|
|
literal_surface="hello",
|
||
|
|
aaak_index="",
|
||
|
|
embedding=[0.1] * EMBED_DIM,
|
||
|
|
community_id=None,
|
||
|
|
centrality=0.0,
|
||
|
|
detail_level=2,
|
||
|
|
pinned=False,
|
||
|
|
stability=0.0,
|
||
|
|
difficulty=0.0,
|
||
|
|
last_reviewed=None,
|
||
|
|
never_decay=False,
|
||
|
|
never_merge=False,
|
||
|
|
provenance=[],
|
||
|
|
created_at=datetime.now(timezone.utc),
|
||
|
|
updated_at=datetime.now(timezone.utc),
|
||
|
|
tags=[],
|
||
|
|
language="en",
|
||
|
|
)
|
||
|
|
base.update(override)
|
||
|
|
return base
|
||
|
|
|
||
|
|
|
||
|
|
# ---------------------------------------------------------------- field default
|
||
|
|
|
||
|
|
|
||
|
|
def test_structure_hv_defaults_to_empty_bytes() -> None:
|
||
|
|
"""Default value is b"" (pre-migration sentinel)."""
|
||
|
|
from iai_mcp.types import MemoryRecord
|
||
|
|
|
||
|
|
rec = MemoryRecord(**_kwargs())
|
||
|
|
assert rec.structure_hv == b""
|
||
|
|
assert isinstance(rec.structure_hv, bytes)
|
||
|
|
|
||
|
|
|
||
|
|
def test_structure_hv_accepts_exact_length() -> None:
|
||
|
|
"""Exactly STRUCTURE_HV_BYTES (1250) bytes must be accepted."""
|
||
|
|
from iai_mcp.types import MemoryRecord, STRUCTURE_HV_BYTES
|
||
|
|
|
||
|
|
payload = bytes(STRUCTURE_HV_BYTES) # all-zero sentinel; right shape
|
||
|
|
rec = MemoryRecord(**_kwargs(structure_hv=payload))
|
||
|
|
assert rec.structure_hv == payload
|
||
|
|
assert len(rec.structure_hv) == STRUCTURE_HV_BYTES
|
||
|
|
|
||
|
|
|
||
|
|
def test_structure_hv_rejects_wrong_length() -> None:
|
||
|
|
"""Anything that is not empty AND not STRUCTURE_HV_BYTES bytes raises."""
|
||
|
|
from iai_mcp.types import MemoryRecord
|
||
|
|
|
||
|
|
with pytest.raises(ValueError, match=r"structure_hv must be empty"):
|
||
|
|
MemoryRecord(**_kwargs(structure_hv=b"too short"))
|
||
|
|
|
||
|
|
with pytest.raises(ValueError, match=r"structure_hv must be empty"):
|
||
|
|
MemoryRecord(**_kwargs(structure_hv=b"x" * 999))
|
||
|
|
|
||
|
|
|
||
|
|
def test_structure_hv_rejects_non_bytes() -> None:
|
||
|
|
"""Non-bytes input (list/str/None) is rejected at the type boundary."""
|
||
|
|
from iai_mcp.types import MemoryRecord
|
||
|
|
|
||
|
|
with pytest.raises(ValueError, match=r"structure_hv must be bytes"):
|
||
|
|
MemoryRecord(**_kwargs(structure_hv=[1, 0, 1]))
|
||
|
|
|
||
|
|
with pytest.raises(ValueError, match=r"structure_hv must be bytes"):
|
||
|
|
MemoryRecord(**_kwargs(structure_hv="not bytes"))
|
||
|
|
|
||
|
|
|
||
|
|
def test_module_constants_match_canonical_dims() -> None:
|
||
|
|
"""STRUCTURE_HV_DIM=10000 (D-TEM-01); STRUCTURE_HV_BYTES=1250 (D/8)."""
|
||
|
|
from iai_mcp.types import STRUCTURE_HV_BYTES, STRUCTURE_HV_DIM
|
||
|
|
|
||
|
|
assert STRUCTURE_HV_DIM == 10000
|
||
|
|
assert STRUCTURE_HV_BYTES == 1250
|
||
|
|
assert STRUCTURE_HV_DIM // 8 == STRUCTURE_HV_BYTES
|
||
|
|
|
||
|
|
|
||
|
|
def test_schema_version_v4_accepted() -> None:
|
||
|
|
"""schema_version=4 (Plan 03-01 marker) must be accepted alongside 1/2/3."""
|
||
|
|
from iai_mcp.types import MemoryRecord, SCHEMA_VERSION_V4
|
||
|
|
|
||
|
|
rec = MemoryRecord(**_kwargs(schema_version=SCHEMA_VERSION_V4))
|
||
|
|
assert rec.schema_version == 4
|