""" Unit-тесты для базового класса LinkerEntity и его механизма сериализации/десериализации. """ import uuid from dataclasses import dataclass, field from uuid import UUID, uuid4 import pytest from ntr_text_fragmentation.models import LinkerEntity, register_entity from tests.custom_entity import \ CustomEntity # Используем существующий кастомный класс # Фикстуры @pytest.fixture def base_entity() -> LinkerEntity: """Фикстура для базовой сущности.""" return LinkerEntity(id=uuid4(), name="Base Name", text="Base Text") @pytest.fixture def link_entity() -> LinkerEntity: """Фикстура для сущности-связи.""" return LinkerEntity( id=uuid4(), name="Link Name", source_id=uuid4(), target_id=uuid4(), number_in_relation=1, ) @pytest.fixture def custom_entity_instance() -> CustomEntity: """Фикстура для кастомной сущности.""" return CustomEntity( id=uuid4(), name="Custom Name", text="Custom Text", custom_field="custom_value", metadata={"existing_meta": "meta_value"}, ) @pytest.fixture def serialized_custom_entity( custom_entity_instance: CustomEntity, ) -> LinkerEntity: """Фикстура для сериализованной кастомной сущности.""" return custom_entity_instance.serialize() # Тесты class TestLinkerEntity: """Тесты для класса LinkerEntity.""" def test_initialization_defaults(self): """Тест инициализации с значениями по умолчанию.""" entity = LinkerEntity() assert isinstance(entity.id, UUID) assert entity.name == "" assert entity.text == "" assert entity.metadata == {} assert entity.in_search_text is None assert entity.source_id is None assert entity.target_id is None assert entity.number_in_relation is None assert entity.groupper is None assert entity.type == "LinkerEntity" # Имя класса по умолчанию def test_initialization_with_values(self, base_entity: LinkerEntity): """Тест инициализации с заданными значениями.""" entity_id = base_entity.id assert base_entity.name == "Base Name" assert base_entity.text == "Base Text" assert base_entity.id == entity_id def test_is_link(self, base_entity: LinkerEntity, link_entity: LinkerEntity): """Тест метода is_link().""" assert not base_entity.is_link() assert link_entity.is_link() def test_owner_id_property(self, base_entity: LinkerEntity, link_entity: LinkerEntity): """Тест свойства owner_id.""" # У обычной сущности owner_id это target_id owner_uuid = uuid4() base_entity.target_id = owner_uuid assert base_entity.owner_id == owner_uuid # У связи нет owner_id assert link_entity.owner_id is None # Попытка установить owner_id для связи должна вызвать ошибку with pytest.raises(ValueError, match="Связь не может иметь владельца"): link_entity.owner_id = uuid4() # Установка owner_id для обычной сущности new_owner_id = uuid4() base_entity.owner_id = new_owner_id assert base_entity.target_id == new_owner_id def test_str_representation(self, base_entity: LinkerEntity): """Тест строкового представления __str__.""" assert str(base_entity) == "Base Name: Base Text" base_entity.in_search_text = "Search text representation" assert str(base_entity) == "Search text representation" def test_equality(self, base_entity: LinkerEntity): """Тест сравнения __eq__.""" entity_copy = LinkerEntity( id=base_entity.id, name="Base Name", text="Base Text" ) different_entity = LinkerEntity(name="Different Name") assert base_entity == entity_copy assert base_entity != different_entity assert base_entity != "not an entity" def test_equality_links(self, link_entity: LinkerEntity): """Тест сравнения связей.""" link_copy = LinkerEntity( id=link_entity.id, name="Link Name", source_id=link_entity.source_id, target_id=link_entity.target_id, number_in_relation=1, ) different_link = LinkerEntity( id=link_entity.id, name="Link Name", source_id=uuid4(), # Другой source_id target_id=link_entity.target_id, number_in_relation=1, ) non_link = LinkerEntity(id=link_entity.id) assert link_entity == link_copy assert link_entity != different_link assert link_entity != non_link # --- Тесты сериализации/десериализации --- def test_serialize_base_entity(self, base_entity: LinkerEntity): """Тест сериализации базовой сущности.""" serialized = base_entity.serialize() assert isinstance(serialized, LinkerEntity) # Проверяем, что это не тот же самый объект, а копия базового типа assert serialized is not base_entity assert type(serialized) is LinkerEntity assert serialized.id == base_entity.id assert serialized.name == base_entity.name assert serialized.text == base_entity.text assert serialized.metadata == {} # Нет доп. полей assert serialized.type == "LinkerEntity" # Сохраняем тип def test_serialize_custom_entity( self, custom_entity_instance: CustomEntity, serialized_custom_entity: LinkerEntity, ): """Тест сериализации кастомной сущности.""" serialized = serialized_custom_entity # Используем фикстуру assert isinstance(serialized, LinkerEntity) assert type(serialized) is LinkerEntity assert serialized.id == custom_entity_instance.id assert serialized.name == custom_entity_instance.name assert serialized.text == custom_entity_instance.text # Проверяем, что кастомное поле и исходные метаданные попали в metadata assert "_custom_field" in serialized.metadata assert serialized.metadata["_custom_field"] == "custom_value" assert "existing_meta" in serialized.metadata assert serialized.metadata["existing_meta"] == "meta_value" # Тип должен быть именем кастомного класса assert serialized.type == "CustomEntity" def test_deserialize_custom_entity( self, serialized_custom_entity: LinkerEntity ): """Тест десериализации в кастомный тип.""" # Используем класс CustomEntity для десериализации, так как он зарегистрирован deserialized = LinkerEntity._deserialize(serialized_custom_entity) assert isinstance(deserialized, CustomEntity) assert deserialized.id == serialized_custom_entity.id assert deserialized.name == serialized_custom_entity.name assert deserialized.text == serialized_custom_entity.text # Проверяем восстановление кастомного поля assert deserialized.custom_field == "custom_value" # Проверяем восстановление исходных метаданных assert "existing_meta" in deserialized.metadata assert deserialized.metadata["existing_meta"] == "meta_value" assert deserialized.type == "CustomEntity" # Тип сохраняется def test_deserialize_base_entity(self, base_entity: LinkerEntity): """Тест десериализации базовой сущности (должна вернуться сама).""" serialized = base_entity.serialize() # Сериализуем базовую deserialized = LinkerEntity._deserialize(serialized) assert deserialized is serialized # Возвращается исходный объект LinkerEntity assert type(deserialized) is LinkerEntity def test_deserialize_unregistered_type(self): """Тест десериализации незарегистрированного типа (должен вернуться исходный объект).""" unregistered_entity = LinkerEntity(id=uuid4(), type="UnregisteredType") deserialized = LinkerEntity._deserialize(unregistered_entity) assert deserialized is unregistered_entity assert deserialized.type == "UnregisteredType" def test_deserialize_to_me_on_custom_class( self, serialized_custom_entity: LinkerEntity ): """Тест прямого вызова _deserialize_to_me на кастомном классе.""" # Вызываем метод десериализации непосредственно у CustomEntity deserialized = CustomEntity._deserialize_to_me(serialized_custom_entity) assert isinstance(deserialized, CustomEntity) assert deserialized.id == serialized_custom_entity.id assert deserialized.custom_field == "custom_value" assert deserialized.metadata["existing_meta"] == "meta_value" def test_deserialize_to_me_type_error(self): """Тест ошибки TypeError в _deserialize_to_me при неверном типе данных.""" with pytest.raises(TypeError): # Пытаемся десериализовать не LinkerEntity CustomEntity._deserialize_to_me("not_an_entity") # type: ignore def test_register_entity_decorator(self): """Тест работы декоратора @register_entity.""" @register_entity @dataclass class TempEntity(LinkerEntity): temp_field: str = "temp" type: str = "Temporary" # Явно указываем тип для регистрации assert "Temporary" in LinkerEntity._entity_classes assert LinkerEntity._entity_classes["Temporary"] is TempEntity # Проверяем, что он десериализуется instance = TempEntity(id=uuid4(), name="Temp instance", temp_field="value") serialized = instance.serialize() assert serialized.type == "Temporary" deserialized = LinkerEntity._deserialize(serialized) assert isinstance(deserialized, TempEntity) assert deserialized.temp_field == "value" # Удаляем временный класс из реестра, чтобы не влиять на другие тесты del LinkerEntity._entity_classes["Temporary"] assert "Temporary" not in LinkerEntity._entity_classes