Repository: jhipster/jhipster-lite
Pull request #9909 (2024-05-26 07:19:15)

feat: add string not matching pattern field assertion


Patch:
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
index f800e78dc..7f1562129 100644
--- a/.mvn/wrapper/maven-wrapper.properties
+++ b/.mvn/wrapper/maven-wrapper.properties
@@ -14,4 +14,4 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.7/apache-maven-3.9.7-bin.zip
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip
diff --git a/src/main/java/tech/jhipster/lite/generator/server/springboot/mvc/security/oauth2/core/domain/OAuth2ModuleFactory.java b/src/main/java/tech/jhipster/lite/generator/server/springboot/mvc/security/oauth2/core/domain/OAuth2ModuleFactory.java
index 173561113..8d9a76cbc 100644
--- a/src/main/java/tech/jhipster/lite/generator/server/springboot/mvc/security/oauth2/core/domain/OAuth2ModuleFactory.java
+++ b/src/main/java/tech/jhipster/lite/generator/server/springboot/mvc/security/oauth2/core/domain/OAuth2ModuleFactory.java
@@ -3,6 +3,7 @@ package tech.jhipster.lite.generator.server.springboot.mvc.security.oauth2.core.
 import static tech.jhipster.lite.generator.server.springboot.mvc.security.common.domain.AuthenticationModulesFactory.authenticationModuleBuilder;
 import static tech.jhipster.lite.module.domain.JHipsterModule.*;
 
+import java.util.regex.Pattern;
 import tech.jhipster.lite.module.domain.JHipsterModule;
 import tech.jhipster.lite.module.domain.docker.DockerImageVersion;
 import tech.jhipster.lite.module.domain.docker.DockerImages;
@@ -21,6 +22,7 @@ public class OAuth2ModuleFactory {
   public static final String CLIENT_SCOPE_NAME = "keycloakClientScopeName";
   public static final String DEFAULT_CLIENT_SCOPE_NAME = "jhipster";
 
+  private static final Pattern NAME_FORMAT = Pattern.compile("^[a-z0-9-]+$");
   private static final TextNeedleBeforeReplacer IMPORT_NEEDLE = lineBeforeText(
     "import org.springframework.boot.test.context.SpringBootTest;"
   );
@@ -52,8 +54,8 @@ public class OAuth2ModuleFactory {
 
     var realmName = properties.getOrDefaultString(REALM_NAME, DEFAULT_REALM_NAME);
     var clientScopeName = properties.getOrDefaultString(CLIENT_SCOPE_NAME, DEFAULT_CLIENT_SCOPE_NAME);
-    Assert.field(REALM_NAME, realmName).notBlank().noWhitespace().maxLength(30).urlSafeSingleWord();
-    Assert.field(CLIENT_SCOPE_NAME, clientScopeName).notBlank().noWhitespace().maxLength(30).urlSafeSingleWord();
+    Assert.field(REALM_NAME, realmName).notNull().matchesPattern(NAME_FORMAT).maxLength(30);
+    Assert.field(CLIENT_SCOPE_NAME, clientScopeName).notNull().matchesPattern(NAME_FORMAT).maxLength(30);
 
     JHipsterModuleBuilder builder = authenticationModuleBuilder(properties);
 
diff --git a/src/main/java/tech/jhipster/lite/module/domain/JHipsterSlug.java b/src/main/java/tech/jhipster/lite/module/domain/JHipsterSlug.java
index 1138c325a..efbd6b9aa 100644
--- a/src/main/java/tech/jhipster/lite/module/domain/JHipsterSlug.java
+++ b/src/main/java/tech/jhipster/lite/module/domain/JHipsterSlug.java
@@ -1,5 +1,6 @@
 package tech.jhipster.lite.module.domain;
 
+import java.util.regex.Pattern;
 import org.apache.commons.lang3.builder.EqualsBuilder;
 import org.apache.commons.lang3.builder.HashCodeBuilder;
 import tech.jhipster.lite.shared.error.domain.Assert;
@@ -7,10 +8,12 @@ import tech.jhipster.lite.shared.generation.domain.ExcludeFromGeneratedCodeCover
 
 public abstract sealed class JHipsterSlug implements Comparable<JHipsterSlug> permits JHipsterModuleSlug, JHipsterFeatureSlug {
 
+  private static final Pattern SLUG_FORMAT = Pattern.compile("^[a-z0-9-]+$");
+
   private final String slug;
 
   protected JHipsterSlug(String slug) {
-    Assert.field("slug", slug).notBlank().urlSafeSingleWord(() -> new InvalidJHipsterSlugException(slug));
+    Assert.field("slug", slug).notBlank().matchesPatternOrThrow(SLUG_FORMAT, () -> new InvalidJHipsterSlugException(slug));
 
     this.slug = slug;
   }
diff --git a/src/main/java/tech/jhipster/lite/module/domain/properties/JHipsterProjectBaseName.java b/src/main/java/tech/jhipster/lite/module/domain/properties/JHipsterProjectBaseName.java
index f885bf6f1..159aaa5bd 100644
--- a/src/main/java/tech/jhipster/lite/module/domain/properties/JHipsterProjectBaseName.java
+++ b/src/main/java/tech/jhipster/lite/module/domain/properties/JHipsterProjectBaseName.java
@@ -1,9 +1,11 @@
 package tech.jhipster.lite.module.domain.properties;
 
+import java.util.regex.Pattern;
 import org.apache.commons.lang3.StringUtils;
 import tech.jhipster.lite.shared.error.domain.Assert;
 
 public record JHipsterProjectBaseName(String name) {
+  private static final Pattern NAME_PATTERN = Pattern.compile("^[a-zA-Z0-9]+$");
   private static final String DEFAULT_NAME = "jhipster";
 
   public static final JHipsterProjectBaseName DEFAULT = new JHipsterProjectBaseName(DEFAULT_NAME);
@@ -17,7 +19,7 @@ public record JHipsterProjectBaseName(String name) {
       return DEFAULT_NAME;
     }
 
-    Assert.field("baseName", name).namePattern(InvalidProjectBaseNameException::new);
+    Assert.field("baseName", name).matchesPatternOrThrow(NAME_PATTERN, InvalidProjectBaseNameException::new);
 
     return name;
   }
diff --git a/src/main/java/tech/jhipster/lite/module/domain/resource/JHipsterModuleTag.java b/src/main/java/tech/jhipster/lite/module/domain/resource/JHipsterModuleTag.java
index 1c77c75a0..fcf8886bc 100644
--- a/src/main/java/tech/jhipster/lite/module/domain/resource/JHipsterModuleTag.java
+++ b/src/main/java/tech/jhipster/lite/module/domain/resource/JHipsterModuleTag.java
@@ -1,10 +1,13 @@
 package tech.jhipster.lite.module.domain.resource;
 
+import java.util.regex.Pattern;
 import tech.jhipster.lite.shared.error.domain.Assert;
 
 public record JHipsterModuleTag(String tag) {
+  private static final Pattern TAG_FORMAT = Pattern.compile("^[a-z0-9-]+$");
+
   public JHipsterModuleTag {
-    Assert.field("tag", tag).notNull().noWhitespace().maxLength(15).urlSafeSingleWord(() -> new InvalidJHipsterModuleTagException(tag));
+    Assert.field("tag", tag).notNull().maxLength(15).matchesPatternOrThrow(TAG_FORMAT, () -> new InvalidJHipsterModuleTagException(tag));
   }
 
   public String get() {
diff --git a/src/main/java/tech/jhipster/lite/shared/error/domain/Assert.java b/src/main/java/tech/jhipster/lite/shared/error/domain/Assert.java
index 6ea169fdd..b47cd8707 100644
--- a/src/main/java/tech/jhipster/lite/shared/error/domain/Assert.java
+++ b/src/main/java/tech/jhipster/lite/shared/error/domain/Assert.java
@@ -315,9 +315,6 @@ public final class Assert {
   public static final class StringAsserter {
 
     public static final Pattern PATTERN_SPACE = Pattern.compile("\\s");
-    private static final Pattern TAG_FORMAT = Pattern.compile("^[a-z0-9-]+$");
-    private static final Pattern NAME_PATTERN = Pattern.compile("^[a-zA-Z0-9]+$");
-
     private final String field;
     private final String value;
 
@@ -374,51 +371,6 @@ public final class Assert {
       return this;
     }
 
-    /**
-     * Ensure that the value only contains lower case letters or numbers, no whitespace or any separator.
-     *
-     * @return The current asserter
-     * @throws UrlSafeSingleWordException
-     *           if the value contain any special character
-     */
-    public StringAsserter urlSafeSingleWord() {
-      return urlSafeSingleWord(() -> new UrlSafeSingleWordException(field));
-    }
-
-    /**
-     * Ensure that the value only contains lower case letters or numbers, no
-     * whitespace or any separator.
-     *
-     * @param exceptionCreator
-     *            the exception to be thrown, if the constraint is not valid.
-     * @return The current asserter
-     * @throws the exception returned by the supplier if the value contain any
-     *             special character
-     */
-    public StringAsserter urlSafeSingleWord(Supplier<RuntimeException> exceptionCreator) {
-      if (!TAG_FORMAT.matcher(value).matches()) {
-        throw exceptionCreator.get();
-      }
-      return this;
-    }
-
-    /**
-     * Ensure that the value only contains ASCII letters or numbers, no
-     * whitespace or any separator.
-     *
-     * @param exceptionCreator
-     *            the exception to be thrown, if the constraint is not valid.
-     * @return The current asserter
-     * @throws the exception returned by the supplier if the value contain any
-     *             special character
-     */
-    public StringAsserter namePattern(Supplier<RuntimeException> exceptionCreator) {
-      if (!NAME_PATTERN.matcher(value).matches()) {
-        throw exceptionCreator.get();
-      }
-      return this;
-    }
-
     /**
      * Ensure that the input value is at least of the given length
      *
@@ -465,6 +417,41 @@ public final class Assert {
 
       return this;
     }
+
+    /**
+     * Ensure that the given input value matches the given pattern
+     *
+     * @param pattern pattern of the {@link String}
+     * @return The current asserter
+     * @throws NotMatchingExpectedPatternException
+     *           if the value does not match the provided pattern
+     */
+    public StringAsserter matchesPattern(Pattern pattern) {
+      return matchesPatternOrThrow(
+        pattern,
+        () -> NotMatchingExpectedPatternException.builder().field(field).value(value).pattern(pattern).build()
+      );
+    }
+
+    /**
+     * Ensure that the given input value matches the given pattern
+     *
+     * @param pattern pattern of the {@link String}
+     * @param exceptionSupplier supplier of the exception to throw if the value does not match the pattern
+     * @return The current asserter
+     * @throws X provided exception if the value does not match the provided pattern
+     */
+    public <X extends RuntimeException> StringAsserter matchesPatternOrThrow(Pattern pattern, Supplier<X> exceptionSupplier) {
+      if (value == null) {
+        return this;
+      }
+
+      if (!pattern.matcher(value).matches()) {
+        throw exceptionSupplier.get();
+      }
+
+      return this;
+    }
   }
 
   /**
diff --git a/src/main/java/tech/jhipster/lite/shared/error/domain/AssertionErrorType.java b/src/main/java/tech/jhipster/lite/shared/error/domain/AssertionErrorType.java
index f2383597d..0b5f8d6f3 100644
--- a/src/main/java/tech/jhipster/lite/shared/error/domain/AssertionErrorType.java
+++ b/src/main/java/tech/jhipster/lite/shared/error/domain/AssertionErrorType.java
@@ -7,9 +7,9 @@ public enum AssertionErrorType {
   NULL_ELEMENT_IN_COLLECTION,
   NUMBER_VALUE_TOO_HIGH,
   NUMBER_VALUE_TOO_LOW,
+  STRING_NOT_MATCHING_PATTERN,
   STRING_TOO_LONG,
   STRING_TOO_SHORT,
   STRING_WITH_WHITESPACES,
   TOO_MANY_ELEMENTS,
-  URL_SAFE_SINGLE_WORD,
 }
diff --git a/src/main/java/tech/jhipster/lite/shared/error/domain/NotMatchingExpectedPatternException.java b/src/main/java/tech/jhipster/lite/shared/error/domain/NotMatchingExpectedPatternException.java
new file mode 100644
index 000000000..ddef3afc9
--- /dev/null
+++ b/src/main/java/tech/jhipster/lite/shared/error/domain/NotMatchingExpectedPatternException.java
@@ -0,0 +1,63 @@
+package tech.jhipster.lite.shared.error.domain;
+
+import java.util.Map;
+import java.util.regex.Pattern;
+
+public final class NotMatchingExpectedPatternException extends AssertionException {
+
+  private final String pattern;
+
+  private NotMatchingExpectedPatternException(NotMatchingExpectedPatternExceptionBuilder builder) {
+    super(builder.field, builder.message());
+    pattern = builder.pattern;
+  }
+
+  public static NotMatchingExpectedPatternExceptionBuilder builder() {
+    return new NotMatchingExpectedPatternExceptionBuilder();
+  }
+
+  public static final class NotMatchingExpectedPatternExceptionBuilder {
+
+    private String value;
+    private String pattern;
+    private String field;
+
+    private NotMatchingExpectedPatternExceptionBuilder() {}
+
+    NotMatchingExpectedPatternExceptionBuilder field(String field) {
+      this.field = field;
+
+      return this;
+    }
+
+    NotMatchingExpectedPatternExceptionBuilder value(String value) {
+      this.value = value;
+
+      return this;
+    }
+
+    NotMatchingExpectedPatternExceptionBuilder pattern(Pattern pattern) {
+      this.pattern = pattern.pattern();
+
+      return this;
+    }
+
+    private String message() {
+      return "The value \"" + value + "\" in field \"" + field + "\" must match the pattern " + pattern;
+    }
+
+    public NotMatchingExpectedPatternException build() {
+      return new NotMatchingExpectedPatternException(this);
+    }
+  }
+
+  @Override
+  public AssertionErrorType type() {
+    return AssertionErrorType.STRING_NOT_MATCHING_PATTERN;
+  }
+
+  @Override
+  public Map<String, String> parameters() {
+    return Map.of("pattern", pattern);
+  }
+}
diff --git a/src/main/java/tech/jhipster/lite/shared/error/domain/UrlSafeSingleWordException.java b/src/main/java/tech/jhipster/lite/shared/error/domain/UrlSafeSingleWordException.java
deleted file mode 100644
index 65bd72bf7..000000000
--- a/src/main/java/tech/jhipster/lite/shared/error/domain/UrlSafeSingleWordException.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package tech.jhipster.lite.shared.error.domain;
-
-public class UrlSafeSingleWordException extends AssertionException {
-
-  public UrlSafeSingleWordException(String field) {
-    super(field, message(field));
-  }
-
-  private static String message(String field) {
-    return "The field \"%s\" is not a single word containing only lower case characters and numbers".formatted(field);
-  }
-
-  @Override
-  public AssertionErrorType type() {
-    return AssertionErrorType.URL_SAFE_SINGLE_WORD;
-  }
-}
diff --git a/src/main/resources/generator/buildtool/maven/.mvn/wrapper/maven-wrapper.properties b/src/main/resources/generator/buildtool/maven/.mvn/wrapper/maven-wrapper.properties
index f800e78dc..7f1562129 100644
--- a/src/main/resources/generator/buildtool/maven/.mvn/wrapper/maven-wrapper.properties
+++ b/src/main/resources/generator/buildtool/maven/.mvn/wrapper/maven-wrapper.properties
@@ -14,4 +14,4 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.7/apache-maven-3.9.7-bin.zip
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip
diff --git a/src/main/resources/messages/assertions-errors/assertion-errors-messages.properties b/src/main/resources/messages/assertions-errors/assertion-errors-messages.properties
index e11cf47ca..af994cff4 100644
--- a/src/main/resources/messages/assertions-errors/assertion-errors-messages.properties
+++ b/src/main/resources/messages/assertions-errors/assertion-errors-messages.properties
@@ -31,5 +31,5 @@ assertion-error.STRING_WITH_WHITESPACES.title=String with whitespaces
 assertion-error.TOO_MANY_ELEMENTS.detail=There is too many elements in {{ field }}, max is {{ maxSize }} (current {{ currentSize }} element(s))
 assertion-error.TOO_MANY_ELEMENTS.title=Too many elements
 
-assertion-error.URL_SAFE_SINGLE_WORD.detail=The string {{ field }} must only have lower case characters and numbers
-assertion-error.URL_SAFE_SINGLE_WORD.title=String with special characters
+assertion-error.STRING_NOT_MATCHING_PATTERN.detail=The string {{ field }} must match the pattern {{ pattern }}
+assertion-error.STRING_NOT_MATCHING_PATTERN.title=String not matching the expected pattern
diff --git a/src/main/resources/messages/assertions-errors/assertion-errors-messages_fr.properties b/src/main/resources/messages/assertions-errors/assertion-errors-messages_fr.properties
index 0a79ac346..3fc35f357 100644
--- a/src/main/resources/messages/assertions-errors/assertion-errors-messages_fr.properties
+++ b/src/main/resources/messages/assertions-errors/assertion-errors-messages_fr.properties
@@ -32,5 +32,5 @@ assertion-error.STRING_WITH_WHITESPACES.title=Chaîne avec des espaces
 assertion-error.TOO_MANY_ELEMENTS.detail=Il y a trop d'éléments dans {{ field }}, le maximum est de {{ maxSize }} (il y a actuellement {{ currentSize }} élément(s))
 assertion-error.TOO_MANY_ELEMENTS.title=Trop d'éléments
 
-assertion-error.URL_SAFE_SINGLE_WORD.detail=The string {{ field }} must only have lower case characters and numbers
-assertion-error.URL_SAFE_SINGLE_WORD.title=String with special characters
+assertion-error.STRING_NOT_MATCHING_PATTERN.detail=La châine {{ field }} doit correspondre au modèle {{ pattern }}
+assertion-error.STRING_NOT_MATCHING_PATTERN.title=Chaîne ne correspondant pas au modèle attendu


Test patch:
diff --git a/src/test/java/tech/jhipster/lite/module/domain/JHipsterTagsTest.java b/src/test/java/tech/jhipster/lite/module/domain/JHipsterTagsTest.java
deleted file mode 100644
index 99681d311..000000000
--- a/src/test/java/tech/jhipster/lite/module/domain/JHipsterTagsTest.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package tech.jhipster.lite.module.domain;
-
-import static org.assertj.core.api.Assertions.*;
-
-import org.junit.jupiter.api.Test;
-import tech.jhipster.lite.UnitTest;
-import tech.jhipster.lite.module.domain.resource.JHipsterModuleTags;
-import tech.jhipster.lite.module.domain.resource.JHipsterModuleTags.JHipsterModuleTagsBuilder;
-import tech.jhipster.lite.shared.error.domain.StringWithWhitespacesException;
-
-@UnitTest
-class JHipsterTagsTest {
-
-  @Test
-  void shouldNotBeValidWithWhitespace() {
-    JHipsterModuleTagsBuilder builder = JHipsterModuleTags.builder();
-
-    assertThatThrownBy(() -> builder.add("my tag").build()).isInstanceOf(StringWithWhitespacesException.class);
-  }
-}
diff --git a/src/test/java/tech/jhipster/lite/module/domain/resource/JHipsterModuleTagTest.java b/src/test/java/tech/jhipster/lite/module/domain/resource/JHipsterModuleTagTest.java
index f241568fe..eba9b4d2b 100644
--- a/src/test/java/tech/jhipster/lite/module/domain/resource/JHipsterModuleTagTest.java
+++ b/src/test/java/tech/jhipster/lite/module/domain/resource/JHipsterModuleTagTest.java
@@ -1,6 +1,7 @@
 package tech.jhipster.lite.module.domain.resource;
 
-import static org.assertj.core.api.Assertions.*;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 import org.apache.commons.lang3.RandomStringUtils;
 import org.junit.jupiter.api.Test;
@@ -9,7 +10,6 @@ import org.junit.jupiter.params.provider.ValueSource;
 import tech.jhipster.lite.UnitTest;
 import tech.jhipster.lite.shared.error.domain.MissingMandatoryValueException;
 import tech.jhipster.lite.shared.error.domain.StringTooLongException;
-import tech.jhipster.lite.shared.error.domain.StringWithWhitespacesException;
 
 @UnitTest
 class JHipsterModuleTagTest {
@@ -21,7 +21,7 @@ class JHipsterModuleTagTest {
 
   @Test
   void shouldNotBuildWithWhitespace() {
-    assertThatThrownBy(() -> new JHipsterModuleTag("my tag")).isInstanceOf(StringWithWhitespacesException.class);
+    assertThatThrownBy(() -> new JHipsterModuleTag("my tag")).isInstanceOf(InvalidJHipsterModuleTagException.class);
   }
 
   @Test
diff --git a/src/test/java/tech/jhipster/lite/shared/error/domain/AssertTest.java b/src/test/java/tech/jhipster/lite/shared/error/domain/AssertTest.java
index 9081454d6..bcc4baaea 100644
--- a/src/test/java/tech/jhipster/lite/shared/error/domain/AssertTest.java
+++ b/src/test/java/tech/jhipster/lite/shared/error/domain/AssertTest.java
@@ -5,12 +5,9 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 import java.math.BigDecimal;
 import java.time.Instant;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
+import java.util.*;
+import java.util.regex.Pattern;
+import org.junit.jupiter.api.*;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.ValueSource;
 import tech.jhipster.lite.UnitTest;
@@ -143,16 +140,23 @@ class AssertTest {
     }
 
     @Test
-    void shouldNotValidateUrlSafeSingleWord() {
-      assertThatThrownBy(() -> Assert.field(FIELD_NAME, "mytag?").urlSafeSingleWord())
-        .isExactlyInstanceOf(UrlSafeSingleWordException.class)
+    void shouldNotValidateStringNotMatchingPattern() {
+      assertThatThrownBy(() -> Assert.field(FIELD_NAME, "mytag?").matchesPattern(Pattern.compile("^[a-z0-9-]+$")))
+        .isExactlyInstanceOf(NotMatchingExpectedPatternException.class)
         .hasMessageContaining(FIELD_NAME)
-        .hasMessageContaining("not a single word containing only lower case characters and");
+        .hasMessageContaining("The value \"mytag?\" in field \"fieldName\" must match the pattern ^[a-z0-9-]+$");
     }
 
     @Test
-    void shouldValidateUrlSafeSingleWord() {
-      assertThatCode(() -> Assert.field(FIELD_NAME, "my-tag").urlSafeSingleWord()).doesNotThrowAnyException();
+    void shouldValidateStringNotMatchingPattern() {
+      assertThatCode(() -> Assert.field(FIELD_NAME, "my-tag").matchesPattern(Pattern.compile("^[a-z0-9-]+$"))).doesNotThrowAnyException();
+    }
+
+    @Test
+    void shouldValidateNullStringWhenVerifyingMatchingPattern() {
+      assertThatCode(
+        () -> Assert.field(FIELD_NAME, (String) null).matchesPattern(Pattern.compile("^[a-z0-9-]+$"))
+      ).doesNotThrowAnyException();
     }
 
     @Test
diff --git a/src/test/java/tech/jhipster/lite/shared/error/infrastructure/primary/AssertionErrorsHandlerIT.java b/src/test/java/tech/jhipster/lite/shared/error/infrastructure/primary/AssertionErrorsHandlerIT.java
index ef995072f..a19b01da8 100644
--- a/src/test/java/tech/jhipster/lite/shared/error/infrastructure/primary/AssertionErrorsHandlerIT.java
+++ b/src/test/java/tech/jhipster/lite/shared/error/infrastructure/primary/AssertionErrorsHandlerIT.java
@@ -1,7 +1,8 @@
 package tech.jhipster.lite.shared.error.infrastructure.primary;
 
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
 
 import java.util.Locale;
 import org.junit.jupiter.api.Test;
@@ -118,13 +119,13 @@ class AssertionErrorsHandlerIT {
   }
 
   @Test
-  void shouldHandleUrlSafeSingleWords() throws Exception {
+  void shouldHandleStringNotMatchingExpectedPattern() throws Exception {
     rest
-      .perform(get("/api/assertion-errors/url-safe-single-word"))
+      .perform(get("/api/assertion-errors/string-not-matching-pattern"))
       .andExpect(status().isBadRequest())
-      .andExpect(jsonPath("title").value("String with special characters"))
-      .andExpect(jsonPath("detail").value("The string myField must only have lower case characters and numbers"))
-      .andExpect(jsonPath("key").value("URL_SAFE_SINGLE_WORD"));
+      .andExpect(jsonPath("title").value("String not matching the expected pattern"))
+      .andExpect(jsonPath("detail").value("The string myField must match the pattern ^[a-z0-9-]+$"))
+      .andExpect(jsonPath("key").value("STRING_NOT_MATCHING_PATTERN"));
   }
 
   @Test
diff --git a/src/test/java/tech/jhipster/lite/shared/error_generator/infrastructure/primary/AssertionsErrorsResource.java b/src/test/java/tech/jhipster/lite/shared/error_generator/infrastructure/primary/AssertionsErrorsResource.java
index 91c474acd..3fedf1723 100644
--- a/src/test/java/tech/jhipster/lite/shared/error_generator/infrastructure/primary/AssertionsErrorsResource.java
+++ b/src/test/java/tech/jhipster/lite/shared/error_generator/infrastructure/primary/AssertionsErrorsResource.java
@@ -3,9 +3,8 @@ package tech.jhipster.lite.shared.error_generator.infrastructure.primary;
 import java.time.Instant;
 import java.util.Arrays;
 import java.util.List;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import java.util.regex.Pattern;
+import org.springframework.web.bind.annotation.*;
 import tech.jhipster.lite.shared.error.domain.Assert;
 import tech.jhipster.lite.shared.error_generator.domain.MissingMandatoryValueFactory;
 
@@ -58,9 +57,9 @@ class AssertionsErrorsResource {
     Assert.field("myField", "with whitespace").noWhitespace();
   }
 
-  @GetMapping("url-safe-single-word")
-  void urlSafeSingleWord() {
-    Assert.field("myField", "WithSpecialCharacters?").urlSafeSingleWord();
+  @GetMapping("string-not-matching-pattern")
+  void stringNotMatchingPattern() {
+    Assert.field("myField", "WithSpecialCharacters?").matchesPattern(Pattern.compile("^[a-z0-9-]+$"));
   }
 
   @GetMapping("not-after-time")