From ad95980457b5f15f5f8a3e25784357d51b2f2297 Mon Sep 17 00:00:00 2001 From: DerMoha <46383545+DerMoha@users.noreply.github.com> Date: Sat, 25 Jul 2026 09:44:37 +0200 Subject: [PATCH] test(ItemUtils): add unit tests + String-based overloads for enchantment helpers - Add package-private matchesEnchantment(String, int, String) and formatEnchantmentName(String) overloads so the helpers can be exercised in unit tests without instantiating org.bukkit.enchantments.Enchantment (whose static initializer requires a live Bukkit server). - Rename shadowed 'rawName' to 'normalizedRaw' in the String overload for clarity. - Move formatNumber up next to applyCustomModelData so related formatting helpers cluster together (was buried at the end of the file). - Add ItemUtilsTest with 24 cases covering formatEnchantmentName, toRoman, and matchesEnchantment across name/level/raw-key/case/whitespace paths. Companion follow-up to #17 (the substring-duplicate bug fix landed there via ags816710's PR; this commit adds the regression coverage). Co-authored-by: ags816710 <57810175+ags816710@users.noreply.github.com> --- .../networkstorage/util/ItemUtils.java | 34 ++-- .../networkstorage/util/ItemUtilsTest.java | 151 ++++++++++++++++++ 2 files changed, 172 insertions(+), 13 deletions(-) create mode 100644 src/test/java/com/dermoha/networkstorage/util/ItemUtilsTest.java diff --git a/src/main/java/com/dermoha/networkstorage/util/ItemUtils.java b/src/main/java/com/dermoha/networkstorage/util/ItemUtils.java index 8da9fb3..b13c267 100644 --- a/src/main/java/com/dermoha/networkstorage/util/ItemUtils.java +++ b/src/main/java/com/dermoha/networkstorage/util/ItemUtils.java @@ -37,9 +37,23 @@ public static void applyCustomModelData(ItemMeta meta, Integer customModelData) } } + public static String formatNumber(long number) { + if (number >= 1_000_000) { + return String.format("%.1fM", number / 1_000_000.0); + } + if (number >= 1_000) { + return String.format("%.1fK", number / 1_000.0); + } + return String.valueOf(number); + } + public static boolean matchesEnchantment(Enchantment enchantment, int level, String lowerCaseFilter) { - String formattedName = formatEnchantmentName(enchantment).toLowerCase(); - String rawName = enchantment.getKey().getKey().toLowerCase(); + return matchesEnchantment(enchantment.getKey().getKey(), level, lowerCaseFilter); + } + + static boolean matchesEnchantment(String rawName, int level, String lowerCaseFilter) { + String formattedName = formatEnchantmentName(rawName).toLowerCase(); + String normalizedRaw = rawName.toLowerCase(); String levelStr = String.valueOf(level); String romanLevel = toRoman(level).toLowerCase(); @@ -48,7 +62,7 @@ public static boolean matchesEnchantment(Enchantment enchantment, int level, Str boolean hasLevelPart = false; for (String part : filterParts) { - if (formattedName.contains(part) || rawName.contains(part)) { + if (formattedName.contains(part) || normalizedRaw.contains(part)) { hasNamePart = true; } if (part.equals(levelStr) || part.equals(romanLevel)) { @@ -62,7 +76,10 @@ public static boolean matchesEnchantment(Enchantment enchantment, int level, Str } public static String formatEnchantmentName(Enchantment enchantment) { - String rawName = enchantment.getKey().getKey(); + return formatEnchantmentName(enchantment.getKey().getKey()); + } + + static String formatEnchantmentName(String rawName) { String[] words = rawName.replace('_', ' ').toLowerCase().split(" "); StringBuilder displayName = new StringBuilder(); @@ -126,13 +143,4 @@ public static String getSortableName(ItemStack item) { return displayName; } - public static String formatNumber(long number) { - if (number >= 1_000_000) { - return String.format("%.1fM", number / 1_000_000.0); - } - if (number >= 1_000) { - return String.format("%.1fK", number / 1_000.0); - } - return String.valueOf(number); - } } diff --git a/src/test/java/com/dermoha/networkstorage/util/ItemUtilsTest.java b/src/test/java/com/dermoha/networkstorage/util/ItemUtilsTest.java new file mode 100644 index 0000000..8126fd7 --- /dev/null +++ b/src/test/java/com/dermoha/networkstorage/util/ItemUtilsTest.java @@ -0,0 +1,151 @@ +package com.dermoha.networkstorage.util; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class ItemUtilsTest { + + @Test + void formatEnchantmentNameSingleWord() { + assertEquals("Sharpness", ItemUtils.formatEnchantmentName("sharpness")); + } + + @Test + void formatEnchantmentNameMultiWord() { + assertEquals("Sweeping Edge", ItemUtils.formatEnchantmentName("sweeping_edge")); + } + + @Test + void formatEnchantmentNameThreeWords() { + assertEquals("Luck Of The Sea", ItemUtils.formatEnchantmentName("luck_of_the_sea")); + } + + @Test + void formatEnchantmentNameEmptyKey() { + assertEquals("", ItemUtils.formatEnchantmentName("")); + } + + @Test + void formatEnchantmentNameUppercaseKey() { + // Implementation lowercases the raw name first, so input case is irrelevant. + assertEquals("Sharpness", ItemUtils.formatEnchantmentName("SHARPNESS")); + } + + @Test + void formatEnchantmentNameAdjacentUnderscores() { + // "a__b" → "a b" → split to ["a", "", "b"]; empty middle word is skipped. + assertEquals("A B", ItemUtils.formatEnchantmentName("a__b")); + } + + @Test + void formatEnchantmentNameLeadingUnderscore() { + // "_b" → " b" → split to ["", "b"]; empty leading word is skipped. + assertEquals("B", ItemUtils.formatEnchantmentName("_b")); + } + + @Test + void formatEnchantmentNameTrailingUnderscore() { + // "b_" → "b " → split to ["b"] (trailing empty stripped by split). + assertEquals("B", ItemUtils.formatEnchantmentName("b_")); + } + + @Test + void toRomanRange() { + assertEquals("I", ItemUtils.toRoman(1)); + assertEquals("II", ItemUtils.toRoman(2)); + assertEquals("III", ItemUtils.toRoman(3)); + assertEquals("IV", ItemUtils.toRoman(4)); + assertEquals("V", ItemUtils.toRoman(5)); + assertEquals("VI", ItemUtils.toRoman(6)); + assertEquals("VII", ItemUtils.toRoman(7)); + assertEquals("VIII", ItemUtils.toRoman(8)); + assertEquals("IX", ItemUtils.toRoman(9)); + assertEquals("X", ItemUtils.toRoman(10)); + } + + @Test + void toRomanZeroReturnsEmpty() { + assertEquals("", ItemUtils.toRoman(0)); + } + + @Test + void toRomanNegativeReturnsEmpty() { + assertEquals("", ItemUtils.toRoman(-1)); + } + + @Test + void toRomanAboveMaxReturnsDecimal() { + // Fall-through to String.valueOf for unsupported numbers. + assertEquals("11", ItemUtils.toRoman(11)); + assertEquals("25", ItemUtils.toRoman(25)); + } + + @Test + void matchesEnchantmentByName() { + assertTrue(ItemUtils.matchesEnchantment("sharpness", 5, "sharpness")); + } + + @Test + void matchesEnchantmentByRawKeySubstring() { + assertTrue(ItemUtils.matchesEnchantment("sharpness", 5, "sharp")); + } + + @Test + void matchesEnchantmentByNumericLevel() { + assertTrue(ItemUtils.matchesEnchantment("sharpness", 5, "5")); + } + + @Test + void matchesEnchantmentByRomanLevel() { + assertTrue(ItemUtils.matchesEnchantment("sharpness", 5, "v")); + } + + @Test + void matchesEnchantmentNameAndLevel() { + assertTrue(ItemUtils.matchesEnchantment("sharpness", 5, "sharpness 5")); + } + + @Test + void matchesEnchantmentRomanNameAndLevel() { + assertTrue(ItemUtils.matchesEnchantment("sharpness", 4, "sharpness iv")); + } + + @Test + void matchesEnchantmentWrongLevel() { + assertFalse(ItemUtils.matchesEnchantment("sharpness", 5, "sharpness 3")); + } + + @Test + void matchesEnchantmentWrongName() { + assertFalse(ItemUtils.matchesEnchantment("sharpness", 5, "efficiency")); + } + + @Test + void matchesEnchantmentNoMatch() { + assertFalse(ItemUtils.matchesEnchantment("sharpness", 5, "xyz")); + } + + @Test + void matchesEnchantmentEmptyFilterMatchesAll() { + // Every string contains "" — call site already short-circuits on empty filter, + // but matchesEnchantment itself returns true for an empty filter. + assertTrue(ItemUtils.matchesEnchantment("sharpness", 5, "")); + } + + @Test + void matchesEnchantmentMixedCaseFilterDoesNotMatch() { + // The filter parameter contract is "lowerCaseFilter" — the implementation + // does NOT lowercase the filter internally. The caller is responsible for + // lowercasing (see TerminalGUI.updateInventory). + assertFalse(ItemUtils.matchesEnchantment("sharpness", 5, "SHARP")); + } + + @Test + void matchesEnchantmentLeadingTrailingWhitespace() { + // split("\\s+") trims surrounding whitespace. + assertTrue(ItemUtils.matchesEnchantment("sharpness", 5, " sharpness ")); + } +}