import pytest
from pattern_core import Pattern, Subject
class TestNoneValueHandling:
def test_none_as_point_value(self):
p = Pattern.point(None)
assert p is not None
assert p.is_atomic()
def test_none_in_pattern_list(self):
p = Pattern.pattern("root", Pattern.from_values([1, None, "test"]))
assert p.length() == 3
values = list(p.values())
assert len(values) == 4 assert None in values
def test_subject_property_with_none(self):
s = Subject("test-id")
with pytest.raises(TypeError, match="Cannot convert"):
s.set_property("prop", None)
def test_map_returning_none(self):
p = Pattern.pattern("root", Pattern.from_values([1, 2, 3]))
result = p.map(lambda x: None)
assert result is not None
assert result.length() == 3
def test_filter_with_none_predicate(self):
p = Pattern.pattern("root", Pattern.from_values([1, None, 3]))
result = p.filter(lambda x: x is not None)
assert isinstance(result, list)
assert len(result) >= 3
class TestDeepNesting:
def test_deeply_nested_patterns(self):
depth = 100
p = Pattern.point(42)
for i in range(depth):
p = Pattern.pattern(f"level-{i}", [p])
assert p.depth() >= depth
assert not p.is_atomic()
assert p.size() > 0
def test_deep_nesting_with_extract(self):
depth = 50
p = Pattern.point("core")
for i in range(depth):
p = Pattern.pattern(f"level-{i}", [p])
extracted = p.extract()
assert extracted is not None
def test_deep_nesting_with_map(self):
depth = 30
p = Pattern.point(1)
for i in range(depth):
p = Pattern.pattern(i, [p])
result = p.map(lambda x: x * 2 if isinstance(x, int) else x)
assert result is not None
assert result.depth() == p.depth()
def test_very_wide_pattern(self):
width = 1000
elements = list(range(width))
p = Pattern.pattern("root", Pattern.from_values(elements))
assert p.length() == width
values = list(p.values())
assert len(values) == width + 1
def test_deep_and_wide_pattern(self):
def create_level(depth_remaining):
if depth_remaining == 0:
return Pattern.point(42)
children = [create_level(depth_remaining - 1) for _ in range(10)]
return Pattern.pattern(f"level-{depth_remaining}", children)
p = create_level(5)
assert not p.is_atomic()
assert p.length() == 10
assert p.depth() >= 5
class TestTypeConversionErrors:
def test_invalid_callback_type(self):
p = Pattern.pattern("root", Pattern.from_values([1, 2, 3]))
try:
result = p.map("not a function")
assert result is not None
except (TypeError, AttributeError):
pass
def test_callback_raising_exception(self):
p = Pattern.pattern("root", Pattern.from_values([1, 2, 3]))
def bad_callback(x):
raise ValueError("Test error")
try:
result = p.map(bad_callback)
assert result is not None
except (ValueError, RuntimeError, Exception):
pass
def test_invalid_property_key_type(self):
s = Subject("test-id")
with pytest.raises(TypeError):
s.set_property(123, "value")
def test_pattern_from_invalid_list_type(self):
with pytest.raises(TypeError):
Pattern.from_values("not a list")
def test_subject_invalid_identity_type(self):
with pytest.raises(TypeError):
Subject(12345)
def test_fold_with_invalid_accumulator(self):
p = Pattern.pattern("root", Pattern.from_values([1, 2, 3]))
def bad_fold(acc, x):
return "string" if acc == 0 else acc + x
try:
result = p.fold(0, bad_fold)
assert result is not None
except (TypeError, RuntimeError):
pass
def test_extend_with_non_function(self):
p = Pattern.pattern("root", Pattern.from_values([1, 2, 3]))
try:
result = p.extend(None)
assert result is not None
except (TypeError, AttributeError):
pass
class TestMemoryAndLimits:
def test_large_pattern_creation(self):
size = 10000
elements = list(range(size))
p = Pattern.pattern("root", Pattern.from_values(elements))
assert p.length() == size
count = 0
for _ in p.values():
count += 1
assert count == size + 1
def test_pattern_with_large_strings(self):
large_string = "x" * 100000 p = Pattern.pattern("root", Pattern.from_values([large_string] * 10))
assert p.length() == 10
values = list(p.values())
large_values = [v for v in values if isinstance(v, str) and len(v) > 1000]
assert len(large_values) > 0
assert len(large_values[0]) == 100000
def test_cyclic_reference_prevention(self):
p1 = Pattern.point(1)
p2 = Pattern.pattern(2, [p1])
assert p2.depth() < 100 assert p2.size() < 100
class TestConcurrencyAndThreadSafety:
def test_pattern_immutability(self):
p1 = Pattern.pattern("root", Pattern.from_values([1, 2, 3]))
p2 = p1.map(lambda x: x * 2 if isinstance(x, int) else x)
v1 = list(p1.values())
v2 = list(p2.values())
assert v1 == ["root", 1, 2, 3]
assert v2[1] == 2
def test_subject_mutation_isolation(self):
s1 = Subject("test")
s1.add_label("Label1")
s1.set_property("key", "value")
p1 = Pattern.point(s1)
s1.add_label("Label2")
s1.set_property("key", "modified")
subject_value = p1.extract()
assert subject_value is not None
class TestErrorMessages:
def test_type_error_message_quality(self):
p = Pattern.pattern("root", Pattern.from_values([1, 2, 3]))
try:
p.map(123) except TypeError as e:
error_msg = str(e)
assert "callable" in error_msg.lower() or "function" in error_msg.lower()
def test_validation_error_clarity(self):
try:
s = Subject("") assert s is not None
except (ValueError, RuntimeError) as e:
error_msg = str(e)
assert len(error_msg) > 0
assert "unwrap" not in error_msg.lower()
assert "panic" not in error_msg.lower()
if __name__ == "__main__":
pytest.main([__file__, "-v"])