aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornatsuoto <[email protected]>2026-07-15 15:06:01 +0530
committernatsuoto <[email protected]>2026-07-15 15:06:01 +0530
commitbadc9afaf1d761910ccfdef644e9b7dd31ad0ece (patch)
treeea0a28eb9bf2442bd91503db52a498cca51057b0
parent45baa7584f767213ac4dcf2b0c02e600329b03d2 (diff)
downloadedify-badc9afaf1d761910ccfdef644e9b7dd31ad0ece.tar.xz
edify-badc9afaf1d761910ccfdef644e9b7dd31ad0ece.zip
test: snapshot deeply-nested alternation, combined lookaround, named-backref, and ReDoS edge fixtures
-rw-r--r--tests/docs/snapshots.test.py7
-rw-r--r--tests/fixtures/edge_cases.test.py123
-rw-r--r--tests/snapshots/fixtures/alternation_with_overlapping_prefixes_redos.regex1
-rw-r--r--tests/snapshots/fixtures/combined_lookaround.regex1
-rw-r--r--tests/snapshots/fixtures/deeply_nested_alternation.regex1
-rw-r--r--tests/snapshots/fixtures/email_shape_pattern.regex1
-rw-r--r--tests/snapshots/fixtures/hex_color_pattern.regex1
-rw-r--r--tests/snapshots/fixtures/named_backreference_pair.regex1
-rw-r--r--tests/snapshots/fixtures/nested_unbounded_quantifier_redos.regex1
-rw-r--r--tests/snapshots/fixtures/pattern_composition_via_use.regex1
10 files changed, 135 insertions, 3 deletions
diff --git a/tests/docs/snapshots.test.py b/tests/docs/snapshots.test.py
index 031a382..f734452 100644
--- a/tests/docs/snapshots.test.py
+++ b/tests/docs/snapshots.test.py
@@ -8,12 +8,11 @@ change surfaces every doc example the change touches so their prose stays
truthful.
"""
+import re
from pathlib import Path
import pytest
-import re
-
import edify
import edify.library as edify_library
from edify import Pattern
@@ -103,7 +102,9 @@ _BLOCKS_DEFERRED_TO_DOCS_REWRITE = frozenset(
@pytest.mark.parametrize(
("rst_path", "block_start", "block_source", "relative_stem"),
DISCOVERED_BLOCKS,
- ids=[f"{relative_stem}:{block_start}" for _, block_start, _, relative_stem in DISCOVERED_BLOCKS],
+ ids=[
+ f"{relative_stem}:{block_start}" for _, block_start, _, relative_stem in DISCOVERED_BLOCKS
+ ],
)
def test_doc_code_block_produces_the_snapshotted_regex(
rst_path, block_start, block_source, relative_stem
diff --git a/tests/fixtures/edge_cases.test.py b/tests/fixtures/edge_cases.test.py
new file mode 100644
index 0000000..4e74983
--- /dev/null
+++ b/tests/fixtures/edge_cases.test.py
@@ -0,0 +1,123 @@
+"""Snapshot suite for the compile-path edge-case fixtures.
+
+Each fixture builds a small, intentionally-tricky pattern and pins the
+emitted regex string. The fixtures are the ones most likely to silently
+drift when the compile path is refactored: deeply nested alternation,
+combined lookaround, item-4 named backreferences, and constructs that
+:mod:`edify` flags for ReDoS.
+"""
+
+from pathlib import Path
+
+import pytest
+
+from edify import Pattern, RegexBuilder
+from edify.testing import assert_snapshot
+
+_SNAPSHOT_ROOT = Path(__file__).parent.parent / "snapshots" / "fixtures"
+
+
+def _deeply_nested_alternation():
+ inner_alt = RegexBuilder().any_of("abc", "def", "ghi")
+ outer_alt = RegexBuilder().any_of("first", "second", "third")
+ return (
+ RegexBuilder()
+ .start_of_input()
+ .subexpression(inner_alt)
+ .string("-")
+ .subexpression(outer_alt)
+ .end_of_input()
+ )
+
+
+def _combined_lookaround_positive_and_negative():
+ return (
+ RegexBuilder()
+ .assert_ahead()
+ .digit()
+ .end()
+ .assert_not_ahead()
+ .string("0")
+ .end()
+ .assert_behind()
+ .string("$")
+ .end()
+ .digit()
+ .digit()
+ .digit()
+ )
+
+
+def _named_backreference_pair():
+ return (
+ RegexBuilder()
+ .named_capture("open")
+ .any_of("[", "(", "{")
+ .end()
+ .zero_or_more()
+ .word()
+ .named_back_reference("open")
+ )
+
+
+def _nested_unbounded_quantifier_redos():
+ return RegexBuilder().one_or_more().group().one_or_more().digit().end()
+
+
+def _alternation_with_overlapping_prefixes_redos():
+ return RegexBuilder().one_or_more().any_of("aa", "aaa", "aaaa")
+
+
+def _hex_color_pattern():
+ return (
+ RegexBuilder()
+ .start_of_input()
+ .string("#")
+ .any_of_chars("0123456789abcdefABCDEF")
+ .exactly(6)
+ .word()
+ .end_of_input()
+ )
+
+
+def _email_shape_pattern():
+ return (
+ RegexBuilder()
+ .start_of_input()
+ .one_or_more()
+ .any_of_chars("a-zA-Z0-9._%+-")
+ .string("@")
+ .one_or_more()
+ .any_of_chars("a-zA-Z0-9.-")
+ .string(".")
+ .at_least(2)
+ .letter()
+ .end_of_input()
+ )
+
+
+def _pattern_composition_via_use():
+ reusable = Pattern().at_least(2).letter()
+ return RegexBuilder().start_of_input().subexpression(reusable).end_of_input()
+
+
+_FIXTURES = [
+ ("deeply_nested_alternation", _deeply_nested_alternation),
+ ("combined_lookaround", _combined_lookaround_positive_and_negative),
+ ("named_backreference_pair", _named_backreference_pair),
+ ("nested_unbounded_quantifier_redos", _nested_unbounded_quantifier_redos),
+ ("alternation_with_overlapping_prefixes_redos", _alternation_with_overlapping_prefixes_redos),
+ ("hex_color_pattern", _hex_color_pattern),
+ ("email_shape_pattern", _email_shape_pattern),
+ ("pattern_composition_via_use", _pattern_composition_via_use),
+]
+
+
+ ("fixture_name", "builder_factory"), _FIXTURES, ids=[name for name, _ in _FIXTURES]
+)
+def test_edge_case_fixture_emits_the_snapshotted_regex(fixture_name, builder_factory):
+ builder = builder_factory()
+ emitted = builder.to_regex_string()
+ snapshot_path = _SNAPSHOT_ROOT / f"{fixture_name}.regex"
+ assert_snapshot(emitted, snapshot_path)
diff --git a/tests/snapshots/fixtures/alternation_with_overlapping_prefixes_redos.regex b/tests/snapshots/fixtures/alternation_with_overlapping_prefixes_redos.regex
new file mode 100644
index 0000000..8d3f7b5
--- /dev/null
+++ b/tests/snapshots/fixtures/alternation_with_overlapping_prefixes_redos.regex
@@ -0,0 +1 @@
+(?:aa|aaa|aaaa)+ \ No newline at end of file
diff --git a/tests/snapshots/fixtures/combined_lookaround.regex b/tests/snapshots/fixtures/combined_lookaround.regex
new file mode 100644
index 0000000..baa7e8f
--- /dev/null
+++ b/tests/snapshots/fixtures/combined_lookaround.regex
@@ -0,0 +1 @@
+(?=\d)(?!0)(?<=\$)\d\d\d \ No newline at end of file
diff --git a/tests/snapshots/fixtures/deeply_nested_alternation.regex b/tests/snapshots/fixtures/deeply_nested_alternation.regex
new file mode 100644
index 0000000..0f2c26b
--- /dev/null
+++ b/tests/snapshots/fixtures/deeply_nested_alternation.regex
@@ -0,0 +1 @@
+^(?:abc|def|ghi)\-(?:first|second|third)$ \ No newline at end of file
diff --git a/tests/snapshots/fixtures/email_shape_pattern.regex b/tests/snapshots/fixtures/email_shape_pattern.regex
new file mode 100644
index 0000000..bd17d4d
--- /dev/null
+++ b/tests/snapshots/fixtures/email_shape_pattern.regex
@@ -0,0 +1 @@
+^[a\-zA\-Z0\-9._%+-]+@[a\-zA\-Z0\-9.-]+\.[a-zA-Z]{2,}$ \ No newline at end of file
diff --git a/tests/snapshots/fixtures/hex_color_pattern.regex b/tests/snapshots/fixtures/hex_color_pattern.regex
new file mode 100644
index 0000000..c2d63c0
--- /dev/null
+++ b/tests/snapshots/fixtures/hex_color_pattern.regex
@@ -0,0 +1 @@
+^\#[0123456789abcdefABCDEF]\w{6}$ \ No newline at end of file
diff --git a/tests/snapshots/fixtures/named_backreference_pair.regex b/tests/snapshots/fixtures/named_backreference_pair.regex
new file mode 100644
index 0000000..1e3fe4a
--- /dev/null
+++ b/tests/snapshots/fixtures/named_backreference_pair.regex
@@ -0,0 +1 @@
+(?P<open>[\[\(\{])\w*(?P=open) \ No newline at end of file
diff --git a/tests/snapshots/fixtures/nested_unbounded_quantifier_redos.regex b/tests/snapshots/fixtures/nested_unbounded_quantifier_redos.regex
new file mode 100644
index 0000000..e5aaf8b
--- /dev/null
+++ b/tests/snapshots/fixtures/nested_unbounded_quantifier_redos.regex
@@ -0,0 +1 @@
+(?:\d+)+ \ No newline at end of file
diff --git a/tests/snapshots/fixtures/pattern_composition_via_use.regex b/tests/snapshots/fixtures/pattern_composition_via_use.regex
new file mode 100644
index 0000000..70b3c7c
--- /dev/null
+++ b/tests/snapshots/fixtures/pattern_composition_via_use.regex
@@ -0,0 +1 @@
+^[a-zA-Z]{2,}$ \ No newline at end of file