From 35f0256305659734713daacb79358f7548511164 Mon Sep 17 00:00:00 2001 From: Gutierrez Soto Jhosepmyr Orlando Date: Fri, 10 Jul 2026 13:41:27 -0500 Subject: [PATCH] fix(discovery): format the duplicate-story similarity with Locale.ROOT The near-duplicate 409's ProblemDetail detail carries the similarity as a machine-parsed value (the client regexes "similarity 0.87" and shows the %). It was formatted with the JVM default locale, so a comma-decimal locale would emit "0,87" and the client would read the score as 0 (0%) on the duplicate surface. Force Locale.ROOT so the decimal is always a dot. --- .../domain/exception/DiscoveryExceptions.java | 7 +++- .../exception/DiscoveryExceptionsTest.java | 34 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 src/test/java/com/kntro/reqsai/discovery/domain/exception/DiscoveryExceptionsTest.java diff --git a/src/main/java/com/kntro/reqsai/discovery/domain/exception/DiscoveryExceptions.java b/src/main/java/com/kntro/reqsai/discovery/domain/exception/DiscoveryExceptions.java index 9b0c028f..74c72ded 100644 --- a/src/main/java/com/kntro/reqsai/discovery/domain/exception/DiscoveryExceptions.java +++ b/src/main/java/com/kntro/reqsai/discovery/domain/exception/DiscoveryExceptions.java @@ -4,6 +4,8 @@ import com.kntro.reqsai.shared.domain.exception.DomainException; import com.kntro.reqsai.shared.domain.exception.EntityNotFoundException; +import java.util.Locale; + /** * Factory for Requirement Discovery domain exceptions — the context-specific counterpart of the shared * {@code Exceptions} and a mirror of {@code WorkspaceExceptions}. @@ -15,8 +17,11 @@ private DiscoveryExceptions() { } public static DomainException duplicateUserStory(double similarity) { + // Format with Locale.ROOT so the decimal separator is always a dot: the client parses this + // similarity out of the ProblemDetail `detail` expecting "0.87", and a comma-decimal JVM locale + // would otherwise make it read the score as 0 (0%) on the duplicate-story surface. return new DomainException(DiscoveryError.DUPLICATE_USER_STORY, - "A near-duplicate user story already exists (similarity %.2f)".formatted(similarity)); + String.format(Locale.ROOT, "A near-duplicate user story already exists (similarity %.2f)", similarity)); } public static EntityNotFoundException sessionNotFound(java.util.UUID id) { diff --git a/src/test/java/com/kntro/reqsai/discovery/domain/exception/DiscoveryExceptionsTest.java b/src/test/java/com/kntro/reqsai/discovery/domain/exception/DiscoveryExceptionsTest.java new file mode 100644 index 00000000..e7f23b6f --- /dev/null +++ b/src/test/java/com/kntro/reqsai/discovery/domain/exception/DiscoveryExceptionsTest.java @@ -0,0 +1,34 @@ +package com.kntro.reqsai.discovery.domain.exception; + +import com.kntro.reqsai.shared.domain.exception.DomainException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Locale; + +import static org.assertj.core.api.Assertions.assertThat; + +@DisplayName("Discovery: duplicate-story exception") +class DiscoveryExceptionsTest { + + private final Locale original = Locale.getDefault(); + + @AfterEach + void restoreLocale() { + Locale.setDefault(original); + } + + @Test + @DisplayName("similarity is dot-formatted even under a comma-decimal default locale") + void similarity_dot_formatted_regardless_of_locale() { + // A comma-decimal locale (es-PE/Germany) would otherwise render "0,87", which the client parses + // as 0 (0%) off the ProblemDetail detail. Locale.ROOT keeps the machine-parsed value a dot. + Locale.setDefault(Locale.GERMANY); + + DomainException ex = DiscoveryExceptions.duplicateUserStory(0.87); + + assertThat(ex.getMessage()).contains("similarity 0.87"); + assertThat(ex.getMessage()).doesNotContain("0,87"); + } +}