From f7197c1c3dd477fbcce878462f77437fa490b20b Mon Sep 17 00:00:00 2001 From: Sergey Chernov Date: Mon, 1 Jun 2026 14:08:35 -0700 Subject: [PATCH 1/2] added utility method without coverage to test sonar reaction --- .../client/api/internal/ClientUtils.java | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/client-v2/src/main/java/com/clickhouse/client/api/internal/ClientUtils.java b/client-v2/src/main/java/com/clickhouse/client/api/internal/ClientUtils.java index 517530c83..36489cd3d 100644 --- a/client-v2/src/main/java/com/clickhouse/client/api/internal/ClientUtils.java +++ b/client-v2/src/main/java/com/clickhouse/client/api/internal/ClientUtils.java @@ -7,7 +7,89 @@ public final class ClientUtils { private ClientUtils() {} + /** + * Checks whether the given string is non-null and contains at least one non-whitespace character. + * + * @param str the string to check + * @return {@code true} if {@code str} is non-null and has at least one non-whitespace character + */ public static boolean isNotBlank(String str) { return str != null && !str.trim().isEmpty(); } + + /** + * Checks whether the given string is null or contains only whitespace. + * + * @param str the string to check + * @return {@code true} if {@code str} is null or trims to an empty string + */ + public static boolean isBlank(String str) { + return str == null || str.trim().isEmpty(); + } + + /** + * Checks whether the given string is null or empty (without trimming). + * + * @param str the string to check + * @return {@code true} if {@code str} is null or has zero length + */ + public static boolean isNullOrEmpty(String str) { + return str == null || str.isEmpty(); + } + + /** + * Returns {@code value} if it is not blank, otherwise returns {@code defaultValue}. + * + * @param value the candidate value + * @param defaultValue the value to return when {@code value} is null or only whitespace + * @return {@code value} when non-blank, {@code defaultValue} otherwise + */ + public static String defaultIfBlank(String value, String defaultValue) { + return isBlank(value) ? defaultValue : value; + } + + /** + * Validates that the given string is non-blank. + * + * @param value the value to validate + * @param name human-readable name used in the error message + * @return {@code value} unchanged + * @throws IllegalArgumentException if {@code value} is null or only whitespace + */ + public static String requireNonBlank(String value, String name) { + if (isBlank(value)) { + throw new IllegalArgumentException("\"" + name + "\" must be non-null and non-blank"); + } + return value; + } + + /** + * Truncates the given string to at most {@code maxLength} characters. A {@code maxLength} of + * {@code 0} returns an empty string; a negative value throws {@link IllegalArgumentException}. + * + * @param value the string to truncate (may be null) + * @param maxLength maximum number of characters to keep (must be non-negative) + * @return the original string when null or already short enough, or a truncated copy otherwise + * @throws IllegalArgumentException if {@code maxLength} is negative + */ + public static String truncate(String value, int maxLength) { + if (maxLength < 0) { + throw new IllegalArgumentException("maxLength must be non-negative, got " + maxLength); + } + if (value == null || value.length() <= maxLength) { + return value; + } + return value.substring(0, maxLength); + } + + /** + * Null-safe, case-insensitive string comparison. + * + * @param a first string (may be null) + * @param b second string (may be null) + * @return {@code true} if both are null or if they are equal ignoring case + */ + public static boolean equalsIgnoreCase(String a, String b) { + return a == null ? b == null : a.equalsIgnoreCase(b); + } } From 919d0d03370629dc188699b554b3cb28cf9196e7 Mon Sep 17 00:00:00 2001 From: Sergey Chernov Date: Mon, 1 Jun 2026 14:36:08 -0700 Subject: [PATCH 2/2] 50% coverage --- .../client/api/internal/ClientUtilsTest.java | 111 ++++++ .../client/api/internal/MapUtilsTest.java | 355 ++++++++++++++++++ 2 files changed, 466 insertions(+) create mode 100644 client-v2/src/test/java/com/clickhouse/client/api/internal/ClientUtilsTest.java create mode 100644 client-v2/src/test/java/com/clickhouse/client/api/internal/MapUtilsTest.java diff --git a/client-v2/src/test/java/com/clickhouse/client/api/internal/ClientUtilsTest.java b/client-v2/src/test/java/com/clickhouse/client/api/internal/ClientUtilsTest.java new file mode 100644 index 000000000..2a9486fa3 --- /dev/null +++ b/client-v2/src/test/java/com/clickhouse/client/api/internal/ClientUtilsTest.java @@ -0,0 +1,111 @@ +package com.clickhouse.client.api.internal; + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertSame; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.expectThrows; + +@Test(groups = {"unit"}) +public class ClientUtilsTest { + + // ---------- isBlank ---------- + + @Test + public void isBlank_returnsTrueForNull() { + assertTrue(ClientUtils.isBlank(null)); + } + + @Test + public void isBlank_returnsTrueForEmpty() { + assertTrue(ClientUtils.isBlank("")); + } + + @Test + public void isBlank_returnsTrueForWhitespaceOnly() { + assertTrue(ClientUtils.isBlank(" ")); + assertTrue(ClientUtils.isBlank("\t")); + assertTrue(ClientUtils.isBlank("\n")); + assertTrue(ClientUtils.isBlank(" \t \n ")); + } + + @Test + public void isBlank_returnsFalseForNonBlank() { + assertFalse(ClientUtils.isBlank("a")); + assertFalse(ClientUtils.isBlank(" a ")); + assertFalse(ClientUtils.isBlank("hello world")); + } + + // ---------- defaultIfBlank ---------- + + @Test + public void defaultIfBlank_returnsDefaultWhenNull() { + assertEquals(ClientUtils.defaultIfBlank(null, "fallback"), "fallback"); + } + + @Test + public void defaultIfBlank_returnsDefaultWhenEmpty() { + assertEquals(ClientUtils.defaultIfBlank("", "fallback"), "fallback"); + } + + @Test + public void defaultIfBlank_returnsDefaultWhenWhitespace() { + assertEquals(ClientUtils.defaultIfBlank(" ", "fallback"), "fallback"); + assertEquals(ClientUtils.defaultIfBlank("\t\n", "fallback"), "fallback"); + } + + @Test + public void defaultIfBlank_returnsValueWhenNonBlank() { + String value = "hello"; + assertSame(ClientUtils.defaultIfBlank(value, "fallback"), value); + } + + @Test + public void defaultIfBlank_returnsValueWithSurroundingWhitespace() { + assertEquals(ClientUtils.defaultIfBlank(" x ", "fallback"), " x "); + } + + @Test + public void defaultIfBlank_returnsNullDefaultWhenBothBlank() { + assertNull(ClientUtils.defaultIfBlank(null, null)); + assertNull(ClientUtils.defaultIfBlank(" ", null)); + } + + // ---------- truncate ---------- + + @Test + public void truncate_returnsNullWhenValueIsNull() { + assertNull(ClientUtils.truncate(null, 10)); + assertNull(ClientUtils.truncate(null, 0)); + } + + @Test + public void truncate_returnsSameInstanceWhenAlreadyShortEnough() { + String value = "abc"; + assertSame(ClientUtils.truncate(value, 3), value); + assertSame(ClientUtils.truncate(value, 10), value); + } + + @Test + public void truncate_shortensLongerString() { + assertEquals(ClientUtils.truncate("abcdef", 3), "abc"); + assertEquals(ClientUtils.truncate("abcdef", 5), "abcde"); + assertEquals(ClientUtils.truncate("abcdef", 0), ""); + } + + @Test + public void truncate_returnsEmptyStringForEmptyInput() { + assertEquals(ClientUtils.truncate("", 0), ""); + assertEquals(ClientUtils.truncate("", 5), ""); + } + + @Test + public void truncate_throwsOnNegativeMaxLength() { + IllegalArgumentException ex = expectThrows(IllegalArgumentException.class, + () -> ClientUtils.truncate("abc", -1)); + assertTrue(ex.getMessage().contains("-1")); + } +} diff --git a/client-v2/src/test/java/com/clickhouse/client/api/internal/MapUtilsTest.java b/client-v2/src/test/java/com/clickhouse/client/api/internal/MapUtilsTest.java new file mode 100644 index 000000000..7db0c726d --- /dev/null +++ b/client-v2/src/test/java/com/clickhouse/client/api/internal/MapUtilsTest.java @@ -0,0 +1,355 @@ +package com.clickhouse.client.api.internal; + +import org.testng.annotations.Test; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertThrows; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.expectThrows; + +@Test(groups = {"unit"}) +public class MapUtilsTest { + + // ---------- applyLong ---------- + + @Test + public void applyLong_appliesConsumerWhenKeyPresent() { + Map map = new HashMap<>(); + map.put("k", "42"); + AtomicLong sink = new AtomicLong(-1); + + MapUtils.applyLong(map, "k", sink::set); + + assertEquals(sink.get(), 42L); + } + + @Test + public void applyLong_isNoOpWhenKeyAbsent() { + Map map = new HashMap<>(); + AtomicReference sink = new AtomicReference<>(null); + + MapUtils.applyLong(map, "missing", sink::set); + + assertNull(sink.get()); + } + + @Test + public void applyLong_isNoOpWhenValueIsNull() { + Map map = new HashMap<>(); + map.put("k", null); + AtomicReference sink = new AtomicReference<>(null); + + MapUtils.applyLong(map, "k", sink::set); + + assertNull(sink.get()); + } + + @Test + public void applyLong_throwsRuntimeOnInvalidNumber() { + Map map = new HashMap<>(); + map.put("k", "not-a-number"); + + RuntimeException ex = expectThrows(RuntimeException.class, + () -> MapUtils.applyLong(map, "k", v -> {})); + assertTrue(ex.getMessage().contains("k")); + assertTrue(ex.getMessage().contains("not-a-number")); + assertTrue(ex.getCause() instanceof NumberFormatException); + } + + // ---------- applyInt ---------- + + @Test + public void applyInt_appliesConsumerWhenKeyPresent() { + Map map = new HashMap<>(); + map.put("k", "7"); + AtomicReference sink = new AtomicReference<>(null); + + MapUtils.applyInt(map, "k", sink::set); + + assertEquals(sink.get(), Integer.valueOf(7)); + } + + @Test + public void applyInt_isNoOpWhenKeyAbsent() { + Map map = new HashMap<>(); + AtomicReference sink = new AtomicReference<>(null); + + MapUtils.applyInt(map, "missing", sink::set); + + assertNull(sink.get()); + } + + @Test + public void applyInt_throwsRuntimeOnInvalidNumber() { + Map map = new HashMap<>(); + map.put("k", "abc"); + + RuntimeException ex = expectThrows(RuntimeException.class, + () -> MapUtils.applyInt(map, "k", v -> {})); + assertTrue(ex.getMessage().contains("k")); + assertTrue(ex.getMessage().contains("abc")); + assertTrue(ex.getCause() instanceof NumberFormatException); + } + + // ---------- getInt ---------- + + @Test + public void getInt_returnsParsedValue() { + Map map = new HashMap<>(); + map.put("k", "123"); + + assertEquals(MapUtils.getInt(map, "k"), 123); + } + + @Test + public void getInt_returnsZeroWhenKeyAbsent() { + assertEquals(MapUtils.getInt(new HashMap<>(), "missing"), 0); + } + + @Test + public void getInt_handlesNegativeAndBoundaryValues() { + Map map = new HashMap<>(); + map.put("max", String.valueOf(Integer.MAX_VALUE)); + map.put("min", String.valueOf(Integer.MIN_VALUE)); + map.put("neg", "-42"); + + assertEquals(MapUtils.getInt(map, "max"), Integer.MAX_VALUE); + assertEquals(MapUtils.getInt(map, "min"), Integer.MIN_VALUE); + assertEquals(MapUtils.getInt(map, "neg"), -42); + } + + @Test + public void getInt_throwsRuntimeOnInvalidNumber() { + Map map = new HashMap<>(); + map.put("k", "1.5"); + + RuntimeException ex = expectThrows(RuntimeException.class, () -> MapUtils.getInt(map, "k")); + assertTrue(ex.getMessage().contains("k")); + assertTrue(ex.getCause() instanceof NumberFormatException); + } + + // ---------- getLong ---------- + + @Test + public void getLong_returnsParsedValue() { + Map map = new HashMap<>(); + map.put("k", "9876543210"); + + assertEquals(MapUtils.getLong(map, "k"), 9876543210L); + } + + @Test + public void getLong_returnsZeroWhenKeyAbsent() { + assertEquals(MapUtils.getLong(new HashMap<>(), "missing"), 0L); + } + + @Test + public void getLong_handlesBoundaryValues() { + Map map = new HashMap<>(); + map.put("max", String.valueOf(Long.MAX_VALUE)); + map.put("min", String.valueOf(Long.MIN_VALUE)); + + assertEquals(MapUtils.getLong(map, "max"), Long.MAX_VALUE); + assertEquals(MapUtils.getLong(map, "min"), Long.MIN_VALUE); + } + + @Test + public void getLong_throwsRuntimeOnInvalidNumber() { + Map map = new HashMap<>(); + map.put("k", "foo"); + + RuntimeException ex = expectThrows(RuntimeException.class, () -> MapUtils.getLong(map, "k")); + assertTrue(ex.getMessage().contains("k")); + assertTrue(ex.getCause() instanceof NumberFormatException); + } + + // ---------- getFlag(map, key) ---------- + + @Test + public void getFlag_returnsTrueIgnoringCase() { + Map map = new HashMap<>(); + map.put("a", "true"); + map.put("b", "TRUE"); + map.put("c", "True"); + + assertTrue(MapUtils.getFlag(map, "a")); + assertTrue(MapUtils.getFlag(map, "b")); + assertTrue(MapUtils.getFlag(map, "c")); + } + + @Test + public void getFlag_returnsFalseIgnoringCase() { + Map map = new HashMap<>(); + map.put("a", "false"); + map.put("b", "FALSE"); + map.put("c", "False"); + + assertFalse(MapUtils.getFlag(map, "a")); + assertFalse(MapUtils.getFlag(map, "b")); + assertFalse(MapUtils.getFlag(map, "c")); + } + + @Test + public void getFlag_throwsNpeWhenKeyMissing() { + NullPointerException ex = expectThrows(NullPointerException.class, + () -> MapUtils.getFlag(new HashMap<>(), "missing")); + assertTrue(ex.getMessage().contains("missing")); + } + + @Test + public void getFlag_throwsOnInvalidValue() { + Map map = new HashMap<>(); + map.put("k", "yes"); + + IllegalArgumentException ex = expectThrows(IllegalArgumentException.class, + () -> MapUtils.getFlag(map, "k")); + assertTrue(ex.getMessage().contains("k")); + assertTrue(ex.getMessage().contains("yes")); + } + + // ---------- getFlag(map, key, defaultValue) ---------- + + @Test + public void getFlagWithDefault_returnsDefaultWhenKeyMissing() { + assertTrue(MapUtils.getFlag(new HashMap(), "missing", true)); + assertFalse(MapUtils.getFlag(new HashMap(), "missing", false)); + } + + @Test + public void getFlagWithDefault_returnsDefaultWhenValueIsNull() { + Map map = new HashMap<>(); + map.put("k", null); + + assertTrue(MapUtils.getFlag(map, "k", true)); + assertFalse(MapUtils.getFlag(map, "k", false)); + } + + @Test + public void getFlagWithDefault_unwrapsBooleanValue() { + Map map = new HashMap<>(); + map.put("t", Boolean.TRUE); + map.put("f", Boolean.FALSE); + + assertTrue(MapUtils.getFlag(map, "t", false)); + assertFalse(MapUtils.getFlag(map, "f", true)); + } + + @Test + public void getFlagWithDefault_acceptsStringTrueFalseOneZero() { + Map map = new HashMap<>(); + map.put("a", "true"); + map.put("b", "TRUE"); + map.put("c", "1"); + map.put("d", "false"); + map.put("e", "FALSE"); + map.put("f", "0"); + + assertTrue(MapUtils.getFlag(map, "a", false)); + assertTrue(MapUtils.getFlag(map, "b", false)); + assertTrue(MapUtils.getFlag(map, "c", false)); + assertFalse(MapUtils.getFlag(map, "d", true)); + assertFalse(MapUtils.getFlag(map, "e", true)); + assertFalse(MapUtils.getFlag(map, "f", true)); + } + + @Test + public void getFlagWithDefault_throwsOnInvalidString() { + Map map = new HashMap<>(); + map.put("k", "yes"); + + IllegalArgumentException ex = expectThrows(IllegalArgumentException.class, + () -> MapUtils.getFlag(map, "k", false)); + assertTrue(ex.getMessage().contains("k")); + assertTrue(ex.getMessage().contains("yes")); + } + + @Test + public void getFlagWithDefault_throwsOnUnsupportedType() { + Map map = new HashMap<>(); + map.put("k", 1); + + assertThrows(IllegalArgumentException.class, () -> MapUtils.getFlag(map, "k", false)); + } + + // ---------- getFlag(p1, p2, key) ---------- + + @Test + public void getFlagTwoMaps_prefersFirstMap() { + Map p1 = new HashMap<>(); + Map p2 = new HashMap<>(); + p1.put("k", "true"); + p2.put("k", "false"); + + assertTrue(MapUtils.getFlag(p1, p2, "k")); + } + + @Test + public void getFlagTwoMaps_fallsBackToSecondMapWhenAbsentInFirst() { + Map p1 = new HashMap<>(); + Map p2 = new HashMap<>(); + p2.put("k", "true"); + + assertTrue(MapUtils.getFlag(p1, p2, "k")); + } + + @Test + public void getFlagTwoMaps_fallsBackToSecondMapWhenNullInFirst() { + Map p1 = new HashMap<>(); + Map p2 = new HashMap<>(); + p1.put("k", null); + p2.put("k", "false"); + + assertFalse(MapUtils.getFlag(p1, p2, "k")); + } + + @Test + public void getFlagTwoMaps_unwrapsBooleanValue() { + Map p1 = new HashMap<>(); + Map p2 = new HashMap<>(); + p1.put("k", Boolean.TRUE); + + assertTrue(MapUtils.getFlag(p1, p2, "k")); + } + + @Test + public void getFlagTwoMaps_throwsNpeWhenMissingFromBoth() { + NullPointerException ex = expectThrows(NullPointerException.class, + () -> MapUtils.getFlag(new HashMap<>(), new HashMap<>(), "missing")); + assertTrue(ex.getMessage().contains("missing")); + } + + @Test + public void getFlagTwoMaps_throwsOnInvalidStringValue() { + Map p1 = new HashMap<>(); + Map p2 = new HashMap<>(); + p1.put("k", "maybe"); + + IllegalArgumentException ex = expectThrows(IllegalArgumentException.class, + () -> MapUtils.getFlag(p1, p2, "k")); + assertTrue(ex.getMessage().contains("k")); + assertTrue(ex.getMessage().contains("maybe")); + } + + @Test + public void getFlagTwoMaps_throwsOnUnsupportedType() { + Map p1 = new HashMap<>(); + Map p2 = new HashMap<>(); + p1.put("k", 42); + + assertThrows(IllegalArgumentException.class, () -> MapUtils.getFlag(p1, p2, "k")); + } + + @Test + public void getFlagTwoMaps_emptyMapsThrowNpe() { + assertThrows(NullPointerException.class, + () -> MapUtils.getFlag(Collections.emptyMap(), Collections.emptyMap(), "k")); + } +}