Skip to content

Commit dd3b23e

Browse files
committed
feat: Optimize slug. Extend tests.
1 parent 9bc7e82 commit dd3b23e

File tree

2 files changed

+108
-1
lines changed

2 files changed

+108
-1
lines changed

src/validators/slug.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
# local
77
from .utils import validator
88

9+
_SLUG_REGEXP = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*$")
10+
911

1012
@validator
1113
def slug(value: str, /):
@@ -27,4 +29,4 @@ def slug(value: str, /):
2729
(Literal[True]): If `value` is a valid slug.
2830
(ValidationError): If `value` is an invalid slug.
2931
"""
30-
return re.match(r"^[a-z0-9]+(?:-[a-z0-9]+)*$", value) if value else False
32+
return _SLUG_REGEXP.match(value) is not None

tests/test_slug.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
"""Test Slug."""
22

33
# external
4+
5+
from typing import Any
6+
47
import pytest
58

69
# local
@@ -10,6 +13,35 @@
1013
@pytest.mark.parametrize(
1114
"value",
1215
[
16+
# with numbers
17+
"hello123",
18+
"123hello",
19+
"123",
20+
# with hyphens
21+
"hello-world",
22+
"hello-world-test",
23+
"test-123-abc",
24+
# with mixed alphanumeric
25+
"hello123-world456",
26+
"123-456-789",
27+
"a1-b2-c3",
28+
# with single character
29+
"a",
30+
"1",
31+
"z",
32+
# with complex slug
33+
"test-123-abc-456-def",
34+
"this-is-a-very-long-slug-with-many-words",
35+
# with multiple hyphens
36+
"a-b-c",
37+
"1-2-3",
38+
# very long
39+
"a" * 100,
40+
# very long with hyphens
41+
"a-" * 50 + "end",
42+
# very long with hyphens and different alphabets|letters
43+
"a1-b2-c3-d4-e5-f6-g7-h8-i9-j0",
44+
# old
1345
"123-asd-7sda",
1446
"123-k-123",
1547
"dac-12sa-459",
@@ -24,6 +56,46 @@ def test_returns_true_on_valid_slug(value: str):
2456
@pytest.mark.parametrize(
2557
"value",
2658
[
59+
# with empty_string
60+
"",
61+
# with upper case
62+
"Hello",
63+
"HELLO",
64+
"hello-World",
65+
"Test-Slug",
66+
# with special characters
67+
"hello world",
68+
"hello_world",
69+
"hello.world",
70+
"hello@world",
71+
"hello#world",
72+
"hello$world",
73+
# starting with hyphen
74+
"-hello",
75+
"-123",
76+
"-hello-world",
77+
# with consecutive hyphens
78+
"hello--world",
79+
"test---slug",
80+
"a--b",
81+
# with unicode characters
82+
"café",
83+
"привет",
84+
"東京",
85+
"hello-мир",
86+
# with spaces
87+
"hello world",
88+
" hello ",
89+
"hello world",
90+
# with only hyphens
91+
"-",
92+
"--",
93+
"---",
94+
# with mixed case
95+
"HelloWorld",
96+
"helloWorld",
97+
"HELLOworld",
98+
# old
2799
"some.slug&",
28100
"1231321%",
29101
" 21312",
@@ -33,3 +105,36 @@ def test_returns_true_on_valid_slug(value: str):
33105
def test_returns_failed_validation_on_invalid_slug(value: str):
34106
"""Test returns failed validation on invalid slug."""
35107
assert isinstance(slug(value), ValidationError)
108+
109+
110+
@pytest.mark.parametrize(
111+
("val", "input_type"),
112+
[
113+
(1, "int"),
114+
(1.0, "float"),
115+
(None, "NoneType"),
116+
([], "list"),
117+
((), "tuple"),
118+
({}, "dict"),
119+
],
120+
)
121+
def test_non_string_input(val: Any, input_type: Any):
122+
"""Test with non string input."""
123+
# given
124+
message = f"expected string or bytes-like object, got '{input_type}'"
125+
# when
126+
result = slug(val)
127+
# then
128+
assert isinstance(result, ValidationError)
129+
assert result.reason == message
130+
131+
132+
def test_slug_function_signature():
133+
"""Test with keyword argument."""
134+
# given
135+
message = "slug() got some positional-only arguments passed as keyword arguments: 'value'"
136+
# when
137+
result = slug(value="test-slug")
138+
# then
139+
assert isinstance(result, ValidationError)
140+
assert result.reason == message

0 commit comments

Comments
 (0)