"""Executable completeness contract for memory namespaces and access.""" from __future__ import annotations import pytest from aios_v2r1.memory_scope import MemoryScope, qualify_memory_key from aios_v2r1.tenant_context import ScopeError, ValidationError def scope(kind: str = "session", **changes: object) -> MemoryScope: values: dict[str, object] = { "tenant_id": "tenant-a", "cell_id": "cell-a", "profile_id": "profile-a", "session_id": "session-a", "kind": kind} values.update(changes) return MemoryScope(**values) # type: ignore[arg-type] @pytest.mark.parametrize(("kind", "session_id", "expected"), [ ("session", "session-a", "tenant/tenant-a/cell/cell-a/profile/profile-a/session/session-a/memory/session"), ("profile", None, "tenant/tenant-a/cell/cell-a/profile/profile-a/memory/profile"), ("cell", None, "tenant/tenant-a/cell/cell-a/memory/cell"), ("tenant", None, "tenant/tenant-a/cell/cell-a/memory/tenant"), ("shared_artifact", None, "tenant/tenant-a/cell/cell-a/memory/shared_artifact")]) def test_every_namespace_is_explicit(kind: str, session_id: str | None, expected: str) -> None: assert scope(kind, session_id=session_id).namespace == expected @pytest.mark.parametrize("requested", [ scope(tenant_id="tenant-b"), scope(cell_id="cell-b"), scope(profile_id="profile-b"), scope(session_id="session-b")]) def test_cross_scope_access_is_denied(requested: MemoryScope) -> None: with pytest.raises(ScopeError): scope().assert_access(requested) def test_cross_kind_access_is_denied() -> None: with pytest.raises(ScopeError, match="cross-kind"): scope("session").assert_access(scope("profile", session_id=None)) def test_shared_artifacts_are_read_only() -> None: owner = scope() shared = scope("shared_artifact", session_id=None) owner.assert_access(shared) with pytest.raises(ScopeError, match="immutable"): owner.assert_access(shared, write=True) def test_qualified_key_contains_the_full_namespace() -> None: assert qualify_memory_key(scope(), "facts.v1") == ( "tenant/tenant-a/cell/cell-a/profile/profile-a/session/session-a/" "memory/session/facts.v1") @pytest.mark.parametrize("key", ["../escape", "a/b", ".", "..", " space ", "x?query"]) def test_memory_key_traversal_and_malformed_keys_are_rejected(key: str) -> None: with pytest.raises(ValidationError): qualify_memory_key(scope(), key) def test_memory_search_surface_is_disabled() -> None: assert not hasattr(scope(), "search") assert not hasattr(scope(), "search_all")