import pytest from myfasthtml.core.instances import ( BaseInstance, SingleInstance, MultipleInstance, InstancesManager, DuplicateInstanceError, special_session, Ids, RootInstance ) @pytest.fixture(autouse=True) def reset_instances(): """Reset instances before each test to ensure isolation.""" InstancesManager.instances.clear() yield InstancesManager.instances.clear() @pytest.fixture def session(): """Create a test session.""" return {"user_info": {"id": "test-user-123"}} @pytest.fixture def another_session(): """Create another test session.""" return {"user_info": {"id": "test-user-456"}} @pytest.fixture def root_instance(session): """Create a root instance for testing.""" return SingleInstance(parent=None, session=session, _id="test-root") # Example subclasses for testing class SubSingleInstance(SingleInstance): """Example subclass of SingleInstance with simplified signature.""" def __init__(self, parent): super().__init__(parent=parent) class SubMultipleInstance(MultipleInstance): """Example subclass of MultipleInstance with custom parameter.""" def __init__(self, parent, _id=None, custom_param=None): super().__init__(parent=parent, _id=_id) self.custom_param = custom_param class TestBaseInstance: def test_i_can_create_a_base_instance_with_positional_args(self, session, root_instance): """Test that a BaseInstance can be created with positional arguments.""" instance = BaseInstance(root_instance, session, "test_id") assert instance is not None assert instance.get_id() == "test_id" assert instance.get_session() == session assert instance.get_parent() == root_instance def test_i_can_create_a_base_instance_with_kwargs(self, session, root_instance): """Test that a BaseInstance can be created with keyword arguments.""" instance = BaseInstance(parent=root_instance, session=session, _id="test_id") assert instance is not None assert instance.get_id() == "test_id" assert instance.get_session() == session assert instance.get_parent() == root_instance def test_i_can_create_a_base_instance_with_mixed_args(self, session, root_instance): """Test that a BaseInstance can be created with mixed positional and keyword arguments.""" instance = BaseInstance(root_instance, session=session, _id="test_id") assert instance is not None assert instance.get_id() == "test_id" assert instance.get_session() == session assert instance.get_parent() == root_instance def test_i_can_retrieve_the_same_instance_when_using_same_session_and_id(self, session, root_instance): """Test that creating an instance with same session and id returns the existing instance.""" instance1 = BaseInstance(root_instance, session, "same_id") instance2 = BaseInstance(root_instance, session, "same_id") assert instance1 is instance2 def test_i_can_control_instances_registration(self, session, root_instance): """Test that auto_register=False prevents automatic registration.""" BaseInstance(parent=root_instance, session=session, _id="test_id", auto_register=False) session_id = InstancesManager.get_session_id(session) key = (session_id, "test_id") assert key not in InstancesManager.instances def test_i_can_have_different_instances_for_different_sessions(self, session, another_session, root_instance): """Test that different sessions can have instances with the same id.""" root_instance2 = SingleInstance(parent=None, session=another_session, _id="test-root") instance1 = BaseInstance(root_instance, session, "same_id") instance2 = BaseInstance(root_instance2, another_session, "same_id") assert instance1 is not instance2 assert instance1.get_session() == session assert instance2.get_session() == another_session def test_i_can_create_instance_with_parent_only(self, session, root_instance): """Test that session can be extracted from parent when not provided.""" instance = BaseInstance(parent=root_instance, _id="test_id") assert instance.get_session() == root_instance.get_session() assert instance.get_parent() == root_instance def test_i_cannot_create_instance_without_parent_or_session(self): """Test that creating an instance without parent or session raises TypeError.""" with pytest.raises(TypeError, match="Either session or parent must be provided"): BaseInstance(None, _id="test_id") def test_i_can_get_auto_generated_id(self, session, root_instance): """Test that if _id is not provided, an ID is auto-generated via compute_id().""" instance = BaseInstance(parent=root_instance, session=session) assert instance.get_id() is not None assert instance.get_id().startswith("mf-base_instance-") def test_i_can_get_prefix_from_class_name(self, session): """Test that get_prefix() returns the correct snake_case prefix.""" prefix = BaseInstance(None, session).get_prefix() assert prefix == "mf-base_instance" class TestSingleInstance: def test_i_can_create_a_single_instance(self, session, root_instance): """Test that a SingleInstance can be created.""" instance = SingleInstance(parent=root_instance, session=session) assert instance is not None assert instance.get_id() == "mf-single_instance" assert instance.get_session() == session assert instance.get_parent() == root_instance def test_i_can_create_single_instance_with_positional_args(self, session, root_instance): """Test that a SingleInstance can be created with positional arguments.""" instance = SingleInstance(root_instance, session, "custom_id") assert instance is not None assert instance.get_id() == "custom_id" assert instance.get_session() == session assert instance.get_parent() == root_instance def test_the_same_instance_is_returned(self, session): """Test that single instance is cached and returned on subsequent calls.""" instance1 = SingleInstance(parent=None, session=session, _id="unique_id") instance2 = SingleInstance(parent=None, session=session, _id="unique_id") assert instance1 is instance2 def test_i_cannot_create_duplicate_single_instance(self, session): """Test that creating a duplicate SingleInstance raises DuplicateInstanceError.""" instance = SingleInstance(parent=None, session=session, _id="unique_id") with pytest.raises(DuplicateInstanceError): InstancesManager.register(session, instance) def test_i_can_retrieve_existing_single_instance(self, session): """Test that attempting to create an existing SingleInstance returns the same instance.""" instance1 = SingleInstance(parent=None, session=session, _id="same_id") instance2 = SingleInstance(parent=None, session=session, _id="same_id", auto_register=False) assert instance1 is instance2 def test_i_can_get_auto_computed_id_for_single_instance(self, session): """Test that the default ID equals prefix for SingleInstance.""" instance = SingleInstance(parent=None, session=session) assert instance.get_id() == "mf-single_instance" assert instance.get_prefix() == "mf-single_instance" class TestSingleInstanceSubclass: def test_i_can_create_subclass_of_single_instance(self, root_instance): """Test that a subclass of SingleInstance works correctly.""" instance = SubSingleInstance(root_instance) assert instance is not None assert isinstance(instance, SingleInstance) assert isinstance(instance, SubSingleInstance) def test_i_can_create_subclass_with_custom_signature(self, root_instance): """Test that subclass with simplified signature works correctly.""" instance = SubSingleInstance(root_instance) assert instance.get_parent() == root_instance assert instance.get_session() == root_instance.get_session() assert instance.get_id() == "mf-sub_single_instance" assert instance.get_prefix() == "mf-sub_single_instance" def test_i_can_retrieve_subclass_instance_from_cache(self, root_instance): """Test that cache works for subclasses.""" instance1 = SubSingleInstance(root_instance) instance2 = SubSingleInstance(root_instance) assert instance1 is instance2 class TestMultipleInstance: def test_i_can_create_multiple_instances_with_same_prefix(self, session, root_instance): """Test that multiple MultipleInstance objects can be created with the same prefix.""" instance1 = MultipleInstance(parent=root_instance, session=session) instance2 = MultipleInstance(parent=root_instance, session=session) assert instance1 is not instance2 assert instance1.get_id() != instance2.get_id() assert instance1.get_id().startswith("mf-multiple_instance-") assert instance2.get_id().startswith("mf-multiple_instance-") def test_i_can_have_auto_generated_unique_ids(self, session, root_instance): """Test that each MultipleInstance receives a unique auto-generated ID.""" instances = [MultipleInstance(parent=root_instance, session=session) for _ in range(5)] ids = [inst.get_id() for inst in instances] # All IDs should be unique assert len(ids) == len(set(ids)) # All IDs should start with the prefix assert all(id.startswith("mf-multiple_instance-") for id in ids) def test_i_can_provide_custom_id_to_multiple_instance(self, session, root_instance): """Test that a custom _id can be provided to MultipleInstance.""" custom_id = "custom-instance-id" instance = MultipleInstance(parent=root_instance, session=session, _id=custom_id) assert instance.get_id() == custom_id def test_i_can_retrieve_multiple_instance_by_custom_id(self, session, root_instance): """Test that a MultipleInstance with custom _id can be retrieved from cache.""" custom_id = "custom-instance-id" instance1 = MultipleInstance(parent=root_instance, session=session, _id=custom_id) instance2 = MultipleInstance(parent=root_instance, session=session, _id=custom_id) assert instance1 is instance2 def test_key_prefixed_by_underscore_uses_the_parent_id_as_prefix(self, root_instance): """Test that key prefixed by underscore uses the parent id as prefix.""" instance = MultipleInstance(parent=root_instance, _id="-test_id") assert instance.get_id() == f"{root_instance.get_id()}-test_id" def test_no_parent_id_as_prefix_if_parent_is_none(self, session, root_instance): """Test that key prefixed by underscore does not use the parent id as prefix if parent is None.""" instance = MultipleInstance(parent=None, session=session, _id="-test_id") assert instance.get_id() == "-test_id" class TestMultipleInstanceSubclass: def test_i_can_create_subclass_of_multiple_instance(self, root_instance): """Test that a subclass of MultipleInstance works correctly.""" instance = SubMultipleInstance(root_instance, custom_param="test") assert instance is not None assert isinstance(instance, MultipleInstance) assert isinstance(instance, SubMultipleInstance) assert instance.custom_param == "test" def test_i_can_create_multiple_subclass_instances_with_auto_generated_ids(self, root_instance): """Test that multiple instances of subclass can be created with unique IDs.""" instance1 = SubMultipleInstance(root_instance, custom_param="first") instance2 = SubMultipleInstance(root_instance, custom_param="second") assert instance1 is not instance2 assert instance1.get_id() != instance2.get_id() assert instance1.get_id().startswith("mf-sub_multiple_instance-") assert instance2.get_id().startswith("mf-sub_multiple_instance-") def test_i_can_create_subclass_with_custom_signature(self, root_instance): """Test that subclass with custom parameters works correctly.""" instance = SubMultipleInstance(root_instance, custom_param="value") assert instance.get_parent() == root_instance assert instance.get_session() == root_instance.get_session() assert instance.custom_param == "value" def test_i_can_retrieve_subclass_instance_from_cache(self, root_instance): """Test that cache works for subclasses.""" instance1 = SubMultipleInstance(root_instance, custom_param="first") instance2 = SubMultipleInstance(root_instance, custom_param="second", _id=instance1.get_id()) assert instance1 is instance2 def test_i_cannot_retrieve_subclass_instance_when_type_differs(self, root_instance): """Test that cache works for subclasses with custom _id.""" # Need to pass _id explicitly to enable caching instance1 = SubMultipleInstance(root_instance) with pytest.raises(TypeError): MultipleInstance(parent=root_instance, _id=instance1.get_id()) def test_i_can_get_correct_prefix_for_multiple_subclass(self, root_instance): """Test that subclass has correct auto-generated prefix.""" prefix = SubMultipleInstance(root_instance).get_prefix() assert prefix == "mf-sub_multiple_instance" class TestInstancesManager: def test_i_can_register_an_instance_manually(self, session, root_instance): """Test that an instance can be manually registered.""" instance = BaseInstance(parent=root_instance, session=session, _id="manual_id", auto_register=False) InstancesManager.register(session, instance) session_id = InstancesManager.get_session_id(session) key = (session_id, "manual_id") assert key in InstancesManager.instances assert InstancesManager.instances[key] is instance def test_i_can_get_existing_instance_by_id(self, session, root_instance): """Test that an existing instance can be retrieved by ID.""" instance = BaseInstance(parent=root_instance, session=session, _id="get_id") retrieved = InstancesManager.get(session, "get_id") assert retrieved is instance def test_i_cannot_get_nonexistent_instance_without_type(self, session): """Test that getting a non-existent instance without type raises KeyError.""" with pytest.raises(KeyError): InstancesManager.get(session, "nonexistent_id") def test_i_can_get_session_id_from_valid_session(self, session): """Test that session ID is correctly extracted from a valid session.""" session_id = InstancesManager.get_session_id(session) assert session_id == "test-user-123" def test_i_can_handle_none_session(self): """Test that None session returns a special identifier.""" session_id = InstancesManager.get_session_id(None) assert session_id == "** NOT LOGGED IN **" def test_i_can_handle_invalid_session(self): """Test that invalid sessions return appropriate identifiers.""" # Session is None session_id = InstancesManager.get_session_id(None) assert session_id == "** NOT LOGGED IN **" # Session without user_info session_no_user = {} session_id = InstancesManager.get_session_id(session_no_user) assert session_id == "** UNKNOWN USER **" # Session with user_info but no id session_no_id = {"user_info": {}} session_id = InstancesManager.get_session_id(session_no_id) assert session_id == "** INVALID SESSION **" def test_i_can_reset_all_instances(self, session, root_instance): """Test that reset() clears all instances.""" BaseInstance(parent=root_instance, session=session, _id="id1") BaseInstance(parent=root_instance, session=session, _id="id2") assert len(InstancesManager.instances) > 0 InstancesManager.reset() assert len(InstancesManager.instances) == 0 class TestRootInstance: def test_i_can_create_root_instance_with_positional_args(self): """Test that RootInstance can be created with positional arguments.""" root = SingleInstance(None, special_session, Ids.Root) assert root is not None assert root.get_id() == Ids.Root assert root.get_session() == special_session assert root.get_parent() is None def test_i_can_access_root_instance(self): """Test that RootInstance is created and accessible.""" assert RootInstance is not None assert RootInstance.get_id() == Ids.Root assert RootInstance.get_session() == special_session