diff --git a/.forge/ACM-CM-002-attempt-1-run-bd1b68b4d412.md b/.forge/ACM-CM-002-attempt-1-run-bd1b68b4d412.md new file mode 100644 index 0000000..b033d3b --- /dev/null +++ b/.forge/ACM-CM-002-attempt-1-run-bd1b68b4d412.md @@ -0,0 +1,3 @@ +# ACM-CM-002-attempt-1-run-bd1b68b4d412 + +Forge 이슈 작업 브랜치 `forge/ACM-CM-002-attempt-1-run-bd1b68b4d412`. diff --git a/boot/common-framework/src/main/java/com/klaro/acquirecore/framework/util/AmountUtil.java b/boot/common-framework/src/main/java/com/klaro/acquirecore/framework/util/AmountUtil.java new file mode 100644 index 0000000..89ff568 --- /dev/null +++ b/boot/common-framework/src/main/java/com/klaro/acquirecore/framework/util/AmountUtil.java @@ -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(); + } +} diff --git a/boot/common-framework/src/main/java/com/klaro/acquirecore/framework/util/DateUtil.java b/boot/common-framework/src/main/java/com/klaro/acquirecore/framework/util/DateUtil.java new file mode 100644 index 0000000..7dd1607 --- /dev/null +++ b/boot/common-framework/src/main/java/com/klaro/acquirecore/framework/util/DateUtil.java @@ -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; + } + } +} diff --git a/boot/common-framework/src/main/java/com/klaro/acquirecore/framework/util/MessageUtil.java b/boot/common-framework/src/main/java/com/klaro/acquirecore/framework/util/MessageUtil.java new file mode 100644 index 0000000..ef9b818 --- /dev/null +++ b/boot/common-framework/src/main/java/com/klaro/acquirecore/framework/util/MessageUtil.java @@ -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); + } +} diff --git a/boot/common-framework/src/test/java/com/klaro/acquirecore/framework/util/AmountUtilTest.java b/boot/common-framework/src/test/java/com/klaro/acquirecore/framework/util/AmountUtilTest.java new file mode 100644 index 0000000..585c860 --- /dev/null +++ b/boot/common-framework/src/test/java/com/klaro/acquirecore/framework/util/AmountUtilTest.java @@ -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); + } +} diff --git a/boot/common-framework/src/test/java/com/klaro/acquirecore/framework/util/DateUtilTest.java b/boot/common-framework/src/test/java/com/klaro/acquirecore/framework/util/DateUtilTest.java new file mode 100644 index 0000000..d40e7d2 --- /dev/null +++ b/boot/common-framework/src/test/java/com/klaro/acquirecore/framework/util/DateUtilTest.java @@ -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)); + } +} diff --git a/boot/common-framework/src/test/java/com/klaro/acquirecore/framework/util/MessageUtilTest.java b/boot/common-framework/src/test/java/com/klaro/acquirecore/framework/util/MessageUtilTest.java new file mode 100644 index 0000000..10e04f4 --- /dev/null +++ b/boot/common-framework/src/test/java/com/klaro/acquirecore/framework/util/MessageUtilTest.java @@ -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")); + } +}