[공통] util → framework 유틸 #2

Open
forge-bot wants to merge 7 commits from forge/ACM-CM-002-attempt-1-run-bd1b68b4d412 into main
7 changed files with 1846 additions and 0 deletions

View file

@ -0,0 +1,3 @@
# ACM-CM-002-attempt-1-run-bd1b68b4d412
Forge 이슈 작업 브랜치 `forge/ACM-CM-002-attempt-1-run-bd1b68b4d412`.

View file

@ -0,0 +1,438 @@
package com.klaro.acquirecore.framework.util;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.Objects;
/**
* Static utility class for amount/money operations migrated from legacy/util_amount.c
* Uses java.math.BigDecimal for precise monetary calculations.
*/
public final class AmountUtil {
public static final int DEFAULT_SCALE = 2;
public static final RoundingMode DEFAULT_ROUNDING_MODE = RoundingMode.HALF_UP;
public static final String KOREAN_WON_SYMBOL = "";
public static final String USDollar_SYMBOL = "$";
public static final String EURO_SYMBOL = "";
private static final DecimalFormat KOREAN_WON_FORMAT = new DecimalFormat("#,###");
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#,###.##");
private static final DecimalFormat CURRENCY_FORMAT = new DecimalFormat("#,###.00");
private AmountUtil() {
throw new UnsupportedOperationException("Utility class cannot be instantiated");
}
/**
* Creates a BigDecimal from a string value.
*
* @param value the string value to convert
* @return the BigDecimal value
* @throws NumberFormatException if the string is not a valid number
*/
public static BigDecimal of(String value) {
Objects.requireNonNull(value, "value must not be null");
return new BigDecimal(value.trim());
}
/**
* Creates a BigDecimal from a long value.
*
* @param value the long value to convert
* @return the BigDecimal value
*/
public static BigDecimal of(long value) {
return BigDecimal.valueOf(value);
}
/**
* Creates a BigDecimal from a double value.
*
* @param value the double value to convert
* @return the BigDecimal value
*/
public static BigDecimal of(double value) {
return BigDecimal.valueOf(value);
}
/**
* Adds two BigDecimal values.
*
* @param a the first operand
* @param b the second operand
* @return the sum
*/
public static BigDecimal add(BigDecimal a, BigDecimal b) {
Objects.requireNonNull(a, "a must not be null");
Objects.requireNonNull(b, "b must not be null");
return a.add(b);
}
/**
* Subtracts the second BigDecimal from the first.
*
* @param a the minuend
* @param b the subtrahend
* @return the difference
*/
public static BigDecimal subtract(BigDecimal a, BigDecimal b) {
Objects.requireNonNull(a, "a must not be null");
Objects.requireNonNull(b, "b must not be null");
return a.subtract(b);
}
/**
* Multiplies two BigDecimal values.
*
* @param a the first operand
* @param b the second operand
* @return the product
*/
public static BigDecimal multiply(BigDecimal a, BigDecimal b) {
Objects.requireNonNull(a, "a must not be null");
Objects.requireNonNull(b, "b must not be null");
return a.multiply(b);
}
/**
* Divides the first BigDecimal by the second.
*
* @param dividend the dividend
* @param divisor the divisor
* @return the quotient
* @throws ArithmeticException if divisor is zero
*/
public static BigDecimal divide(BigDecimal dividend, BigDecimal divisor) {
Objects.requireNonNull(dividend, "dividend must not be null");
Objects.requireNonNull(divisor, "divisor must not be null");
if (BigDecimal.ZERO.compareTo(divisor) == 0) {
throw new ArithmeticException("Division by zero");
}
return dividend.divide(divisor, DEFAULT_SCALE, DEFAULT_ROUNDING_MODE);
}
/**
* Divides the first BigDecimal by the second with specified scale and rounding mode.
*
* @param dividend the dividend
* @param divisor the divisor
* @param scale the scale
* @param roundingMode the rounding mode
* @return the quotient
* @throws ArithmeticException if divisor is zero
*/
public static BigDecimal divide(BigDecimal dividend, BigDecimal divisor, int scale, RoundingMode roundingMode) {
Objects.requireNonNull(dividend, "dividend must not be null");
Objects.requireNonNull(divisor, "divisor must not be null");
Objects.requireNonNull(roundingMode, "roundingMode must not be null");
if (BigDecimal.ZERO.compareTo(divisor) == 0) {
throw new ArithmeticException("Division by zero");
}
return dividend.divide(divisor, scale, roundingMode);
}
/**
* Rounds a BigDecimal to the default scale (2) using default rounding mode (HALF_UP).
*
* @param value the value to round
* @return the rounded value
*/
public static BigDecimal round(BigDecimal value) {
Objects.requireNonNull(value, "value must not be null");
return value.setScale(DEFAULT_SCALE, DEFAULT_ROUNDING_MODE);
}
/**
* Rounds a BigDecimal to the specified scale using the specified rounding mode.
*
* @param value the value to round
* @param scale the scale
* @param roundingMode the rounding mode
* @return the rounded value
*/
public static BigDecimal round(BigDecimal value, int scale, RoundingMode roundingMode) {
Objects.requireNonNull(value, "value must not be null");
Objects.requireNonNull(roundingMode, "roundingMode must not be null");
return value.setScale(scale, roundingMode);
}
/**
* Calculates percentage of a value.
*
* @param value the base value
* @param percentage the percentage (e.g., 10 for 10%)
* @return the percentage amount
*/
public static BigDecimal percentage(BigDecimal value, BigDecimal percentage) {
Objects.requireNonNull(value, "value must not be null");
Objects.requireNonNull(percentage, "percentage must not be null");
return value.multiply(percentage).divide(BigDecimal.valueOf(100), DEFAULT_SCALE, DEFAULT_ROUNDING_MODE);
}
/**
* Calculates percentage of a value and adds it.
*
* @param value the base value
* @param percentage the percentage to add
* @return the value plus percentage amount
*/
public static BigDecimal addPercentage(BigDecimal value, BigDecimal percentage) {
Objects.requireNonNull(value, "value must not be null");
Objects.requireNonNull(percentage, "percentage must not be null");
return value.add(percentage(value, percentage));
}
/**
* Calculates percentage of a value and subtracts it.
*
* @param value the base value
* @param percentage the percentage to subtract
* @return the value minus percentage amount
*/
public static BigDecimal subtractPercentage(BigDecimal value, BigDecimal percentage) {
Objects.requireNonNull(value, "value must not be null");
Objects.requireNonNull(percentage, "percentage must not be null");
return value.subtract(percentage(value, percentage));
}
/**
* Formats a BigDecimal as a Korean Won amount (e.g., "₩1,000").
*
* @param amount the amount to format
* @return the formatted string
*/
public static String formatKoreanWon(BigDecimal amount) {
Objects.requireNonNull(amount, "amount must not be null");
return KOREAN_WON_SYMBOL + KOREAN_WON_FORMAT.format(amount.setScale(0, RoundingMode.DOWN));
}
/**
* Formats a BigDecimal as a currency amount with 2 decimal places.
*
* @param amount the amount to format
* @return the formatted string
*/
public static String formatCurrency(BigDecimal amount) {
Objects.requireNonNull(amount, "amount must not be null");
return CURRENCY_FORMAT.format(amount);
}
/**
* Formats a BigDecimal as a decimal number.
*
* @param amount the amount to format
* @return the formatted string
*/
public static String formatDecimal(BigDecimal amount) {
Objects.requireNonNull(amount, "amount must not be null");
return DECIMAL_FORMAT.format(amount);
}
/**
* Formats a BigDecimal with a custom pattern.
*
* @param amount the amount to format
* @param pattern the DecimalFormat pattern
* @return the formatted string
*/
public static String format(BigDecimal amount, String pattern) {
Objects.requireNonNull(amount, "amount must not be null");
Objects.requireNonNull(pattern, "pattern must not be null");
DecimalFormat formatter = new DecimalFormat(pattern);
return formatter.format(amount);
}
/**
* Compares two BigDecimal values.
*
* @param a the first value
* @param b the second value
* @return -1 if a < b, 0 if a == b, 1 if a > b
*/
public static int compare(BigDecimal a, BigDecimal b) {
Objects.requireNonNull(a, "a must not be null");
Objects.requireNonNull(b, "b must not be null");
return a.compareTo(b);
}
/**
* Checks if the first value is greater than the second.
*
* @param a the first value
* @param b the second value
* @return true if a > b
*/
public static boolean isGreaterThan(BigDecimal a, BigDecimal b) {
return compare(a, b) > 0;
}
/**
* Checks if the first value is less than the second.
*
* @param a the first value
* @param b the second value
* @return true if a < b
*/
public static boolean isLessThan(BigDecimal a, BigDecimal b) {
return compare(a, b) < 0;
}
/**
* Checks if the first value is greater than or equal to the second.
*
* @param a the first value
* @param b the second value
* @return true if a >= b
*/
public static boolean isGreaterThanOrEqual(BigDecimal a, BigDecimal b) {
return compare(a, b) >= 0;
}
/**
* Checks if the first value is less than or equal to the second.
*
* @param a the first value
* @param b the second value
* @return true if a <= b
*/
public static boolean isLessThanOrEqual(BigDecimal a, BigDecimal b) {
return compare(a, b) <= 0;
}
/**
* Checks if the value is zero.
*
* @param value the value to check
* @return true if value is zero
*/
public static boolean isZero(BigDecimal value) {
Objects.requireNonNull(value, "value must not be null");
return BigDecimal.ZERO.compareTo(value) == 0;
}
/**
* Checks if the value is positive (greater than zero).
*
* @param value the value to check
* @return true if value > 0
*/
public static boolean isPositive(BigDecimal value) {
return compare(value, BigDecimal.ZERO) > 0;
}
/**
* Checks if the value is negative (less than zero).
*
* @param value the value to check
* @return true if value < 0
*/
public static boolean isNegative(BigDecimal value) {
return compare(value, BigDecimal.ZERO) < 0;
}
/**
* Returns the absolute value.
*
* @param value the value
* @return the absolute value
*/
public static BigDecimal abs(BigDecimal value) {
Objects.requireNonNull(value, "value must not be null");
return value.abs();
}
/**
* Negates the value.
*
* @param value the value
* @return the negated value
*/
public static BigDecimal negate(BigDecimal value) {
Objects.requireNonNull(value, "value must not be null");
return value.negate();
}
/**
* Returns the maximum of two values.
*
* @param a the first value
* @param b the second value
* @return the maximum value
*/
public static BigDecimal max(BigDecimal a, BigDecimal b) {
Objects.requireNonNull(a, "a must not be null");
Objects.requireNonNull(b, "b must not be null");
return a.max(b);
}
/**
* Returns the minimum of two values.
*
* @param a the first value
* @param b the second value
* @return the minimum value
*/
public static BigDecimal min(BigDecimal a, BigDecimal b) {
Objects.requireNonNull(a, "a must not be null");
Objects.requireNonNull(b, "b must not be null");
return a.min(b);
}
/**
* Parses a string that may contain currency symbols and commas.
*
* @param value the string to parse
* @return the parsed BigDecimal
*/
public static BigDecimal parseAmount(String value) {
Objects.requireNonNull(value, "value must not be null");
String cleaned = value.replace(KOREAN_WON_SYMBOL, "")
.replace(USDollar_SYMBOL, "")
.replace(EURO_SYMBOL, "")
.replace(",", "")
.replace(" ", "")
.trim();
return new BigDecimal(cleaned);
}
/**
* Validates if a string is a valid amount.
*
* @param value the string to validate
* @return true if valid, false otherwise
*/
public static boolean isValidAmount(String value) {
if (value == null || value.isEmpty()) {
return false;
}
try {
parseAmount(value);
return true;
} catch (NumberFormatException e) {
return false;
}
}
/**
* Converts cents/won to decimal amount.
*
* @param cents the amount in cents
* @return the decimal amount
*/
public static BigDecimal fromCents(long cents) {
return BigDecimal.valueOf(cents).divide(BigDecimal.valueOf(100), DEFAULT_SCALE, DEFAULT_ROUNDING_MODE);
}
/**
* Converts decimal amount to cents/won.
*
* @param amount the decimal amount
* @return the amount in cents
*/
public static long toCents(BigDecimal amount) {
Objects.requireNonNull(amount, "amount must not be null");
return amount.multiply(BigDecimal.valueOf(100)).setScale(0, RoundingMode.DOWN).longValue();
}
}

View file

@ -0,0 +1,311 @@
package com.klaro.acquirecore.framework.util;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoUnit;
import java.util.Objects;
/**
* Static utility class for date operations migrated from legacy/util_date.c
* Uses java.time.LocalDate and java.time.LocalDateTime for date handling.
*/
public final class DateUtil {
public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
public static final String DEFAULT_DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
public static final String KOREAN_DATE_FORMAT = "yyyy년 MM월 dd일";
public static final String ISO_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss";
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT);
private static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern(DEFAULT_DATETIME_FORMAT);
private static final DateTimeFormatter KOREAN_FORMATTER = DateTimeFormatter.ofPattern(KOREAN_DATE_FORMAT);
private static final DateTimeFormatter ISO_FORMATTER = DateTimeFormatter.ofPattern(ISO_DATE_FORMAT);
private DateUtil() {
throw new UnsupportedOperationException("Utility class cannot be instantiated");
}
/**
* Parses a date string using the default format (yyyy-MM-dd).
*
* @param dateStr the date string to parse
* @return the parsed LocalDate
* @throws DateTimeParseException if the string cannot be parsed
*/
public static LocalDate parseDate(String dateStr) {
Objects.requireNonNull(dateStr, "dateStr must not be null");
return LocalDate.parse(dateStr, DATE_FORMATTER);
}
/**
* Parses a date string using the specified format pattern.
*
* @param dateStr the date string to parse
* @param pattern the date format pattern
* @return the parsed LocalDate
* @throws DateTimeParseException if the string cannot be parsed
*/
public static LocalDate parseDate(String dateStr, String pattern) {
Objects.requireNonNull(dateStr, "dateStr must not be null");
Objects.requireNonNull(pattern, "pattern must not be null");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
return LocalDate.parse(dateStr, formatter);
}
/**
* Parses a datetime string using the default format (yyyy-MM-dd HH:mm:ss).
*
* @param dateTimeStr the datetime string to parse
* @return the parsed LocalDateTime
* @throws DateTimeParseException if the string cannot be parsed
*/
public static LocalDateTime parseDateTime(String dateTimeStr) {
Objects.requireNonNull(dateTimeStr, "dateTimeStr must not be null");
return LocalDateTime.parse(dateTimeStr, DATETIME_FORMATTER);
}
/**
* Parses a datetime string using the specified format pattern.
*
* @param dateTimeStr the datetime string to parse
* @param pattern the datetime format pattern
* @return the parsed LocalDateTime
* @throws DateTimeParseException if the string cannot be parsed
*/
public static LocalDateTime parseDateTime(String dateTimeStr, String pattern) {
Objects.requireNonNull(dateTimeStr, "dateTimeStr must not be null");
Objects.requireNonNull(pattern, "pattern must not be null");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
return LocalDateTime.parse(dateTimeStr, formatter);
}
/**
* Formats a LocalDate using the default format (yyyy-MM-dd).
*
* @param date the date to format
* @return the formatted date string
*/
public static String formatDate(LocalDate date) {
Objects.requireNonNull(date, "date must not be null");
return date.format(DATE_FORMATTER);
}
/**
* Formats a LocalDate using the specified format pattern.
*
* @param date the date to format
* @param pattern the format pattern
* @return the formatted date string
*/
public static String formatDate(LocalDate date, String pattern) {
Objects.requireNonNull(date, "date must not be null");
Objects.requireNonNull(pattern, "pattern must not be null");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
return date.format(formatter);
}
/**
* Formats a LocalDateTime using the default format (yyyy-MM-dd HH:mm:ss).
*
* @param dateTime the datetime to format
* @return the formatted datetime string
*/
public static String formatDateTime(LocalDateTime dateTime) {
Objects.requireNonNull(dateTime, "dateTime must not be null");
return dateTime.format(DATETIME_FORMATTER);
}
/**
* Formats a LocalDateTime using the specified format pattern.
*
* @param dateTime the datetime to format
* @param pattern the format pattern
* @return the formatted datetime string
*/
public static String formatDateTime(LocalDateTime dateTime, String pattern) {
Objects.requireNonNull(dateTime, "dateTime must not be null");
Objects.requireNonNull(pattern, "pattern must not be null");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
return dateTime.format(formatter);
}
/**
* Formats a LocalDate in Korean date format (yyyy년 MM월 dd일).
*
* @param date the date to format
* @return the formatted Korean date string
*/
public static String formatDateKorean(LocalDate date) {
Objects.requireNonNull(date, "date must not be null");
return date.format(KOREAN_FORMATTER);
}
/**
* Calculates the number of days between two dates.
*
* @param startDate the start date
* @param endDate the end date
* @return the number of days between the dates
*/
public static long daysBetween(LocalDate startDate, LocalDate endDate) {
Objects.requireNonNull(startDate, "startDate must not be null");
Objects.requireNonNull(endDate, "endDate must not be null");
return ChronoUnit.DAYS.between(startDate, endDate);
}
/**
* Adds days to a date.
*
* @param date the base date
* @param days the number of days to add (can be negative)
* @return the resulting date
*/
public static LocalDate addDays(LocalDate date, long days) {
Objects.requireNonNull(date, "date must not be null");
return date.plusDays(days);
}
/**
* Adds months to a date.
*
* @param date the base date
* @param months the number of months to add (can be negative)
* @return the resulting date
*/
public static LocalDate addMonths(LocalDate date, long months) {
Objects.requireNonNull(date, "date must not be null");
return date.plusMonths(months);
}
/**
* Adds years to a date.
*
* @param date the base date
* @param years the number of years to add (can be negative)
* @return the resulting date
*/
public static LocalDate addYears(LocalDate date, long years) {
Objects.requireNonNull(date, "date must not be null");
return date.plusYears(years);
}
/**
* Checks if a date is before another date.
*
* @param date the date to check
* @param dateToCompare the date to compare against
* @return true if date is before dateToCompare
*/
public static boolean isBefore(LocalDate date, LocalDate dateToCompare) {
Objects.requireNonNull(date, "date must not be null");
Objects.requireNonNull(dateToCompare, "dateToCompare must not be null");
return date.isBefore(dateToCompare);
}
/**
* Checks if a date is after another date.
*
* @param date the date to check
* @param dateToCompare the date to compare against
* @return true if date is after dateToCompare
*/
public static boolean isAfter(LocalDate date, LocalDate dateToCompare) {
Objects.requireNonNull(date, "date must not be null");
Objects.requireNonNull(dateToCompare, "dateToCompare must not be null");
return date.isAfter(dateToCompare);
}
/**
* Checks if a date is between two other dates (inclusive).
*
* @param date the date to check
* @param startDate the start of the range
* @param endDate the end of the range
* @return true if date is between startDate and endDate (inclusive)
*/
public static boolean isBetween(LocalDate date, LocalDate startDate, LocalDate endDate) {
Objects.requireNonNull(date, "date must not be null");
Objects.requireNonNull(startDate, "startDate must not be null");
Objects.requireNonNull(endDate, "endDate must not be null");
return !date.isBefore(startDate) && !date.isAfter(endDate);
}
/**
* Gets the start of the day for a given date.
*
* @param date the date
* @return the start of the day as LocalDateTime (00:00:00)
*/
public static LocalDateTime startOfDay(LocalDate date) {
Objects.requireNonNull(date, "date must not be null");
return date.atStartOfDay();
}
/**
* Gets the end of the day for a given date.
*
* @param date the date
* @return the end of the day as LocalDateTime (23:59:59.999999999)
*/
public static LocalDateTime endOfDay(LocalDate date) {
Objects.requireNonNull(date, "date must not be null");
return date.atTime(23, 59, 59, 999999999);
}
/**
* Gets today's date.
*
* @return the current date
*/
public static LocalDate today() {
return LocalDate.now();
}
/**
* Gets the current datetime.
*
* @return the current datetime
*/
public static LocalDateTime now() {
return LocalDateTime.now();
}
/**
* Validates if a string is a valid date in the default format.
*
* @param dateStr the date string to validate
* @return true if valid, false otherwise
*/
public static boolean isValidDate(String dateStr) {
if (dateStr == null || dateStr.isEmpty()) {
return false;
}
try {
parseDate(dateStr);
return true;
} catch (DateTimeParseException e) {
return false;
}
}
/**
* Validates if a string is a valid date in the specified format.
*
* @param dateStr the date string to validate
* @param pattern the expected format pattern
* @return true if valid, false otherwise
*/
public static boolean isValidDate(String dateStr, String pattern) {
if (dateStr == null || dateStr.isEmpty() || pattern == null) {
return false;
}
try {
parseDate(dateStr, pattern);
return true;
} catch (DateTimeParseException e) {
return false;
}
}
}

View file

@ -0,0 +1,507 @@
package com.klaro.acquirecore.framework.util;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Objects;
import java.util.regex.Pattern;
/**
* Static utility class for message encoding/decoding migrated from legacy/util_msg.c
* Uses java.util.Base64 and java.nio.charset for codec operations.
*/
public final class MessageUtil {
public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
public static final Charset EUC_KR_CHARSET = Charset.forName("EUC-KR");
public static final Charset ISO_8859_1_CHARSET = StandardCharsets.ISO_8859_1;
private static final Pattern EMAIL_PATTERN = Pattern.compile(
"^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$"
);
private static final Pattern PHONE_PATTERN = Pattern.compile(
"^\\d{2,4}-\\d{3,4}-\\d{4}$|^\\d{10,11}$"
);
private static final Pattern KOREAN_PHONE_PATTERN = Pattern.compile(
"^01[016789]-\\d{3,4}-\\d{4}$"
);
private MessageUtil() {
throw new UnsupportedOperationException("Utility class cannot be instantiated");
}
// ==================== Base64 Encoding/Decoding ====================
/**
* Encodes a string to Base64.
*
* @param input the string to encode
* @return the Base64 encoded string
*/
public static String encodeBase64(String input) {
Objects.requireNonNull(input, "input must not be null");
return Base64.getEncoder().encodeToString(input.getBytes(DEFAULT_CHARSET));
}
/**
* Encodes a string to Base64 with specified charset.
*
* @param input the string to encode
* @param charset the charset to use
* @return the Base64 encoded string
*/
public static String encodeBase64(String input, Charset charset) {
Objects.requireNonNull(input, "input must not be null");
Objects.requireNonNull(charset, "charset must not be null");
return Base64.getEncoder().encodeToString(input.getBytes(charset));
}
/**
* Encodes bytes to Base64.
*
* @param bytes the bytes to encode
* @return the Base64 encoded string
*/
public static String encodeBase64(byte[] bytes) {
Objects.requireNonNull(bytes, "bytes must not be null");
return Base64.getEncoder().encodeToString(bytes);
}
/**
* Decodes a Base64 string to plain string.
*
* @param encoded the Base64 encoded string
* @return the decoded string
*/
public static String decodeBase64(String encoded) {
Objects.requireNonNull(encoded, "encoded must not be null");
byte[] decoded = Base64.getDecoder().decode(encoded);
return new String(decoded, DEFAULT_CHARSET);
}
/**
* Decodes a Base64 string to plain string with specified charset.
*
* @param encoded the Base64 encoded string
* @param charset the charset to use for decoding
* @return the decoded string
*/
public static String decodeBase64(String encoded, Charset charset) {
Objects.requireNonNull(encoded, "encoded must not be null");
Objects.requireNonNull(charset, "charset must not be null");
byte[] decoded = Base64.getDecoder().decode(encoded);
return new String(decoded, charset);
}
/**
* Decodes a Base64 string to bytes.
*
* @param encoded the Base64 encoded string
* @return the decoded bytes
*/
public static byte[] decodeBase64ToBytes(String encoded) {
Objects.requireNonNull(encoded, "encoded must not be null");
return Base64.getDecoder().decode(encoded);
}
/**
* Encodes a URL-safe Base64 string.
*
* @param input the string to encode
* @return the URL-safe Base64 encoded string
*/
public static String encodeBase64Url(String input) {
Objects.requireNonNull(input, "input must not be null");
return Base64.getUrlEncoder().encodeToString(input.getBytes(DEFAULT_CHARSET));
}
/**
* Decodes a URL-safe Base64 string.
*
* @param encoded the URL-safe Base64 encoded string
* @return the decoded string
*/
public static String decodeBase64Url(String encoded) {
Objects.requireNonNull(encoded, "encoded must not be null");
byte[] decoded = Base64.getUrlDecoder().decode(encoded);
return new String(decoded, DEFAULT_CHARSET);
}
// ==================== Hex Encoding/Decoding ====================
/**
* Encodes bytes to hexadecimal string.
*
* @param bytes the bytes to encode
* @return the hexadecimal string
*/
public static String encodeHex(byte[] bytes) {
Objects.requireNonNull(bytes, "bytes must not be null");
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
/**
* Decodes a hexadecimal string to bytes.
*
* @param hex the hexadecimal string
* @return the decoded bytes
*/
public static byte[] decodeHex(String hex) {
Objects.requireNonNull(hex, "hex must not be null");
if (hex.length() % 2 != 0) {
throw new IllegalArgumentException("Hex string must have even length");
}
int len = hex.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4)
+ Character.digit(hex.charAt(i + 1), 16));
}
return data;
}
// ==================== URL Encoding/Decoding ====================
/**
* URL encodes a string using UTF-8.
*
* @param input the string to encode
* @return the URL encoded string
*/
public static String urlEncode(String input) {
Objects.requireNonNull(input, "input must not be null");
try {
return java.net.URLEncoder.encode(input, DEFAULT_CHARSET.name());
} catch (java.io.UnsupportedEncodingException e) {
throw new RuntimeException("Unsupported encoding", e);
}
}
/**
* URL decodes a string using UTF-8.
*
* @param encoded the URL encoded string
* @return the decoded string
*/
public static String urlDecode(String encoded) {
Objects.requireNonNull(encoded, "encoded must not be null");
try {
return java.net.URLDecoder.decode(encoded, DEFAULT_CHARSET.name());
} catch (java.io.UnsupportedEncodingException e) {
throw new RuntimeException("Unsupported encoding", e);
}
}
// ==================== String Manipulation ====================
/**
* Masks a string with asterisks, showing only the first and last characters.
*
* @param input the string to mask
* @return the masked string
*/
public static String mask(String input) {
return mask(input, 1, 1);
}
/**
* Masks a string with asterisks, showing only specified first and last characters.
*
* @param input the string to mask
* @param showFirst number of characters to show at the start
* @param showLast number of characters to show at the end
* @return the masked string
*/
public static String mask(String input, int showFirst, int showLast) {
if (input == null || input.isEmpty()) {
return input;
}
int length = input.length();
if (length <= showFirst + showLast) {
return input;
}
StringBuilder sb = new StringBuilder();
sb.append(input, 0, showFirst);
for (int i = showFirst; i < length - showLast; i++) {
sb.append('*');
}
sb.append(input.substring(length - showLast));
return sb.toString();
}
/**
* Masks an email address.
*
* @param email the email to mask
* @return the masked email
*/
public static String maskEmail(String email) {
if (email == null || !email.contains("@")) {
return email;
}
String[] parts = email.split("@");
String localPart = parts[0];
String domain = parts[1];
if (localPart.length() <= 2) {
return mask(localPart, 1, 0) + "@" + domain;
}
return mask(localPart, 1, 1) + "@" + domain;
}
/**
* Masks a phone number.
*
* @param phone the phone number to mask
* @return the masked phone number
*/
public static String maskPhone(String phone) {
if (phone == null || phone.isEmpty()) {
return phone;
}
String digitsOnly = phone.replaceAll("[^0-9]", "");
if (digitsOnly.length() < 7) {
return phone;
}
int length = digitsOnly.length();
String masked = digitsOnly.substring(0, 3) + "****" + digitsOnly.substring(length - 4);
if (phone.contains("-")) {
return masked.substring(0, 3) + "-" + "****" + "-" + masked.substring(7);
}
return masked;
}
// ==================== Validation ====================
/**
* Validates if a string is a valid email address.
*
* @param email the email to validate
* @return true if valid, false otherwise
*/
public static boolean isValidEmail(String email) {
if (email == null || email.isEmpty()) {
return false;
}
return EMAIL_PATTERN.matcher(email).matches();
}
/**
* Validates if a string is a valid phone number.
*
* @param phone the phone number to validate
* @return true if valid, false otherwise
*/
public static boolean isValidPhone(String phone) {
if (phone == null || phone.isEmpty()) {
return false;
}
return PHONE_PATTERN.matcher(phone).matches();
}
/**
* Validates if a string is a valid Korean phone number.
*
* @param phone the phone number to validate
* @return true if valid, false otherwise
*/
public static boolean isValidKoreanPhone(String phone) {
if (phone == null || phone.isEmpty()) {
return false;
}
return KOREAN_PHONE_PATTERN.matcher(phone).matches();
}
/**
* Checks if a string is null or empty.
*
* @param input the string to check
* @return true if null or empty
*/
public static boolean isEmpty(String input) {
return input == null || input.isEmpty();
}
/**
* Checks if a string is null, empty, or whitespace only.
*
* @param input the string to check
* @return true if null, empty, or whitespace only
*/
public static boolean isBlank(String input) {
return input == null || input.trim().isEmpty();
}
// ==================== Charset Conversion ====================
/**
* Converts a string from one charset to another.
*
* @param input the input string
* @param fromCharset the source charset
* @param toCharset the target charset
* @return the converted string
*/
public static String convertCharset(String input, Charset fromCharset, Charset toCharset) {
Objects.requireNonNull(input, "input must not be null");
Objects.requireNonNull(fromCharset, "fromCharset must not be null");
Objects.requireNonNull(toCharset, "toCharset must not be null");
byte[] bytes = input.getBytes(fromCharset);
return new String(bytes, toCharset);
}
/**
* Converts EUC-KR encoded bytes to UTF-8 string.
*
* @param eucKrBytes the EUC-KR encoded bytes
* @return the UTF-8 string
*/
public static String eucKrToUtf8(byte[] eucKrBytes) {
Objects.requireNonNull(eucKrBytes, "eucKrBytes must not be null");
return new String(eucKrBytes, EUC_KR_CHARSET);
}
/**
* Converts UTF-8 string to EUC-KR encoded bytes.
*
* @param input the UTF-8 string
* @return the EUC-KR encoded bytes
*/
public static byte[] utf8ToEucKr(String input) {
Objects.requireNonNull(input, "input must not be null");
return input.getBytes(EUC_KR_CHARSET);
}
// ==================== Truncation and Padding ====================
/**
* Truncates a string to the specified length.
*
* @param input the string to truncate
* @param maxLength the maximum length
* @return the truncated string
*/
public static String truncate(String input, int maxLength) {
if (input == null) {
return null;
}
if (input.length() <= maxLength) {
return input;
}
return input.substring(0, maxLength);
}
/**
* Pads a string on the left with spaces to reach the specified length.
*
* @param input the string to pad
* @param length the target length
* @return the padded string
*/
public static String padLeft(String input, int length) {
if (input == null) {
input = "";
}
return String.format("%" + length + "s", input);
}
/**
* Pads a string on the right with spaces to reach the specified length.
*
* @param input the string to pad
* @param length the target length
* @return the padded string
*/
public static String padRight(String input, int length) {
if (input == null) {
input = "";
}
return String.format("%- " + length + "s", input);
}
/**
* Pads a string on the left with zeros to reach the specified length.
*
* @param input the string to pad
* @param length the target length
* @return the zero-padded string
*/
public static String padLeftWithZeros(String input, int length) {
if (input == null) {
input = "";
}
return String.format("%0" + length + "d", Long.parseLong(input));
}
/**
* Left pads a numeric string with zeros.
*
* @param value the numeric value
* @param length the target length
* @return the zero-padded string
*/
public static String zeroPad(long value, int length) {
return String.format("%0" + length + "d", value);
}
// ==================== Byte Array Utilities ====================
/**
* Converts a string to bytes using the default charset.
*
* @param input the string to convert
* @return the bytes
*/
public static byte[] toBytes(String input) {
if (input == null) {
return null;
}
return input.getBytes(DEFAULT_CHARSET);
}
/**
* Converts a string to bytes using the specified charset.
*
* @param input the string to convert
* @param charset the charset to use
* @return the bytes
*/
public static byte[] toBytes(String input, Charset charset) {
if (input == null) {
return null;
}
Objects.requireNonNull(charset, "charset must not be null");
return input.getBytes(charset);
}
/**
* Converts bytes to string using the default charset.
*
* @param bytes the bytes to convert
* @return the string
*/
public static String toString(byte[] bytes) {
if (bytes == null) {
return null;
}
return new String(bytes, DEFAULT_CHARSET);
}
/**
* Converts bytes to string using the specified charset.
*
* @param bytes the bytes to convert
* @param charset the charset to use
* @return the string
*/
public static String toString(byte[] bytes, Charset charset) {
if (bytes == null) {
return null;
}
Objects.requireNonNull(charset, "charset must not be null");
return new String(bytes, charset);
}
}

View file

@ -0,0 +1,221 @@
package com.klaro.acquirecore.framework.util;
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import java.math.RoundingMode;
import static org.junit.jupiter.api.Assertions.*;
class AmountUtilTest {
@Test
void of_withStringValue_returnsBigDecimal() {
BigDecimal result = AmountUtil.of("1234.56");
assertEquals(new BigDecimal("1234.56"), result);
}
@Test
void of_withLongValue_returnsBigDecimal() {
BigDecimal result = AmountUtil.of(1000L);
assertEquals(new BigDecimal("1000"), result);
}
@Test
void of_withDoubleValue_returnsBigDecimal() {
BigDecimal result = AmountUtil.of(1234.56);
assertEquals(new BigDecimal("1234.56"), result);
}
@Test
void add_addsTwoValues() {
BigDecimal a = new BigDecimal("100.50");
BigDecimal b = new BigDecimal("50.25");
BigDecimal result = AmountUtil.add(a, b);
assertEquals(new BigDecimal("150.75"), result);
}
@Test
void subtract_subtractsValues() {
BigDecimal a = new BigDecimal("100.50");
BigDecimal b = new BigDecimal("50.25");
BigDecimal result = AmountUtil.subtract(a, b);
assertEquals(new BigDecimal("50.25"), result);
}
@Test
void multiply_multipliesValues() {
BigDecimal a = new BigDecimal("10.00");
BigDecimal b = new BigDecimal("5.00");
BigDecimal result = AmountUtil.multiply(a, b);
assertEquals(new BigDecimal("50.00"), result);
}
@Test
void divide_dividesValues() {
BigDecimal dividend = new BigDecimal("100.00");
BigDecimal divisor = new BigDecimal("4.00");
BigDecimal result = AmountUtil.divide(dividend, divisor);
assertEquals(new BigDecimal("25.00"), result);
}
@Test
void divide_byZero_throwsException() {
BigDecimal dividend = new BigDecimal("100.00");
BigDecimal divisor = BigDecimal.ZERO;
assertThrows(ArithmeticException.class, () -> AmountUtil.divide(dividend, divisor));
}
@Test
void round_roundsToDefaultScale() {
BigDecimal value = new BigDecimal("123.456");
BigDecimal result = AmountUtil.round(value);
assertEquals(new BigDecimal("123.46"), result);
}
@Test
void round_roundsWithCustomScaleAndRoundingMode() {
BigDecimal value = new BigDecimal("123.456");
BigDecimal result = AmountUtil.round(value, 3, RoundingMode.HALF_UP);
assertEquals(new BigDecimal("123.456"), result);
}
@Test
void percentage_calculatesCorrectPercentage() {
BigDecimal value = new BigDecimal("1000.00");
BigDecimal percentage = new BigDecimal("10");
BigDecimal result = AmountUtil.percentage(value, percentage);
assertEquals(new BigDecimal("100.00"), result);
}
@Test
void addPercentage_addsPercentageToValue() {
BigDecimal value = new BigDecimal("1000.00");
BigDecimal percentage = new BigDecimal("10");
BigDecimal result = AmountUtil.addPercentage(value, percentage);
assertEquals(new BigDecimal("1100.00"), result);
}
@Test
void subtractPercentage_subtractsPercentageFromValue() {
BigDecimal value = new BigDecimal("1000.00");
BigDecimal percentage = new BigDecimal("10");
BigDecimal result = AmountUtil.subtractPercentage(value, percentage);
assertEquals(new BigDecimal("900.00"), result);
}
@Test
void formatKoreanWon_formatsCorrectly() {
BigDecimal amount = new BigDecimal("1234567");
String result = AmountUtil.formatKoreanWon(amount);
assertEquals("₩1,234,567", result);
}
@Test
void formatCurrency_formatsWithTwoDecimals() {
BigDecimal amount = new BigDecimal("1234.5");
String result = AmountUtil.formatCurrency(amount);
assertEquals("1,234.50", result);
}
@Test
void compare_comparesValues() {
BigDecimal a = new BigDecimal("100.00");
BigDecimal b = new BigDecimal("200.00");
assertEquals(-1, AmountUtil.compare(a, b));
assertEquals(1, AmountUtil.compare(b, a));
assertEquals(0, AmountUtil.compare(a, new BigDecimal("100.00")));
}
@Test
void isGreaterThan_returnsCorrectResult() {
BigDecimal a = new BigDecimal("200.00");
BigDecimal b = new BigDecimal("100.00");
assertTrue(AmountUtil.isGreaterThan(a, b));
assertFalse(AmountUtil.isGreaterThan(b, a));
}
@Test
void isLessThan_returnsCorrectResult() {
BigDecimal a = new BigDecimal("100.00");
BigDecimal b = new BigDecimal("200.00");
assertTrue(AmountUtil.isLessThan(a, b));
assertFalse(AmountUtil.isLessThan(b, a));
}
@Test
void isZero_withZeroValue_returnsTrue() {
assertTrue(AmountUtil.isZero(BigDecimal.ZERO));
}
@Test
void isPositive_withPositiveValue_returnsTrue() {
assertTrue(AmountUtil.isPositive(new BigDecimal("1.00")));
}
@Test
void isNegative_withNegativeValue_returnsTrue() {
assertTrue(AmountUtil.isNegative(new BigDecimal("-1.00")));
}
@Test
void abs_returnsAbsoluteValue() {
BigDecimal value = new BigDecimal("-100.00");
BigDecimal result = AmountUtil.abs(value);
assertEquals(new BigDecimal("100.00"), result);
}
@Test
void negate_negatesValue() {
BigDecimal value = new BigDecimal("100.00");
BigDecimal result = AmountUtil.negate(value);
assertEquals(new BigDecimal("-100.00"), result);
}
@Test
void max_returnsMaximumValue() {
BigDecimal a = new BigDecimal("100.00");
BigDecimal b = new BigDecimal("200.00");
BigDecimal result = AmountUtil.max(a, b);
assertEquals(new BigDecimal("200.00"), result);
}
@Test
void min_returnsMinimumValue() {
BigDecimal a = new BigDecimal("100.00");
BigDecimal b = new BigDecimal("200.00");
BigDecimal result = AmountUtil.min(a, b);
assertEquals(new BigDecimal("100.00"), result);
}
@Test
void parseAmount_withCurrencySymbol_parsesCorrectly() {
BigDecimal result = AmountUtil.parseAmount("₩1,000,000");
assertEquals(new BigDecimal("1000000"), result);
}
@Test
void isValidAmount_withValidAmount_returnsTrue() {
assertTrue(AmountUtil.isValidAmount("₩1,000,000"));
assertTrue(AmountUtil.isValidAmount("1000.50"));
}
@Test
void isValidAmount_withInvalidAmount_returnsFalse() {
assertFalse(AmountUtil.isValidAmount("invalid"));
assertFalse(AmountUtil.isValidAmount(null));
}
@Test
void fromCents_convertsToDecimal() {
BigDecimal result = AmountUtil.fromCents(123456);
assertEquals(new BigDecimal("1234.56"), result);
}
@Test
void toCents_convertsToCents() {
BigDecimal amount = new BigDecimal("1234.56");
long result = AmountUtil.toCents(amount);
assertEquals(123456, result);
}
}

View file

@ -0,0 +1,161 @@
package com.klaro.acquirecore.framework.util;
import org.junit.jupiter.api.Test;
import java.time.LocalDate;
import java.time.LocalDateTime;
import static org.junit.jupiter.api.Assertions.*;
class DateUtilTest {
@Test
void parseDate_withValidDateString_returnsLocalDate() {
LocalDate result = DateUtil.parseDate("2024-01-15");
assertEquals(LocalDate.of(2024, 1, 15), result);
}
@Test
void parseDate_withNull_throwsException() {
assertThrows(NullPointerException.class, () -> DateUtil.parseDate(null));
}
@Test
void formatDate_withValidDate_returnsFormattedString() {
LocalDate date = LocalDate.of(2024, 1, 15);
String result = DateUtil.formatDate(date);
assertEquals("2024-01-15", result);
}
@Test
void formatDate_withNull_throwsException() {
assertThrows(NullPointerException.class, () -> DateUtil.formatDate(null));
}
@Test
void daysBetween_calculatesCorrectDays() {
LocalDate start = LocalDate.of(2024, 1, 1);
LocalDate end = LocalDate.of(2024, 1, 11);
long result = DateUtil.daysBetween(start, end);
assertEquals(10, result);
}
@Test
void addDays_addsCorrectDays() {
LocalDate date = LocalDate.of(2024, 1, 15);
LocalDate result = DateUtil.addDays(date, 5);
assertEquals(LocalDate.of(2024, 1, 20), result);
}
@Test
void addDays_withNegativeValue_subtractsDays() {
LocalDate date = LocalDate.of(2024, 1, 15);
LocalDate result = DateUtil.addDays(date, -5);
assertEquals(LocalDate.of(2024, 1, 10), result);
}
@Test
void isBetween_withDateInRange_returnsTrue() {
LocalDate date = LocalDate.of(2024, 6, 15);
LocalDate start = LocalDate.of(2024, 1, 1);
LocalDate end = LocalDate.of(2024, 12, 31);
assertTrue(DateUtil.isBetween(date, start, end));
}
@Test
void isBetween_withDateOutOfRange_returnsFalse() {
LocalDate date = LocalDate.of(2025, 1, 1);
LocalDate start = LocalDate.of(2024, 1, 1);
LocalDate end = LocalDate.of(2024, 12, 31);
assertFalse(DateUtil.isBetween(date, start, end));
}
@Test
void isValidDate_withValidDate_returnsTrue() {
assertTrue(DateUtil.isValidDate("2024-01-15"));
}
@Test
void isValidDate_withInvalidDate_returnsFalse() {
assertFalse(DateUtil.isValidDate("invalid-date"));
}
@Test
void isValidDate_withNull_returnsFalse() {
assertFalse(DateUtil.isValidDate(null));
}
@Test
void today_returnsCurrentDate() {
LocalDate result = DateUtil.today();
assertEquals(LocalDate.now(), result);
}
@Test
void now_returnsCurrentDateTime() {
LocalDateTime result = DateUtil.now();
assertNotNull(result);
}
@Test
void parseDateTime_withValidDateTimeString_returnsLocalDateTime() {
LocalDateTime result = DateUtil.parseDateTime("2024-01-15 10:30:45");
assertEquals(LocalDateTime.of(2024, 1, 15, 10, 30, 45), result);
}
@Test
void formatDateTime_withValidDateTime_returnsFormattedString() {
LocalDateTime dateTime = LocalDateTime.of(2024, 1, 15, 10, 30, 45);
String result = DateUtil.formatDateTime(dateTime);
assertEquals("2024-01-15 10:30:45", result);
}
@Test
void formatDateKorean_withValidDate_returnsKoreanFormat() {
LocalDate date = LocalDate.of(2024, 1, 15);
String result = DateUtil.formatDateKorean(date);
assertEquals("2024년 01월 15일", result);
}
@Test
void startOfDay_returnsStartOfDayDateTime() {
LocalDate date = LocalDate.of(2024, 1, 15);
LocalDateTime result = DateUtil.startOfDay(date);
assertEquals(LocalDateTime.of(2024, 1, 15, 0, 0, 0), result);
}
@Test
void endOfDay_returnsEndOfDayDateTime() {
LocalDate date = LocalDate.of(2024, 1, 15);
LocalDateTime result = DateUtil.endOfDay(date);
assertEquals(LocalDateTime.of(2024, 1, 15, 23, 59, 59, 999999999), result);
}
@Test
void addMonths_addsCorrectMonths() {
LocalDate date = LocalDate.of(2024, 1, 15);
LocalDate result = DateUtil.addMonths(date, 3);
assertEquals(LocalDate.of(2024, 4, 15), result);
}
@Test
void addYears_addsCorrectYears() {
LocalDate date = LocalDate.of(2024, 1, 15);
LocalDate result = DateUtil.addYears(date, 1);
assertEquals(LocalDate.of(2025, 1, 15), result);
}
@Test
void isBefore_withEarlierDate_returnsTrue() {
LocalDate date = LocalDate.of(2024, 1, 15);
LocalDate dateToCompare = LocalDate.of(2024, 1, 20);
assertTrue(DateUtil.isBefore(date, dateToCompare));
}
@Test
void isAfter_withLaterDate_returnsTrue() {
LocalDate date = LocalDate.of(2024, 1, 20);
LocalDate dateToCompare = LocalDate.of(2024, 1, 15);
assertTrue(DateUtil.isAfter(date, dateToCompare));
}
}

View file

@ -0,0 +1,205 @@
package com.klaro.acquirecore.framework.util;
import org.junit.jupiter.api.Test;
import java.nio.charset.StandardCharsets;
import static org.junit.jupiter.api.Assertions.*;
class MessageUtilTest {
@Test
void encodeBase64_encodesString() {
String input = "Hello, World!";
String result = MessageUtil.encodeBase64(input);
assertEquals("SGVsbG8sIFdvcmxkIQ==", result);
}
@Test
void decodeBase64_decodesString() {
String encoded = "SGVsbG8sIFdvcmxkIQ==";
String result = MessageUtil.decodeBase64(encoded);
assertEquals("Hello, World!", result);
}
@Test
void encodeBase64Url_encodesUrlSafe() {
String input = "Hello+World/Test";
String result = MessageUtil.encodeBase64Url(input);
assertEquals("SGVsbG8rV29ybGQvVGVzdA==", result);
}
@Test
void decodeBase64Url_decodesUrlSafe() {
String encoded = "SGVsbG8rV29ybGQvVGVzdA==";
String result = MessageUtil.decodeBase64Url(encoded);
assertEquals("Hello+World/Test", result);
}
@Test
void encodeHex_encodesBytesToHex() {
byte[] bytes = {(byte) 0x48, (byte) 0x65, (byte) 0x6c, (byte) 0x6c, (byte) 0x6f};
String result = MessageUtil.encodeHex(bytes);
assertEquals("48656c6c6f", result);
}
@Test
void decodeHex_decodesHexToBytes() {
String hex = "48656c6c6f";
byte[] result = MessageUtil.decodeHex(hex);
assertArrayEquals(new byte[]{(byte) 0x48, (byte) 0x65, (byte) 0x6c, (byte) 0x6c, (byte) 0x6f}, result);
}
@Test
void urlEncode_encodesString() {
String input = "Hello World";
String result = MessageUtil.urlEncode(input);
assertEquals("Hello+World", result);
}
@Test
void urlDecode_decodesString() {
String encoded = "Hello+World";
String result = MessageUtil.urlDecode(encoded);
assertEquals("Hello World", result);
}
@Test
void mask_masksMiddleCharacters() {
String input = "1234567890";
String result = MessageUtil.mask(input);
assertEquals("1********0", result);
}
@Test
void mask_withCustomShowCount_masksCorrectly() {
String input = "1234567890";
String result = MessageUtil.mask(input, 2, 2);
assertEquals("12******90", result);
}
@Test
void maskEmail_masksEmailCorrectly() {
String email = "test@example.com";
String result = MessageUtil.maskEmail(email);
assertEquals("t***t@example.com", result);
}
@Test
void maskPhone_masksPhoneCorrectly() {
String phone = "010-1234-5678";
String result = MessageUtil.maskPhone(phone);
assertEquals("010-****-5678", result);
}
@Test
void isValidEmail_withValidEmail_returnsTrue() {
assertTrue(MessageUtil.isValidEmail("test@example.com"));
assertTrue(MessageUtil.isValidEmail("user.name+tag@domain.co.kr"));
}
@Test
void isValidEmail_withInvalidEmail_returnsFalse() {
assertFalse(MessageUtil.isValidEmail("invalid-email"));
assertFalse(MessageUtil.isValidEmail("@domain.com"));
assertFalse(MessageUtil.isValidEmail(null));
}
@Test
void isValidPhone_withValidPhone_returnsTrue() {
assertTrue(MessageUtil.isValidPhone("010-1234-5678"));
assertTrue(MessageUtil.isValidPhone("021234567"));
}
@Test
void isValidKoreanPhone_withValidKoreanPhone_returnsTrue() {
assertTrue(MessageUtil.isValidKoreanPhone("010-1234-5678"));
assertTrue(MessageUtil.isValidKoreanPhone("016-123-4567"));
}
@Test
void isEmpty_withNullOrEmpty_returnsTrue() {
assertTrue(MessageUtil.isEmpty(null));
assertTrue(MessageUtil.isEmpty(""));
}
@Test
void isEmpty_withNonEmpty_returnsFalse() {
assertFalse(MessageUtil.isEmpty("test"));
}
@Test
void isBlank_withWhitespace_returnsTrue() {
assertTrue(MessageUtil.isBlank(" "));
assertTrue(MessageUtil.isBlank(null));
}
@Test
void convertCharset_convertsCorrectly() {
String input = "안녕하세요";
String result = MessageUtil.convertCharset(input, StandardCharsets.UTF_8, StandardCharsets.ISO_8859_1);
assertNotNull(result);
}
@Test
void truncate_truncatesLongString() {
String input = "This is a long string";
String result = MessageUtil.truncate(input, 10);
assertEquals("This is a ", result);
}
@Test
void truncate_withShortString_returnsOriginal() {
String input = "Short";
String result = MessageUtil.truncate(input, 10);
assertEquals("Short", result);
}
@Test
void padLeft_padsWithSpaces() {
String input = "123";
String result = MessageUtil.padLeft(input, 10);
assertEquals(" 123", result);
}
@Test
void padRight_padsWithSpaces() {
String input = "123";
String result = MessageUtil.padRight(input, 10);
assertEquals("123 ", result);
}
@Test
void zeroPad_padsWithZeros() {
long value = 123;
String result = MessageUtil.zeroPad(value, 10);
assertEquals("0000000123", result);
}
@Test
void toBytes_convertsStringToBytes() {
String input = "Hello";
byte[] result = MessageUtil.toBytes(input);
assertArrayEquals(new byte[]{72, 101, 108, 108, 111}, result);
}
@Test
void toString_convertsBytesToString() {
byte[] bytes = {72, 101, 108, 108, 111};
String result = MessageUtil.toString(bytes);
assertEquals("Hello", result);
}
@Test
void encodeBase64_withKoreanCharacters_encodesCorrectly() {
String input = "안녕하세요";
String encoded = MessageUtil.encodeBase64(input);
String decoded = MessageUtil.decodeBase64(encoded);
assertEquals(input, decoded);
}
@Test
void decodeHex_withInvalidHex_throwsException() {
assertThrows(IllegalArgumentException.class, () -> MessageUtil.decodeHex("123"));
}
}