- TESTING_REPORT.md: полный отчёт по тестированию - benchmarks_production.py: production бенчмарки - test_edge_cases_names.py: тесты edge cases (unicode, emoji, длинные имена) Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
333 lines
11 KiB
Python
333 lines
11 KiB
Python
"""
|
||
Тесты edge cases со странными названиями инжекторов, типов и зависимостей.
|
||
|
||
Проверяет устойчивость библиотеки к нестандартным именам.
|
||
"""
|
||
|
||
from dataclasses import dataclass
|
||
from typing import Any
|
||
|
||
import pytest
|
||
|
||
from breakshaft import ConvRepo
|
||
from breakshaft.util import hashname, universal_qualname
|
||
|
||
|
||
# =============================================================================
|
||
# Тесты hashname
|
||
# =============================================================================
|
||
|
||
class TestHashnameEdgeCases:
|
||
"""Тесты hashname со странными значениями."""
|
||
|
||
def test_hashname_with_special_chars(self):
|
||
"""Хэш с специальными символами."""
|
||
result = hashname("test-name")
|
||
assert isinstance(result, str)
|
||
assert "-" not in result
|
||
|
||
def test_hashname_with_unicode(self):
|
||
"""Хэш с unicode символами."""
|
||
result = hashname("тест_привет")
|
||
assert isinstance(result, str)
|
||
|
||
def test_hashname_with_emoji(self):
|
||
"""Хэш с emoji."""
|
||
result = hashname("test_🚀_rocket")
|
||
assert isinstance(result, str)
|
||
|
||
def test_hashname_with_spaces(self):
|
||
"""Хэш с пробелами."""
|
||
result = hashname("test name with spaces")
|
||
assert isinstance(result, str)
|
||
|
||
def test_hashname_empty_string(self):
|
||
"""Хэш пустой строки."""
|
||
result = hashname("")
|
||
assert isinstance(result, str)
|
||
assert len(result) > 0
|
||
|
||
def test_hashname_very_long_string(self):
|
||
"""Хэш очень длинной строки."""
|
||
long_name = "a" * 10000
|
||
result = hashname(long_name)
|
||
assert isinstance(result, str)
|
||
assert len(result) < 100
|
||
|
||
def test_hashname_consistency(self):
|
||
"""Хэш должен быть консистентным."""
|
||
result1 = hashname("test")
|
||
result2 = hashname("test")
|
||
assert result1 == result2
|
||
|
||
|
||
# =============================================================================
|
||
# Тесты universal_qualname
|
||
# =============================================================================
|
||
|
||
class TestUniversalQualnameEdgeCases:
|
||
"""Тесты universal_qualname со странными значениями."""
|
||
|
||
def test_universal_qualname_with_special_chars(self):
|
||
"""qualname с специальными символами."""
|
||
result = universal_qualname("test-name")
|
||
assert isinstance(result, str)
|
||
|
||
def test_universal_qualname_with_unicode(self):
|
||
"""qualname с unicode."""
|
||
result = universal_qualname("тест_привет")
|
||
assert isinstance(result, str)
|
||
|
||
def test_universal_qualname_with_emoji(self):
|
||
"""qualname с emoji."""
|
||
result = universal_qualname("test_🚀")
|
||
assert isinstance(result, str)
|
||
|
||
def test_universal_qualname_with_brackets(self):
|
||
"""qualname с скобками (Generic types)."""
|
||
result = universal_qualname("List[int]")
|
||
assert isinstance(result, str)
|
||
|
||
def test_universal_qualname_with_angle_brackets(self):
|
||
"""qualname с угловыми скобками."""
|
||
result = universal_qualname("Dict[str, int]")
|
||
assert isinstance(result, str)
|
||
|
||
def test_universal_qualname_class(self):
|
||
"""qualname класса."""
|
||
class TestClass:
|
||
pass
|
||
result = universal_qualname(TestClass)
|
||
assert isinstance(result, str)
|
||
assert "TestClass" in result
|
||
|
||
|
||
# =============================================================================
|
||
# Тесты странных имён типов
|
||
# =============================================================================
|
||
|
||
class TestStrangeTypeNames:
|
||
"""Тесты со странными именами типов."""
|
||
|
||
def test_type_with_special_chars_in_name(self):
|
||
"""Тип с специальными символами в имени."""
|
||
StrangeType = type("Type-With-Dash", (), {"value": 42})
|
||
|
||
repo = ConvRepo()
|
||
|
||
@repo.mark_injector()
|
||
def int_to_strange(i: int) -> StrangeType:
|
||
return StrangeType()
|
||
|
||
def consumer(dep: StrangeType) -> int:
|
||
return dep.value
|
||
|
||
fn = repo.get_conversion((int,), consumer, force_commutative=False)
|
||
result = fn(42)
|
||
assert result == 42
|
||
|
||
def test_type_with_unicode_name(self):
|
||
"""Тип с unicode именем."""
|
||
UnicodeType = type("测试类型", (), {"value": 42})
|
||
|
||
repo = ConvRepo()
|
||
|
||
@repo.mark_injector()
|
||
def int_to_unicode(i: int) -> UnicodeType:
|
||
return UnicodeType()
|
||
|
||
def consumer(dep: UnicodeType) -> int:
|
||
return dep.value
|
||
|
||
fn = repo.get_conversion((int,), consumer, force_commutative=False)
|
||
result = fn(42)
|
||
assert result == 42
|
||
|
||
def test_type_with_very_long_name(self):
|
||
"""Тип с очень длинным именем."""
|
||
LongType = type("a" * 500, (), {"value": 42})
|
||
|
||
repo = ConvRepo()
|
||
|
||
@repo.mark_injector()
|
||
def int_to_long(i: int) -> LongType:
|
||
return LongType()
|
||
|
||
def consumer(dep: LongType) -> int:
|
||
return dep.value
|
||
|
||
fn = repo.get_conversion((int,), consumer, force_commutative=False)
|
||
result = fn(42)
|
||
assert result == 42
|
||
|
||
def test_type_with_spaces_in_name(self):
|
||
"""Тип с пробелами в имени."""
|
||
SpaceType = type("Type With Spaces", (), {"value": 42})
|
||
|
||
repo = ConvRepo()
|
||
|
||
@repo.mark_injector()
|
||
def int_to_space(i: int) -> SpaceType:
|
||
return SpaceType()
|
||
|
||
def consumer(dep: SpaceType) -> int:
|
||
return dep.value
|
||
|
||
fn = repo.get_conversion((int,), consumer, force_commutative=False)
|
||
result = fn(42)
|
||
assert result == 42
|
||
|
||
def test_type_with_reserved_word_name(self):
|
||
"""Тип с именем зарезервированного слова."""
|
||
ClassType = type("class", (), {"value": 42})
|
||
|
||
repo = ConvRepo()
|
||
|
||
@repo.mark_injector()
|
||
def int_to_class(i: int) -> ClassType:
|
||
return ClassType()
|
||
|
||
def consumer(dep: ClassType) -> int:
|
||
return dep.value
|
||
|
||
fn = repo.get_conversion((int,), consumer, force_commutative=False)
|
||
result = fn(42)
|
||
assert result == 42
|
||
|
||
def test_multiple_types_with_same_name(self):
|
||
"""Несколько типов с одинаковым именем (в разных scope)."""
|
||
Type1 = type("SameType", (), {"value": 1})
|
||
Type2 = type("SameType", (), {"value": 2})
|
||
|
||
repo = ConvRepo()
|
||
|
||
@repo.mark_injector()
|
||
def int_to_type1(i: int) -> Type1:
|
||
return Type1()
|
||
|
||
@repo.mark_injector()
|
||
def int_to_type2(i: int) -> Type2:
|
||
return Type2()
|
||
|
||
def consumer1(dep: Type1) -> int:
|
||
return dep.value
|
||
|
||
def consumer2(dep: Type2) -> int:
|
||
return dep.value
|
||
|
||
fn1 = repo.get_conversion((int,), consumer1, force_commutative=False)
|
||
result1 = fn1(42)
|
||
assert result1 == 1
|
||
|
||
fn2 = repo.get_conversion((int,), consumer2, force_commutative=False)
|
||
result2 = fn2(42)
|
||
assert result2 == 2
|
||
|
||
|
||
# =============================================================================
|
||
# Тесты странных зависимостей
|
||
# =============================================================================
|
||
|
||
class TestStrangeDependencies:
|
||
"""Тесты со странными зависимостями."""
|
||
|
||
def test_circular_type_dependency(self):
|
||
"""Циклическая зависимость типов."""
|
||
repo = ConvRepo()
|
||
|
||
@dataclass
|
||
class TypeA:
|
||
value: int
|
||
|
||
@dataclass
|
||
class TypeB:
|
||
value: int
|
||
|
||
@repo.mark_injector()
|
||
def a_to_b(a: TypeA) -> TypeB:
|
||
return TypeB(a.value)
|
||
|
||
@repo.mark_injector()
|
||
def b_to_a(b: TypeB) -> TypeA:
|
||
return TypeA(b.value)
|
||
|
||
def consumer(dep: TypeA) -> int:
|
||
return dep.value
|
||
|
||
# Должно работать без бесконечной рекурсии
|
||
fn = repo.get_conversion((TypeA,), consumer, force_commutative=False)
|
||
result = fn(TypeA(42))
|
||
assert result == 42
|
||
|
||
def test_many_similar_types(self):
|
||
"""Много похожих типов."""
|
||
repo = ConvRepo()
|
||
|
||
# Создаём 50 похожих типов
|
||
types = []
|
||
for i in range(50):
|
||
t = type(f"Type{i}", (), {"value": i})
|
||
types.append(t)
|
||
|
||
def make_injector(idx, type_t):
|
||
def injector(i: int) -> type_t:
|
||
return type_t()
|
||
injector.__name__ = f"int_to_type_{idx}"
|
||
return injector
|
||
|
||
repo.add_injector(make_injector(i, t))
|
||
|
||
def consumer(dep: types[0]) -> int:
|
||
return dep.value
|
||
|
||
# Должно работать без коллизий
|
||
fn = repo.get_conversion((int,), consumer, force_commutative=False)
|
||
result = fn(42)
|
||
assert result == 0
|
||
|
||
|
||
# =============================================================================
|
||
# Тесты коллизий хэшей
|
||
# =============================================================================
|
||
|
||
class TestHashCollisions:
|
||
"""Тесты коллизий хэшей."""
|
||
|
||
def test_hashname_different_types(self):
|
||
"""Разные типы должны иметь разные хэши (обычно)."""
|
||
h1 = hashname(int)
|
||
h2 = hashname(str)
|
||
|
||
assert isinstance(h1, str)
|
||
assert isinstance(h2, str)
|
||
|
||
def test_universal_qualname_different_types(self):
|
||
"""universal_qualname для разных типов."""
|
||
q1 = universal_qualname(int)
|
||
q2 = universal_qualname(str)
|
||
|
||
assert q1 != q2
|
||
|
||
|
||
# =============================================================================
|
||
# Тесты Any аннотаций
|
||
# =============================================================================
|
||
|
||
class TestAnyAnnotation:
|
||
"""Тесты с Any аннотациями."""
|
||
|
||
def test_injector_with_any_annotation(self):
|
||
"""Инжектор с Any аннотацией."""
|
||
repo = ConvRepo()
|
||
|
||
@repo.mark_injector()
|
||
def int_to_any(i: int) -> Any:
|
||
return i
|
||
|
||
def consumer(dep: Any) -> int:
|
||
return dep
|
||
|
||
fn = repo.get_conversion((int,), consumer, force_commutative=False)
|
||
result = fn(42)
|
||
assert result == 42
|