diff --git a/devkit-utils/src/main/java/com/onixbyte/devkit/utils/AesUtil.java b/devkit-utils/src/main/java/com/onixbyte/devkit/utils/AesUtil.java index b834ecc..00619a3 100644 --- a/devkit-utils/src/main/java/com/onixbyte/devkit/utils/AesUtil.java +++ b/devkit-utils/src/main/java/com/onixbyte/devkit/utils/AesUtil.java @@ -17,21 +17,12 @@ package com.onixbyte.devkit.utils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.crypto.BadPaddingException; import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; +import java.security.GeneralSecurityException; import java.util.Base64; -import java.util.Objects; import java.util.UUID; /** @@ -42,28 +33,26 @@ * The utility methods in this class are useful for scenarios where data needs to be securely * encrypted and decrypted. *
- * + * *Example usage:
- *
- * {@code
+ * {@code
* // Encrypting and decrypting byte array data
* byte[] secretKey = "43f72073956d4c81".getBytes(StandardCharsets.UTF_8);
* byte[] data = "Hello World".getBytes(StandardCharsets.UTF_8);
* byte[] encryptedData = AesUtil.encrypt(data, secretKey);
* byte[] decryptedData = AesUtil.decrypt(encryptedData, secretKey);
* System.out.println(new String(decryptedData, StandardCharsets.UTF_8)); // Output: Hello World
- *
+ *
* // Encrypting and decrypting string data
* String secret = "43f72073956d4c81";
* String encryptedString = AesUtil.encrypt("Hello World", secret);
* String decryptedString = AesUtil.decrypt(encryptedString, secret);
* System.out.println(decryptedString); // Output: Hello World
- *
+ *
* // Generating a random secret key
* String randomSecret = AesUtil.generateRandomSecret();
- * System.out.println(randomSecret); // Output: A ramdomly generated 16-character long secret
- * }
- *
+ * System.out.println(randomSecret); // Output: A randomly generated 16-character long secret
+ * }
*
* @author hubin@baomidou
* @version 1.1.0
@@ -71,81 +60,61 @@
*/
public final class AesUtil {
- private final static Logger log = LoggerFactory.getLogger(AesUtil.class);
-
/**
- * Encrypts the data using the AES algorithm with the given secret.
+ * Encrypts the specified data using the AES algorithm with the provided secret key.
*
* @param data the data to be encrypted
- * @param secret the secret to encrypt the data
- * @return the encryption result or {@code null} if encryption failed
+ * @param secret the secret key used for encryption
+ * @return the encrypted data as a byte array
+ * @throws GeneralSecurityException if any cryptographic error occurs during encryption
*/
- public static byte[] encrypt(byte[] data, byte[] secret) {
- try {
- var secretKeySpec = new SecretKeySpec(new SecretKeySpec(secret, AES).getEncoded(), AES);
- var cipher = Cipher.getInstance(AES_CBC_CIPHER);
- cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(secret)); // set IV to secret
- return cipher.doFinal(data);
- } catch (NoSuchAlgorithmException | NoSuchPaddingException | UnsupportedOperationException |
- InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException |
- BadPaddingException exception) {
- log.error(exception.getMessage());
- for (var stackTraceElement : exception.getStackTrace()) {
- log.error("{}", stackTraceElement.toString());
- }
- }
- return null;
+ public static byte[] encrypt(byte[] data, byte[] secret) throws GeneralSecurityException {
+ var secretKeySpec = new SecretKeySpec(new SecretKeySpec(secret, AES).getEncoded(), AES);
+ var cipher = Cipher.getInstance(AES_CBC_CIPHER);
+ cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(secret));
+ return cipher.doFinal(data);
}
/**
- * Decrypts the data using the AES algorithm with the given secret.
+ * Decrypts the specified data using the AES algorithm with the provided secret key.
*
- * @param data the data to be decrypted
- * @param secret the secret to encrypt the data
- * @return the decryption result or {@code null} if decryption failed
+ * @param data the data to be decrypted
+ * @param secret the secret key used for decryption
+ * @return the decrypted data as a byte array
+ * @throws GeneralSecurityException if any cryptographic error occurs during decryption
*/
- public static byte[] decrypt(byte[] data, byte[] secret) {
- try {
- var secretKeySpec = new SecretKeySpec(new SecretKeySpec(secret, AES).getEncoded(), AES);
- var cipher = Cipher.getInstance(AES_CBC_CIPHER);
- cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(secret)); // set IV to secret
- return cipher.doFinal(data);
- } catch (NoSuchAlgorithmException | NoSuchPaddingException |
- UnsupportedOperationException | InvalidKeyException |
- InvalidAlgorithmParameterException | IllegalBlockSizeException |
- BadPaddingException exception) {
- log.error(exception.getMessage());
- for (var stackTraceElement : exception.getStackTrace()) {
- log.error("{}", stackTraceElement.toString());
- }
- }
- return null;
+ public static byte[] decrypt(byte[] data, byte[] secret) throws GeneralSecurityException {
+ var secretKeySpec = new SecretKeySpec(new SecretKeySpec(secret, AES).getEncoded(), AES);
+ var cipher = Cipher.getInstance(AES_CBC_CIPHER);
+ cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(secret));
+ return cipher.doFinal(data);
}
/**
- * Encrypts the data using the AES algorithm with the given secret.
+ * Encrypts the specified string data using the AES algorithm with the provided secret key.
*
- * @param data the data to be encrypted
- * @param secret the secret to encrypt the data
- * @return the encryption result or {@code null} if encryption failed
+ * @param data the string data to be encrypted
+ * @param secret the secret key used for encryption
+ * @return the encrypted data encoded in Base64
+ * @throws GeneralSecurityException if any cryptographic error occurs during encryption
*/
- public static String encrypt(String data, String secret) {
+ public static String encrypt(String data, String secret) throws GeneralSecurityException {
return Base64.getEncoder().encodeToString(encrypt(data.getBytes(StandardCharsets.UTF_8),
secret.getBytes(StandardCharsets.UTF_8)));
}
/**
- * Decrypts the data using the AES algorithm with the given secret.
+ * Decrypts the specified Base64-encoded string data using the AES algorithm with the provided secret key.
*
- * @param data the data to be decrypted
- * @param secret the secret to encrypt the data
- * @return the decryption result or {@code null} if decryption failed
+ * @param data the Base64-encoded string data to be decrypted
+ * @param secret the secret key used for decryption
+ * @return the decrypted string data
+ * @throws GeneralSecurityException if any cryptographic error occurs during decryption
*/
- public static String decrypt(String data, String secret) {
- return new String(Objects.requireNonNull(
- decrypt(Base64.getDecoder().decode(data.getBytes()),
- secret.getBytes(StandardCharsets.UTF_8)))
- );
+ public static String decrypt(String data, String secret) throws GeneralSecurityException {
+ var decrypted = decrypt(Base64.getDecoder().decode(data.getBytes(StandardCharsets.UTF_8)),
+ secret.getBytes(StandardCharsets.UTF_8));
+ return new String(decrypted, StandardCharsets.UTF_8);
}
/**
diff --git a/devkit-utils/src/main/java/com/onixbyte/devkit/utils/Base64Util.java b/devkit-utils/src/main/java/com/onixbyte/devkit/utils/Base64Util.java
index 4062a0a..93d0347 100644
--- a/devkit-utils/src/main/java/com/onixbyte/devkit/utils/Base64Util.java
+++ b/devkit-utils/src/main/java/com/onixbyte/devkit/utils/Base64Util.java
@@ -17,9 +17,6 @@
package com.onixbyte.devkit.utils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
@@ -58,8 +55,6 @@
*/
public final class Base64Util {
- private final static Logger log = LoggerFactory.getLogger(Base64Util.class);
-
/**
* Ensure that there is only one Base64 Encoder.
*
diff --git a/devkit-utils/src/main/java/com/onixbyte/devkit/utils/BoolUtil.java b/devkit-utils/src/main/java/com/onixbyte/devkit/utils/BoolUtil.java
index 310eb7a..4bf81da 100644
--- a/devkit-utils/src/main/java/com/onixbyte/devkit/utils/BoolUtil.java
+++ b/devkit-utils/src/main/java/com/onixbyte/devkit/utils/BoolUtil.java
@@ -17,9 +17,6 @@
package com.onixbyte.devkit.utils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import java.util.Arrays;
import java.util.Objects;
import java.util.function.BooleanSupplier;
@@ -47,8 +44,6 @@
*/
public final class BoolUtil {
- private final static Logger log = LoggerFactory.getLogger(BoolUtil.class);
-
/**
* Logical and calculation.
*
diff --git a/devkit-utils/src/main/java/com/onixbyte/devkit/utils/BranchUtil.java b/devkit-utils/src/main/java/com/onixbyte/devkit/utils/BranchUtil.java
index 6f9f136..f493a70 100644
--- a/devkit-utils/src/main/java/com/onixbyte/devkit/utils/BranchUtil.java
+++ b/devkit-utils/src/main/java/com/onixbyte/devkit/utils/BranchUtil.java
@@ -206,18 +206,4 @@ public void then(Runnable trueHandler) {
then(trueHandler, null);
}
- /**
- * Get the boolean result.
- *
- * Note: {@link BranchUtil} is not responsible for getting a raw boolean result, consider use
- * {@link BoolUtil} to replace.
- *
- * @return the result
- * @see BoolUtil
- */
- @Deprecated(forRemoval = true)
- public boolean getResult() {
- return result;
- }
-
}
diff --git a/devkit-utils/src/main/java/com/onixbyte/devkit/utils/CollectionUtil.java b/devkit-utils/src/main/java/com/onixbyte/devkit/utils/CollectionUtil.java
index 44c75c0..fd4534a 100644
--- a/devkit-utils/src/main/java/com/onixbyte/devkit/utils/CollectionUtil.java
+++ b/devkit-utils/src/main/java/com/onixbyte/devkit/utils/CollectionUtil.java
@@ -17,9 +17,6 @@
package com.onixbyte.devkit.utils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -33,8 +30,6 @@
*/
public final class CollectionUtil {
- private static final Logger log = LoggerFactory.getLogger(CollectionUtil.class);
-
/**
* Private constructor to prevent instantiation of this utility class.
*/
@@ -68,7 +63,7 @@ public static
- * If the original list's size is less than or equal to {@code maxSize}, a single sublist
- * containing all elements is returned. If the list is empty, an empty list of sub lists
- * is returned.
- *
- * @param
- * If the original list's size is less than or equal to {@code maxSize}, a single sublist
- * containing all elements is returned. If the list is empty, an empty list of sub lists
- * is returned.
- *
- * @param
+ * If {@code start} is less than {@code end}, an ascending range (exclusive of {@code end})
+ * is generated. If {@code start} is greater than {@code end}, a descending range (exclusive of {@code end})
+ * is generated. If {@code start} equals {@code end}, an empty stream is returned.
+ *
* Example Usage:
*
+ * The range includes both {@code start} and {@code end}.
+ *
* Example Usage:
*
* It creates a sequential, ordered {@code IntStream} that can be used for iteration or
* further processing.
*
+ * The stream excludes the {@code end} value.
+ *
* Example Usage:
*
+ * This implementation utilises a cryptographically strong {@link SecureRandom} instance to produce the random
+ * component of the UUID. The first 6 bytes of the UUID encode the current timestamp in milliseconds, ensuring that
+ * generated UUIDs are roughly ordered by creation time.
+ *
+ * The generated UUID adheres strictly to the layout and variant bits of UUID version 7 as defined in the specification.
+ * > chunk(List
> listFactory) {
- // check input
- if (Objects.isNull(originalList)) {
- throw new IllegalArgumentException("List cannot be null");
- }
-
- if (maxSize <= 0) {
- throw new IllegalArgumentException("Max size should be greater than 0");
- }
-
- if (Objects.isNull(listFactory)) {
- throw new IllegalArgumentException("List factory cannot be null");
- }
-
- var result = new ArrayList
>();
- var size = originalList.size();
-
- // if the original list is empty or smaller than maxSize, return it as a single sublist
- if (size <= maxSize) {
- var singleSubList = listFactory.get();
- singleSubList.addAll(originalList);
- result.add(singleSubList);
- return result;
- }
-
- // split the list
- for (var i = 0; i < size; i += maxSize) {
- var end = Math.min(i + maxSize, size); // ensure not to exceed list length
- var subList = originalList.subList(i, end);
- var subListWrapper = listFactory.get();
- subListWrapper.addAll(subList);
- result.add(subListWrapper); // create a new list to avoid reference issues
- }
-
- return result;
- }
-
- /**
- * Splits a given List into a List of sub lists, where each sublist contains at most
- * {@code maxSize} elements. The original list is not modified, and new sub lists are created
- * to hold the partitioned data.
- *
> chunk(List
{@code
* RangeUtil.range(3, 8).forEach(System.out::println);
@@ -91,20 +90,32 @@ public static IntStream range(int end) {
* // 5
* // 6
* // 7
+ *
+ * RangeUtil.range(8, 3).forEach(System.out::println);
+ *
+ * // Output:
+ * // 8
+ * // 7
+ * // 6
+ * // 5
+ * // 4
* }
*
* @param start the starting value of the range (inclusive)
* @param end upper-bound of the range (exclusive)
- * @return an {@code IntStream} of integers from {@code 0} (inclusive) to
- * {@code end} (exclusive)
- * @throws IllegalArgumentException if the given {@code end} value is less equal to 0
+ * @return an {@code IntStream} of integers in ascending or descending order, exclusive of {@code end}
* @see IntStream
*/
public static IntStream range(int start, int end) {
- if (end >= start) {
- throw new IllegalStateException("Parameter [start] should less than parameter [end].");
+ if (start == end) {
+ return IntStream.empty();
+ }
+ if (start < end) {
+ return IntStream.range(start, end);
+ } else {
+ // Descending range (exclusive of end)
+ return IntStream.iterate(start, n -> n > end, n -> n - 1);
}
- return IntStream.range(start, end);
}
/**
@@ -114,6 +125,8 @@ public static IntStream range(int start, int end) {
* It creates a sequential, ordered {@code IntStream} that can be used for iteration or
* further processing.
* {@code
* RangeUtil.rangeClosed(3, 8).forEach(System.out::println);
@@ -129,9 +142,7 @@ public static IntStream range(int start, int end) {
*
* @param start the starting value of the range (inclusive)
* @param end upper-bound of the range (inclusive)
- * @return an {@code IntStream} of integers from {@code 0} (inclusive) to
- * {@code end} (inclusive)
- * @throws IllegalArgumentException if the given {@code end} value is less equal to 0
+ * @return an {@code IntStream} of integers from {@code start} to {@code end} inclusive
* @see IntStream
*/
public static IntStream rangeClosed(int start, int end) {
@@ -139,28 +150,39 @@ public static IntStream rangeClosed(int start, int end) {
}
/**
- * Generates a stream of integers starting from the specified {@code start} value, increment by
+ * Generates a stream of integers starting from the specified {@code start} value, incremented by
* the specified {@code step}, up to the specified {@code end} value.
* {@code
- * RangeUtil.range(3, 8, 2).forEach(System.out::println);
+ * RangeUtil.range(3, 10, 2).forEach(System.out::println);
*
* // Output:
* // 3
* // 5
* // 7
+ * // 9
+ *
+ * RangeUtil.range(10, 3, -2).forEach(System.out::println);
+ *
+ * // Output:
+ * // 10
+ * // 8
+ * // 6
+ * // 4
* }
*
* @param start the starting value of the range (inclusive)
* @param end upper-bound of the range (exclusive)
- * @param step the increment (or decrement) between each value
- * @return an {@code IntStream} of integers from {@code 0} (inclusive) to
- * {@code end} (exclusive)
- * @throws IllegalArgumentException if the given {@code end} value is less equal to 0
+ * @param step the increment or decrement between each value (non-zero)
+ * @return an {@code IntStream} of integers from {@code start} to {@code end} exclusive stepping by {@code step}
+ * @throws IllegalArgumentException if {@code step} is zero or if {@code start} and {@code end} are inconsistent
+ * with the direction imposed by {@code step}
* @see IntStream
*/
public static IntStream range(int start, int end, int step) {
@@ -170,7 +192,7 @@ public static IntStream range(int start, int end, int step) {
if ((step > 0 && start >= end) || (step < 0 && start <= end)) {
throw new IllegalArgumentException("Range parameters are inconsistent with the step value.");
}
- return IntStream.iterate(start, (n) -> n < end, (n) -> n + step);
+ return IntStream.iterate(start, (n) -> step > 0 ? n < end : n > end, (n) -> n + step);
}
}
diff --git a/devkit-utils/src/test/java/com/onixbyte/devkit/utils/AesUtilTest.java b/devkit-utils/src/test/java/com/onixbyte/devkit/utils/AesUtilTest.java
new file mode 100644
index 0000000..c8390d1
--- /dev/null
+++ b/devkit-utils/src/test/java/com/onixbyte/devkit/utils/AesUtilTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2024-2025 OnixByte.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.onixbyte.devkit.utils;
+
+import org.junit.jupiter.api.Test;
+
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class AesUtilTest {
+
+ @Test
+ void testEncryptAndDecryptByte() throws GeneralSecurityException {
+ byte[] secretKey = "43f72073956d4c81".getBytes(StandardCharsets.UTF_8);
+ byte[] originalData = "Hello World".getBytes(StandardCharsets.UTF_8);
+
+ byte[] encryptedData = AesUtil.encrypt(originalData, secretKey);
+ assertNotNull(encryptedData);
+
+ byte[] decryptedData = AesUtil.decrypt(encryptedData, secretKey);
+ assertNotNull(decryptedData);
+
+ assertArrayEquals(originalData, decryptedData);
+ }
+
+ @Test
+ void testEncryptAndDecryptString() throws GeneralSecurityException {
+ var secret = "43f72073956d4c81";
+ var originalData = "Hello World";
+
+ var encryptedData = AesUtil.encrypt(originalData, secret);
+ assertNotNull(encryptedData);
+ assertNotEquals(originalData, encryptedData);
+
+ var decryptedData = AesUtil.decrypt(encryptedData, secret);
+ assertNotNull(decryptedData);
+ assertEquals(originalData, decryptedData);
+ }
+
+ @Test
+ void testEncryptWithWrongKeyFails() throws GeneralSecurityException {
+ var secret = "43f72073956d4c81";
+ var wrongSecret = "0000000000000000";
+ var originalData = "Hello World";
+
+ var encryptedData = AesUtil.encrypt(originalData.getBytes(StandardCharsets.UTF_8),
+ secret.getBytes(StandardCharsets.UTF_8));
+ assertNotNull(encryptedData);
+
+ // When decrypting with the wrong key, a BadPaddingException or IllegalBlockSizeException is expected to be thrown
+ assertThrows(GeneralSecurityException.class, () -> {
+ AesUtil.decrypt(encryptedData, wrongSecret.getBytes(StandardCharsets.UTF_8));
+ });
+ }
+
+ @Test
+ void testGenerateRandomSecret() {
+ var randomSecret = AesUtil.generateRandomSecret();
+ assertNotNull(randomSecret);
+ assertEquals(16, randomSecret.length());
+ }
+}
diff --git a/devkit-utils/src/test/java/com/onixbyte/devkit/utils/Base64UtilTest.java b/devkit-utils/src/test/java/com/onixbyte/devkit/utils/Base64UtilTest.java
new file mode 100644
index 0000000..aec54fb
--- /dev/null
+++ b/devkit-utils/src/test/java/com/onixbyte/devkit/utils/Base64UtilTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2024-2025 OnixByte.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.onixbyte.devkit.utils;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.nio.charset.StandardCharsets;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class Base64UtilTest {
+
+ @Test
+ void testEncodeAndDecodeWithUtf8() {
+ var original = "Hello, Base64!";
+ var encoded = Base64Util.encode(original);
+ assertNotNull(encoded);
+ assertNotEquals(original, encoded);
+
+ var decoded = Base64Util.decode(encoded);
+ assertNotNull(decoded);
+ assertEquals(original, decoded);
+ }
+
+ @Test
+ void testEncodeAndDecodeWithCharset() {
+ var original = "编码测试"; // Some unicode characters (Chinese)
+ var charset = StandardCharsets.UTF_8;
+
+ var encoded = Base64Util.encode(original, charset);
+ assertNotNull(encoded);
+ assertNotEquals(original, encoded);
+
+ var decoded = Base64Util.decode(encoded, charset);
+ assertNotNull(decoded);
+ assertEquals(original, decoded);
+ }
+
+ @Test
+ void testEncodeUrlComponentsAndDecodeWithUtf8() {
+ var original = "This is a test for URL-safe Base64 encoding+!";
+
+ var encodedUrl = Base64Util.encodeUrlComponents(original);
+ assertNotNull(encodedUrl);
+ assertNotEquals(original, encodedUrl);
+ // URL-safe encoding should not contain '+' or '/' characters
+ assertFalse(encodedUrl.contains("+"));
+ assertFalse(encodedUrl.contains("/"));
+
+ var decodedUrl = Base64Util.decodeUrlComponents(encodedUrl);
+ assertNotNull(decodedUrl);
+ assertEquals(original, decodedUrl);
+ }
+
+ @Test
+ void testEncodeUrlComponentsAndDecodeWithCharset() {
+ var original = "测试 URL 安全编码"; // Unicode string
+ var charset = StandardCharsets.UTF_8;
+
+ var encodedUrl = Base64Util.encodeUrlComponents(original, charset);
+ assertNotNull(encodedUrl);
+ assertNotEquals(original, encodedUrl);
+
+ var decodedUrl = Base64Util.decodeUrlComponents(encodedUrl, charset);
+ assertNotNull(decodedUrl);
+ assertEquals(original, decodedUrl);
+ }
+
+ @Test
+ void testEncodeAndDecodeEmptyString() {
+ var original = "";
+
+ var encoded = Base64Util.encode(original);
+ assertNotNull(encoded);
+ assertEquals("", Base64Util.decode(encoded));
+ }
+
+ @Test
+ void testEncodeAndDecodeNullSafety() {
+ // Since Base64Util does not explicitly handle null, the test expects NPE if null is input
+ assertThrows(NullPointerException.class, () -> Base64Util.encode(null));
+ assertThrows(NullPointerException.class, () -> Base64Util.decode(null));
+ }
+
+}
diff --git a/devkit-utils/src/test/java/com/onixbyte/devkit/utils/BoolUtilTest.java b/devkit-utils/src/test/java/com/onixbyte/devkit/utils/BoolUtilTest.java
new file mode 100644
index 0000000..23d38de
--- /dev/null
+++ b/devkit-utils/src/test/java/com/onixbyte/devkit/utils/BoolUtilTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2024-2025 OnixByte.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.onixbyte.devkit.utils;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.function.BooleanSupplier;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class BoolUtilTest {
+
+ // Tests for and(Boolean... values)
+
+ @Test
+ void and_AllTrueValues_ReturnsTrue() {
+ assertTrue(BoolUtil.and(true, true, true));
+ }
+
+ @Test
+ void and_SomeFalseValues_ReturnsFalse() {
+ assertFalse(BoolUtil.and(true, false, true));
+ }
+
+ @Test
+ void and_AllFalseValues_ReturnsFalse() {
+ assertFalse(BoolUtil.and(false, false));
+ }
+
+ @Test
+ void and_WithNullValues_IgnoresNulls() {
+ assertTrue(BoolUtil.and(true, null, true));
+ assertFalse(BoolUtil.and(true, null, false));
+ }
+
+ @Test
+ void and_AllNullValues_ReturnsTrue() {
+ // Stream after filtering null is empty, allMatch on empty returns true
+ assertTrue(BoolUtil.and((Boolean) null, null));
+ }
+
+ // Tests for and(BooleanSupplier... valueSuppliers)
+
+ @Test
+ void and_AllSuppliersTrue_ReturnsTrue() {
+ BooleanSupplier trueSupplier = () -> true;
+ BooleanSupplier falseSupplier = () -> false;
+
+ assertTrue(BoolUtil.and(trueSupplier, trueSupplier));
+ assertFalse(BoolUtil.and(trueSupplier, falseSupplier));
+ }
+
+ @Test
+ void and_WithNullSuppliers_IgnoresNull() {
+ BooleanSupplier trueSupplier = () -> true;
+
+ assertTrue(BoolUtil.and(trueSupplier, null, trueSupplier));
+ assertFalse(BoolUtil.and(trueSupplier, null, () -> false));
+ }
+
+ @Test
+ void and_AllNullSuppliers_ReturnsTrue() {
+ assertTrue(BoolUtil.and((BooleanSupplier) null, null));
+ }
+
+
+ // Tests for or(Boolean... values)
+
+ @Test
+ void or_AllTrueValues_ReturnsTrue() {
+ assertTrue(BoolUtil.or(true, true, true));
+ }
+
+ @Test
+ void or_SomeTrueValues_ReturnsTrue() {
+ assertTrue(BoolUtil.or(false, true, false));
+ }
+
+ @Test
+ void or_AllFalseValues_ReturnsFalse() {
+ assertFalse(BoolUtil.or(false, false));
+ }
+
+ @Test
+ void or_WithNullValues_IgnoresNull() {
+ assertTrue(BoolUtil.or(false, null, true));
+ assertFalse(BoolUtil.or(false, null, false));
+ }
+
+ @Test
+ void or_AllNullValues_ReturnsFalse() {
+ // Stream after filtering null is empty, anyMatch on empty returns false
+ assertFalse(BoolUtil.or((Boolean) null, null));
+ }
+
+ // Tests for or(BooleanSupplier... valueSuppliers)
+
+ @Test
+ void or_AllSuppliersTrue_ReturnsTrue() {
+ BooleanSupplier trueSupplier = () -> true;
+ BooleanSupplier falseSupplier = () -> false;
+
+ assertTrue(BoolUtil.or(trueSupplier, trueSupplier));
+ assertTrue(BoolUtil.or(falseSupplier, trueSupplier));
+ assertFalse(BoolUtil.or(falseSupplier, falseSupplier));
+ }
+
+ @Test
+ void or_WithNullSuppliers_IgnoresNull() {
+ BooleanSupplier trueSupplier = () -> true;
+ BooleanSupplier falseSupplier = () -> false;
+
+ assertTrue(BoolUtil.or(falseSupplier, null, trueSupplier));
+ assertFalse(BoolUtil.or(falseSupplier, null, falseSupplier));
+ }
+
+ @Test
+ void or_AllNullSuppliers_ReturnsFalse() {
+ assertFalse(BoolUtil.or((BooleanSupplier) null, null));
+ }
+}
diff --git a/devkit-utils/src/test/java/com/onixbyte/devkit/utils/BranchUtilTest.java b/devkit-utils/src/test/java/com/onixbyte/devkit/utils/BranchUtilTest.java
new file mode 100644
index 0000000..05a2f14
--- /dev/null
+++ b/devkit-utils/src/test/java/com/onixbyte/devkit/utils/BranchUtilTest.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2024-2025 OnixByte.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.onixbyte.devkit.utils;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.BooleanSupplier;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class BranchUtilTest {
+
+ // Test the static methods or(Boolean... values) and and(Boolean... values)
+ @Test
+ void testOrWithBooleanValues() {
+ BranchUtil trueResult = BranchUtil.or(true, false, false);
+ assertNotNull(trueResult);
+
+ BranchUtil falseResult = BranchUtil.or(false, false, false);
+ assertNotNull(falseResult);
+ }
+
+ @Test
+ void testAndWithBooleanValues() {
+ BranchUtil trueResult = BranchUtil.and(true, true, true);
+ assertNotNull(trueResult);
+
+ BranchUtil falseResult = BranchUtil.and(true, false, true);
+ assertNotNull(falseResult);
+ }
+
+ // Test the static methods or(BooleanSupplier... valueSuppliers) and and(BooleanSupplier... valueSuppliers)
+ @Test
+ void testOrWithBooleanSuppliers() {
+ BooleanSupplier trueSupplier = () -> true;
+ BooleanSupplier falseSupplier = () -> false;
+
+ BranchUtil trueResult = BranchUtil.or(falseSupplier, trueSupplier);
+
+ BranchUtil falseResult = BranchUtil.or(falseSupplier, falseSupplier);
+ }
+
+ @Test
+ void testAndWithBooleanSuppliers() {
+ BooleanSupplier trueSupplier = () -> true;
+ BooleanSupplier falseSupplier = () -> false;
+
+ BranchUtil trueResult = BranchUtil.and(trueSupplier, trueSupplier);
+
+ BranchUtil falseResult = BranchUtil.and(trueSupplier, falseSupplier);
+ }
+
+ // Test thenSupply(T, T)
+ @Test
+ void testThenSupplyBothSuppliers_ResultTrue() {
+ BranchUtil b = BranchUtil.and(true);
+ String trueVal = "yes";
+ String falseVal = "no";
+
+ String result = b.thenSupply(() -> trueVal, () -> falseVal);
+ assertEquals(trueVal, result);
+ }
+
+ @Test
+ void testThenSupplyBothSuppliers_ResultFalse_WithFalseSupplier() {
+ BranchUtil b = BranchUtil.and(false);
+ String trueVal = "yes";
+ String falseVal = "no";
+
+ String result = b.thenSupply(() -> trueVal, () -> falseVal);
+ assertEquals(falseVal, result);
+ }
+
+ @Test
+ void testThenSupplyBothSuppliers_ResultFalse_NoFalseSupplier() {
+ BranchUtil b = BranchUtil.and(false);
+ String trueVal = "yes";
+
+ String result = b.thenSupply(() -> trueVal, null);
+ assertNull(result);
+ }
+
+ @Test
+ void testThenSupplySingleTrueSupplier_ResultTrue() {
+ BranchUtil b = BranchUtil.and(true);
+ String trueVal = "success";
+
+ String result = b.thenSupply(() -> trueVal);
+ assertEquals(trueVal, result);
+ }
+
+ @Test
+ void testThenSupplySingleTrueSupplier_ResultFalse() {
+ BranchUtil b = BranchUtil.and(false);
+ String trueVal = "success";
+
+ String result = b.thenSupply(() -> trueVal);
+ assertNull(result);
+ }
+
+ // Test then(Runnable, Runnable)
+ @Test
+ void testThenWithBothHandlers_ResultTrue() {
+ BranchUtil b = BranchUtil.and(true);
+ AtomicBoolean trueRun = new AtomicBoolean(false);
+ AtomicBoolean falseRun = new AtomicBoolean(false);
+
+ b.then(() -> trueRun.set(true), () -> falseRun.set(true));
+
+ assertTrue(trueRun.get());
+ assertFalse(falseRun.get());
+ }
+
+ @Test
+ void testThenWithBothHandlers_ResultFalse() {
+ BranchUtil b = BranchUtil.and(false);
+ AtomicBoolean trueRun = new AtomicBoolean(false);
+ AtomicBoolean falseRun = new AtomicBoolean(false);
+
+ b.then(() -> trueRun.set(true), () -> falseRun.set(true));
+
+ assertFalse(trueRun.get());
+ assertTrue(falseRun.get());
+ }
+
+ @Test
+ void testThenWithOnlyTrueHandler_ResultTrue() {
+ BranchUtil b = BranchUtil.and(true);
+ AtomicBoolean trueRun = new AtomicBoolean(false);
+
+ b.then(() -> trueRun.set(true));
+
+ assertTrue(trueRun.get());
+ }
+
+ @Test
+ void testThenWithOnlyTrueHandler_ResultFalse() {
+ BranchUtil b = BranchUtil.and(false);
+ AtomicBoolean trueRun = new AtomicBoolean(false);
+
+ b.then(() -> trueRun.set(true));
+
+ assertFalse(trueRun.get());
+ }
+}
diff --git a/devkit-utils/src/test/java/com/onixbyte/devkit/utils/CollectionUtilTest.java b/devkit-utils/src/test/java/com/onixbyte/devkit/utils/CollectionUtilTest.java
new file mode 100644
index 0000000..4c7f197
--- /dev/null
+++ b/devkit-utils/src/test/java/com/onixbyte/devkit/utils/CollectionUtilTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2024-2025 OnixByte.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.onixbyte.devkit.utils;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.*;
+import java.util.function.Supplier;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class CollectionUtilTest {
+
+ @Test
+ void chunk_NullOriginalCollection_ThrowsException() {
+ IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
+ () -> CollectionUtil.chunk(null, 3, ArrayList::new));
+ assertEquals("Collection must not be null.", ex.getMessage());
+ }
+
+ @Test
+ void chunk_NegativeMaxSize_ThrowsException() {
+ IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
+ () -> CollectionUtil.chunk(List.of(1, 2), -1, ArrayList::new));
+ assertEquals("maxSize must greater than 0.", ex.getMessage());
+ }
+
+ @Test
+ void chunk_NullCollectionFactory_ThrowsException() {
+ IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
+ () -> CollectionUtil.chunk(List.of(1, 2), 2, null));
+ assertEquals("Factory method cannot be null.", ex.getMessage());
+ }
+
+ @Test
+ void chunk_EmptyCollection_ReturnsOneEmptySubCollection() {
+ List> chunks = CollectionUtil.chunk(Collections.emptyList(), 3, ArrayList::new);
+ assertEquals(1, chunks.size());
+ assertTrue(chunks.get(0).isEmpty());
+ }
+
+ @Test
+ void chunk_CollectionSizeLessThanMaxSize_ReturnsOneSubCollectionWithAllElements() {
+ List
> chunks = CollectionUtil.chunk(list, 5, ArrayList::new);
+ assertEquals(1, chunks.size());
+ assertEquals(list, chunks.get(0));
+ }
+
+ @Test
+ void chunk_CollectionSizeEqualMaxSize_ReturnsOneSubCollectionWithAllElements() {
+ List
> chunks = CollectionUtil.chunk(list, 3, ArrayList::new);
+ assertEquals(1, chunks.size());
+ assertEquals(list, chunks.get(0));
+ }
+
+ @Test
+ void chunk_CollectionSizeGreaterThanMaxSize_ReturnsMultipleSubCollections() {
+ List
> chunks = CollectionUtil.chunk(list, maxSize, ArrayList::new);
+
+ // Expect 3 subcollections: [1,2,3], [4,5,6], [7]
+ assertEquals(3, chunks.size());
+ assertEquals(List.of(1, 2, 3), chunks.get(0));
+ assertEquals(List.of(4, 5, 6), chunks.get(1));
+ assertEquals(List.of(7), chunks.get(2));
+ }
+
+ @Test
+ void chunk_UsesDifferentCollectionTypeAsSubCollections() {
+ LinkedList
> chunks = CollectionUtil.chunk(list, 1, ArrayList::new);
+ assertEquals(1, chunks.size());
+ assertEquals(list, chunks.get(0));
+ }
+
+ @Test
+ void chunk_MaxSizeZero_ThrowsException() {
+ IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
+ () -> CollectionUtil.chunk(List.of(1), 0, ArrayList::new));
+ assertEquals("maxSize must greater than 0.", ex.getMessage());
+ }
+}
diff --git a/devkit-utils/src/test/java/com/onixbyte/devkit/utils/HashUtilTest.java b/devkit-utils/src/test/java/com/onixbyte/devkit/utils/HashUtilTest.java
new file mode 100644
index 0000000..6b9cb6b
--- /dev/null
+++ b/devkit-utils/src/test/java/com/onixbyte/devkit/utils/HashUtilTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2024-2025 OnixByte.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.onixbyte.devkit.utils;
+
+import org.junit.jupiter.api.Test;
+
+import java.nio.charset.StandardCharsets;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class HashUtilTest {
+
+ // Test MD2 hashing with explicit charset and default charset
+ @Test
+ void testMd2() {
+ String input = "test";
+ // Known MD2 hash of "test" with UTF-8
+ String expectedHash = "dd34716876364a02d0195e2fb9ae2d1b";
+ assertEquals(expectedHash, HashUtil.md2(input, StandardCharsets.UTF_8));
+ assertEquals(expectedHash, HashUtil.md2(input));
+ // Test null charset fallback to UTF-8
+ assertEquals(expectedHash, HashUtil.md2(input, null));
+ }
+
+ // Test MD5 hashing with explicit charset and default charset
+ @Test
+ void testMd5() {
+ String input = "test";
+ // Known MD5 hash of "test"
+ String expectedHash = "098f6bcd4621d373cade4e832627b4f6";
+ assertEquals(expectedHash, HashUtil.md5(input, StandardCharsets.UTF_8));
+ assertEquals(expectedHash, HashUtil.md5(input));
+ assertEquals(expectedHash, HashUtil.md5(input, null));
+ }
+
+ // Test SHA-1 hashing with explicit charset and default charset
+ @Test
+ void testSha1() {
+ String input = "test";
+ // Known SHA-1 hash of "test"
+ String expectedHash = "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3";
+ assertEquals(expectedHash, HashUtil.sha1(input, StandardCharsets.UTF_8));
+ assertEquals(expectedHash, HashUtil.sha1(input));
+ assertEquals(expectedHash, HashUtil.sha1(input, null));
+ }
+
+ // Test SHA-224 hashing with explicit charset and default charset
+ @Test
+ void testSha224() {
+ String input = "test";
+ // Known SHA-224 hash of "test"
+ String expectedHash = "90a3ed9e32b2aaf4c61c410eb925426119e1a9dc53d4286ade99a809";
+ assertEquals(expectedHash, HashUtil.sha224(input, StandardCharsets.UTF_8));
+ assertEquals(expectedHash, HashUtil.sha224(input));
+ assertEquals(expectedHash, HashUtil.sha224(input, null));
+ }
+
+ // Test SHA-256 hashing with explicit charset and default charset
+ @Test
+ void testSha256() {
+ String input = "test";
+ // Known SHA-256 hash of "test"
+ String expectedHash = "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08";
+ assertEquals(expectedHash, HashUtil.sha256(input, StandardCharsets.UTF_8));
+ assertEquals(expectedHash, HashUtil.sha256(input));
+ assertEquals(expectedHash, HashUtil.sha256(input, null));
+ }
+
+ // Test SHA-384 hashing with explicit charset and default charset
+ @Test
+ void testSha384() {
+ String input = "test";
+ // Known SHA-384 hash of "test"
+ String expectedHash = "768412320f7b0aa5812fce428dc4706b3cae50e02a64caa16a782249bfe8efc4b7ef1ccb126255d196047dfedf17a0a9";
+ assertEquals(expectedHash, HashUtil.sha384(input, StandardCharsets.UTF_8));
+ assertEquals(expectedHash, HashUtil.sha384(input));
+ assertEquals(expectedHash, HashUtil.sha384(input, null));
+ }
+
+ // Test SHA-512 hashing with explicit charset and default charset
+ @Test
+ void testSha512() {
+ String input = "test";
+ // Known SHA-512 hash of "test"
+ String expectedHash = "ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff";
+ // remove all whitespace in expected to match format generated
+ expectedHash = expectedHash.replaceAll("\\s+", "");
+ assertEquals(expectedHash, HashUtil.sha512(input, StandardCharsets.UTF_8));
+ assertEquals(expectedHash, HashUtil.sha512(input));
+ assertEquals(expectedHash, HashUtil.sha512(input, null));
+ }
+
+ // Test empty string input
+ @Test
+ void testEmptyString() {
+ String input = "";
+ // MD5 hash of empty string
+ String expectedMd5 = "d41d8cd98f00b204e9800998ecf8427e";
+ assertEquals(expectedMd5, HashUtil.md5(input));
+ // SHA-256 hash of empty string
+ String expectedSha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
+ assertEquals(expectedSha256, HashUtil.sha256(input));
+ }
+
+ // Test null charset fallback for one algorithm as a sample
+ @Test
+ void testNullCharsetFallsBackToUtf8() {
+ String input = "abc";
+ String hashWithNull = HashUtil.md5(input, null);
+ String hashWithUtf8 = HashUtil.md5(input, StandardCharsets.UTF_8);
+ assertEquals(hashWithUtf8, hashWithNull);
+ }
+}
diff --git a/devkit-utils/src/test/java/com/onixbyte/devkit/utils/RangeUtilTest.java b/devkit-utils/src/test/java/com/onixbyte/devkit/utils/RangeUtilTest.java
new file mode 100644
index 0000000..13db7fb
--- /dev/null
+++ b/devkit-utils/src/test/java/com/onixbyte/devkit/utils/RangeUtilTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2024-2025 OnixByte.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.onixbyte.devkit.utils;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class RangeUtilTest {
+
+ /**
+ * Tests generating ascending range from 0 up to end (exclusive).
+ */
+ @Test
+ void testRangeEndValid() {
+ int[] expected = {0, 1, 2, 3, 4};
+ assertArrayEquals(expected, RangeUtil.range(5).toArray());
+ }
+
+ /**
+ * Tests that range(end) throws IllegalArgumentException for end less than or equal to zero.
+ */
+ @Test
+ void testRangeEndInvalidThrows() {
+ IllegalArgumentException ex1 = assertThrows(IllegalArgumentException.class,
+ () -> RangeUtil.range(0));
+ assertTrue(ex1.getMessage().contains("should not be less than or equal to 0"));
+
+ IllegalArgumentException ex2 = assertThrows(IllegalArgumentException.class,
+ () -> RangeUtil.range(-3));
+ assertTrue(ex2.getMessage().contains("should not be less than or equal to 0"));
+ }
+
+ /**
+ * Tests ascending range where start is less than end.
+ */
+ @Test
+ void testRangeStartEndAscending() {
+ int[] expected = {3, 4, 5, 6, 7};
+ assertArrayEquals(expected, RangeUtil.range(3, 8).toArray());
+ }
+
+ /**
+ * Tests descending range where start is greater than end.
+ */
+ @Test
+ void testRangeStartEndDescending() {
+ int[] expected = {8, 7, 6, 5, 4};
+ assertArrayEquals(expected, RangeUtil.range(8, 3).toArray());
+ }
+
+ /**
+ * Tests empty stream when start equals end.
+ */
+ @Test
+ void testRangeStartEqualsEndReturnsEmpty() {
+ assertEquals(0, RangeUtil.range(5, 5).count());
+ }
+
+ /**
+ * Tests that rangeClosed generates inclusive range in ascending order.
+ */
+ @Test
+ void testRangeClosedAscending() {
+ int[] expected = {3, 4, 5, 6, 7, 8};
+ assertArrayEquals(expected, RangeUtil.rangeClosed(3, 8).toArray());
+ }
+
+ /**
+ * Tests range method with positive step generating ascending sequence.
+ */
+ @Test
+ void testRangeWithPositiveStep() {
+ int[] expected = {2, 4, 6, 8};
+ assertArrayEquals(expected, RangeUtil.range(2, 10, 2).toArray());
+ }
+
+ /**
+ * Tests range method with negative step generating descending sequence.
+ */
+ @Test
+ void testRangeWithNegativeStep() {
+ int[] expected = {10, 7, 4, 1};
+ assertArrayEquals(expected, RangeUtil.range(10, 0, -3).toArray());
+ }
+
+ /**
+ * Tests that passing zero step throws IllegalArgumentException.
+ */
+ @Test
+ void testRangeStepZeroThrows() {
+ IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
+ () -> RangeUtil.range(0, 10, 0));
+ assertEquals("Step value must not be zero.", ex.getMessage());
+ }
+
+ /**
+ * Tests that range with positive step but invalid start/end throws IllegalArgumentException.
+ */
+ @Test
+ void testRangePositiveStepInvalidRangeThrows() {
+ IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
+ () -> RangeUtil.range(10, 5, 1));
+ assertEquals("Range parameters are inconsistent with the step value.", ex.getMessage());
+ }
+
+ /**
+ * Tests that range with negative step but invalid start/end throws IllegalArgumentException.
+ */
+ @Test
+ void testRangeNegativeStepInvalidRangeThrows() {
+ IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
+ () -> RangeUtil.range(5, 10, -1));
+ assertEquals("Range parameters are inconsistent with the step value.", ex.getMessage());
+ }
+}
diff --git a/devkit-utils/src/test/java/com/onixbyte/devkit/utils/TestAesUtil.java b/devkit-utils/src/test/java/com/onixbyte/devkit/utils/TestAesUtil.java
deleted file mode 100644
index 39843ba..0000000
--- a/devkit-utils/src/test/java/com/onixbyte/devkit/utils/TestAesUtil.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2024-2025 OnixByte.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.onixbyte.devkit.utils;
-
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class TestAesUtil {
-
- private final static Logger log = LoggerFactory.getLogger(TestAesUtil.class);
-
- @Test
- public void testGenerateRandomSecret() {
- log.info("Secret is {}", AesUtil.generateRandomSecret());
- }
-
- @Test
- public void testEncrypt() {
- var secret = "43f72073956d4c81";
-
- Assertions.assertEquals("IbbYZu8GtMruBURfMBVy/w==", AesUtil.encrypt("Hello World", secret));
- Assertions.assertEquals("1eVA7oQpTIhI7jc+6cdkmg==", AesUtil.encrypt("OnixByte", secret));
- Assertions.assertEquals("fk6oNRJK8a+Pz7zVwtlD0UQocq5c3GkRuem0Z6jdAN8=", AesUtil.encrypt("Welcome to use JDevKit!", secret));
- Assertions.assertEquals("dqzGjawNcQdBpXJWk/08UQ==", AesUtil.encrypt("127.0.0.1", secret));
- Assertions.assertEquals("uwQQI60yAGL91q9jCDgoeA==", AesUtil.encrypt("root", secret));
- }
-
- @Test
- public void testDecrypt() {
- var secret = "43f72073956d4c81";
-
- Assertions.assertEquals("Hello World", AesUtil.decrypt("IbbYZu8GtMruBURfMBVy/w==", secret));
- Assertions.assertEquals("OnixByte", AesUtil.decrypt("1eVA7oQpTIhI7jc+6cdkmg==", secret));
- Assertions.assertEquals("Welcome to use JDevKit!", AesUtil.decrypt("fk6oNRJK8a+Pz7zVwtlD0UQocq5c3GkRuem0Z6jdAN8=", secret));
- Assertions.assertEquals("127.0.0.1", AesUtil.decrypt("dqzGjawNcQdBpXJWk/08UQ==", secret));
- Assertions.assertEquals("root", AesUtil.decrypt("uwQQI60yAGL91q9jCDgoeA==", secret));
- }
-
-}
diff --git a/devkit-utils/src/test/java/com/onixbyte/devkit/utils/TestBase64Util.java b/devkit-utils/src/test/java/com/onixbyte/devkit/utils/TestBase64Util.java
deleted file mode 100644
index 6bbc3b1..0000000
--- a/devkit-utils/src/test/java/com/onixbyte/devkit/utils/TestBase64Util.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2024-2025 OnixByte.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.onixbyte.devkit.utils;
-
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class TestBase64Util {
-
- private final static Logger log = LoggerFactory.getLogger(TestBase64Util.class);
-
- @Test
- public void testEncode() {
- Assertions.assertEquals("SGVsbG8gV29ybGQ=", Base64Util.encode("Hello World"));
- Assertions.assertEquals("MTI3LjAuMC4x", Base64Util.encode("127.0.0.1"));
- Assertions.assertEquals("cm9vdA==", Base64Util.encode("root"));
- }
-
- @Test
- public void testDecode() {
- Assertions.assertEquals("Hello World", Base64Util.decode("SGVsbG8gV29ybGQ="));
- Assertions.assertEquals("127.0.0.1", Base64Util.decode("MTI3LjAuMC4x"));
- Assertions.assertEquals("root", Base64Util.decode("cm9vdA=="));
- }
-
- @Test
- public void testEncodeUriComponent() {
- Assertions.assertEquals("aHR0cHM6Ly9nb29nbGUuY29t", Base64Util.encodeUrlComponents("https://google.com"));
- Assertions.assertEquals("aHR0cDovLzEyNy4wLjAuMTo4MDgwL2FwaS91c2VyLzEyMzQ1", Base64Util.encodeUrlComponents("http://127.0.0.1:8080/api/user/12345"));
- }
-
- @Test
- public void testDecodeUriComponent() {
- Assertions.assertEquals("https://google.com", Base64Util.decodeUrlComponents("aHR0cHM6Ly9nb29nbGUuY29t"));
- Assertions.assertEquals("http://127.0.0.1:8080/api/user/12345", Base64Util.decodeUrlComponents("aHR0cDovLzEyNy4wLjAuMTo4MDgwL2FwaS91c2VyLzEyMzQ1"));
- }
-
-}
diff --git a/guid/src/main/java/com/onixbyte/guid/impl/SequentialUuidCreator.java b/guid/src/main/java/com/onixbyte/guid/impl/SequentialUuidCreator.java
new file mode 100644
index 0000000..309837d
--- /dev/null
+++ b/guid/src/main/java/com/onixbyte/guid/impl/SequentialUuidCreator.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2024-2025 OnixByte.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.onixbyte.guid.impl;
+
+import com.onixbyte.guid.GuidCreator;
+
+import java.nio.ByteBuffer;
+import java.security.SecureRandom;
+import java.util.UUID;
+
+/**
+ * A {@code SequentialUuidCreator} is responsible for generating UUIDs following the UUID version 7 specification, which
+ * combines a timestamp with random bytes to create time-ordered unique identifiers.
+ *