From 53cf2a6f48f8b41d7d8ec99efeaadf13b440ec11 Mon Sep 17 00:00:00 2001 From: Clemens Schwaighofer Date: Thu, 23 Oct 2025 15:47:19 +0900 Subject: [PATCH] Add prepare_url_slash to string_helpers.py and tests Function cleans up url paths (without domain) by ensuring they start with a single slash and removing double slashes. --- .../string_handling/string_helpers.py | 18 ++++ test-run/string_handling/string_helpers.py | 15 +++- .../string_handling/test_string_helpers.py | 90 ++++++++++++++++++- 3 files changed, 121 insertions(+), 2 deletions(-) diff --git a/src/corelibs/string_handling/string_helpers.py b/src/corelibs/string_handling/string_helpers.py index 366c483..8fe02a9 100644 --- a/src/corelibs/string_handling/string_helpers.py +++ b/src/corelibs/string_handling/string_helpers.py @@ -2,6 +2,7 @@ String helpers """ +import re from decimal import Decimal, getcontext from textwrap import shorten @@ -101,4 +102,21 @@ def format_number(number: float, precision: int = 0) -> str: "f}" ).format(_number) + +def prepare_url_slash(url: str) -> str: + """ + if the URL does not start with /, add slash + strip all double slashes in URL + + Arguments: + url {str} -- _description_ + + Returns: + str -- _description_ + """ + url = re.sub(r'\/+', '/', url) + if not url.startswith("/"): + url = "/" + url + return url + # __END__ diff --git a/test-run/string_handling/string_helpers.py b/test-run/string_handling/string_helpers.py index f17b849..a9a5c29 100644 --- a/test-run/string_handling/string_helpers.py +++ b/test-run/string_handling/string_helpers.py @@ -5,7 +5,7 @@ Test string_handling/string_helpers import sys from decimal import Decimal, getcontext from textwrap import shorten -from corelibs.string_handling.string_helpers import shorten_string, format_number +from corelibs.string_handling.string_helpers import shorten_string, format_number, prepare_url_slash from corelibs.string_handling.text_colors import Colors @@ -73,6 +73,18 @@ def __sh_colors(): print(f"Underline/Yellow/Bold: {Colors.underline}{Colors.bold}{Colors.yellow}UNDERLINE YELLOW BOLD{Colors.reset}") +def __prepare_url_slash(): + urls = [ + "api/v1/resource", + "/api/v1/resource", + "///api//v1//resource//", + "api//v1/resource/", + ] + for url in urls: + prepared = prepare_url_slash(url) + print(f"IN: {url} -> OUT: {prepared}") + + def main(): """ Test: corelibs.string_handling.string_helpers @@ -80,6 +92,7 @@ def main(): __sh_shorten_string() __sh_format_number() __sh_colors() + __prepare_url_slash() if __name__ == "__main__": diff --git a/tests/unit/string_handling/test_string_helpers.py b/tests/unit/string_handling/test_string_helpers.py index fcd676f..55430ab 100644 --- a/tests/unit/string_handling/test_string_helpers.py +++ b/tests/unit/string_handling/test_string_helpers.py @@ -5,7 +5,7 @@ PyTest: string_handling/string_helpers from textwrap import shorten import pytest from corelibs.string_handling.string_helpers import ( - shorten_string, left_fill, format_number + shorten_string, left_fill, format_number, prepare_url_slash ) @@ -191,6 +191,75 @@ class TestFormatNumber: assert result == "0.001" +class TestPrepareUrlSlash: + """Tests for prepare_url_slash function""" + + def test_url_without_leading_slash(self): + """Test that URL without leading slash gets one added""" + result = prepare_url_slash("api/users") + assert result == "/api/users" + + def test_url_with_leading_slash(self): + """Test that URL with leading slash remains unchanged""" + result = prepare_url_slash("/api/users") + assert result == "/api/users" + + def test_url_with_double_slashes(self): + """Test that double slashes are reduced to single slash""" + result = prepare_url_slash("/api//users") + assert result == "/api/users" + + def test_url_with_multiple_slashes(self): + """Test that multiple consecutive slashes are reduced to single slash""" + result = prepare_url_slash("api///users////data") + assert result == "/api/users/data" + + def test_url_with_leading_double_slash(self): + """Test URL starting with double slash""" + result = prepare_url_slash("//api/users") + assert result == "/api/users" + + def test_url_without_slash_and_double_slashes(self): + """Test URL without leading slash and containing double slashes""" + result = prepare_url_slash("api//users//data") + assert result == "/api/users/data" + + def test_single_slash(self): + """Test single slash URL""" + result = prepare_url_slash("/") + assert result == "/" + + def test_multiple_slashes_only(self): + """Test URL with only multiple slashes""" + result = prepare_url_slash("///") + assert result == "/" + + def test_empty_string(self): + """Test empty string""" + result = prepare_url_slash("") + assert result == "/" + + def test_url_with_query_params(self): + """Test URL with query parameters""" + result = prepare_url_slash("/api/users?id=1") + assert result == "/api/users?id=1" + + def test_url_with_double_slashes_and_query(self): + """Test URL with double slashes and query parameters""" + result = prepare_url_slash("api//users?id=1") + assert result == "/api/users?id=1" + + def test_complex_url_path(self): + """Test complex URL path with multiple segments""" + result = prepare_url_slash("api/v1/users/123/profile") + assert result == "/api/v1/users/123/profile" + + def test_complex_url_with_multiple_issues(self): + """Test URL with both missing leading slash and multiple double slashes""" + result = prepare_url_slash("api//v1///users//123////profile") + assert result == "/api/v1/users/123/profile" + + # Additional integration tests class TestIntegration: """Integration tests combining functions""" @@ -236,4 +305,23 @@ def test_format_number_parametrized(number: float | int, precision: int, expecte """Parametrized test for format_number""" assert format_number(number, precision) == expected + +@pytest.mark.parametrize("input_url,expected", [ + ("api/users", "/api/users"), + ("/api/users", "/api/users"), + ("api//users", "/api/users"), + ("/api//users", "/api/users"), + ("//api/users", "/api/users"), + ("api///users////data", "/api/users/data"), + ("/", "/"), + ("///", "/"), + ("", "/"), + ("api/v1/users/123", "/api/v1/users/123"), + ("/api/users?id=1&name=test", "/api/users?id=1&name=test"), + ("api//users//123//profile", "/api/users/123/profile"), +]) +def test_prepare_url_slash_parametrized(input_url: str, expected: str): + """Parametrized test for prepare_url_slash""" + assert prepare_url_slash(input_url) == expected + # __END__