# tests/unit/services/memory/scoping/test_scope.py """Unit tests for scope management.""" from uuid import uuid4 import pytest from app.services.memory.scoping.scope import ( ScopeManager, ScopePolicy, get_scope_manager, ) from app.services.memory.types import ScopeLevel class TestScopePolicy: """Tests for ScopePolicy dataclass.""" def test_default_values(self) -> None: """Test default policy values.""" policy = ScopePolicy( scope_type=ScopeLevel.PROJECT, scope_id="test-project", ) assert policy.can_read is True assert policy.can_write is True assert policy.can_inherit is True assert policy.allowed_memory_types == ["all"] def test_allows_read(self) -> None: """Test allows_read method.""" policy = ScopePolicy( scope_type=ScopeLevel.PROJECT, scope_id="test", can_read=True, ) assert policy.allows_read() is True policy.can_read = False assert policy.allows_read() is False def test_allows_write(self) -> None: """Test allows_write method.""" policy = ScopePolicy( scope_type=ScopeLevel.PROJECT, scope_id="test", can_write=True, ) assert policy.allows_write() is True policy.can_write = False assert policy.allows_write() is False def test_allows_inherit(self) -> None: """Test allows_inherit method.""" policy = ScopePolicy( scope_type=ScopeLevel.PROJECT, scope_id="test", can_inherit=True, ) assert policy.allows_inherit() is True policy.can_inherit = False assert policy.allows_inherit() is False def test_allows_memory_type(self) -> None: """Test allows_memory_type method.""" policy = ScopePolicy( scope_type=ScopeLevel.PROJECT, scope_id="test", allowed_memory_types=["all"], ) assert policy.allows_memory_type("working") is True assert policy.allows_memory_type("episodic") is True policy.allowed_memory_types = ["working", "episodic"] assert policy.allows_memory_type("working") is True assert policy.allows_memory_type("episodic") is True assert policy.allows_memory_type("semantic") is False class TestScopeManager: """Tests for ScopeManager class.""" @pytest.fixture def manager(self) -> ScopeManager: """Create a scope manager.""" return ScopeManager() def test_create_global_scope( self, manager: ScopeManager, ) -> None: """Test creating a global scope.""" scope = manager.create_scope( scope_type=ScopeLevel.GLOBAL, scope_id="global", ) assert scope.scope_type == ScopeLevel.GLOBAL assert scope.scope_id == "global" assert scope.parent is None def test_create_project_scope( self, manager: ScopeManager, ) -> None: """Test creating a project scope.""" global_scope = manager.create_scope( scope_type=ScopeLevel.GLOBAL, scope_id="global", ) project_scope = manager.create_scope( scope_type=ScopeLevel.PROJECT, scope_id="project-1", parent=global_scope, ) assert project_scope.scope_type == ScopeLevel.PROJECT assert project_scope.scope_id == "project-1" assert project_scope.parent is global_scope def test_create_scope_auto_parent( self, manager: ScopeManager, ) -> None: """Test that non-global scopes auto-create parent chain.""" scope = manager.create_scope( scope_type=ScopeLevel.PROJECT, scope_id="test-project", ) assert scope.scope_type == ScopeLevel.PROJECT assert scope.parent is not None assert scope.parent.scope_type == ScopeLevel.GLOBAL def test_create_scope_invalid_hierarchy( self, manager: ScopeManager, ) -> None: """Test that invalid hierarchy raises error.""" project_scope = manager.create_scope( scope_type=ScopeLevel.PROJECT, scope_id="project-1", ) with pytest.raises(ValueError, match="Invalid scope hierarchy"): manager.create_scope( scope_type=ScopeLevel.GLOBAL, scope_id="global", parent=project_scope, ) def test_create_scope_from_ids( self, manager: ScopeManager, ) -> None: """Test creating scope from individual IDs.""" project_id = uuid4() agent_type_id = uuid4() scope = manager.create_scope_from_ids( project_id=project_id, agent_type_id=agent_type_id, ) assert scope.scope_type == ScopeLevel.AGENT_TYPE assert scope.scope_id == str(agent_type_id) assert scope.parent is not None assert scope.parent.scope_type == ScopeLevel.PROJECT def test_create_scope_from_ids_with_session( self, manager: ScopeManager, ) -> None: """Test creating scope with session ID.""" project_id = uuid4() session_id = "session-123" scope = manager.create_scope_from_ids( project_id=project_id, session_id=session_id, ) assert scope.scope_type == ScopeLevel.SESSION assert scope.scope_id == session_id def test_get_default_policy( self, manager: ScopeManager, ) -> None: """Test getting default policy.""" scope = manager.create_scope( scope_type=ScopeLevel.PROJECT, scope_id="test-project", ) policy = manager.get_policy(scope) assert policy.can_read is True assert policy.can_write is True def test_set_and_get_policy( self, manager: ScopeManager, ) -> None: """Test setting and retrieving a policy.""" scope = manager.create_scope( scope_type=ScopeLevel.PROJECT, scope_id="test-project", ) custom_policy = ScopePolicy( scope_type=ScopeLevel.PROJECT, scope_id="test-project", can_write=False, ) manager.set_policy(scope, custom_policy) retrieved = manager.get_policy(scope) assert retrieved.can_write is False def test_get_scope_depth( self, manager: ScopeManager, ) -> None: """Test getting scope depth.""" assert manager.get_scope_depth(ScopeLevel.GLOBAL) == 0 assert manager.get_scope_depth(ScopeLevel.PROJECT) == 1 assert manager.get_scope_depth(ScopeLevel.AGENT_TYPE) == 2 assert manager.get_scope_depth(ScopeLevel.AGENT_INSTANCE) == 3 assert manager.get_scope_depth(ScopeLevel.SESSION) == 4 def test_get_parent_level( self, manager: ScopeManager, ) -> None: """Test getting parent level.""" assert manager.get_parent_level(ScopeLevel.GLOBAL) is None assert manager.get_parent_level(ScopeLevel.PROJECT) == ScopeLevel.GLOBAL assert manager.get_parent_level(ScopeLevel.AGENT_TYPE) == ScopeLevel.PROJECT assert manager.get_parent_level(ScopeLevel.SESSION) == ScopeLevel.AGENT_INSTANCE def test_get_child_level( self, manager: ScopeManager, ) -> None: """Test getting child level.""" assert manager.get_child_level(ScopeLevel.GLOBAL) == ScopeLevel.PROJECT assert manager.get_child_level(ScopeLevel.PROJECT) == ScopeLevel.AGENT_TYPE assert manager.get_child_level(ScopeLevel.SESSION) is None def test_is_ancestor( self, manager: ScopeManager, ) -> None: """Test ancestor checking.""" global_scope = manager.create_scope(ScopeLevel.GLOBAL, "global") project_scope = manager.create_scope( ScopeLevel.PROJECT, "project", parent=global_scope ) agent_scope = manager.create_scope( ScopeLevel.AGENT_TYPE, "agent", parent=project_scope ) assert manager.is_ancestor(global_scope, agent_scope) is True assert manager.is_ancestor(project_scope, agent_scope) is True assert manager.is_ancestor(agent_scope, global_scope) is False assert manager.is_ancestor(agent_scope, project_scope) is False def test_get_common_ancestor( self, manager: ScopeManager, ) -> None: """Test finding common ancestor.""" global_scope = manager.create_scope(ScopeLevel.GLOBAL, "global") project_scope = manager.create_scope( ScopeLevel.PROJECT, "project", parent=global_scope ) agent1 = manager.create_scope( ScopeLevel.AGENT_TYPE, "agent1", parent=project_scope ) agent2 = manager.create_scope( ScopeLevel.AGENT_TYPE, "agent2", parent=project_scope ) common = manager.get_common_ancestor(agent1, agent2) assert common is not None assert common.scope_type == ScopeLevel.PROJECT def test_can_access_same_scope( self, manager: ScopeManager, ) -> None: """Test access to same scope.""" scope = manager.create_scope(ScopeLevel.PROJECT, "project") assert manager.can_access(scope, scope) is True assert manager.can_access(scope, scope, "write") is True def test_can_access_ancestor( self, manager: ScopeManager, ) -> None: """Test access to ancestor scope.""" global_scope = manager.create_scope(ScopeLevel.GLOBAL, "global") project_scope = manager.create_scope( ScopeLevel.PROJECT, "project", parent=global_scope ) # Child can read from parent assert manager.can_access(project_scope, global_scope, "read") is True def test_cannot_access_descendant( self, manager: ScopeManager, ) -> None: """Test that parent cannot access child scope.""" global_scope = manager.create_scope(ScopeLevel.GLOBAL, "global") project_scope = manager.create_scope( ScopeLevel.PROJECT, "project", parent=global_scope ) # Parent cannot access child assert manager.can_access(global_scope, project_scope) is False def test_cannot_access_sibling( self, manager: ScopeManager, ) -> None: """Test that sibling scopes cannot access each other.""" global_scope = manager.create_scope(ScopeLevel.GLOBAL, "global") project1 = manager.create_scope( ScopeLevel.PROJECT, "project1", parent=global_scope ) project2 = manager.create_scope( ScopeLevel.PROJECT, "project2", parent=global_scope ) assert manager.can_access(project1, project2) is False assert manager.can_access(project2, project1) is False class TestGetScopeManager: """Tests for singleton getter.""" def test_returns_instance(self) -> None: """Test that getter returns instance.""" manager = get_scope_manager() assert manager is not None assert isinstance(manager, ScopeManager) def test_returns_same_instance(self) -> None: """Test that getter returns same instance.""" manager1 = get_scope_manager() manager2 = get_scope_manager() assert manager1 is manager2