Merge pull request #64 from onixbyte/release/2.2.0

Release 2.2.0
This commit is contained in:
Jam'o Siu
2025-06-04 11:19:46 +08:00
committed by GitHub
21 changed files with 1026 additions and 366 deletions
@@ -17,21 +17,12 @@
package com.onixbyte.devkit.utils; package com.onixbyte.devkit.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher; import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException; import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64; import java.util.Base64;
import java.util.Objects;
import java.util.UUID; import java.util.UUID;
/** /**
@@ -44,8 +35,7 @@ import java.util.UUID;
* </p> * </p>
* *
* <p><b>Example usage:</b></p> * <p><b>Example usage:</b></p>
* <pre> * <pre>{@code
* {@code
* // Encrypting and decrypting byte array data * // Encrypting and decrypting byte array data
* byte[] secretKey = "43f72073956d4c81".getBytes(StandardCharsets.UTF_8); * byte[] secretKey = "43f72073956d4c81".getBytes(StandardCharsets.UTF_8);
* byte[] data = "Hello World".getBytes(StandardCharsets.UTF_8); * byte[] data = "Hello World".getBytes(StandardCharsets.UTF_8);
@@ -61,9 +51,8 @@ import java.util.UUID;
* *
* // Generating a random secret key * // Generating a random secret key
* String randomSecret = AesUtil.generateRandomSecret(); * 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
* } * }</pre>
* </pre>
* *
* @author hubin@baomidou * @author hubin@baomidou
* @version 1.1.0 * @version 1.1.0
@@ -71,81 +60,61 @@ import java.util.UUID;
*/ */
public final class AesUtil { 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 data the data to be encrypted
* @param secret the secret to encrypt the data * @param secret the secret key used for encryption
* @return the encryption result or {@code null} if encryption failed * @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) { public static byte[] encrypt(byte[] data, byte[] secret) throws GeneralSecurityException {
try { var secretKeySpec = new SecretKeySpec(new SecretKeySpec(secret, AES).getEncoded(), AES);
var secretKeySpec = new SecretKeySpec(new SecretKeySpec(secret, AES).getEncoded(), AES); var cipher = Cipher.getInstance(AES_CBC_CIPHER);
var cipher = Cipher.getInstance(AES_CBC_CIPHER); cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(secret));
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(secret)); // set IV to secret return cipher.doFinal(data);
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;
} }
/** /**
* 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 data the data to be decrypted
* @param secret the secret to encrypt the data * @param secret the secret key used for decryption
* @return the decryption result or {@code null} if decryption failed * @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) { public static byte[] decrypt(byte[] data, byte[] secret) throws GeneralSecurityException {
try { var secretKeySpec = new SecretKeySpec(new SecretKeySpec(secret, AES).getEncoded(), AES);
var secretKeySpec = new SecretKeySpec(new SecretKeySpec(secret, AES).getEncoded(), AES); var cipher = Cipher.getInstance(AES_CBC_CIPHER);
var cipher = Cipher.getInstance(AES_CBC_CIPHER); cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(secret));
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(secret)); // set IV to secret return cipher.doFinal(data);
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;
} }
/** /**
* 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 data the string data to be encrypted
* @param secret the secret to encrypt the data * @param secret the secret key used for encryption
* @return the encryption result or {@code null} if encryption failed * @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), return Base64.getEncoder().encodeToString(encrypt(data.getBytes(StandardCharsets.UTF_8),
secret.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 data the Base64-encoded string data to be decrypted
* @param secret the secret to encrypt the data * @param secret the secret key used for decryption
* @return the decryption result or {@code null} if decryption failed * @return the decrypted string data
* @throws GeneralSecurityException if any cryptographic error occurs during decryption
*/ */
public static String decrypt(String data, String secret) { public static String decrypt(String data, String secret) throws GeneralSecurityException {
return new String(Objects.requireNonNull( var decrypted = decrypt(Base64.getDecoder().decode(data.getBytes(StandardCharsets.UTF_8)),
decrypt(Base64.getDecoder().decode(data.getBytes()), secret.getBytes(StandardCharsets.UTF_8));
secret.getBytes(StandardCharsets.UTF_8))) return new String(decrypted, StandardCharsets.UTF_8);
);
} }
/** /**
@@ -17,9 +17,6 @@
package com.onixbyte.devkit.utils; package com.onixbyte.devkit.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Base64; import java.util.Base64;
@@ -58,8 +55,6 @@ import java.util.Objects;
*/ */
public final class Base64Util { public final class Base64Util {
private final static Logger log = LoggerFactory.getLogger(Base64Util.class);
/** /**
* Ensure that there is only one Base64 Encoder. * Ensure that there is only one Base64 Encoder.
* *
@@ -17,9 +17,6 @@
package com.onixbyte.devkit.utils; package com.onixbyte.devkit.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays; import java.util.Arrays;
import java.util.Objects; import java.util.Objects;
import java.util.function.BooleanSupplier; import java.util.function.BooleanSupplier;
@@ -47,8 +44,6 @@ import java.util.function.BooleanSupplier;
*/ */
public final class BoolUtil { public final class BoolUtil {
private final static Logger log = LoggerFactory.getLogger(BoolUtil.class);
/** /**
* Logical and calculation. * Logical and calculation.
* *
@@ -206,18 +206,4 @@ public final class BranchUtil {
then(trueHandler, null); then(trueHandler, null);
} }
/**
* Get the boolean result.
* <p>
* <b>Note:</b> {@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;
}
} }
@@ -17,9 +17,6 @@
package com.onixbyte.devkit.utils; package com.onixbyte.devkit.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@@ -33,8 +30,6 @@ import java.util.function.Supplier;
*/ */
public final class CollectionUtil { public final class CollectionUtil {
private static final Logger log = LoggerFactory.getLogger(CollectionUtil.class);
/** /**
* Private constructor to prevent instantiation of this utility class. * Private constructor to prevent instantiation of this utility class.
*/ */
@@ -68,7 +63,7 @@ public final class CollectionUtil {
throw new IllegalArgumentException("Collection must not be null."); throw new IllegalArgumentException("Collection must not be null.");
} }
if (maxSize < 0) { if (maxSize <= 0) {
throw new IllegalArgumentException("maxSize must greater than 0."); throw new IllegalArgumentException("maxSize must greater than 0.");
} }
@@ -17,14 +17,10 @@
package com.onixbyte.devkit.utils; package com.onixbyte.devkit.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
/** /**
@@ -70,8 +66,6 @@ import java.util.Optional;
*/ */
public final class HashUtil { public final class HashUtil {
private final static Logger log = LoggerFactory.getLogger(HashUtil.class);
/** /**
* Calculates the MD2 hash value of the specified string using the given charset. * Calculates the MD2 hash value of the specified string using the given charset.
* *
@@ -1,114 +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 java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Supplier;
/**
* A utility class providing static methods for manipulating lists.
*
* @author siujamo
* @author zihluwang
*/
public final class ListUtil {
/**
* Private constructor to prevent instantiation of this utility class.
*/
private ListUtil() {
}
/**
* 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.
* <p>
* 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 <T> the type of elements in the list
* @param originalList the list to be split, must not be null
* @param maxSize the maximum number of elements in each sublist, must be positive
* @param listFactory list factory
* @return a List of sub lists, where each sublist has at most {@code maxSize} elements
* @throws IllegalArgumentException if {@code originalList} is null or {@code maxSize} is less
* than or equal to 0
*/
public static <T> List<List<T>> chunk(List<T> originalList, int maxSize, Supplier<List<T>> 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<List<T>>();
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.
* <p>
* 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 <T> the type of elements in the list
* @param originalList the list to be split, must not be null
* @param maxSize the maximum number of elements in each sublist, must be positive
* @return a List of sub lists, where each sublist has at most {@code maxSize} elements
* @throws IllegalArgumentException if {@code originalList} is null or {@code maxSize} is less
* than or equal to 0
* @see #chunk(List, int, Supplier)
*/
public static <T> List<List<T>> chunk(List<T> originalList, int maxSize) {
return chunk(originalList, maxSize, ArrayList::new);
}
}
@@ -17,9 +17,6 @@
package com.onixbyte.devkit.utils; package com.onixbyte.devkit.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map; import java.util.Map;
/** /**
@@ -85,8 +82,6 @@ import java.util.Map;
*/ */
public final class MapUtil { public final class MapUtil {
private final static Logger log = LoggerFactory.getLogger(MapUtil.class);
/** /**
* Converts an object to a map by mapping the field names to their corresponding values. * Converts an object to a map by mapping the field names to their corresponding values.
* *
@@ -17,9 +17,6 @@
package com.onixbyte.devkit.utils; package com.onixbyte.devkit.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.stream.IntStream; import java.util.stream.IntStream;
/** /**
@@ -34,8 +31,6 @@ import java.util.stream.IntStream;
*/ */
public final class RangeUtil { public final class RangeUtil {
private final static Logger log = LoggerFactory.getLogger(RangeUtil.class);
/** /**
* Private constructor to prevent instantiation of this utility class. * Private constructor to prevent instantiation of this utility class.
*/ */
@@ -68,7 +63,7 @@ public final class RangeUtil {
*/ */
public static IntStream range(int end) { public static IntStream range(int end) {
if (end <= 0) { if (end <= 0) {
throw new IllegalArgumentException("Parameter [end] should not less than 0, provided is " + throw new IllegalArgumentException("Parameter [end] should not be less than or equal to 0, provided: " +
end); end);
} }
return IntStream.range(0, end); return IntStream.range(0, end);
@@ -81,6 +76,10 @@ public final class RangeUtil {
* It creates a sequential, ordered {@code IntStream} that can be used for iteration or * It creates a sequential, ordered {@code IntStream} that can be used for iteration or
* further processing. * further processing.
* <p> * <p>
* 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.
* <p>
* <b>Example Usage:</b> * <b>Example Usage:</b>
* <pre>{@code * <pre>{@code
* RangeUtil.range(3, 8).forEach(System.out::println); * RangeUtil.range(3, 8).forEach(System.out::println);
@@ -91,20 +90,32 @@ public final class RangeUtil {
* // 5 * // 5
* // 6 * // 6
* // 7 * // 7
*
* RangeUtil.range(8, 3).forEach(System.out::println);
*
* // Output:
* // 8
* // 7
* // 6
* // 5
* // 4
* }</pre> * }</pre>
* *
* @param start the starting value of the range (inclusive) * @param start the starting value of the range (inclusive)
* @param end upper-bound of the range (exclusive) * @param end upper-bound of the range (exclusive)
* @return an {@code IntStream} of integers from {@code 0} (inclusive) to * @return an {@code IntStream} of integers in ascending or descending order, exclusive of {@code end}
* {@code end} (exclusive)
* @throws IllegalArgumentException if the given {@code end} value is less equal to 0
* @see IntStream * @see IntStream
*/ */
public static IntStream range(int start, int end) { public static IntStream range(int start, int end) {
if (end >= start) { if (start == end) {
throw new IllegalStateException("Parameter [start] should less than parameter [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 final class RangeUtil {
* It creates a sequential, ordered {@code IntStream} that can be used for iteration or * It creates a sequential, ordered {@code IntStream} that can be used for iteration or
* further processing. * further processing.
* <p> * <p>
* The range includes both {@code start} and {@code end}.
* <p>
* <b>Example Usage:</b> * <b>Example Usage:</b>
* <pre>{@code * <pre>{@code
* RangeUtil.rangeClosed(3, 8).forEach(System.out::println); * RangeUtil.rangeClosed(3, 8).forEach(System.out::println);
@@ -129,9 +142,7 @@ public final class RangeUtil {
* *
* @param start the starting value of the range (inclusive) * @param start the starting value of the range (inclusive)
* @param end upper-bound of the range (inclusive) * @param end upper-bound of the range (inclusive)
* @return an {@code IntStream} of integers from {@code 0} (inclusive) to * @return an {@code IntStream} of integers from {@code start} to {@code end} inclusive
* {@code end} (inclusive)
* @throws IllegalArgumentException if the given {@code end} value is less equal to 0
* @see IntStream * @see IntStream
*/ */
public static IntStream rangeClosed(int start, int end) { public static IntStream rangeClosed(int start, int end) {
@@ -139,28 +150,39 @@ public final class RangeUtil {
} }
/** /**
* 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. * the specified {@code step}, up to the specified {@code end} value.
* <p> * <p>
* It creates a sequential, ordered {@code IntStream} that can be used for iteration or * It creates a sequential, ordered {@code IntStream} that can be used for iteration or
* further processing. * further processing.
* <p> * <p>
* The stream excludes the {@code end} value.
* <p>
* <b>Example Usage:</b> * <b>Example Usage:</b>
* <pre>{@code * <pre>{@code
* RangeUtil.range(3, 8, 2).forEach(System.out::println); * RangeUtil.range(3, 10, 2).forEach(System.out::println);
* *
* // Output: * // Output:
* // 3 * // 3
* // 5 * // 5
* // 7 * // 7
* // 9
*
* RangeUtil.range(10, 3, -2).forEach(System.out::println);
*
* // Output:
* // 10
* // 8
* // 6
* // 4
* }</pre> * }</pre>
* *
* @param start the starting value of the range (inclusive) * @param start the starting value of the range (inclusive)
* @param end upper-bound of the range (exclusive) * @param end upper-bound of the range (exclusive)
* @param step the increment (or decrement) between each value * @param step the increment or decrement between each value (non-zero)
* @return an {@code IntStream} of integers from {@code 0} (inclusive) to * @return an {@code IntStream} of integers from {@code start} to {@code end} exclusive stepping by {@code step}
* {@code end} (exclusive) * @throws IllegalArgumentException if {@code step} is zero or if {@code start} and {@code end} are inconsistent
* @throws IllegalArgumentException if the given {@code end} value is less equal to 0 * with the direction imposed by {@code step}
* @see IntStream * @see IntStream
*/ */
public static IntStream range(int start, int end, int step) { public static IntStream range(int start, int end, int step) {
@@ -170,7 +192,7 @@ public final class RangeUtil {
if ((step > 0 && start >= end) || (step < 0 && start <= end)) { if ((step > 0 && start >= end) || (step < 0 && start <= end)) {
throw new IllegalArgumentException("Range parameters are inconsistent with the step value."); 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);
} }
} }
@@ -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());
}
}
@@ -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));
}
}
@@ -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));
}
}
@@ -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());
}
}
@@ -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<List<Integer>> chunks = CollectionUtil.chunk(Collections.emptyList(), 3, ArrayList::new);
assertEquals(1, chunks.size());
assertTrue(chunks.get(0).isEmpty());
}
@Test
void chunk_CollectionSizeLessThanMaxSize_ReturnsOneSubCollectionWithAllElements() {
List<Integer> list = List.of(1, 2);
List<List<Integer>> chunks = CollectionUtil.chunk(list, 5, ArrayList::new);
assertEquals(1, chunks.size());
assertEquals(list, chunks.get(0));
}
@Test
void chunk_CollectionSizeEqualMaxSize_ReturnsOneSubCollectionWithAllElements() {
List<Integer> list = List.of(1, 2, 3);
List<List<Integer>> chunks = CollectionUtil.chunk(list, 3, ArrayList::new);
assertEquals(1, chunks.size());
assertEquals(list, chunks.get(0));
}
@Test
void chunk_CollectionSizeGreaterThanMaxSize_ReturnsMultipleSubCollections() {
List<Integer> list = List.of(1, 2, 3, 4, 5, 6, 7);
int maxSize = 3;
List<List<Integer>> 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<Integer> list = new LinkedList<>(List.of(1, 2, 3, 4));
Supplier<LinkedList<Integer>> factory = LinkedList::new;
List<LinkedList<Integer>> chunks = CollectionUtil.chunk(list, 2, factory);
assertEquals(2, chunks.size());
assertInstanceOf(LinkedList.class, chunks.get(0));
assertInstanceOf(LinkedList.class, chunks.get(1));
assertEquals(List.of(1, 2), chunks.get(0));
assertEquals(List.of(3, 4), chunks.get(1));
}
@Test
void chunk_CollectionWithOneElementAndMaxSizeOne_ReturnsOneSubCollection() {
List<String> list = List.of("a");
List<List<String>> 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());
}
}
@@ -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);
}
}
@@ -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());
}
}
@@ -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));
}
}
@@ -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"));
}
}
@@ -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.
* <p>
* 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.
* <p>
* The generated UUID adheres strictly to the layout and variant bits of UUID version 7 as defined in the specification.
* </p>
*
* @implNote This class implements the {@link GuidCreator} interface, providing UUID instances as unique identifiers.
*/
public class SequentialUuidCreator implements GuidCreator<UUID> {
private final SecureRandom random;
/**
* Constructs a new {@code SequentialUuidCreator} with its own {@link SecureRandom} instance.
*/
public SequentialUuidCreator() {
this.random = new SecureRandom();
}
/**
* Generates and returns the next UUID version 7 identifier.
*
* @return a {@link UUID} instance representing a unique, time-ordered identifier
*/
@Override
public UUID nextId() {
var value = randomBytes();
var buf = ByteBuffer.wrap(value);
var high = buf.getLong();
var low = buf.getLong();
return new UUID(high, low);
}
/**
* Produces a byte array representation of a UUID version 7,
* combining the current timestamp with random bytes.
*
* @return a 16-byte array conforming to UUIDv7 layout and variant bits
*/
private byte[] randomBytes() {
var value = new byte[16];
random.nextBytes(value);
var timestamp = ByteBuffer.allocate(Long.BYTES);
timestamp.putLong(System.currentTimeMillis());
System.arraycopy(timestamp.array(), 2, value, 0, 6);
// Set version to 7 (UUIDv7)
value[6] = (byte) ((value[6] & 0x0F) | 0x70);
// Set variant bits as per RFC 4122
value[8] = (byte) ((value[8] & 0x3F) | 0x80);
return value;
}
}
@@ -19,8 +19,6 @@ package com.onixbyte.guid.impl;
import com.onixbyte.guid.GuidCreator; import com.onixbyte.guid.GuidCreator;
import com.onixbyte.guid.exceptions.TimingException; import com.onixbyte.guid.exceptions.TimingException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId; import java.time.ZoneId;
@@ -49,8 +47,6 @@ import java.time.ZoneId;
*/ */
public final class SnowflakeGuidCreator implements GuidCreator<Long> { public final class SnowflakeGuidCreator implements GuidCreator<Long> {
private final static Logger log = LoggerFactory.getLogger(SnowflakeGuidCreator.class);
/** /**
* Constructs a SnowflakeGuidGenerator with the default start epoch and custom worker ID, data * Constructs a SnowflakeGuidGenerator with the default start epoch and custom worker ID, data
* centre ID. * centre ID.
@@ -72,10 +72,10 @@ public final class PercentileCalculator {
* @return a {@code Double} value representing the calculated percentile * @return a {@code Double} value representing the calculated percentile
*/ */
public static Double calculatePercentile(List<Double> values, Double percentile) { public static Double calculatePercentile(List<Double> values, Double percentile) {
var sorted = values.stream().sorted().toList(); if (values.isEmpty()) {
if (sorted.isEmpty()) {
throw new IllegalArgumentException("Unable to sort an empty list."); throw new IllegalArgumentException("Unable to sort an empty list.");
} }
var sorted = values.stream().sorted().toList();
var rank = percentile / 100. * (sorted.size() - 1); var rank = percentile / 100. * (sorted.size() - 1);
var lowerIndex = (int) Math.floor(rank); var lowerIndex = (int) Math.floor(rank);