File size: 11,422 Bytes
744a170
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
"""
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