From abb25d4e452ced6aee0835c80139949259508629 Mon Sep 17 00:00:00 2001 From: James Mortemore Date: Sat, 18 Apr 2026 20:03:46 +0100 Subject: [PATCH 1/4] chore: modernise codebase for Java 17 baseline Brings the codebase forward to take full advantage of the existing Java 17 toolchain bump and to remove a long tail of dependencies that no longer compile, build, or run cleanly. Test framework - Migrate every test from JUnit 4 + PowerMock to JUnit 5 (Jupiter). - Mockito 3.7.7 -> 5.14.2; drop PowerMock entirely. - javafaker 1.0.2 -> datafaker 2.5.4 (javafaker is unmaintained). - hamcrest-library 1.2.1 -> hamcrest 2.2; awaitility 4.0.1 -> 4.3.0; mariaDB4j 2.6.0 -> 3.3.1; jacoco 0.8.11 -> 0.8.12. - Add BasePluginDbTest.requireCommand(name) helper so command tests declare the assumption once in @BeforeEach instead of repeating Assumptions.assumeTrue(cmd != null) per @Test. Runtime libraries - HikariCP 6.3.3, ORMLite 6.1, MariaDB JDBC 3.5.7, MySQL Connector/J 8.4.0 (H2 deferred to avoid an on-disk migration). DatabaseConfig now emits driver-specific JDBC URLs (sslMode for MariaDB 3.x; drops the deprecated disableMariaDbDriver flag for MySQL 8.x). - SLF4J 1.x -> 2.0.17 with a custom BanManagerSlf4jServiceProvider registered via META-INF/services. Bukkit shadowJar enables mergeServiceFiles() and relocates org.slf4j at task level so the ServiceLoader finds the relocated provider in the shaded JAR. - SnakeYAML 2.x: configure LoaderOptions with allowDuplicateKeys=true, 32 MiB code-point limit and depth=100 to keep older user-edited YAML loading without surprises. - bStats 3.2.1, PlaceholderAPI/Adventure refresh, Lombok 1.18.38. - ORMLite logging is now scoped via Logger.setGlobalLogLevel on the shaded backend rather than the global LOCAL_LOG_LEVEL system property, so other plugins' ORMLite is unaffected. Language idioms - Convert plain DTOs to records: HistoryEntry, PlayerNameSummary, CommandInfo, Fetcher, UUIDFetcher, Hook, and a unified Webhook record (replaces the previous WebhookHookConfig/WebhookData pair). Records use compact constructors for defensive copying of headers and pre/post hook lists. - HttpClient field is now volatile for the double-checked init in BanManagerPlugin; HTTP/2 is the JDK default so the explicit version builder call is dropped. getHttpClient() carries thread-safety javadoc. - Pattern matching for instanceof, switch expressions in the SLF4J logger and RollbackSync, immutable factories (Set.of/List.of/ Map.copyOf) where appropriate. - New StorageUtils.toSqlException helper to wrap the broader Exception thrown by ORMLite 6.x AutoCloseable resources, used by MigrationRunner, PlayerHistoryStorage, PlayerWarnStorage and InfoCommand. MigrationRunner.getCurrentVersion now logs unexpected exceptions at WARNING. Test stability - IPUtilsTest swaps faker.internet().ipV6Address().replaceAll("0","") for a curated IPv6 sample set with canonical-form comparison; the previous random data occasionally produced invalid addresses. - forkEvery=1 stays on (and is documented inline) because several tests leak static state - JDBC drivers, native handles, daemon executors - that hangs the suite in a single JVM. Documentation - Add UPGRADE.md describing user-visible runtime changes (MariaDB JDBC scheme, MySQL Connector/J 8.x, SnakeYAML 2.x behaviour, bStats/PlaceholderAPI bumps, SLF4J 2.x on Bukkit). Verification - ./gradlew :BanManagerCommon:test - 439 tests, 0 failures, 0 errors, 14 skipped (platform-specific *All commands not registered on common, same as before the assumeTrue refactor). - Sequential ./gradlew build -x test produces every artifact across Bukkit, Bungee, Velocity, Sponge, and all 4 Fabric variants. --- README.md | 2 +- UPGRADE.md | 72 ++++++ buildSrc/src/main/kotlin/CommonConfig.kt | 8 +- buildSrc/src/main/kotlin/PlatformConfig.kt | 8 +- buildSrc/src/main/kotlin/Versions.kt | 2 - bukkit/build.gradle.kts | 22 +- .../bukkit/listeners/WebhookListener.java | 28 +-- .../org/slf4j/impl/BanManagerSlf4jLogger.java | 213 +++------------- .../impl/BanManagerSlf4jServiceProvider.java | 53 ++++ .../org/slf4j/impl/StaticLoggerBinder.java | 39 --- .../java/org/slf4j/impl/StaticMDCBinder.java | 21 -- .../org/slf4j/impl/StaticMarkerBinder.java | 26 -- bungee/build.gradle.kts | 2 +- .../bungee/listeners/WebhookListener.java | 28 +-- common/build.gradle.kts | 24 +- .../banmanager/common/BanManagerPlugin.java | 84 ++++++- .../common/commands/CommonCommand.java | 12 +- .../common/commands/InfoCommand.java | 36 +-- .../common/commands/NamesCommand.java | 22 +- .../common/configs/DatabaseConfig.java | 42 +++- .../banmanager/common/configs/Fetcher.java | 11 +- .../common/configs/GeoIpConfig.java | 35 ++- .../banmanager/common/configs/Hook.java | 26 +- .../banmanager/common/configs/PluginInfo.java | 16 +- .../common/configs/UUIDFetcher.java | 11 +- .../common/configs/WebhookConfig.java | 68 ++--- .../configuration/file/YamlConfiguration.java | 31 ++- .../configuration/file/YamlConstructor.java | 4 +- .../configuration/file/YamlRepresenter.java | 4 +- .../banmanager/common/data/HistoryEntry.java | 31 +-- .../common/data/PlayerNameSummary.java | 19 +- .../banmanager/common/data/Webhook.java | 43 ++++ .../common/listeners/CommonHooksListener.java | 34 +-- .../listeners/CommonWebhookListener.java | 235 ++++++------------ .../common/runnables/RollbackSync.java | 96 +++---- .../common/storage/ActivityStorage.java | 7 +- .../common/storage/HistoryStorage.java | 6 +- .../common/storage/PlayerHistoryStorage.java | 10 +- .../common/storage/PlayerWarnStorage.java | 16 +- .../common/storage/conversion/H2.java | 45 ++-- .../storage/mariadb/MariaDBDatabase.java | 2 +- .../storage/migration/MigrationRunner.java | 128 +++------- .../common/storage/mysql/MySQLDatabase.java | 6 +- .../banmanager/common/util/DateUtils.java | 10 +- .../common/util/MessageRegistry.java | 47 ++-- .../banmanager/common/util/StorageUtils.java | 26 +- .../banmanager/common/util/UUIDUtils.java | 84 +++---- .../org.slf4j.spi.SLF4JServiceProvider | 1 + .../banmanager/common/BasePluginDbTest.java | 53 ++-- .../banmanager/common/BasePluginTest.java | 24 +- .../banmanager/common/CommonLoggerTest.java | 6 +- .../banmanager/common/PluginTest.java | 4 +- .../common/StderrRegressionTest.java | 14 +- .../confuser/banmanager/common/TestUtils.java | 4 +- .../common/commands/AddNoteCommandTest.java | 12 +- .../common/commands/BanCommandTest.java | 16 +- .../common/commands/BanIpCommandTest.java | 18 +- .../commands/BanIpRangeCommandTest.java | 14 +- .../common/commands/BanNameCommandTest.java | 12 +- .../common/commands/CommandParserTest.java | 4 +- .../common/commands/InfoCommandTest.java | 24 +- .../common/commands/KickAllCommandTest.java | 8 +- .../common/commands/KickCommandTest.java | 8 +- .../commands/LoglessKickAllCommandTest.java | 8 +- .../common/commands/MuteCommandTest.java | 18 +- .../common/commands/MuteIpCommandTest.java | 18 +- .../common/commands/NamesCommandTest.java | 6 +- .../common/commands/ReportCommandTest.java | 27 +- .../common/commands/RollbackCommandTest.java | 24 +- .../common/commands/TabCompletionTest.java | 32 +-- .../common/commands/TempBanCommandTest.java | 16 +- .../common/commands/TempMuteCommandTest.java | 20 +- .../common/commands/TempWarnCommandTest.java | 12 +- .../common/commands/UnbanCommandTest.java | 20 +- .../common/commands/UnmuteCommandTest.java | 20 +- .../common/commands/WarnCommandTest.java | 10 +- .../commands/global/BanAllCommandTest.java | 26 +- .../commands/global/MuteAllCommandTest.java | 26 +- .../commands/global/UnbanAllCommandTest.java | 26 +- .../commands/global/UnmuteAllCommandTest.java | 26 +- .../commands/report/AssignSubCommandTest.java | 4 +- .../commands/report/CloseSubCommandTest.java | 4 +- .../common/configs/ConfigReloadTest.java | 111 +++------ .../common/configs/ConsoleConfigTest.java | 6 +- .../common/configs/DefaultConfigTest.java | 4 +- .../common/configs/GeoIpConfigTest.java | 4 +- .../configs/NotificationsConfigTest.java | 4 +- .../common/configs/SchedulesConfigTest.java | 4 +- .../listeners/CommonJoinListenerTest.java | 34 +-- .../common/listeners/NoteListenerTest.java | 6 +- .../common/runnables/BmRunnableTest.java | 12 +- .../common/runnables/RollbackSyncTest.java | 8 +- .../common/storage/IpBanStorageTest.java | 4 +- .../common/storage/IpRangeBanStorageTest.java | 4 +- .../common/storage/PlayerBanStorageTest.java | 4 +- .../storage/PlayerHistoryStorageTest.java | 18 +- .../common/storage/PlayerMuteStorageTest.java | 9 +- .../common/storage/PlayerStorageTest.java | 4 +- .../common/storage/PlayerWarnStorageTest.java | 4 +- .../conversion/LiteBansImportTest.java | 55 ++-- .../migration/MigrationIntegrationTest.java | 34 +-- .../migration/MigrationRunnerTest.java | 4 +- .../common/util/ColorUtilsTest.java | 40 +-- .../banmanager/common/util/DateUtilsTest.java | 4 +- .../banmanager/common/util/IPUtilsTest.java | 39 ++- .../common/util/LocaleNormalisationTest.java | 4 +- .../common/util/MessageDeferredTest.java | 10 +- .../common/util/MessageLoadingTest.java | 6 +- .../common/util/MessageRegistryTest.java | 8 +- .../common/util/MessageRendererTest.java | 14 +- .../util/MessagesYamlValidatorTest.java | 66 +++-- .../common/util/PaginatedViewTest.java | 4 +- .../common/util/SchedulerTimeTest.java | 24 +- .../common/util/StringUtilsTest.java | 4 +- .../util/parsers/InfoCommandParserTest.java | 4 +- .../util/parsers/UnbanCommandParserTest.java | 4 +- .../util/parsers/WarnCommandParserTest.java | 4 +- .../fabric/listeners/WebhookListener.java | 28 +-- gradle.properties | 2 +- libs/build.gradle.kts | 26 +- sponge/build.gradle.kts | 2 +- .../sponge/listeners/WebhookListener.java | 28 +-- velocity/build.gradle.kts | 2 +- .../velocity/listeners/WebhookListener.java | 28 +-- 124 files changed, 1411 insertions(+), 1633 deletions(-) create mode 100644 UPGRADE.md create mode 100644 bukkit/src/main/java/org/slf4j/impl/BanManagerSlf4jServiceProvider.java delete mode 100644 bukkit/src/main/java/org/slf4j/impl/StaticLoggerBinder.java delete mode 100644 bukkit/src/main/java/org/slf4j/impl/StaticMDCBinder.java delete mode 100644 bukkit/src/main/java/org/slf4j/impl/StaticMarkerBinder.java create mode 100644 common/src/main/java/me/confuser/banmanager/common/data/Webhook.java create mode 100644 common/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider diff --git a/README.md b/README.md index d77915765..efd204a9a 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ To learn more about configuration, usage and features of BanManager, take a look at [the website](https://banmanagement.com/docs/banmanager/configuration) or view [the website demo](https://demo.banmanagement.com). ## Requirements -- Java 8+ (JDK21+ required to build from source) +- Java 17+ (JDK21+ required to build from source) - CraftBukkit/Spigot/Paper, BungeeCord or Sponge for Minecraft 1.7.2+ - Optionally [MySQL or MariaDB](https://banmanagement.com/docs/banmanager/install#setup-shared-database-optional) diff --git a/UPGRADE.md b/UPGRADE.md new file mode 100644 index 000000000..79852f110 --- /dev/null +++ b/UPGRADE.md @@ -0,0 +1,72 @@ +# Upgrade Notes + +End-user-visible changes that may require attention when upgrading. Versioned +changes are listed newest-first. + +## Java 17 modernisation + +This release modernises the codebase from Java 8 to Java 17 and bumps a number +of bundled dependencies. Most installs upgrade transparently, but the items +below are worth checking. + +### Required runtime: Java 17 or newer + +- BanManager now requires **Java 17+** at runtime (Java 21 to build from + source). +- This matches the supported runtimes for Spigot/Paper 1.20+, Velocity 3.3+, + and Sponge API 8+, so no action is normally required for modern servers. +- If you are still running Java 8/11 you must update your JRE before installing + this version. + +### MariaDB JDBC driver upgraded to 3.x + +- The bundled `mariadb-java-client` is now `3.5.x`. The 3.x driver no longer + hijacks `jdbc:mysql://` URLs, and it warns about legacy parameters that the + 2.x line silently accepted. +- BanManager now builds a per-driver JDBC URL automatically based on + `storageType` in `config.yml`, so you should not see `WARN` lines about + unknown options like `autoReconnect`, `serverTimezone`, or + `verifyServerCertificate` after upgrading. +- `useSSL` and `verifyServerCertificate` are translated to MariaDB's + `sslMode` (`disable`, `trust`, or `verify-full`). No `config.yml` changes + are required. +- If you previously set `storageType: mysql` but pointed at a MariaDB server, + consider switching to `storageType: mariadb` so the correct driver is used. + +### MySQL Connector/J upgraded to 8.4.x + +- The shaded `mysql-connector-j` is now `8.4.0`, replacing the legacy + `mysql-connector-java` artifact. +- The legacy `&disableMariaDbDriver` URL fragment has been removed because the + modern MariaDB driver no longer needs to be opted out. + +### SnakeYAML upgraded to 2.x + +- The bundled SnakeYAML jumped from `1.29` to `2.4`. SnakeYAML 2.x flips a + handful of defaults that could otherwise break existing user-edited + configs. BanManager pre-configures the loader to keep the old behaviour: + - `allowDuplicateKeys` is forced back to `true` so a duplicate key in + `messages.yml` won't refuse to load (the last value wins, as before). + - `codePointLimit` is raised to 32 MB so very large translation files keep + loading. + - `nestingDepthLimit` is raised to 100 for deeply nested webhook payloads. +- If you intentionally relied on duplicate-key detection, consider linting + your YAML separately. + +### Bundled bStats / PlaceholderAPI upgrades + +- bStats was bumped to `3.2.1` across all platforms. +- PlaceholderAPI was bumped to `2.12.2` (Bukkit only, soft-dependency). + +### SLF4J upgraded to 2.x on Bukkit + +- The Bukkit module now ships an SLF4J 2.x service-provider implementation so + that ORMLite and HikariCP log through BanManager's own logger rather than + the generic console. +- `disableDatabaseLogging()` is now a no-op on Bukkit (the new provider + filters log levels itself). + +### Tests: JUnit 5 + Mockito 5 + +- Internal change only - the test suite migrated from JUnit 4/Mockito 3 to + JUnit 5/Mockito 5. No effect on the runtime jar. diff --git a/buildSrc/src/main/kotlin/CommonConfig.kt b/buildSrc/src/main/kotlin/CommonConfig.kt index e8f5ab4c2..1c8b9b146 100644 --- a/buildSrc/src/main/kotlin/CommonConfig.kt +++ b/buildSrc/src/main/kotlin/CommonConfig.kt @@ -14,11 +14,11 @@ fun Project.applyCommonConfiguration() { } dependencies { - "compileOnly"("org.projectlombok:lombok:1.18.36") - "annotationProcessor"("org.projectlombok:lombok:1.18.36") + "compileOnly"("org.projectlombok:lombok:1.18.38") + "annotationProcessor"("org.projectlombok:lombok:1.18.38") - "testCompileOnly"("org.projectlombok:lombok:1.18.36") - "testAnnotationProcessor"("org.projectlombok:lombok:1.18.36") + "testCompileOnly"("org.projectlombok:lombok:1.18.38") + "testAnnotationProcessor"("org.projectlombok:lombok:1.18.38") } configurations.all { diff --git a/buildSrc/src/main/kotlin/PlatformConfig.kt b/buildSrc/src/main/kotlin/PlatformConfig.kt index 03da6c0dd..e4ebb0554 100644 --- a/buildSrc/src/main/kotlin/PlatformConfig.kt +++ b/buildSrc/src/main/kotlin/PlatformConfig.kt @@ -2,18 +2,12 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import com.github.jengelman.gradle.plugins.shadow.ShadowExtension import org.gradle.api.Project import org.gradle.api.plugins.JavaPluginExtension -import org.gradle.api.plugins.quality.CheckstyleExtension -import org.gradle.api.publish.PublishingExtension -import org.gradle.api.publish.maven.MavenPublication import org.gradle.api.tasks.javadoc.Javadoc -import org.gradle.api.tasks.testing.Test import org.gradle.external.javadoc.StandardJavadocDocletOptions import org.gradle.kotlin.dsl.apply import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.dependencies -import org.gradle.kotlin.dsl.get import org.gradle.kotlin.dsl.named -import org.gradle.kotlin.dsl.register import org.gradle.kotlin.dsl.withType import org.gradle.kotlin.dsl.the @@ -34,7 +28,7 @@ fun Project.applyPlatformAndCoreConfiguration() { ext["internalVersion"] = internalVersion - // Java 8 turns on doclint which we fail + // Suppress doclint to keep javadoc builds green; tags below document supported custom tags. tasks.withType().configureEach { (options as StandardJavadocDocletOptions).apply { addStringOption("Xdoclint:none", "-quiet") diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 3a969842c..f8117ced9 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -1,5 +1,3 @@ object Versions { - const val JUNIT = "4.13" - const val MOCKITO = "3.7.7" const val ADVENTURE = "4.21.0" } diff --git a/bukkit/build.gradle.kts b/bukkit/build.gradle.kts index c494940b0..34796b7cf 100644 --- a/bukkit/build.gradle.kts +++ b/bukkit/build.gradle.kts @@ -83,9 +83,9 @@ dependencies { "compileOnly"("net.kyori:adventure-api:${Versions.ADVENTURE}") "compileOnly"("net.kyori:adventure-text-serializer-gson:${Versions.ADVENTURE}") "compileOnly"("net.kyori:adventure-text-serializer-json:${Versions.ADVENTURE}") - "compileOnly"("me.clip:placeholderapi:2.10.9") - "shadeOnly"("org.bstats:bstats-bukkit:2.2.1") - "shadeOnly"("org.slf4j:slf4j-api:1.7.36") + "compileOnly"("me.clip:placeholderapi:2.12.2") + "shadeOnly"("org.bstats:bstats-bukkit:3.2.1") + "shadeOnly"("org.slf4j:slf4j-api:2.0.17") } tasks.named("processResources") { @@ -119,15 +119,23 @@ tasks.named("shadowJar") { include(dependency("org.bstats:.*:.*")) include(dependency("org.slf4j:.*:.*")) - - relocate("org.bstats", "me.confuser.banmanager.common.bstats") - relocate("org.slf4j", "me.confuser.banmanager.common.slf4j") } + // Relocate at task level so resource paths and service file contents are + // also rewritten (the SLF4J 2.x ServiceLoader needs the META-INF/services + // entry to be relocated to the shaded package). + relocate("org.bstats", "me.confuser.banmanager.common.bstats") + relocate("org.slf4j", "me.confuser.banmanager.common.slf4j") + + // Required for SLF4J 2.x service discovery: merges + relocates the + // META-INF/services/org.slf4j.spi.SLF4JServiceProvider entry. + mergeServiceFiles() + exclude("GradleStart**") exclude(".cache"); exclude("LICENSE*") - exclude("META-INF/services/**") + // Keep META-INF/services so the relocated SLF4J 2.x ServiceLoader can find + // BanManagerSlf4jServiceProvider; shaded deps here ship no service files. exclude("META-INF/maven/**") exclude("org/intellij/**") exclude("org/jetbrains/**") diff --git a/bukkit/src/main/java/me/confuser/banmanager/bukkit/listeners/WebhookListener.java b/bukkit/src/main/java/me/confuser/banmanager/bukkit/listeners/WebhookListener.java index b0872accd..7263ba555 100644 --- a/bukkit/src/main/java/me/confuser/banmanager/bukkit/listeners/WebhookListener.java +++ b/bukkit/src/main/java/me/confuser/banmanager/bukkit/listeners/WebhookListener.java @@ -3,7 +3,7 @@ import me.confuser.banmanager.bukkit.api.events.*; import me.confuser.banmanager.common.BanManagerPlugin; import me.confuser.banmanager.common.listeners.CommonWebhookListener; -import me.confuser.banmanager.common.listeners.CommonWebhookListener.WebhookData; +import me.confuser.banmanager.common.data.Webhook; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; @@ -19,62 +19,62 @@ public WebhookListener(BanManagerPlugin plugin) { @EventHandler(priority = EventPriority.MONITOR) public void notifyOnBan(PlayerBannedEvent event) { - List webhooks = listener.notifyOnBan(event.getBan()); + List webhooks = listener.notifyOnBan(event.getBan()); sendAll(webhooks, event.isSilent()); } @EventHandler(priority = EventPriority.MONITOR) public void notifyOnMute(PlayerMutedEvent event) { - List webhooks = listener.notifyOnMute(event.getMute()); + List webhooks = listener.notifyOnMute(event.getMute()); sendAll(webhooks, event.isSilent()); } @EventHandler(priority = EventPriority.MONITOR) public void notifyOnBan(IpBannedEvent event) { - List webhooks = listener.notifyOnBan(event.getBan()); + List webhooks = listener.notifyOnBan(event.getBan()); sendAll(webhooks, event.isSilent()); } @EventHandler(priority = EventPriority.MONITOR) public void notifyOnKick(PlayerKickedEvent event) { - List webhooks = listener.notifyOnKick(event.getKick()); + List webhooks = listener.notifyOnKick(event.getKick()); sendAll(webhooks, event.isSilent()); } @EventHandler(priority = EventPriority.MONITOR) public void notifyOnWarn(PlayerWarnedEvent event) { - List webhooks = listener.notifyOnWarn(event.getWarning()); + List webhooks = listener.notifyOnWarn(event.getWarning()); sendAll(webhooks, event.isSilent()); } @EventHandler(priority = EventPriority.MONITOR) public void notifyOnUnban(PlayerUnbanEvent event) { - List webhooks = listener.notifyOnUnban(event.getBan(), event.getActor(), event.getReason()); + List webhooks = listener.notifyOnUnban(event.getBan(), event.getActor(), event.getReason()); sendAll(webhooks, event.isSilent()); } @EventHandler(priority = EventPriority.MONITOR) public void notifyOnUnban(IpUnbanEvent event) { - List webhooks = listener.notifyOnUnban(event.getBan(), event.getActor(), event.getReason()); + List webhooks = listener.notifyOnUnban(event.getBan(), event.getActor(), event.getReason()); sendAll(webhooks, event.isSilent()); } @EventHandler(priority = EventPriority.MONITOR) public void notifyOnUnmute(PlayerUnmuteEvent event) { - List webhooks = listener.notifyOnUnmute(event.getMute(), event.getActor(), event.getReason()); + List webhooks = listener.notifyOnUnmute(event.getMute(), event.getActor(), event.getReason()); sendAll(webhooks, event.isSilent()); } @EventHandler(priority = EventPriority.MONITOR) public void notifyOnReport(PlayerReportedEvent event) { - List webhooks = listener.notifyOnReport(event.getReport(), event.getReport().getActor(), event.getReport().getReason()); + List webhooks = listener.notifyOnReport(event.getReport(), event.getReport().getActor(), event.getReport().getReason()); sendAll(webhooks, event.isSilent()); } - private void sendAll(List webhooks, boolean isSilent) { - for (WebhookData data : webhooks) { - if (isSilent && data.ignoreSilent) continue; - if (data.url == null || data.payload == null || data.url.isEmpty() || data.payload.isEmpty()) continue; + private void sendAll(List webhooks, boolean isSilent) { + for (Webhook data : webhooks) { + if (isSilent && data.ignoreSilent()) continue; + if (data.url() == null || data.payload() == null || data.url().isEmpty() || data.payload().isEmpty()) continue; listener.sendAsync(data); } } diff --git a/bukkit/src/main/java/org/slf4j/impl/BanManagerSlf4jLogger.java b/bukkit/src/main/java/org/slf4j/impl/BanManagerSlf4jLogger.java index 10dc7dead..371487b49 100644 --- a/bukkit/src/main/java/org/slf4j/impl/BanManagerSlf4jLogger.java +++ b/bukkit/src/main/java/org/slf4j/impl/BanManagerSlf4jLogger.java @@ -2,18 +2,19 @@ import me.confuser.banmanager.common.BanManagerPlugin; import me.confuser.banmanager.common.CommonLogger; -import org.slf4j.helpers.FormattingTuple; -import org.slf4j.helpers.MarkerIgnoringBase; +import org.slf4j.Marker; +import org.slf4j.event.Level; +import org.slf4j.helpers.LegacyAbstractLogger; import org.slf4j.helpers.MessageFormatter; /** - * SLF4J logger that delegates to BanManager's {@link CommonLogger}. + * SLF4J 2.x logger that delegates to BanManager's {@link CommonLogger}. * *

Level filtering: when debug mode is off, TRACE / DEBUG / INFO are * suppressed. This replaces the old {@code disableDatabaseLogging()} system * property which has no effect when ORMLite uses SLF4J instead of LocalLog.

*/ -public class BanManagerSlf4jLogger extends MarkerIgnoringBase { +public class BanManagerSlf4jLogger extends LegacyAbstractLogger { private static final long serialVersionUID = 1L; @@ -31,8 +32,6 @@ private boolean isDebugMode() { return plugin != null && plugin.getConfig() != null && plugin.getConfig().isDebugEnabled(); } - // --- Level checks ----------------------------------------------------------- - @Override public boolean isTraceEnabled() { return isDebugMode(); @@ -58,178 +57,44 @@ public boolean isErrorEnabled() { return true; } - // --- TRACE ------------------------------------------------------------------ - - @Override - public void trace(String msg) { - if (!isTraceEnabled()) return; - CommonLogger l = logger(); - if (l != null) l.info("[TRACE] " + msg); - } - - @Override - public void trace(String format, Object arg) { - if (!isTraceEnabled()) return; - FormattingTuple ft = MessageFormatter.format(format, arg); - trace(ft.getMessage()); - } - - @Override - public void trace(String format, Object arg1, Object arg2) { - if (!isTraceEnabled()) return; - FormattingTuple ft = MessageFormatter.format(format, arg1, arg2); - trace(ft.getMessage()); - } - - @Override - public void trace(String format, Object... arguments) { - if (!isTraceEnabled()) return; - FormattingTuple ft = MessageFormatter.arrayFormat(format, arguments); - trace(ft.getMessage()); - } - - @Override - public void trace(String msg, Throwable t) { - if (!isTraceEnabled()) return; - CommonLogger l = logger(); - if (l != null) l.warning("[TRACE] " + msg, t); - } - - // --- DEBUG ------------------------------------------------------------------ - - @Override - public void debug(String msg) { - if (!isDebugEnabled()) return; - CommonLogger l = logger(); - if (l != null) l.info("[DEBUG] " + msg); - } - - @Override - public void debug(String format, Object arg) { - if (!isDebugEnabled()) return; - FormattingTuple ft = MessageFormatter.format(format, arg); - debug(ft.getMessage()); - } - - @Override - public void debug(String format, Object arg1, Object arg2) { - if (!isDebugEnabled()) return; - FormattingTuple ft = MessageFormatter.format(format, arg1, arg2); - debug(ft.getMessage()); - } - - @Override - public void debug(String format, Object... arguments) { - if (!isDebugEnabled()) return; - FormattingTuple ft = MessageFormatter.arrayFormat(format, arguments); - debug(ft.getMessage()); - } - - @Override - public void debug(String msg, Throwable t) { - if (!isDebugEnabled()) return; - CommonLogger l = logger(); - if (l != null) l.warning("[DEBUG] " + msg, t); - } - - // --- INFO ------------------------------------------------------------------- - - @Override - public void info(String msg) { - if (!isInfoEnabled()) return; - CommonLogger l = logger(); - if (l != null) l.info(msg); - } - - @Override - public void info(String format, Object arg) { - if (!isInfoEnabled()) return; - FormattingTuple ft = MessageFormatter.format(format, arg); - info(ft.getMessage()); - } - - @Override - public void info(String format, Object arg1, Object arg2) { - if (!isInfoEnabled()) return; - FormattingTuple ft = MessageFormatter.format(format, arg1, arg2); - info(ft.getMessage()); - } - - @Override - public void info(String format, Object... arguments) { - if (!isInfoEnabled()) return; - FormattingTuple ft = MessageFormatter.arrayFormat(format, arguments); - info(ft.getMessage()); - } - - @Override - public void info(String msg, Throwable t) { - if (!isInfoEnabled()) return; - CommonLogger l = logger(); - if (l != null) l.warning(msg, t); - } - - // --- WARN ------------------------------------------------------------------- - - @Override - public void warn(String msg) { - CommonLogger l = logger(); - if (l != null) l.warning(msg); - } - - @Override - public void warn(String format, Object arg) { - FormattingTuple ft = MessageFormatter.format(format, arg); - warn(ft.getMessage()); - } - - @Override - public void warn(String format, Object arg1, Object arg2) { - FormattingTuple ft = MessageFormatter.format(format, arg1, arg2); - warn(ft.getMessage()); - } - - @Override - public void warn(String format, Object... arguments) { - FormattingTuple ft = MessageFormatter.arrayFormat(format, arguments); - warn(ft.getMessage()); - } - - @Override - public void warn(String msg, Throwable t) { - CommonLogger l = logger(); - if (l != null) l.warning(msg, t); - } - - // --- ERROR ------------------------------------------------------------------ - - @Override - public void error(String msg) { - CommonLogger l = logger(); - if (l != null) l.severe(msg); - } - - @Override - public void error(String format, Object arg) { - FormattingTuple ft = MessageFormatter.format(format, arg); - error(ft.getMessage()); - } - - @Override - public void error(String format, Object arg1, Object arg2) { - FormattingTuple ft = MessageFormatter.format(format, arg1, arg2); - error(ft.getMessage()); - } - @Override - public void error(String format, Object... arguments) { - FormattingTuple ft = MessageFormatter.arrayFormat(format, arguments); - error(ft.getMessage()); + protected String getFullyQualifiedCallerName() { + return null; } @Override - public void error(String msg, Throwable t) { + protected void handleNormalizedLoggingCall(Level level, + Marker marker, + String messagePattern, + Object[] arguments, + Throwable throwable) { CommonLogger l = logger(); - if (l != null) l.severe(msg, t); + if (l == null) return; + + String formatted = (arguments == null || arguments.length == 0) + ? messagePattern + : MessageFormatter.arrayFormat(messagePattern, arguments).getMessage(); + + String prefix = switch (level) { + case TRACE -> "[TRACE] "; + case DEBUG -> "[DEBUG] "; + default -> ""; + }; + String message = prefix + formatted; + + switch (level) { + case TRACE, DEBUG, INFO -> { + if (throwable == null) l.info(message); + else l.warning(message, throwable); + } + case WARN -> { + if (throwable == null) l.warning(message); + else l.warning(message, throwable); + } + case ERROR -> { + if (throwable == null) l.severe(message); + else l.severe(message, throwable); + } + } } } diff --git a/bukkit/src/main/java/org/slf4j/impl/BanManagerSlf4jServiceProvider.java b/bukkit/src/main/java/org/slf4j/impl/BanManagerSlf4jServiceProvider.java new file mode 100644 index 000000000..6e7978379 --- /dev/null +++ b/bukkit/src/main/java/org/slf4j/impl/BanManagerSlf4jServiceProvider.java @@ -0,0 +1,53 @@ +package org.slf4j.impl; + +import org.slf4j.ILoggerFactory; +import org.slf4j.IMarkerFactory; +import org.slf4j.helpers.BasicMDCAdapter; +import org.slf4j.helpers.BasicMarkerFactory; +import org.slf4j.spi.MDCAdapter; +import org.slf4j.spi.SLF4JServiceProvider; + +/** + * SLF4J 2.x service provider that wires {@link BanManagerLoggerFactory} into + * the SLF4J API. Discovered via {@code META-INF/services/org.slf4j.spi.SLF4JServiceProvider} + * (also relocated alongside the rest of {@code org.slf4j} so the shaded + * HikariCP / ORMLite pick it up automatically). + */ +public class BanManagerSlf4jServiceProvider implements SLF4JServiceProvider { + + // Track the SLF4J API version this provider was compiled against. Must be + // updated when bumping the slf4j-api dependency to keep the version-mismatch + // warning accurate. + public static final String REQUESTED_API_VERSION = "2.0.99"; + + private ILoggerFactory loggerFactory; + private IMarkerFactory markerFactory; + private MDCAdapter mdcAdapter; + + @Override + public void initialize() { + loggerFactory = new BanManagerLoggerFactory(); + markerFactory = new BasicMarkerFactory(); + mdcAdapter = new BasicMDCAdapter(); + } + + @Override + public ILoggerFactory getLoggerFactory() { + return loggerFactory; + } + + @Override + public IMarkerFactory getMarkerFactory() { + return markerFactory; + } + + @Override + public MDCAdapter getMDCAdapter() { + return mdcAdapter; + } + + @Override + public String getRequestedApiVersion() { + return REQUESTED_API_VERSION; + } +} diff --git a/bukkit/src/main/java/org/slf4j/impl/StaticLoggerBinder.java b/bukkit/src/main/java/org/slf4j/impl/StaticLoggerBinder.java deleted file mode 100644 index 6692c58bc..000000000 --- a/bukkit/src/main/java/org/slf4j/impl/StaticLoggerBinder.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.slf4j.impl; - -import org.slf4j.ILoggerFactory; -import org.slf4j.spi.LoggerFactoryBinder; - -/** - * SLF4J 1.7.x binding entry point. Shadow relocates this alongside the rest of - * org.slf4j so the shaded HikariCP / ORMLite pick it up automatically. - * - *

If ever upgrading to SLF4J 2.x, this class must be replaced with a - * {@code SLF4JServiceProvider} registered via {@code META-INF/services}.

- */ -public class StaticLoggerBinder implements LoggerFactoryBinder { - - private static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder(); - - // Must NOT be final so the compiler doesn't inline it. - public static String REQUESTED_API_VERSION = "1.6.99"; - - private final ILoggerFactory loggerFactory; - - private StaticLoggerBinder() { - loggerFactory = new BanManagerLoggerFactory(); - } - - public static StaticLoggerBinder getSingleton() { - return SINGLETON; - } - - @Override - public ILoggerFactory getLoggerFactory() { - return loggerFactory; - } - - @Override - public String getLoggerFactoryClassStr() { - return BanManagerLoggerFactory.class.getName(); - } -} diff --git a/bukkit/src/main/java/org/slf4j/impl/StaticMDCBinder.java b/bukkit/src/main/java/org/slf4j/impl/StaticMDCBinder.java deleted file mode 100644 index b3e9d9b06..000000000 --- a/bukkit/src/main/java/org/slf4j/impl/StaticMDCBinder.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.slf4j.impl; - -import org.slf4j.helpers.NOPMDCAdapter; -import org.slf4j.spi.MDCAdapter; - -public class StaticMDCBinder { - - public static final StaticMDCBinder SINGLETON = new StaticMDCBinder(); - - public static StaticMDCBinder getSingleton() { - return SINGLETON; - } - - public MDCAdapter getMDCA() { - return new NOPMDCAdapter(); - } - - public String getMDCAdapterClassStr() { - return NOPMDCAdapter.class.getName(); - } -} diff --git a/bukkit/src/main/java/org/slf4j/impl/StaticMarkerBinder.java b/bukkit/src/main/java/org/slf4j/impl/StaticMarkerBinder.java deleted file mode 100644 index 6f6ee4c3f..000000000 --- a/bukkit/src/main/java/org/slf4j/impl/StaticMarkerBinder.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.slf4j.impl; - -import org.slf4j.IMarkerFactory; -import org.slf4j.helpers.BasicMarkerFactory; -import org.slf4j.spi.MarkerFactoryBinder; - -public class StaticMarkerBinder implements MarkerFactoryBinder { - - public static final StaticMarkerBinder SINGLETON = new StaticMarkerBinder(); - - private final IMarkerFactory markerFactory = new BasicMarkerFactory(); - - public static StaticMarkerBinder getSingleton() { - return SINGLETON; - } - - @Override - public IMarkerFactory getMarkerFactory() { - return markerFactory; - } - - @Override - public String getMarkerFactoryClassStr() { - return BasicMarkerFactory.class.getName(); - } -} diff --git a/bungee/build.gradle.kts b/bungee/build.gradle.kts index 2789c0391..0f503e056 100644 --- a/bungee/build.gradle.kts +++ b/bungee/build.gradle.kts @@ -64,7 +64,7 @@ configurations { dependencies { api(project(":BanManagerCommon")) compileOnly("net.md-5:bungeecord-api:1.21-R0.4") - "shadeOnly"("org.bstats:bstats-bungeecord:2.2.1") + "shadeOnly"("org.bstats:bstats-bungeecord:3.2.1") } tasks.named("processResources") { diff --git a/bungee/src/main/java/me/confuser/banmanager/bungee/listeners/WebhookListener.java b/bungee/src/main/java/me/confuser/banmanager/bungee/listeners/WebhookListener.java index 7d34a0b59..a58c3fee8 100644 --- a/bungee/src/main/java/me/confuser/banmanager/bungee/listeners/WebhookListener.java +++ b/bungee/src/main/java/me/confuser/banmanager/bungee/listeners/WebhookListener.java @@ -3,7 +3,7 @@ import me.confuser.banmanager.bungee.api.events.*; import me.confuser.banmanager.common.BanManagerPlugin; import me.confuser.banmanager.common.listeners.CommonWebhookListener; -import me.confuser.banmanager.common.listeners.CommonWebhookListener.WebhookData; +import me.confuser.banmanager.common.data.Webhook; import net.md_5.bungee.api.plugin.Listener; import net.md_5.bungee.event.EventHandler; import net.md_5.bungee.event.EventPriority; @@ -19,62 +19,62 @@ public WebhookListener(BanManagerPlugin plugin) { @EventHandler(priority = EventPriority.HIGHEST) public void notifyOnBan(PlayerBannedEvent event) { - List webhooks = listener.notifyOnBan(event.getBan()); + List webhooks = listener.notifyOnBan(event.getBan()); sendAll(webhooks, event.isSilent()); } @EventHandler(priority = EventPriority.HIGHEST) public void notifyOnMute(PlayerMutedEvent event) { - List webhooks = listener.notifyOnMute(event.getMute()); + List webhooks = listener.notifyOnMute(event.getMute()); sendAll(webhooks, event.isSilent()); } @EventHandler(priority = EventPriority.HIGHEST) public void notifyOnBan(IpBannedEvent event) { - List webhooks = listener.notifyOnBan(event.getBan()); + List webhooks = listener.notifyOnBan(event.getBan()); sendAll(webhooks, event.isSilent()); } @EventHandler(priority = EventPriority.HIGHEST) public void notifyOnKick(PlayerKickedEvent event) { - List webhooks = listener.notifyOnKick(event.getKick()); + List webhooks = listener.notifyOnKick(event.getKick()); sendAll(webhooks, event.isSilent()); } @EventHandler(priority = EventPriority.HIGHEST) public void notifyOnWarn(PlayerWarnedEvent event) { - List webhooks = listener.notifyOnWarn(event.getWarning()); + List webhooks = listener.notifyOnWarn(event.getWarning()); sendAll(webhooks, event.isSilent()); } @EventHandler(priority = EventPriority.HIGHEST) public void notifyOnUnban(PlayerUnbanEvent event) { - List webhooks = listener.notifyOnUnban(event.getBan(), event.getActor(), event.getReason()); + List webhooks = listener.notifyOnUnban(event.getBan(), event.getActor(), event.getReason()); sendAll(webhooks, event.isSilent()); } @EventHandler(priority = EventPriority.HIGHEST) public void notifyOnUnban(IpUnbanEvent event) { - List webhooks = listener.notifyOnUnban(event.getBan(), event.getActor(), event.getReason()); + List webhooks = listener.notifyOnUnban(event.getBan(), event.getActor(), event.getReason()); sendAll(webhooks, event.isSilent()); } @EventHandler(priority = EventPriority.HIGHEST) public void notifyOnUnmute(PlayerUnmuteEvent event) { - List webhooks = listener.notifyOnUnmute(event.getMute(), event.getActor(), event.getReason()); + List webhooks = listener.notifyOnUnmute(event.getMute(), event.getActor(), event.getReason()); sendAll(webhooks, event.isSilent()); } @EventHandler(priority = EventPriority.HIGHEST) public void notifyOnReport(PlayerReportedEvent event) { - List webhooks = listener.notifyOnReport(event.getReport(), event.getReport().getActor(), event.getReport().getReason()); + List webhooks = listener.notifyOnReport(event.getReport(), event.getReport().getActor(), event.getReport().getReason()); sendAll(webhooks, event.isSilent()); } - private void sendAll(List webhooks, boolean isSilent) { - for (WebhookData data : webhooks) { - if (isSilent && data.ignoreSilent) continue; - if (data.url == null || data.payload == null || data.url.isEmpty() || data.payload.isEmpty()) continue; + private void sendAll(List webhooks, boolean isSilent) { + for (Webhook data : webhooks) { + if (isSilent && data.ignoreSilent()) continue; + if (data.url() == null || data.payload() == null || data.url().isEmpty() || data.payload().isEmpty()) continue; listener.sendAsync(data); } } diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 0a9943bb7..6e2944a7b 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -48,24 +48,28 @@ mavenPublishing { dependencies { api(project(":BanManagerLibs")) - testImplementation("junit:junit:4.13") - testImplementation("org.hamcrest:hamcrest-library:1.2.1") - testImplementation("org.powermock:powermock-module-junit4:2.0.2") - testImplementation("org.powermock:powermock-api-mockito2:2.0.2") - testImplementation("com.github.javafaker:javafaker:1.0.2") - testImplementation("org.awaitility:awaitility:4.0.1") - testImplementation("ch.vorburger.mariaDB4j:mariaDB4j:2.6.0") + testImplementation(platform("org.junit:junit-bom:5.11.4")) + testImplementation("org.junit.jupiter:junit-jupiter") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") + testImplementation("org.hamcrest:hamcrest:2.2") + testImplementation("org.mockito:mockito-core:5.14.2") + testImplementation("net.datafaker:datafaker:2.5.4") + testImplementation("org.awaitility:awaitility:4.3.0") + testImplementation("ch.vorburger.mariaDB4j:mariaDB4j:3.3.1") } tasks.withType().configureEach { - useJUnit() + useJUnitPlatform() maxHeapSize = "512m" - forkEvery = 1 // Fork a new JVM for each test class to prevent memory accumulation + // Fork a new JVM per test class. Several tests (storage, configs, MariaDB4j) leak + // static state - native handles, jdbc drivers, daemon executors - that hangs the + // suite if a single JVM keeps accumulating fixtures. Slower, but reliable. + forkEvery = 1 finalizedBy(tasks.jacocoTestReport) } jacoco { - toolVersion = "0.8.11" + toolVersion = "0.8.12" } tasks.jacocoTestReport { diff --git a/common/src/main/java/me/confuser/banmanager/common/BanManagerPlugin.java b/common/src/main/java/me/confuser/banmanager/common/BanManagerPlugin.java index b0fa85e7c..03dfdc778 100644 --- a/common/src/main/java/me/confuser/banmanager/common/BanManagerPlugin.java +++ b/common/src/main/java/me/confuser/banmanager/common/BanManagerPlugin.java @@ -9,9 +9,10 @@ import me.confuser.banmanager.common.hikari.HikariDataSource; import me.confuser.banmanager.common.ormlite.dao.GenericRawResults; import me.confuser.banmanager.common.ormlite.db.DatabaseType; -import me.confuser.banmanager.common.ormlite.db.H2DatabaseType; import me.confuser.banmanager.common.ormlite.jdbc.DataSourceConnectionSource; -import me.confuser.banmanager.common.ormlite.logger.LocalLog; +import me.confuser.banmanager.common.ormlite.jdbc.db.H2DatabaseType; +import me.confuser.banmanager.common.ormlite.logger.Level; +import me.confuser.banmanager.common.ormlite.logger.Logger; import me.confuser.banmanager.common.ormlite.support.ConnectionSource; import me.confuser.banmanager.common.ormlite.support.DatabaseConnection; import me.confuser.banmanager.common.configuration.file.YamlConfiguration; @@ -27,9 +28,15 @@ import me.confuser.banmanager.common.util.MessageRenderer; import java.io.*; +import java.net.http.HttpClient; import java.nio.file.Files; import java.sql.SQLException; +import java.time.Duration; import java.util.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import static java.lang.Long.parseLong; @@ -185,6 +192,14 @@ public class BanManagerPlugin { @Getter private MessageRegistry messageRegistry; + // Shared HTTP infrastructure for webhooks, UUID lookups and GeoIP downloads. + // Lazily initialised on first use so unit tests that don't touch HTTP avoid + // spinning up the executor. volatile ensures the double-checked locking in + // getHttpClient() publishes a fully-constructed HttpClient to other threads. + private volatile HttpClient httpClient; + private volatile ExecutorService httpExecutor; + private final Object httpLock = new Object(); + public BanManagerPlugin(PluginInfo pluginInfo, CommonLogger logger, File dataFolder, CommonScheduler scheduler, CommonServer server, CommonMetrics metrics) { this.pluginInfo = pluginInfo; this.logger = logger; @@ -297,6 +312,56 @@ public final void disable() { if (globalConn != null) { globalConn.closeQuietly(); } + + shutdownHttpExecutor(); + } + + /** + * Returns the shared {@link HttpClient}, creating it on first use. Callers + * (webhooks, UUID lookups, GeoIP downloads) share a single dedicated thread + * pool so webhook bursts do not starve the common ForkJoinPool. + * + *

Thread-safe: uses double-checked locking with a {@code volatile} field + * so concurrent callers will see a fully-constructed instance.

+ */ + public HttpClient getHttpClient() { + HttpClient existing = httpClient; + if (existing != null) return existing; + synchronized (httpLock) { + if (httpClient == null) { + AtomicInteger counter = new AtomicInteger(); + httpExecutor = Executors.newCachedThreadPool(r -> { + Thread t = new Thread(r, "BanManager-Http-" + counter.incrementAndGet()); + t.setDaemon(true); + return t; + }); + httpClient = HttpClient.newBuilder() + .connectTimeout(Duration.ofSeconds(10)) + .followRedirects(HttpClient.Redirect.NORMAL) + .executor(httpExecutor) + .build(); + } + return httpClient; + } + } + + private void shutdownHttpExecutor() { + ExecutorService executor; + synchronized (httpLock) { + executor = httpExecutor; + httpExecutor = null; + httpClient = null; + } + if (executor == null) return; + executor.shutdown(); + try { + if (!executor.awaitTermination(5, TimeUnit.SECONDS)) { + executor.shutdownNow(); + } + } catch (InterruptedException e) { + executor.shutdownNow(); + Thread.currentThread().interrupt(); + } } public void setupConfigs() { @@ -487,11 +552,16 @@ private T reloadConfig(T newConfig, T currentConfig, String f return newConfig; } - // Only effective on platforms where ORMLite falls back to LocalLog (no SLF4J/Log4j2 - // on the classpath). On Bukkit, ORMLite auto-detects the shaded SLF4J and level - // filtering is handled by BanManagerSlf4jLogger instead. + // Silences chatty ORMLite/HikariCP TRACE/DEBUG/INFO output unless debug mode is on. + // Uses Logger.setGlobalLogLevel rather than the LOCAL_LOG_LEVEL_PROPERTY system + // property because the property name ("com.j256.simplelogging.level") is a + // hard-coded string that survives shading; setting it would also throttle any + // other plugin on the server that bundles ORMLite. setGlobalLogLevel mutates a + // static field on our shaded Logger class, so the change is isolated to BanManager. + // On Bukkit the shaded SLF4J 2.x bridge handles filtering itself, so this is a + // no-op there (Logger doesn't observe the level once an SLF4J backend is wired). private void disableDatabaseLogging() { - System.setProperty(LocalLog.LOCAL_LOG_LEVEL_PROPERTY, "WARNING"); + Logger.setGlobalLogLevel(Level.WARNING); } public boolean setupConnections() throws SQLException { @@ -572,7 +642,7 @@ public void setupStorage() throws SQLException { if (config.getLocalDb().getStorageType().equals("h2")) { try (DatabaseConnection conn = getLocalConn().getReadWriteConnection("")) { conn.executeStatement("CREATE ALIAS IF NOT EXISTS INET6_NTOA FOR \"me.confuser.banmanager.common.util.IPUtils.toString\"", DatabaseConnection.DEFAULT_RESULT_FLAGS); - } catch (IOException | SQLException e) { + } catch (Exception e) { logger.severe("An error occurred setting up H2 storage", e); } } diff --git a/common/src/main/java/me/confuser/banmanager/common/commands/CommonCommand.java b/common/src/main/java/me/confuser/banmanager/common/commands/CommonCommand.java index 2ce90c6a7..e927e48ea 100644 --- a/common/src/main/java/me/confuser/banmanager/common/commands/CommonCommand.java +++ b/common/src/main/java/me/confuser/banmanager/common/commands/CommonCommand.java @@ -44,9 +44,9 @@ public CommonCommand(BanManagerPlugin plugin, String commandName, boolean enable PluginInfo.CommandInfo info = plugin.getPluginInfo().getCommand(commandName); - this.usage = info.getUsage(); - this.permission = info.getPermission(); - this.aliases = info.getAliases(); + this.usage = info.usage(); + this.permission = info.permission(); + this.aliases = info.aliases(); this.parser = CommandParser.class; } @@ -57,9 +57,9 @@ public CommonCommand(BanManagerPlugin plugin, String commandName, boolean enable PluginInfo.CommandInfo info = pluginInfo.getCommand(commandName); - this.usage = info.getUsage(); - this.permission = info.getPermission(); - this.aliases = info.getAliases(); + this.usage = info.usage(); + this.permission = info.permission(); + this.aliases = info.aliases(); this.parser = CommandParser.class; } diff --git a/common/src/main/java/me/confuser/banmanager/common/commands/InfoCommand.java b/common/src/main/java/me/confuser/banmanager/common/commands/InfoCommand.java index 3e3c2aeb2..e911924f9 100644 --- a/common/src/main/java/me/confuser/banmanager/common/commands/InfoCommand.java +++ b/common/src/main/java/me/confuser/banmanager/common/commands/InfoCommand.java @@ -19,7 +19,7 @@ import me.confuser.banmanager.common.util.DateUtils; import me.confuser.banmanager.common.util.IPUtils; import me.confuser.banmanager.common.util.Message; - +import me.confuser.banmanager.common.util.StorageUtils; import me.confuser.banmanager.common.util.StringUtils; import me.confuser.banmanager.common.util.parsers.InfoCommandParser; @@ -131,12 +131,12 @@ public void ipInfo(CommonSender sender, IPAddress ip, InfoCommandParser parser) for (HistoryEntry result : results) { Message message = Message.get("info.history.row") - .set("id", result.getId()) - .set("reason", result.getReason()) - .set("type", result.getType()) - .set("created", DateUtils.format(dateTimeFormat, result.getCreated())) - .set("actor", result.getActor()) - .set("meta", result.getMeta()); + .set("id", result.id()) + .set("reason", result.reason()) + .set("type", result.type()) + .set("created", DateUtils.format(dateTimeFormat, result.created())) + .set("actor", result.actor()) + .set("meta", result.meta()); messages.add(message.toString()); } @@ -390,12 +390,12 @@ public void playerInfo(CommonSender sender, String name, Integer index, InfoComm for (HistoryEntry result : results) { Message message = Message.get("info.history.row") - .set("id", result.getId()) - .set("reason", result.getReason()) - .set("type", result.getType()) - .set("created", DateUtils.format(dateTimeFormat, result.getCreated())) - .set("actor", result.getActor()) - .set("meta", result.getMeta()); + .set("id", result.id()) + .set("reason", result.reason()) + .set("type", result.type()) + .set("created", DateUtils.format(dateTimeFormat, result.created())) + .set("actor", result.actor()) + .set("meta", result.meta()); messages.add(message.toString()); } @@ -445,13 +445,13 @@ public void playerInfo(CommonSender sender, String name, Integer index, InfoComm } } } finally { - try { results.close(); } catch (IOException ignored) { } + try { results.close(); } catch (Exception ignored) { } } } finally { - try { stmt.close(); } catch (IOException ignored) { } + try { stmt.close(); } catch (Exception ignored) { } } - } catch (IOException e) { - throw new SQLException("Failed to query player stats", e); + } catch (Exception e) { + throw StorageUtils.toSqlException("Failed to query player stats", e); } messages.add(Message.get("info.stats.player") @@ -538,7 +538,7 @@ public void playerInfo(CommonSender sender, String name, Integer index, InfoComm } else { StringBuilder namesBuilder = new StringBuilder(); for (PlayerNameSummary nameData : playerNames) { - namesBuilder.append(nameData.getName()).append(", "); + namesBuilder.append(nameData.name()).append(", "); } if (namesBuilder.length() >= 2) namesBuilder.setLength(namesBuilder.length() - 2); messages.add(namesBuilder.toString()); diff --git a/common/src/main/java/me/confuser/banmanager/common/commands/NamesCommand.java b/common/src/main/java/me/confuser/banmanager/common/commands/NamesCommand.java index 92c36e977..e8a229bc7 100644 --- a/common/src/main/java/me/confuser/banmanager/common/commands/NamesCommand.java +++ b/common/src/main/java/me/confuser/banmanager/common/commands/NamesCommand.java @@ -67,9 +67,9 @@ public boolean onCommand(final CommonSender sender, CommandParser parser) { } else { for (PlayerNameSummary nameData : names) { Message.get("names.row") - .set("name", nameData.getName()) - .set("firstSeen", DateUtils.format(dateTimeFormat, nameData.getFirstSeen())) - .set("lastSeen", DateUtils.format(dateTimeFormat, nameData.getLastSeen())) + .set("name", nameData.name()) + .set("firstSeen", DateUtils.format(dateTimeFormat, nameData.firstSeen())) + .set("lastSeen", DateUtils.format(dateTimeFormat, nameData.lastSeen())) .sendTo(sender); } } @@ -90,22 +90,22 @@ static TextComponent buildNamesComponent(List names, String d for (PlayerNameSummary nameData : names) { if (hasInteractiveTemplate) { Component entry = Message.get("names.interactive") - .set("name", nameData.getName()) - .set("firstSeen", DateUtils.format(dateTimeFormat, nameData.getFirstSeen())) - .set("lastSeen", DateUtils.format(dateTimeFormat, nameData.getLastSeen())) + .set("name", nameData.name()) + .set("firstSeen", DateUtils.format(dateTimeFormat, nameData.firstSeen())) + .set("lastSeen", DateUtils.format(dateTimeFormat, nameData.lastSeen())) .resolveComponent(); message.append(entry); } else { String hoverText = Message.get("names.row") - .set("name", nameData.getName()) - .set("firstSeen", DateUtils.format(dateTimeFormat, nameData.getFirstSeen())) - .set("lastSeen", DateUtils.format(dateTimeFormat, nameData.getLastSeen())) + .set("name", nameData.name()) + .set("firstSeen", DateUtils.format(dateTimeFormat, nameData.firstSeen())) + .set("lastSeen", DateUtils.format(dateTimeFormat, nameData.lastSeen())) .toString(); message.append( - Component.text(nameData.getName()) + Component.text(nameData.name()) .color(NamedTextColor.YELLOW) - .clickEvent(ClickEvent.runCommand("/bminfo " + nameData.getName())) + .clickEvent(ClickEvent.runCommand("/bminfo " + nameData.name())) .hoverEvent(Component.text(hoverText))); } diff --git a/common/src/main/java/me/confuser/banmanager/common/configs/DatabaseConfig.java b/common/src/main/java/me/confuser/banmanager/common/configs/DatabaseConfig.java index 64069d97a..1900e164b 100644 --- a/common/src/main/java/me/confuser/banmanager/common/configs/DatabaseConfig.java +++ b/common/src/main/java/me/confuser/banmanager/common/configs/DatabaseConfig.java @@ -79,20 +79,40 @@ private DatabaseConfig(File dataFolder, ConfigurationSection conf) { } public String getJDBCUrl() { - if (storageType.equals("h2")) return "jdbc:h2:file:" + new File(dataFolder, name).getAbsolutePath() + ";mode=MySQL;DB_CLOSE_ON_EXIT=TRUE;FILE_LOCK=NO;IGNORECASE=TRUE"; - - String url = "jdbc:" + storageType + "://" + host + ":" + port + "/" + name + - "?autoReconnect=true&failOverReadOnly=false&maxReconnects=10&useUnicode=true&characterEncoding=utf-8" + - "&serverTimezone=UTC" + - "&useSSL=" + useSSL + - "&allowPublicKeyRetrieval=" + allowPublicKeyRetrieval + - "&verifyServerCertificate=" + verifyServerCertificate; + if (storageType.equals("h2")) { + return "jdbc:h2:file:" + new File(dataFolder, name).getAbsolutePath() + + ";mode=MySQL;DB_CLOSE_ON_EXIT=TRUE;FILE_LOCK=NO;IGNORECASE=TRUE"; + } - if (!storageType.equals("mariadb")) { - url += "&disableMariaDbDriver"; + if (storageType.equals("mariadb")) { + // mariadb-java-client 3.x rejects most legacy mysql parameters with a WARN log. + // Only emit options the driver actually supports and translate useSSL + + // verifyServerCertificate into the modern sslMode setting. + String sslMode; + if (!useSSL) { + sslMode = "disable"; + } else if (verifyServerCertificate) { + sslMode = "verify-full"; + } else { + sslMode = "trust"; + } + return "jdbc:mariadb://" + host + ":" + port + "/" + name + + "?useServerPrepStmts=true" + + "&cachePrepStmts=true" + + "&prepStmtCacheSize=250" + + "&prepStmtCacheSqlLimit=2048" + + "&sslMode=" + sslMode; } - return url; + // mysql via mysql-connector-j 8.x. mariadb-java-client 3.x no longer hijacks + // jdbc:mysql:// URLs, so the legacy "disableMariaDbDriver" hint is gone. + return "jdbc:mysql://" + host + ":" + port + "/" + name + + "?autoReconnect=true&failOverReadOnly=false&maxReconnects=10" + + "&useUnicode=true&characterEncoding=utf-8" + + "&serverTimezone=UTC" + + "&useSSL=" + useSSL + + "&allowPublicKeyRetrieval=" + allowPublicKeyRetrieval + + "&verifyServerCertificate=" + verifyServerCertificate; } public DatabaseTableConfig getTable(String table) { diff --git a/common/src/main/java/me/confuser/banmanager/common/configs/Fetcher.java b/common/src/main/java/me/confuser/banmanager/common/configs/Fetcher.java index 7cf6f9483..9cda06a83 100644 --- a/common/src/main/java/me/confuser/banmanager/common/configs/Fetcher.java +++ b/common/src/main/java/me/confuser/banmanager/common/configs/Fetcher.java @@ -1,13 +1,4 @@ package me.confuser.banmanager.common.configs; -import lombok.AllArgsConstructor; -import lombok.Getter; - -@AllArgsConstructor -public -class Fetcher { - @Getter - private final String url; - @Getter - private final String key; +public record Fetcher(String url, String key) { } diff --git a/common/src/main/java/me/confuser/banmanager/common/configs/GeoIpConfig.java b/common/src/main/java/me/confuser/banmanager/common/configs/GeoIpConfig.java index 5c4ae4c78..cedb29992 100644 --- a/common/src/main/java/me/confuser/banmanager/common/configs/GeoIpConfig.java +++ b/common/src/main/java/me/confuser/banmanager/common/configs/GeoIpConfig.java @@ -1,6 +1,7 @@ package me.confuser.banmanager.common.configs; import lombok.Getter; +import me.confuser.banmanager.common.BanManagerPlugin; import me.confuser.banmanager.common.CommonLogger; import me.confuser.banmanager.common.apachecommons.compress.archivers.ArchiveEntry; import me.confuser.banmanager.common.apachecommons.compress.archivers.tar.TarArchiveInputStream; @@ -14,8 +15,11 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.net.URL; -import java.net.URLConnection; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.time.Duration; import java.util.HashSet; import java.util.zip.GZIPInputStream; @@ -141,17 +145,32 @@ private void downloadDatabase(String downloadUrl, File location) throws IOExcept location.delete(); } - URL url = new URL(downloadUrl); - URLConnection con = url.openConnection(); + HttpClient client = BanManagerPlugin.getInstance().getHttpClient(); + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(downloadUrl)) + .timeout(Duration.ofMinutes(5)) + .header("User-Agent", "BanManager") + .GET() + .build(); + + HttpResponse response; + try { + response = client.send(request, HttpResponse.BodyHandlers.ofInputStream()); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IOException("Interrupted while downloading " + downloadUrl, e); + } - con.setConnectTimeout(6000); - con.connect(); + if (response.statusCode() != 200) { + response.body().close(); + throw new IOException("Unexpected response " + response.statusCode() + " from " + downloadUrl); + } - try (InputStream input = new GZIPInputStream(con.getInputStream()); + try (InputStream input = new GZIPInputStream(response.body()); TarArchiveInputStream inputStream = new TarArchiveInputStream(input); FileOutputStream outputStream = new FileOutputStream(location)) { - ArchiveEntry entry = null; + ArchiveEntry entry; while ((entry = inputStream.getNextEntry()) != null) { if (entry.isDirectory()) continue; if (!entry.getName().endsWith(".mmdb")) continue; diff --git a/common/src/main/java/me/confuser/banmanager/common/configs/Hook.java b/common/src/main/java/me/confuser/banmanager/common/configs/Hook.java index c97840577..5cd5d1add 100644 --- a/common/src/main/java/me/confuser/banmanager/common/configs/Hook.java +++ b/common/src/main/java/me/confuser/banmanager/common/configs/Hook.java @@ -1,25 +1,19 @@ package me.confuser.banmanager.common.configs; -import lombok.Getter; - import java.util.List; -public class Hook { - - @Getter - private final List pre; - @Getter - private final List post; - @Getter - private final boolean ignoreSilent; +/** + * Immutable description of a punishment-event hook (commands to run before + * and/or after the event fires). Lists are defensively copied so callers + * cannot mutate the hook's identity after construction. + */ +public record Hook(List pre, List post, boolean ignoreSilent) { + public Hook { + pre = pre == null ? List.of() : List.copyOf(pre); + post = post == null ? List.of() : List.copyOf(post); + } public Hook(List pre, List post) { this(pre, post, true); } - - public Hook(List pre, List post, boolean ignoreSilent) { - this.pre = pre; - this.post = post; - this.ignoreSilent = ignoreSilent; - } } diff --git a/common/src/main/java/me/confuser/banmanager/common/configs/PluginInfo.java b/common/src/main/java/me/confuser/banmanager/common/configs/PluginInfo.java index 4ccd573cb..0f1625239 100644 --- a/common/src/main/java/me/confuser/banmanager/common/configs/PluginInfo.java +++ b/common/src/main/java/me/confuser/banmanager/common/configs/PluginInfo.java @@ -1,8 +1,5 @@ package me.confuser.banmanager.common.configs; -import lombok.AllArgsConstructor; -import lombok.Getter; - import java.util.HashMap; import java.util.List; import java.util.Map; @@ -19,18 +16,9 @@ public CommandInfo getCommand(String commandName) { } public CommandInfo setCommand(CommandInfo command) { - return commands.put(command.getName(), command); + return commands.put(command.name(), command); } - @AllArgsConstructor - public static class CommandInfo { - @Getter - private String name; - @Getter - private String permission; - @Getter - private String usage; - @Getter - private List aliases; + public record CommandInfo(String name, String permission, String usage, List aliases) { } } diff --git a/common/src/main/java/me/confuser/banmanager/common/configs/UUIDFetcher.java b/common/src/main/java/me/confuser/banmanager/common/configs/UUIDFetcher.java index 02b4d2919..ac4166075 100644 --- a/common/src/main/java/me/confuser/banmanager/common/configs/UUIDFetcher.java +++ b/common/src/main/java/me/confuser/banmanager/common/configs/UUIDFetcher.java @@ -1,13 +1,4 @@ package me.confuser.banmanager.common.configs; -import lombok.AllArgsConstructor; -import lombok.Getter; - -@AllArgsConstructor -public class UUIDFetcher { - @Getter - private final Fetcher idToName; - @Getter - private final Fetcher nameToId; +public record UUIDFetcher(Fetcher idToName, Fetcher nameToId) { } - diff --git a/common/src/main/java/me/confuser/banmanager/common/configs/WebhookConfig.java b/common/src/main/java/me/confuser/banmanager/common/configs/WebhookConfig.java index b4c2ded74..9364dcf15 100644 --- a/common/src/main/java/me/confuser/banmanager/common/configs/WebhookConfig.java +++ b/common/src/main/java/me/confuser/banmanager/common/configs/WebhookConfig.java @@ -1,26 +1,26 @@ package me.confuser.banmanager.common.configs; -import lombok.AllArgsConstructor; import lombok.Getter; import me.confuser.banmanager.common.CommonLogger; import me.confuser.banmanager.common.configuration.ConfigurationSection; +import me.confuser.banmanager.common.data.Webhook; import me.confuser.banmanager.common.gson.Gson; import me.confuser.banmanager.common.gson.GsonBuilder; import java.io.File; import java.io.StringWriter; -import java.net.MalformedURLException; -import java.net.URL; +import java.net.URI; +import java.net.URISyntaxException; import java.util.*; public class WebhookConfig extends Config { - private static final Set ALLOWED_METHODS = new HashSet<>(Arrays.asList( + private static final Set ALLOWED_METHODS = Set.of( "GET", "POST", "PUT", "PATCH", "DELETE" - )); + ); @Getter private boolean hooksEnabled = false; - private Map> hookTypes = new HashMap<>(); + private Map> hookTypes = new HashMap<>(); public WebhookConfig(File dataFolder, CommonLogger logger) { super(dataFolder, "webhooks.yml", logger); @@ -55,18 +55,16 @@ public void afterLoad() { } for (String type : hooks.getKeys(false)) { - List webhooksList = new ArrayList<>(); + List webhooksList = new ArrayList<>(); Object typeValue = hooks.get(type); - if (typeValue instanceof List) { - // New array format - List hookList = (List) typeValue; + if (typeValue instanceof List hookList) { int index = 0; for (Object hookObj : hookList) { if (hookObj instanceof Map) { @SuppressWarnings("unchecked") Map hookMap = (Map) hookObj; - WebhookHookConfig config = parseWebhookConfig(hookMap, type, index); + Webhook config = parseWebhookConfig(hookMap, type, index); if (config != null) { webhooksList.add(config); } @@ -77,7 +75,7 @@ public void afterLoad() { // Legacy single-object format - wrap in list ConfigurationSection hookSection = hooks.getConfigurationSection(type); if (hookSection != null) { - WebhookHookConfig config = parseLegacyWebhookConfig(hookSection, type); + Webhook config = parseLegacyWebhookConfig(hookSection, type); if (config != null) { webhooksList.add(config); } @@ -88,7 +86,7 @@ public void afterLoad() { } } - private WebhookHookConfig parseWebhookConfig(Map hookMap, String type, int index) { + private Webhook parseWebhookConfig(Map hookMap, String type, int index) { String name = (String) hookMap.getOrDefault("name", "webhook-" + index); String url = (String) hookMap.get("url"); String method = ((String) hookMap.getOrDefault("method", "POST")).toUpperCase(); @@ -109,11 +107,9 @@ private WebhookHookConfig parseWebhookConfig(Map hookMap, String // Parse headers Map headers = new HashMap<>(); Object headersObj = hookMap.get("headers"); - if (headersObj instanceof Map) { - @SuppressWarnings("unchecked") - Map headersMap = (Map) headersObj; - for (Map.Entry entry : headersMap.entrySet()) { - headers.put(entry.getKey(), String.valueOf(entry.getValue())); + if (headersObj instanceof Map headersMap) { + for (Map.Entry entry : headersMap.entrySet()) { + headers.put(String.valueOf(entry.getKey()), String.valueOf(entry.getValue())); } } @@ -127,10 +123,10 @@ private WebhookHookConfig parseWebhookConfig(Map hookMap, String payload = writer.toString(); } - return new WebhookHookConfig(name, url, method, headers, payload, ignoreSilent); + return new Webhook(name, url, method, headers, payload, ignoreSilent); } - private WebhookHookConfig parseLegacyWebhookConfig(ConfigurationSection hookSection, String type) { + private Webhook parseLegacyWebhookConfig(ConfigurationSection hookSection, String type) { String url = hookSection.getString("url"); String method = hookSection.getString("method", "POST").toUpperCase(); boolean ignoreSilent = hookSection.getBoolean("ignoreSilent", true); @@ -166,7 +162,7 @@ private WebhookHookConfig parseLegacyWebhookConfig(ConfigurationSection hookSect payload = writer.toString(); } - return new WebhookHookConfig("webhook-0", url, method, headers, payload, ignoreSilent); + return new Webhook("webhook-0", url, method, headers, payload, ignoreSilent); } private boolean isValidUrl(String url) { @@ -174,10 +170,12 @@ private boolean isValidUrl(String url) { return false; } try { - URL parsed = new URL(url); - String protocol = parsed.getProtocol().toLowerCase(); - return "http".equals(protocol) || "https".equals(protocol); - } catch (MalformedURLException e) { + URI parsed = new URI(url); + String scheme = parsed.getScheme(); + if (scheme == null) return false; + scheme = scheme.toLowerCase(Locale.ROOT); + return ("http".equals(scheme) || "https".equals(scheme)) && parsed.getHost() != null; + } catch (URISyntaxException e) { return false; } } @@ -186,24 +184,8 @@ private boolean isValidUrl(String url) { public void onSave() { } - public List getHooks(String type) { - List hooks = hookTypes.get(type); + public List getHooks(String type) { + List hooks = hookTypes.get(type); return hooks != null ? hooks : Collections.emptyList(); } - - @AllArgsConstructor - public static class WebhookHookConfig { - @Getter - private String name; - @Getter - private String url; - @Getter - private String method; - @Getter - private Map headers; - @Getter - private String payload; - @Getter - private boolean ignoreSilent; - } } diff --git a/common/src/main/java/me/confuser/banmanager/common/configuration/file/YamlConfiguration.java b/common/src/main/java/me/confuser/banmanager/common/configuration/file/YamlConfiguration.java index 363a2202b..d808d2dd1 100644 --- a/common/src/main/java/me/confuser/banmanager/common/configuration/file/YamlConfiguration.java +++ b/common/src/main/java/me/confuser/banmanager/common/configuration/file/YamlConfiguration.java @@ -5,6 +5,7 @@ import me.confuser.banmanager.common.configuration.ConfigurationSection; import me.confuser.banmanager.common.configuration.InvalidConfigurationException; import me.confuser.banmanager.common.snakeyaml.DumperOptions; +import me.confuser.banmanager.common.snakeyaml.LoaderOptions; import me.confuser.banmanager.common.snakeyaml.Yaml; import me.confuser.banmanager.common.snakeyaml.error.YAMLException; import me.confuser.banmanager.common.snakeyaml.representer.Representer; @@ -23,8 +24,34 @@ public class YamlConfiguration extends FileConfiguration { private static final String COMMENT_PREFIX = "# "; private static final String BLANK_CONFIG = "{}\n"; private final DumperOptions yamlOptions = new DumperOptions(); - private final Representer yamlRepresenter = new YamlRepresenter(); - private final Yaml yaml = new Yaml(new YamlConstructor(), yamlRepresenter, yamlOptions); + private final LoaderOptions loaderOptions = createLoaderOptions(); + private final Representer yamlRepresenter = new YamlRepresenter(yamlOptions); + private final Yaml yaml = new Yaml(new YamlConstructor(loaderOptions), yamlRepresenter, yamlOptions, loaderOptions); + + /** + * Build {@link LoaderOptions} that preserve SnakeYAML 1.x-compatible + * behaviour for existing user-authored config files. The 2.x defaults made + * three potentially breaking changes for our use case: + * + *
    + *
  • {@code allowDuplicateKeys} flipped from {@code true} to {@code false}. + * Operators routinely hand-edit messages.yml and may have introduced + * duplicate keys; refusing to load would brick the plugin on upgrade. The + * last value still wins, matching legacy behaviour.
  • + *
  • {@code codePointLimit} now defaults to 3 MB. Large message bundles + * (translations, big punishment payloads) can exceed this, so we raise it + * to 32 MB.
  • + *
  • {@code nestingDepthLimit} now defaults to 50. Bumped to 100 to give + * headroom for deeply nested webhook payloads.
  • + *
+ */ + private static LoaderOptions createLoaderOptions() { + LoaderOptions options = new LoaderOptions(); + options.setAllowDuplicateKeys(true); + options.setCodePointLimit(32 * 1024 * 1024); + options.setNestingDepthLimit(100); + return options; + } /** * Creates a new {@link YamlConfiguration}, loading from the given file. diff --git a/common/src/main/java/me/confuser/banmanager/common/configuration/file/YamlConstructor.java b/common/src/main/java/me/confuser/banmanager/common/configuration/file/YamlConstructor.java index 11bdba4fe..b75174e71 100644 --- a/common/src/main/java/me/confuser/banmanager/common/configuration/file/YamlConstructor.java +++ b/common/src/main/java/me/confuser/banmanager/common/configuration/file/YamlConstructor.java @@ -1,6 +1,7 @@ package me.confuser.banmanager.common.configuration.file; import me.confuser.banmanager.common.configuration.serialization.ConfigurationSerialization; +import me.confuser.banmanager.common.snakeyaml.LoaderOptions; import me.confuser.banmanager.common.snakeyaml.constructor.SafeConstructor; import me.confuser.banmanager.common.snakeyaml.error.YAMLException; import me.confuser.banmanager.common.snakeyaml.nodes.Node; @@ -11,7 +12,8 @@ public class YamlConstructor extends SafeConstructor { - YamlConstructor() { + YamlConstructor(LoaderOptions loaderOptions) { + super(loaderOptions); yamlConstructors.put(Tag.MAP, new ConstructCustomObject()); } diff --git a/common/src/main/java/me/confuser/banmanager/common/configuration/file/YamlRepresenter.java b/common/src/main/java/me/confuser/banmanager/common/configuration/file/YamlRepresenter.java index 3963d7fd5..e938e2346 100644 --- a/common/src/main/java/me/confuser/banmanager/common/configuration/file/YamlRepresenter.java +++ b/common/src/main/java/me/confuser/banmanager/common/configuration/file/YamlRepresenter.java @@ -3,6 +3,7 @@ import me.confuser.banmanager.common.configuration.ConfigurationSection; import me.confuser.banmanager.common.configuration.serialization.ConfigurationSerializable; import me.confuser.banmanager.common.configuration.serialization.ConfigurationSerialization; +import me.confuser.banmanager.common.snakeyaml.DumperOptions; import me.confuser.banmanager.common.snakeyaml.nodes.Node; import me.confuser.banmanager.common.snakeyaml.representer.Representer; @@ -11,7 +12,8 @@ class YamlRepresenter extends Representer { - YamlRepresenter() { + YamlRepresenter(DumperOptions options) { + super(options); this.multiRepresenters.put(ConfigurationSection.class, new RepresentConfigurationSection()); this.multiRepresenters .put(ConfigurationSerializable.class, new RepresentConfigurationSerializable()); diff --git a/common/src/main/java/me/confuser/banmanager/common/data/HistoryEntry.java b/common/src/main/java/me/confuser/banmanager/common/data/HistoryEntry.java index fa39dce0f..72382d837 100644 --- a/common/src/main/java/me/confuser/banmanager/common/data/HistoryEntry.java +++ b/common/src/main/java/me/confuser/banmanager/common/data/HistoryEntry.java @@ -1,33 +1,4 @@ package me.confuser.banmanager.common.data; -import lombok.Getter; - -public class HistoryEntry { - - @Getter - private final int id; - - @Getter - private final String type; - - @Getter - private final String actor; - - @Getter - private final long created; - - @Getter - private final String reason; - - @Getter - private final String meta; - - public HistoryEntry(int id, String type, String actor, long created, String reason, String meta) { - this.id = id; - this.type = type; - this.actor = actor; - this.created = created; - this.reason = reason; - this.meta = meta; - } +public record HistoryEntry(int id, String type, String actor, long created, String reason, String meta) { } diff --git a/common/src/main/java/me/confuser/banmanager/common/data/PlayerNameSummary.java b/common/src/main/java/me/confuser/banmanager/common/data/PlayerNameSummary.java index 5bcafd8cb..de2886856 100644 --- a/common/src/main/java/me/confuser/banmanager/common/data/PlayerNameSummary.java +++ b/common/src/main/java/me/confuser/banmanager/common/data/PlayerNameSummary.java @@ -1,25 +1,8 @@ package me.confuser.banmanager.common.data; -import lombok.Getter; - /** * Summary of a player's usage of a particular name. * Aggregated from interval history: firstSeen = min(fromSeen), lastSeen = max(toSeen or current time). */ -public class PlayerNameSummary { - - @Getter - private final String name; - - @Getter - private final long firstSeen; - - @Getter - private final long lastSeen; - - public PlayerNameSummary(String name, long firstSeen, long lastSeen) { - this.name = name; - this.firstSeen = firstSeen; - this.lastSeen = lastSeen; - } +public record PlayerNameSummary(String name, long firstSeen, long lastSeen) { } diff --git a/common/src/main/java/me/confuser/banmanager/common/data/Webhook.java b/common/src/main/java/me/confuser/banmanager/common/data/Webhook.java new file mode 100644 index 000000000..6341e0512 --- /dev/null +++ b/common/src/main/java/me/confuser/banmanager/common/data/Webhook.java @@ -0,0 +1,43 @@ +package me.confuser.banmanager.common.data; + +import java.util.List; +import java.util.Map; + +/** + * Immutable description of a webhook to send for a punishment event. + * + *

The same shape is used both for raw, parsed webhooks loaded from + * {@code webhooks.yml} (with placeholders such as {@code [player]} still in + * the headers/payload) and for the resolved webhook that is dispatched over + * HTTP. Treating both as the same value class avoids near-duplicate records + * and keeps the placeholder substitution a pure transformation.

+ * + *

{@code headers} are defensively copied at construction time so callers + * may pass mutable maps without leaking them into the record's identity.

+ */ +public record Webhook(String name, String url, String method, + Map headers, String payload, + boolean ignoreSilent) { + public Webhook { + headers = headers == null ? Map.of() : Map.copyOf(headers); + if (payload == null) payload = ""; + } + + /** + * Returns a copy of this webhook with new {@code headers} and {@code payload}. + * Use this when applying placeholder substitutions to a raw config webhook. + */ + public Webhook withResolved(Map resolvedHeaders, String resolvedPayload) { + return new Webhook(name, url, method, resolvedHeaders, resolvedPayload, ignoreSilent); + } + + /** True if this webhook has no body to send (GET/DELETE methods). */ + public boolean hasBody() { + return !"GET".equals(method) && !"DELETE".equals(method); + } + + /** Convenience for callers that only need a stable list-of-empty default. */ + public static List empty() { + return List.of(); + } +} diff --git a/common/src/main/java/me/confuser/banmanager/common/listeners/CommonHooksListener.java b/common/src/main/java/me/confuser/banmanager/common/listeners/CommonHooksListener.java index 86e346798..bf91db048 100755 --- a/common/src/main/java/me/confuser/banmanager/common/listeners/CommonHooksListener.java +++ b/common/src/main/java/me/confuser/banmanager/common/listeners/CommonHooksListener.java @@ -19,7 +19,7 @@ public CommonHooksListener(BanManagerPlugin plugin) { } private boolean shouldSkip(Hook hook, boolean silent) { - return silent && hook.isIgnoreSilent(); + return silent && hook.ignoreSilent(); } public void onBan(PlayerBanData data, boolean pre) { @@ -33,7 +33,7 @@ public void onBan(PlayerBanData data, boolean pre, boolean silent) { if (hook == null) return; if (shouldSkip(hook, silent)) return; - List commands = pre ? hook.getPre() : hook.getPost(); + List commands = pre ? hook.pre() : hook.post(); if (commands.size() != 0) { executeCommands(commands, ImmutableMap.of( @@ -57,8 +57,8 @@ public void onUnban(PlayerBanData data, PlayerData actor, String reason, boolean if (hook == null) return; if (shouldSkip(hook, silent)) return; - if (hook.getPost().size() != 0) { - executeCommands(hook.getPost(), ImmutableMap.of( + if (hook.post().size() != 0) { + executeCommands(hook.post(), ImmutableMap.of( "player", data.getPlayer().getName() , "playerId", data.getPlayer().getUUID().toString() , "actor", actor.getName() @@ -79,7 +79,7 @@ public void onMute(PlayerMuteData data, boolean pre, boolean silent) { if (hook == null) return; if (shouldSkip(hook, silent)) return; - List commands = pre ? hook.getPre() : hook.getPost(); + List commands = pre ? hook.pre() : hook.post(); if (commands.size() != 0) { executeCommands(commands, ImmutableMap.of( @@ -103,8 +103,8 @@ public void onUnmute(PlayerMuteData data, PlayerData actor, String reason, boole if (hook == null) return; if (shouldSkip(hook, silent)) return; - if (hook.getPost().size() != 0) { - executeCommands(hook.getPost(), ImmutableMap.of( + if (hook.post().size() != 0) { + executeCommands(hook.post(), ImmutableMap.of( "player", data.getPlayer().getName() , "playerId", data.getPlayer().getUUID().toString() , "actor", actor.getName() @@ -125,7 +125,7 @@ public void onBan(IpBanData data, boolean pre, boolean silent) { if (hook == null) return; if (shouldSkip(hook, silent)) return; - List commands = pre ? hook.getPre() : hook.getPost(); + List commands = pre ? hook.pre() : hook.post(); if (commands.size() != 0) { executeCommands(commands, ImmutableMap.of( @@ -148,8 +148,8 @@ public void onUnban(IpBanData data, PlayerData actor, String reason, boolean sil if (hook == null) return; if (shouldSkip(hook, silent)) return; - if (hook.getPost().size() != 0) { - executeCommands(hook.getPost(), ImmutableMap.of( + if (hook.post().size() != 0) { + executeCommands(hook.post(), ImmutableMap.of( "ip", data.getIp().toString() , "actor", actor.getName() , "reason", reason @@ -170,7 +170,7 @@ public void onBan(IpRangeBanData data, boolean pre, boolean silent) { if (hook == null) return; if (shouldSkip(hook, silent)) return; - List commands = pre ? hook.getPre() : hook.getPost(); + List commands = pre ? hook.pre() : hook.post(); if (commands.size() != 0) { executeCommands(commands, ImmutableMap.of( @@ -194,8 +194,8 @@ public void onUnban(IpRangeBanData data, PlayerData actor, String reason, boolea if (hook == null) return; if (shouldSkip(hook, silent)) return; - if (hook.getPost().size() != 0) { - executeCommands(hook.getPost(), ImmutableMap.of( + if (hook.post().size() != 0) { + executeCommands(hook.post(), ImmutableMap.of( "from", data.getFromIp().toString() , "to", data.getToIp().toString() , "actor", actor.getName() @@ -216,7 +216,7 @@ public void onWarn(PlayerWarnData data, boolean pre, boolean silent) { if (hook == null) return; if (shouldSkip(hook, silent)) return; - List commands = pre ? hook.getPre() : hook.getPost(); + List commands = pre ? hook.pre() : hook.post(); if (commands.size() != 0) { executeCommands(commands, ImmutableMap.of( @@ -240,8 +240,8 @@ public void onNote(PlayerNoteData data, boolean silent) { if (hook == null) return; if (shouldSkip(hook, silent)) return; - if (hook.getPost().size() != 0) { - executeCommands(hook.getPost(), ImmutableMap.of( + if (hook.post().size() != 0) { + executeCommands(hook.post(), ImmutableMap.of( "player", data.getPlayer().getName() , "playerId", data.getPlayer().getUUID().toString() , "actor", data.getActor().getName() @@ -261,7 +261,7 @@ public void onReport(PlayerReportData data, boolean pre, boolean silent) { if (hook == null) return; if (shouldSkip(hook, silent)) return; - List commands = pre ? hook.getPre() : hook.getPost(); + List commands = pre ? hook.pre() : hook.post(); if (commands.size() != 0) { executeCommands(commands, ImmutableMap.of( diff --git a/common/src/main/java/me/confuser/banmanager/common/listeners/CommonWebhookListener.java b/common/src/main/java/me/confuser/banmanager/common/listeners/CommonWebhookListener.java index 87881062a..b5e14b49a 100644 --- a/common/src/main/java/me/confuser/banmanager/common/listeners/CommonWebhookListener.java +++ b/common/src/main/java/me/confuser/banmanager/common/listeners/CommonWebhookListener.java @@ -1,7 +1,6 @@ package me.confuser.banmanager.common.listeners; import me.confuser.banmanager.common.BanManagerPlugin; -import me.confuser.banmanager.common.configs.WebhookConfig.WebhookHookConfig; import me.confuser.banmanager.common.data.*; import me.confuser.banmanager.common.util.DateUtils; @@ -9,15 +8,13 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.CompletableFuture; -import java.net.HttpURLConnection; +import java.net.URI; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; +import java.time.Duration; import java.time.format.DateTimeFormatter; -import java.io.BufferedReader; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.net.URL; import java.sql.SQLException; import java.time.ZoneId; @@ -33,10 +30,9 @@ private String toISO8601(long timestamp) { .format(java.time.Instant.ofEpochMilli(timestamp).atZone(ZoneId.systemDefault())); } - public List notifyOnBan(PlayerBanData ban) { + public List notifyOnBan(PlayerBanData ban) { String type = ban.getExpires() == 0 ? "ban" : "tempban"; - List hooks = plugin.getWebhookConfig().getHooks(type); - List results = new ArrayList<>(); + List hooks = plugin.getWebhookConfig().getHooks(type); Map replacements = new HashMap<>(); replacements.put("[player]", ban.getPlayer().getName()); @@ -51,17 +47,12 @@ public List notifyOnBan(PlayerBanData ban) { replacements.put("[expires]", DateUtils.getDifferenceFormat(ban.getExpires())); } - for (WebhookHookConfig hook : hooks) { - results.add(createWebhookData(hook, replacements)); - } - - return results; + return resolve(hooks, replacements); } - public List notifyOnBan(IpBanData ban) { + public List notifyOnBan(IpBanData ban) { String type = ban.getExpires() == 0 ? "banip" : "tempbanip"; - List hooks = plugin.getWebhookConfig().getHooks(type); - List results = new ArrayList<>(); + List hooks = plugin.getWebhookConfig().getHooks(type); List players = plugin.getPlayerStorage().getDuplicatesInTime(ban.getIp(), plugin.getConfig().getTimeAssociatedAlts()); @@ -87,16 +78,11 @@ public List notifyOnBan(IpBanData ban) { replacements.put("[expires]", DateUtils.getDifferenceFormat(ban.getExpires())); } - for (WebhookHookConfig hook : hooks) { - results.add(createWebhookData(hook, replacements)); - } - - return results; + return resolve(hooks, replacements); } - public List notifyOnKick(PlayerKickData kick) { - List hooks = plugin.getWebhookConfig().getHooks("kick"); - List results = new ArrayList<>(); + public List notifyOnKick(PlayerKickData kick) { + List hooks = plugin.getWebhookConfig().getHooks("kick"); Map replacements = new HashMap<>(); replacements.put("[player]", kick.getPlayer().getName()); @@ -107,17 +93,12 @@ public List notifyOnKick(PlayerKickData kick) { replacements.put("[created]", toISO8601(kick.getCreated())); replacements.put("[reason]", kick.getReason()); - for (WebhookHookConfig hook : hooks) { - results.add(createWebhookData(hook, replacements)); - } - - return results; + return resolve(hooks, replacements); } - public List notifyOnMute(PlayerMuteData mute) { + public List notifyOnMute(PlayerMuteData mute) { String type = mute.getExpires() == 0 ? "mute" : "tempmute"; - List hooks = plugin.getWebhookConfig().getHooks(type); - List results = new ArrayList<>(); + List hooks = plugin.getWebhookConfig().getHooks(type); Map replacements = new HashMap<>(); replacements.put("[player]", mute.getPlayer().getName()); @@ -132,17 +113,12 @@ public List notifyOnMute(PlayerMuteData mute) { replacements.put("[expires]", DateUtils.getDifferenceFormat(mute.getExpires())); } - for (WebhookHookConfig hook : hooks) { - results.add(createWebhookData(hook, replacements)); - } - - return results; + return resolve(hooks, replacements); } - public List notifyOnWarn(PlayerWarnData warn) { + public List notifyOnWarn(PlayerWarnData warn) { String type = warn.getExpires() == 0 ? "warning" : "tempwarning"; - List hooks = plugin.getWebhookConfig().getHooks(type); - List results = new ArrayList<>(); + List hooks = plugin.getWebhookConfig().getHooks(type); Map replacements = new HashMap<>(); replacements.put("[player]", warn.getPlayer().getName()); @@ -158,16 +134,11 @@ public List notifyOnWarn(PlayerWarnData warn) { replacements.put("[expires]", DateUtils.getDifferenceFormat(warn.getExpires())); } - for (WebhookHookConfig hook : hooks) { - results.add(createWebhookData(hook, replacements)); - } - - return results; + return resolve(hooks, replacements); } - public List notifyOnUnban(PlayerBanData ban, PlayerData actor, String reason) { - List hooks = plugin.getWebhookConfig().getHooks("unban"); - List results = new ArrayList<>(); + public List notifyOnUnban(PlayerBanData ban, PlayerData actor, String reason) { + List hooks = plugin.getWebhookConfig().getHooks("unban"); Map replacements = new HashMap<>(); replacements.put("[player]", ban.getPlayer().getName()); @@ -178,16 +149,11 @@ public List notifyOnUnban(PlayerBanData ban, PlayerData actor, Stri replacements.put("[created]", toISO8601(ban.getCreated())); replacements.put("[reason]", reason); - for (WebhookHookConfig hook : hooks) { - results.add(createWebhookData(hook, replacements)); - } - - return results; + return resolve(hooks, replacements); } - public List notifyOnUnban(IpBanData ban, PlayerData actor, String reason) { - List hooks = plugin.getWebhookConfig().getHooks("unbanip"); - List results = new ArrayList<>(); + public List notifyOnUnban(IpBanData ban, PlayerData actor, String reason) { + List hooks = plugin.getWebhookConfig().getHooks("unbanip"); Map replacements = new HashMap<>(); replacements.put("[ip]", ban.getIp().toString()); @@ -197,16 +163,11 @@ public List notifyOnUnban(IpBanData ban, PlayerData actor, String r replacements.put("[created]", toISO8601(ban.getCreated())); replacements.put("[reason]", reason); - for (WebhookHookConfig hook : hooks) { - results.add(createWebhookData(hook, replacements)); - } - - return results; + return resolve(hooks, replacements); } - public List notifyOnUnmute(PlayerMuteData mute, PlayerData actor, String reason) { - List hooks = plugin.getWebhookConfig().getHooks("unmute"); - List results = new ArrayList<>(); + public List notifyOnUnmute(PlayerMuteData mute, PlayerData actor, String reason) { + List hooks = plugin.getWebhookConfig().getHooks("unmute"); Map replacements = new HashMap<>(); replacements.put("[player]", mute.getPlayer().getName()); @@ -217,22 +178,17 @@ public List notifyOnUnmute(PlayerMuteData mute, PlayerData actor, S replacements.put("[created]", toISO8601(mute.getCreated())); replacements.put("[reason]", reason); - for (WebhookHookConfig hook : hooks) { - results.add(createWebhookData(hook, replacements)); - } - - return results; + return resolve(hooks, replacements); } - public List notifyOnReport(PlayerReportData report, PlayerData actor, String reason) { - List hooks = plugin.getWebhookConfig().getHooks("report"); - List results = new ArrayList<>(); + public List notifyOnReport(PlayerReportData report, PlayerData actor, String reason) { + List hooks = plugin.getWebhookConfig().getHooks("report"); List locations = null; try { locations = plugin.getPlayerReportLocationStorage().getByReport(report); } catch (SQLException e) { - plugin.getLogger().warning("Failed to send webhook", e); + plugin.getLogger().warning("Failed to load report locations for webhook", e); } Map replacements = new HashMap<>(); @@ -275,20 +231,24 @@ public List notifyOnReport(PlayerReportData report, PlayerData acto } } - for (WebhookHookConfig hook : hooks) { - results.add(createWebhookData(hook, replacements)); - } + return resolve(hooks, replacements); + } + private List resolve(List hooks, Map replacements) { + List results = new ArrayList<>(hooks.size()); + for (Webhook hook : hooks) { + results.add(applyReplacements(hook, replacements)); + } return results; } - private WebhookData createWebhookData(WebhookHookConfig hook, Map replacements) { - String payload = applyReplacements(hook.getPayload(), replacements); + private Webhook applyReplacements(Webhook hook, Map replacements) { + String payload = applyReplacements(hook.payload(), replacements); Map headers = new HashMap<>(); - for (Map.Entry entry : hook.getHeaders().entrySet()) { + for (Map.Entry entry : hook.headers().entrySet()) { headers.put(entry.getKey(), applyReplacements(entry.getValue(), replacements)); } - return new WebhookData(hook.getName(), hook.getUrl(), hook.getMethod(), headers, payload, hook.isIgnoreSilent()); + return hook.withResolved(headers, payload); } private String applyReplacements(String input, Map replacements) { @@ -300,87 +260,54 @@ private String applyReplacements(String input, Map replacements) return result; } - public void sendAsync(WebhookData data) { - CompletableFuture.runAsync(() -> send(data)); - } - - public void send(WebhookData data) { + public void sendAsync(Webhook data) { if (plugin.getConfig().isDebugEnabled()) { - plugin.getLogger().info("Sending webhook '" + data.name + "' to " + data.url + " with method " + data.method); + plugin.getLogger().info("Sending webhook '" + data.name() + "' to " + data.url() + " with method " + data.method()); } - HttpURLConnection connection = null; + HttpRequest request; try { - connection = (HttpURLConnection) new URL(data.url).openConnection(); - connection.addRequestProperty("Content-Type", "application/json"); - connection.addRequestProperty("User-Agent", "BanManager"); - - // Apply custom headers - for (Map.Entry header : data.headers.entrySet()) { - connection.addRequestProperty(header.getKey(), header.getValue()); - } - - connection.setRequestMethod(data.method); + request = buildRequest(data); + } catch (IllegalArgumentException e) { + plugin.getLogger().warning("Failed to send webhook '" + data.name() + "': invalid URL or method - " + e.getMessage()); + return; + } - // Only set output for methods that support a body - if (!"GET".equals(data.method) && !"DELETE".equals(data.method)) { - connection.setDoOutput(true); - try (OutputStream stream = connection.getOutputStream()) { - stream.write(data.payload.getBytes()); - stream.flush(); - } - } + plugin.getHttpClient() + .sendAsync(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)) + .whenComplete((response, throwable) -> { + if (throwable != null) { + plugin.getLogger().warning("Failed to send webhook '" + data.name() + "'", throwable); + return; + } - int responseCode = connection.getResponseCode(); - if (responseCode > 299) { - plugin.getLogger().warning("Failed to send webhook '" + data.name + "'"); - plugin.getLogger().warning("Response code: " + responseCode); - - InputStream errorStream = connection.getErrorStream(); - if (errorStream != null) { - try (BufferedReader reader = new BufferedReader(new InputStreamReader(errorStream))) { - StringBuilder responseBody = new StringBuilder(); - String line; - while ((line = reader.readLine()) != null) { - responseBody.append(line); + int responseCode = response.statusCode(); + if (responseCode > 299) { + plugin.getLogger().warning("Failed to send webhook '" + data.name() + "'"); + plugin.getLogger().warning("Response code: " + responseCode); + String body = response.body(); + if (body != null && !body.isEmpty()) { + plugin.getLogger().warning("Response body: " + body); } - plugin.getLogger().warning("Response body: " + responseBody.toString()); } - } - } else { - try (InputStream in = connection.getInputStream()) { - while (in.read() != -1) {} - } - } - } catch (Exception e) { - plugin.getLogger().warning("Failed to send webhook '" + data.name + "'"); - plugin.getLogger().warning("Error: " + e.getMessage()); - plugin.getLogger().warning("Failed to send webhook", e); - } finally { - if (connection != null) { - connection.disconnect(); - } - } + }); } - /** - * Data class to hold all information needed to send a webhook request. - */ - public static class WebhookData { - public final String name; - public final String url; - public final String method; - public final Map headers; - public final String payload; - public final boolean ignoreSilent; - - public WebhookData(String name, String url, String method, Map headers, String payload, boolean ignoreSilent) { - this.name = name; - this.url = url; - this.method = method; - this.headers = headers; - this.payload = payload; - this.ignoreSilent = ignoreSilent; + private HttpRequest buildRequest(Webhook data) { + HttpRequest.Builder builder = HttpRequest.newBuilder() + .uri(URI.create(data.url())) + .timeout(Duration.ofSeconds(15)) + .header("Content-Type", "application/json") + .header("User-Agent", "BanManager"); + + for (Map.Entry header : data.headers().entrySet()) { + builder.header(header.getKey(), header.getValue()); } + + HttpRequest.BodyPublisher body = data.hasBody() + ? HttpRequest.BodyPublishers.ofString(data.payload(), StandardCharsets.UTF_8) + : HttpRequest.BodyPublishers.noBody(); + + return builder.method(data.method(), body).build(); } } diff --git a/common/src/main/java/me/confuser/banmanager/common/runnables/RollbackSync.java b/common/src/main/java/me/confuser/banmanager/common/runnables/RollbackSync.java index bafaac7be..7b45a783a 100644 --- a/common/src/main/java/me/confuser/banmanager/common/runnables/RollbackSync.java +++ b/common/src/main/java/me/confuser/banmanager/common/runnables/RollbackSync.java @@ -28,65 +28,43 @@ public void run() { final RollbackData data = itr.next(); switch (data.getType()) { - case "bans": - evictFromCache(plugin.getPlayerBanStorage().getBans(), - v -> v.getActor().getUUID(), PlayerBanData::getCreated, data); - break; - - case "ipbans": - evictFromCache(plugin.getIpBanStorage().getBans(), - v -> v.getActor().getUUID(), IpBanData::getCreated, data); - break; - - case "ipmutes": - evictFromCache(plugin.getIpMuteStorage().getMutes(), - v -> v.getActor().getUUID(), IpMuteData::getCreated, data); - break; - - case "mutes": - evictFromCache(plugin.getPlayerMuteStorage().getMutes(), - v -> v.getActor().getUUID(), PlayerMuteData::getCreated, data); - break; - - case "banrecords": - restoreToCache(plugin.getPlayerBanStorage() - .queryBuilder().where() - .le("created", data.getCreated()) - .and().ge("created", data.getExpires()) - .iterator(), - ban -> !plugin.getPlayerBanStorage().isBanned(ban.getPlayer().getUUID()), - ban -> plugin.getPlayerBanStorage().addBan(ban)); - break; - - case "ipbanrecords": - restoreToCache(plugin.getIpBanStorage() - .queryBuilder().where() - .le("created", data.getCreated()) - .and().ge("created", data.getExpires()) - .iterator(), - ban -> !plugin.getIpBanStorage().isBanned(ban.getIp()), - ban -> plugin.getIpBanStorage().addBan(ban)); - break; - - case "muterecords": - restoreToCache(plugin.getPlayerMuteStorage() - .queryBuilder().where() - .le("created", data.getCreated()) - .and().ge("created", data.getExpires()) - .iterator(), - mute -> !plugin.getPlayerMuteStorage().isMuted(mute.getPlayer().getUUID()), - mute -> plugin.getPlayerMuteStorage().addMute(mute)); - break; - - case "ipmuterecords": - restoreToCache(plugin.getIpMuteStorage() - .queryBuilder().where() - .le("created", data.getCreated()) - .and().ge("created", data.getExpires()) - .iterator(), - mute -> !plugin.getIpMuteStorage().isMuted(mute.getIp()), - mute -> plugin.getIpMuteStorage().addMute(mute)); - break; + case "bans" -> evictFromCache(plugin.getPlayerBanStorage().getBans(), + v -> v.getActor().getUUID(), PlayerBanData::getCreated, data); + case "ipbans" -> evictFromCache(plugin.getIpBanStorage().getBans(), + v -> v.getActor().getUUID(), IpBanData::getCreated, data); + case "ipmutes" -> evictFromCache(plugin.getIpMuteStorage().getMutes(), + v -> v.getActor().getUUID(), IpMuteData::getCreated, data); + case "mutes" -> evictFromCache(plugin.getPlayerMuteStorage().getMutes(), + v -> v.getActor().getUUID(), PlayerMuteData::getCreated, data); + case "banrecords" -> restoreToCache(plugin.getPlayerBanStorage() + .queryBuilder().where() + .le("created", data.getCreated()) + .and().ge("created", data.getExpires()) + .iterator(), + ban -> !plugin.getPlayerBanStorage().isBanned(ban.getPlayer().getUUID()), + ban -> plugin.getPlayerBanStorage().addBan(ban)); + case "ipbanrecords" -> restoreToCache(plugin.getIpBanStorage() + .queryBuilder().where() + .le("created", data.getCreated()) + .and().ge("created", data.getExpires()) + .iterator(), + ban -> !plugin.getIpBanStorage().isBanned(ban.getIp()), + ban -> plugin.getIpBanStorage().addBan(ban)); + case "muterecords" -> restoreToCache(plugin.getPlayerMuteStorage() + .queryBuilder().where() + .le("created", data.getCreated()) + .and().ge("created", data.getExpires()) + .iterator(), + mute -> !plugin.getPlayerMuteStorage().isMuted(mute.getPlayer().getUUID()), + mute -> plugin.getPlayerMuteStorage().addMute(mute)); + case "ipmuterecords" -> restoreToCache(plugin.getIpMuteStorage() + .queryBuilder().where() + .le("created", data.getCreated()) + .and().ge("created", data.getExpires()) + .iterator(), + mute -> !plugin.getIpMuteStorage().isMuted(mute.getIp()), + mute -> plugin.getIpMuteStorage().addMute(mute)); + default -> { /* unknown rollback type */ } } } diff --git a/common/src/main/java/me/confuser/banmanager/common/storage/ActivityStorage.java b/common/src/main/java/me/confuser/banmanager/common/storage/ActivityStorage.java index 3b7f076a9..9454b4c62 100644 --- a/common/src/main/java/me/confuser/banmanager/common/storage/ActivityStorage.java +++ b/common/src/main/java/me/confuser/banmanager/common/storage/ActivityStorage.java @@ -7,10 +7,7 @@ import me.confuser.banmanager.common.ormlite.support.CompiledStatement; import me.confuser.banmanager.common.ormlite.support.DatabaseConnection; import me.confuser.banmanager.common.ormlite.support.DatabaseResults; -import me.confuser.banmanager.common.util.IPUtils; -import java.io.IOException; -import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -296,11 +293,11 @@ public List> getSince(long since, PlayerData actor) { result.closeQuietly(); } } finally { - try { statement.close(); } catch (IOException ignored) { } + try { statement.close(); } catch (Exception ignored) { } } return results; - } catch (SQLException | IOException e) { + } catch (Exception e) { plugin.getLogger().warning("Failed to process activity operation", e); return null; } diff --git a/common/src/main/java/me/confuser/banmanager/common/storage/HistoryStorage.java b/common/src/main/java/me/confuser/banmanager/common/storage/HistoryStorage.java index 0a9e64489..7b0656d15 100644 --- a/common/src/main/java/me/confuser/banmanager/common/storage/HistoryStorage.java +++ b/common/src/main/java/me/confuser/banmanager/common/storage/HistoryStorage.java @@ -11,8 +11,6 @@ import me.confuser.banmanager.common.ormlite.support.DatabaseResults; import me.confuser.banmanager.common.util.parsers.InfoCommandParser; -import java.io.IOException; -import java.sql.SQLException; import java.util.ArrayList; import java.util.List; @@ -180,11 +178,11 @@ private List executeQuery(Object paramValue, SqlType paramType, dbResults.closeQuietly(); } } finally { - try { statement.close(); } catch (IOException ignored) { } + try { statement.close(); } catch (Exception ignored) { } } return results; - } catch (SQLException | IOException e) { + } catch (Exception e) { plugin.getLogger().warning("Failed to process history operation", e); return null; } diff --git a/common/src/main/java/me/confuser/banmanager/common/storage/PlayerHistoryStorage.java b/common/src/main/java/me/confuser/banmanager/common/storage/PlayerHistoryStorage.java index 134dc4b77..3c80a3254 100644 --- a/common/src/main/java/me/confuser/banmanager/common/storage/PlayerHistoryStorage.java +++ b/common/src/main/java/me/confuser/banmanager/common/storage/PlayerHistoryStorage.java @@ -17,8 +17,8 @@ import me.confuser.banmanager.common.ormlite.support.CompiledStatement; import me.confuser.banmanager.common.ormlite.support.DatabaseConnection; import me.confuser.banmanager.common.ormlite.support.DatabaseResults; +import me.confuser.banmanager.common.util.StorageUtils; -import java.io.IOException; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; @@ -176,13 +176,13 @@ public List getNamesSummary(PlayerData player) throws SQLExce results.getLong(2))); } } finally { - try { results.close(); } catch (IOException ignored) { } + try { results.close(); } catch (Exception ignored) { } } } finally { - try { statement.close(); } catch (IOException ignored) { } + try { statement.close(); } catch (Exception ignored) { } } - } catch (IOException e) { - throw new SQLException("Failed to query name summary", e); + } catch (Exception e) { + throw StorageUtils.toSqlException("Failed to query name summary", e); } return summaries; diff --git a/common/src/main/java/me/confuser/banmanager/common/storage/PlayerWarnStorage.java b/common/src/main/java/me/confuser/banmanager/common/storage/PlayerWarnStorage.java index 59bae920d..8cf83da34 100644 --- a/common/src/main/java/me/confuser/banmanager/common/storage/PlayerWarnStorage.java +++ b/common/src/main/java/me/confuser/banmanager/common/storage/PlayerWarnStorage.java @@ -20,9 +20,9 @@ import me.confuser.banmanager.common.ormlite.support.DatabaseResults; import me.confuser.banmanager.common.ormlite.table.DatabaseTableConfig; import me.confuser.banmanager.common.ormlite.table.TableUtils; +import me.confuser.banmanager.common.util.StorageUtils; import me.confuser.banmanager.common.util.UUIDUtils; -import java.io.IOException; import java.sql.SQLException; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -108,7 +108,9 @@ public double getPointsCount(PlayerData player) throws SQLException { DatabaseResults results = statement.runQuery(null); if (results.next()) return results.getDouble(0); - } catch (IOException e) { + } catch (SQLException e) { + throw e; + } catch (Exception e) { plugin.getLogger().warning("Failed to process player warn operation", e); } @@ -127,7 +129,9 @@ public double getPointsCount(PlayerData player, long timeframe) throws SQLExcept DatabaseResults results = statement.runQuery(null); if (results.next()) return results.getDouble(0); - } catch (IOException e) { + } catch (SQLException e) { + throw e; + } catch (Exception e) { plugin.getLogger().warning("Failed to process player warn operation", e); } @@ -166,10 +170,10 @@ public int deleteRecent(PlayerData player) throws SQLException { statement.setObject(0, player.getId(), SqlType.BYTE_ARRAY); return statement.runUpdate(); } finally { - try { statement.close(); } catch (IOException ignored) { } + try { statement.close(); } catch (Exception ignored) { } } - } catch (IOException e) { - throw new SQLException("Failed to delete recent warning", e); + } catch (Exception e) { + throw StorageUtils.toSqlException("Failed to delete recent warning", e); } } diff --git a/common/src/main/java/me/confuser/banmanager/common/storage/conversion/H2.java b/common/src/main/java/me/confuser/banmanager/common/storage/conversion/H2.java index b847a6543..8d9d5cc5f 100644 --- a/common/src/main/java/me/confuser/banmanager/common/storage/conversion/H2.java +++ b/common/src/main/java/me/confuser/banmanager/common/storage/conversion/H2.java @@ -10,7 +10,6 @@ import me.confuser.banmanager.common.storage.*; import java.io.File; -import java.io.IOException; import java.sql.SQLException; import java.util.HashMap; import java.util.Map; @@ -126,7 +125,7 @@ public void importPlayers() { plugin.getLogger().severe("Failed to import player " + data.getUUID()); } } - } catch (IOException e) { + } catch (Exception e) { plugin.getLogger().warning("Failed during H2 conversion", e); } @@ -148,7 +147,7 @@ public void importPlayerBans() { plugin.getLogger().severe("Failed to import player ban " + data.getPlayer().getUUID()); } } - } catch (IOException e) { + } catch (Exception e) { plugin.getLogger().warning("Failed during H2 conversion", e); } @@ -167,7 +166,7 @@ public void importPlayerBans() { plugin.getLogger().severe("Failed to import player ban record " + data.getId()); } } - } catch (IOException e) { + } catch (Exception e) { plugin.getLogger().warning("Failed during H2 conversion", e); } @@ -189,7 +188,7 @@ public void importPlayerMutes() { plugin.getLogger().severe("Failed to import player mute " + data.getPlayer().getUUID()); } } - } catch (IOException e) { + } catch (Exception e) { plugin.getLogger().warning("Failed during H2 conversion", e); } @@ -208,7 +207,7 @@ public void importPlayerMutes() { plugin.getLogger().severe("Failed to import player mute record " + data.getId()); } } - } catch (IOException e) { + } catch (Exception e) { plugin.getLogger().warning("Failed during H2 conversion", e); } @@ -230,7 +229,7 @@ public void importPlayerWarnings() { plugin.getLogger().severe("Failed to import player warning " + data.getId()); } } - } catch (IOException e) { + } catch (Exception e) { plugin.getLogger().warning("Failed during H2 conversion", e); } @@ -251,7 +250,7 @@ public void importPlayerKicks() { plugin.getLogger().severe("Failed to import player kick " + data.getId()); } } - } catch (IOException e) { + } catch (Exception e) { plugin.getLogger().warning("Failed during H2 conversion", e); } @@ -272,7 +271,7 @@ public void importPlayerNotes() { plugin.getLogger().severe("Failed to import player note " + data.getId()); } } - } catch (IOException e) { + } catch (Exception e) { plugin.getLogger().warning("Failed during H2 conversion", e); } @@ -293,7 +292,7 @@ public void importPlayerHistory() { plugin.getLogger().severe("Failed to import player note " + data.getId()); } } - } catch (IOException e) { + } catch (Exception e) { plugin.getLogger().warning("Failed during H2 conversion", e); } @@ -326,7 +325,7 @@ public void importPlayerReports() { plugin.getLogger().severe("Failed to import player report state " + h2State.getId()); } } - } catch (IOException e) { + } catch (Exception e) { plugin.getLogger().warning("Failed during H2 conversion", e); } @@ -345,7 +344,7 @@ public void importPlayerReports() { plugin.getLogger().severe("Failed to import player report state " + data.getId()); } } - } catch (IOException e) { + } catch (Exception e) { plugin.getLogger().warning("Failed during H2 conversion", e); } @@ -364,7 +363,7 @@ public void importPlayerReports() { plugin.getLogger().severe("Failed to import player report comment " + data.getId()); } } - } catch (IOException e) { + } catch (Exception e) { plugin.getLogger().warning("Failed during H2 conversion", e); } @@ -410,7 +409,7 @@ public void importPlayerReports() { plugin.getLogger().severe("Failed to import player report " + data.getId()); } } - } catch (IOException e) { + } catch (Exception e) { plugin.getLogger().warning("Failed during H2 conversion", e); } @@ -429,7 +428,7 @@ public void importPlayerReports() { plugin.getLogger().severe("Failed to import player report location " + data.getId()); } } - } catch (IOException e) { + } catch (Exception e) { plugin.getLogger().warning("Failed during H2 conversion", e); } @@ -451,7 +450,7 @@ public void importIpBans() { plugin.getLogger().severe("Failed to import ip ban " + data.getIp()); } } - } catch (IOException e) { + } catch (Exception e) { plugin.getLogger().warning("Failed during H2 conversion", e); } @@ -470,7 +469,7 @@ public void importIpBans() { plugin.getLogger().severe("Failed to import ip ban record " + data.getId()); } } - } catch (IOException e) { + } catch (Exception e) { plugin.getLogger().warning("Failed during H2 conversion", e); } @@ -491,7 +490,7 @@ public void importIpMutes() { plugin.getLogger().severe("Failed to import ip mute " + data.getIp()); } } - } catch (IOException e) { + } catch (Exception e) { plugin.getLogger().warning("Failed during H2 conversion", e); } @@ -510,7 +509,7 @@ public void importIpMutes() { plugin.getLogger().severe("Failed to import ip mute record " + data.getId()); } } - } catch (IOException e) { + } catch (Exception e) { plugin.getLogger().warning("Failed during H2 conversion", e); } @@ -532,7 +531,7 @@ public void importIpRangeBans() { plugin.getLogger().severe("Failed to import ip range ban " + data.getFromIp() + " - " + data.getToIp()); } } - } catch (IOException e) { + } catch (Exception e) { plugin.getLogger().warning("Failed during H2 conversion", e); } @@ -551,7 +550,7 @@ public void importIpRangeBans() { plugin.getLogger().severe("Failed to import ip range ban record " + data.getId()); } } - } catch (IOException e) { + } catch (Exception e) { plugin.getLogger().warning("Failed during H2 conversion", e); } @@ -572,7 +571,7 @@ public void importNameBans() { plugin.getLogger().severe("Failed to import name ban " + data.getName()); } } - } catch (IOException e) { + } catch (Exception e) { plugin.getLogger().warning("Failed during H2 conversion", e); } @@ -591,7 +590,7 @@ public void importNameBans() { plugin.getLogger().severe("Failed to import name ban record " + data.getId()); } } - } catch (IOException e) { + } catch (Exception e) { plugin.getLogger().warning("Failed during H2 conversion", e); } diff --git a/common/src/main/java/me/confuser/banmanager/common/storage/mariadb/MariaDBDatabase.java b/common/src/main/java/me/confuser/banmanager/common/storage/mariadb/MariaDBDatabase.java index afe75ccca..50451cc7b 100644 --- a/common/src/main/java/me/confuser/banmanager/common/storage/mariadb/MariaDBDatabase.java +++ b/common/src/main/java/me/confuser/banmanager/common/storage/mariadb/MariaDBDatabase.java @@ -1,6 +1,6 @@ package me.confuser.banmanager.common.storage.mariadb; -import me.confuser.banmanager.common.ormlite.db.MariaDbDatabaseType; +import me.confuser.banmanager.common.ormlite.jdbc.db.MariaDbDatabaseType; public class MariaDBDatabase extends MariaDbDatabaseType { diff --git a/common/src/main/java/me/confuser/banmanager/common/storage/migration/MigrationRunner.java b/common/src/main/java/me/confuser/banmanager/common/storage/migration/MigrationRunner.java index 6b358fda0..ac2937a03 100644 --- a/common/src/main/java/me/confuser/banmanager/common/storage/migration/MigrationRunner.java +++ b/common/src/main/java/me/confuser/banmanager/common/storage/migration/MigrationRunner.java @@ -9,6 +9,7 @@ import me.confuser.banmanager.common.ormlite.support.DatabaseConnection; import me.confuser.banmanager.common.ormlite.support.DatabaseResults; import me.confuser.banmanager.common.ormlite.table.TableUtils; +import me.confuser.banmanager.common.util.StorageUtils; import java.io.BufferedReader; import java.io.IOException; @@ -60,7 +61,7 @@ public void migrate() throws SQLException { String detectionTableName = dbConfig.getTable(detectionTableKey).getTableName(); - int latestVersion = migrations.get(migrations.size() - 1).version; + int latestVersion = migrations.get(migrations.size() - 1).version(); boolean isH2 = dbConfig.getStorageType().equals("h2"); DatabaseConnection conn = connectionSource.getReadWriteConnection(""); @@ -88,18 +89,18 @@ public void migrate() throws SQLException { int applied = 0; for (MigrationFile migration : migrations) { - if (migration.version <= currentVersion) { + if (migration.version() <= currentVersion) { continue; } - plugin.getLogger().info("[Migration:" + instanceScope + "] Applying V" + migration.version + " " + migration.description); - String sql = loadSqlFile(migration.filename); + plugin.getLogger().info("[Migration:" + instanceScope + "] Applying V" + migration.version() + " " + migration.description()); + String sql = loadSqlFile(migration.filename()); if (sql.isEmpty()) { - throw new SQLException("[Migration:" + instanceScope + "] Migration file not found or empty: " + migration.filename); + throw new SQLException("[Migration:" + instanceScope + "] Migration file not found or empty: " + migration.filename()); } sql = substitutePlaceholders(sql); - executeMigrationStatements(conn, sql, migration.lenient); - insertVersion(conn, migration.version, migration.description); + executeMigrationStatements(conn, sql, migration.lenient()); + insertVersion(conn, migration.version(), migration.description()); applied++; } @@ -117,21 +118,16 @@ public void migrate() throws SQLException { } private void acquireAdvisoryLock(DatabaseConnection conn) throws SQLException { - CompiledStatement stmt = conn.compileStatement( + try (CompiledStatement stmt = conn.compileStatement( "SELECT GET_LOCK('bm_migration_" + instanceScope + "', 30)", StatementBuilder.StatementType.SELECT, null, DatabaseConnection.DEFAULT_RESULT_FLAGS, false); - try { - DatabaseResults results = stmt.runQuery(null); - try { - if (!results.next() || results.getInt(0) != 1) { - throw new SQLException("[Migration:" + instanceScope + "] Could not acquire advisory lock (another server may be migrating)"); - } - } finally { - closeQuietly(results); + DatabaseResults results = stmt.runQuery(null)) { + if (!results.next() || results.getInt(0) != 1) { + throw new SQLException("[Migration:" + instanceScope + "] Could not acquire advisory lock (another server may be migrating)"); } - } finally { - closeQuietly(stmt); + } catch (Exception e) { + throw StorageUtils.toSqlException("[Migration:" + instanceScope + "] Failed acquiring advisory lock", e); } } @@ -178,7 +174,7 @@ private List loadManifest() { plugin.getLogger().warning("[Migration:" + instanceScope + "] Failed to read manifest: " + e.getMessage()); } - migrations.sort(Comparator.comparingInt(m -> m.version)); + migrations.sort(Comparator.comparingInt(MigrationFile::version)); return migrations; } @@ -193,26 +189,26 @@ private boolean tableExists(DatabaseConnection conn, String tableName) { } private int getCurrentVersion(DatabaseConnection conn) throws SQLException { - try { - CompiledStatement stmt = conn.compileStatement( - "SELECT COALESCE(MAX(version), 0) FROM " + SCHEMA_TABLE + " WHERE scope = ?", - StatementBuilder.StatementType.SELECT, null, - DatabaseConnection.DEFAULT_RESULT_FLAGS, false); - try { - stmt.setObject(0, instanceScope, SqlType.STRING); - DatabaseResults results = stmt.runQuery(null); - try { - if (results.next()) { - return results.getInt(0); - } - } finally { - closeQuietly(results); + try (CompiledStatement stmt = conn.compileStatement( + "SELECT COALESCE(MAX(version), 0) FROM " + SCHEMA_TABLE + " WHERE scope = ?", + StatementBuilder.StatementType.SELECT, null, + DatabaseConnection.DEFAULT_RESULT_FLAGS, false)) { + stmt.setObject(0, instanceScope, SqlType.STRING); + try (DatabaseResults results = stmt.runQuery(null)) { + if (results.next()) { + return results.getInt(0); } - } finally { - closeQuietly(stmt); } } catch (SQLException e) { - // Table may not exist yet + // The schema_version table is created above via TableUtils.createTableIfNotExists, + // so an SQLException here usually indicates a real failure (permissions, deadlock, + // wrong scope value, etc.) rather than a missing table. Log it so the operator can + // diagnose, then fall through to 0 so we re-baseline rather than silently no-op. + plugin.getLogger().warning("[Migration:" + instanceScope + + "] Failed to read schema_version, treating as baseline V0", e); + } catch (Exception e) { + plugin.getLogger().warning("[Migration:" + instanceScope + + "] Unexpected error reading schema_version, treating as baseline V0", e); } return 0; } @@ -226,8 +222,7 @@ private String loadSqlFile(String filename) { return ""; } - byte[] bytes = readAllBytes(is); - return new String(bytes, StandardCharsets.UTF_8); + return new String(is.readAllBytes(), StandardCharsets.UTF_8); } catch (IOException e) { plugin.getLogger().warning("[Migration:" + instanceScope + "] Failed to read SQL file: " + path); return ""; @@ -334,67 +329,20 @@ static List splitStatements(String sql) { private void insertVersion(DatabaseConnection conn, int version, String description) throws SQLException { long appliedAt = System.currentTimeMillis() / 1000L; - CompiledStatement stmt = conn.compileStatement( + try (CompiledStatement stmt = conn.compileStatement( "INSERT INTO " + SCHEMA_TABLE + " (version, description, appliedAt, scope) VALUES (?, ?, ?, ?)", StatementBuilder.StatementType.UPDATE, null, - DatabaseConnection.DEFAULT_RESULT_FLAGS, false); - try { + DatabaseConnection.DEFAULT_RESULT_FLAGS, false)) { stmt.setObject(0, version, SqlType.INTEGER); stmt.setObject(1, description, SqlType.STRING); stmt.setObject(2, appliedAt, SqlType.LONG); stmt.setObject(3, instanceScope, SqlType.STRING); stmt.runUpdate(); - } finally { - closeQuietly(stmt); + } catch (Exception e) { + throw StorageUtils.toSqlException("[Migration:" + instanceScope + "] Failed inserting schema version", e); } } - private static void closeQuietly(CompiledStatement stmt) { - if (stmt != null) { - try { stmt.close(); } catch (IOException ignored) { } - } - } - - private static void closeQuietly(DatabaseResults results) { - if (results != null) { - try { results.close(); } catch (IOException ignored) { } - } - } - - private static byte[] readAllBytes(InputStream is) throws IOException { - byte[] buffer = new byte[4096]; - int bytesRead; - List chunks = new ArrayList<>(); - int totalLen = 0; - - while ((bytesRead = is.read(buffer)) != -1) { - byte[] chunk = new byte[bytesRead]; - System.arraycopy(buffer, 0, chunk, 0, bytesRead); - chunks.add(chunk); - totalLen += bytesRead; - } - - byte[] result = new byte[totalLen]; - int offset = 0; - for (byte[] chunk : chunks) { - System.arraycopy(chunk, 0, result, offset, chunk.length); - offset += chunk.length; - } - - return result; - } - - static class MigrationFile { - final String filename; - final int version; - final String description; - final boolean lenient; - - MigrationFile(String filename, int version, String description, boolean lenient) { - this.filename = filename; - this.version = version; - this.description = description; - this.lenient = lenient; - } + record MigrationFile(String filename, int version, String description, boolean lenient) { } } diff --git a/common/src/main/java/me/confuser/banmanager/common/storage/mysql/MySQLDatabase.java b/common/src/main/java/me/confuser/banmanager/common/storage/mysql/MySQLDatabase.java index 968092e9f..0e46da9b5 100644 --- a/common/src/main/java/me/confuser/banmanager/common/storage/mysql/MySQLDatabase.java +++ b/common/src/main/java/me/confuser/banmanager/common/storage/mysql/MySQLDatabase.java @@ -1,6 +1,6 @@ package me.confuser.banmanager.common.storage.mysql; -import me.confuser.banmanager.common.ormlite.db.MysqlDatabaseType; +import me.confuser.banmanager.common.ormlite.jdbc.db.MysqlDatabaseType; public class MySQLDatabase extends MysqlDatabaseType { @@ -11,7 +11,7 @@ public MySQLDatabase() { } @Override - protected String getDriverClassName() { - return DRIVER_CLASS_NAME; + protected String[] getDriverClassNames() { + return new String[] { DRIVER_CLASS_NAME }; } } diff --git a/common/src/main/java/me/confuser/banmanager/common/util/DateUtils.java b/common/src/main/java/me/confuser/banmanager/common/util/DateUtils.java index 7a3e32911..20321fa9b 100644 --- a/common/src/main/java/me/confuser/banmanager/common/util/DateUtils.java +++ b/common/src/main/java/me/confuser/banmanager/common/util/DateUtils.java @@ -11,7 +11,7 @@ public class DateUtils { - private static final List times = Arrays.asList( + private static final List times = List.of( Calendar.YEAR, Calendar.MONTH, Calendar.WEEK_OF_MONTH, @@ -19,10 +19,10 @@ public class DateUtils { Calendar.HOUR_OF_DAY, Calendar.MINUTE, Calendar.SECOND); - private static final List timesString = Arrays - .asList("year", "month", "week", "day", "hour", "minute", "second"); - private static final List shortTimesString = Arrays - .asList("y", "mo", "w", "d", "h", "m", "s"); + private static final List timesString = List.of( + "year", "month", "week", "day", "hour", "minute", "second"); + private static final List shortTimesString = List.of( + "y", "mo", "w", "d", "h", "m", "s"); @Getter private static long timeDiff = 0; private static Pattern timePattern = Pattern diff --git a/common/src/main/java/me/confuser/banmanager/common/util/MessageRegistry.java b/common/src/main/java/me/confuser/banmanager/common/util/MessageRegistry.java index 341946a65..bfc32fe15 100644 --- a/common/src/main/java/me/confuser/banmanager/common/util/MessageRegistry.java +++ b/common/src/main/java/me/confuser/banmanager/common/util/MessageRegistry.java @@ -4,14 +4,7 @@ public class MessageRegistry { - private static final class Snapshot { - final String defaultLocale; - final Map> locales; - - Snapshot(String defaultLocale, Map> locales) { - this.defaultLocale = defaultLocale; - this.locales = locales; - } + private record Snapshot(String defaultLocale, Map> locales) { } private volatile Snapshot snapshot; @@ -26,33 +19,33 @@ public static String normaliseLocale(String locale) { } public String getDefaultLocale() { - return snapshot.defaultLocale; + return snapshot.defaultLocale(); } public void loadLocale(String locale, Map messages) { String normalised = normaliseLocale(locale); Snapshot s = this.snapshot; - Map> copy = new HashMap<>(s.locales); - copy.put(normalised, Collections.unmodifiableMap(new HashMap<>(messages))); - this.snapshot = new Snapshot(s.defaultLocale, copy); + Map> copy = new HashMap<>(s.locales()); + copy.put(normalised, Map.copyOf(messages)); + this.snapshot = new Snapshot(s.defaultLocale(), copy); } public String getMessage(String key, String locale) { String normalised = normaliseLocale(locale); Snapshot s = this.snapshot; - String value = getFromLocale(s.locales, key, normalised); + String value = getFromLocale(s.locales(), key, normalised); if (value != null) return value; int underscore = normalised.indexOf('_'); if (underscore > 0) { String baseLanguage = normalised.substring(0, underscore); - value = getFromLocale(s.locales, key, baseLanguage); + value = getFromLocale(s.locales(), key, baseLanguage); if (value != null) return value; } - if (!normalised.equals(s.defaultLocale)) { - value = getFromLocale(s.locales, key, s.defaultLocale); + if (!normalised.equals(s.defaultLocale())) { + value = getFromLocale(s.locales(), key, s.defaultLocale()); if (value != null) return value; } @@ -60,17 +53,17 @@ public String getMessage(String key, String locale) { } public String getMessage(String key) { - return getMessage(key, snapshot.defaultLocale); + return getMessage(key, snapshot.defaultLocale()); } public void putMessage(String key, String message) { - putMessage(key, message, snapshot.defaultLocale); + putMessage(key, message, snapshot.defaultLocale()); } public void putMessage(String key, String message, String locale) { String normalised = normaliseLocale(locale); Snapshot s = this.snapshot; - Map> copy = new HashMap<>(s.locales); + Map> copy = new HashMap<>(s.locales()); Map localeMessages = copy.get(normalised); if (localeMessages == null) { @@ -80,29 +73,29 @@ public void putMessage(String key, String message, String locale) { } localeMessages.put(key, message); - copy.put(normalised, Collections.unmodifiableMap(localeMessages)); - this.snapshot = new Snapshot(s.defaultLocale, copy); + copy.put(normalised, Map.copyOf(localeMessages)); + this.snapshot = new Snapshot(s.defaultLocale(), copy); } public Set getAvailableLocales() { - return Collections.unmodifiableSet(new HashSet<>(snapshot.locales.keySet())); + return Set.copyOf(snapshot.locales().keySet()); } public Set getKeys(String locale) { - Map localeMessages = snapshot.locales.get(normaliseLocale(locale)); + Map localeMessages = snapshot.locales().get(normaliseLocale(locale)); if (localeMessages == null) return Collections.emptySet(); return Collections.unmodifiableSet(localeMessages.keySet()); } public Map getMessages(String locale) { - Map localeMessages = snapshot.locales.get(normaliseLocale(locale)); + Map localeMessages = snapshot.locales().get(normaliseLocale(locale)); if (localeMessages == null) return Collections.emptyMap(); return localeMessages; } public boolean hasAnyMessages() { Snapshot s = this.snapshot; - for (Map msgs : s.locales.values()) { + for (Map msgs : s.locales().values()) { if (!msgs.isEmpty()) return true; } return false; @@ -110,7 +103,7 @@ public boolean hasAnyMessages() { public int getMissingKeyCount(String locale) { Snapshot s = this.snapshot; - Set defaultKeys = getKeysFromSnapshot(s, s.defaultLocale); + Set defaultKeys = getKeysFromSnapshot(s, s.defaultLocale()); Set localeKeys = getKeysFromSnapshot(s, normaliseLocale(locale)); int missing = 0; @@ -126,7 +119,7 @@ public void atomicSwap(MessageRegistry newRegistry) { } private static Set getKeysFromSnapshot(Snapshot s, String locale) { - Map messages = s.locales.get(locale); + Map messages = s.locales().get(locale); if (messages == null) return Collections.emptySet(); return messages.keySet(); } diff --git a/common/src/main/java/me/confuser/banmanager/common/util/StorageUtils.java b/common/src/main/java/me/confuser/banmanager/common/util/StorageUtils.java index 7b42786cb..c14a8fbcc 100644 --- a/common/src/main/java/me/confuser/banmanager/common/util/StorageUtils.java +++ b/common/src/main/java/me/confuser/banmanager/common/util/StorageUtils.java @@ -8,11 +8,31 @@ import me.confuser.banmanager.common.ormlite.support.ConnectionSource; import me.confuser.banmanager.common.ormlite.support.DatabaseConnection; -import java.io.IOException; import java.sql.SQLException; public class StorageUtils { + /** + * Wraps an arbitrary exception as a {@link SQLException} for callers that + * declare {@code throws SQLException}. Returns the original instance when + * already a SQLException so the original message and stack trace are + * preserved; otherwise wraps with the supplied context message. + * + *

Callers should use the {@code throw} keyword:

+ *
+   *   try (CompiledStatement stmt = ...) { ... }
+   *   catch (Exception e) { throw StorageUtils.toSqlException("Context", e); }
+   * 
+ * + *

This pattern exists because ORMLite 6.x's {@code AutoCloseable} + * declarations throw the broader {@code Exception}, forcing every + * try-with-resources around a statement to also catch {@code Exception}.

+ */ + public static SQLException toSqlException(String message, Exception e) { + if (e instanceof SQLException sqle) return sqle; + return new SQLException(message, e); + } + /** * Updates the created and updated timestamps to database time using the DAO. * This ensures timestamps are always from the database, not the JVM, for cross-server sync. @@ -54,8 +74,8 @@ public static void updateTimestampsToDbTime(ConnectionSource connectionSource, D null, DatabaseConnection.DEFAULT_RESULT_FLAGS, false); statement.setObject(0, id, SqlType.INTEGER); statement.runUpdate(); - } catch (IOException e) { - throw new SQLException("Failed to update timestamps", e); + } catch (Exception e) { + throw toSqlException("Failed to update timestamps", e); } } diff --git a/common/src/main/java/me/confuser/banmanager/common/util/UUIDUtils.java b/common/src/main/java/me/confuser/banmanager/common/util/UUIDUtils.java index 0a2cdaa13..e54221ff7 100644 --- a/common/src/main/java/me/confuser/banmanager/common/util/UUIDUtils.java +++ b/common/src/main/java/me/confuser/banmanager/common/util/UUIDUtils.java @@ -1,10 +1,12 @@ package me.confuser.banmanager.common.util; -import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; -import java.net.HttpURLConnection; -import java.net.URL; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.time.Duration; import java.util.UUID; import me.confuser.banmanager.common.BanManagerPlugin; @@ -16,16 +18,14 @@ * Based on UUIDFetcher by evilmidget38 */ public class UUIDUtils { - private static HttpURLConnection createConnection(String urlStr, String method) throws Exception { - URL url = new URL(urlStr); - HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - connection.setRequestMethod(method); - connection.setRequestProperty("Content-Type", "application/json"); - connection.setUseCaches(false); - connection.setDoInput(true); - - if (method.equals("POST")) connection.setDoOutput(true); - return connection; + private static HttpRequest buildGet(String urlStr) { + return HttpRequest.newBuilder() + .uri(URI.create(urlStr)) + .timeout(Duration.ofSeconds(15)) + .header("Content-Type", "application/json") + .header("User-Agent", "BanManager") + .GET() + .build(); } private static UUID getUUID(String id) { @@ -58,55 +58,41 @@ public static UUIDProfile getUUIDOf(BanManagerPlugin plugin, String name) throws } plugin.getLogger().info("Requesting UUID for " + name); - Fetcher fetcher = plugin.getConfig().getUuidFetcher().getNameToId(); - String url = fetcher.getUrl().replace("[name]", name); + Fetcher fetcher = plugin.getConfig().getUuidFetcher().nameToId(); + String url = fetcher.url().replace("[name]", name); - HttpURLConnection connection = createConnection(url, "GET"); + HttpResponse response = sendBlocking(plugin.getHttpClient(), buildGet(url)); + int status = response.statusCode(); - try { - int status = connection.getResponseCode(); + plugin.getLogger().info(url + " " + status); - plugin.getLogger().info(url + " " + status); + if (status != 200) throw new Exception("Error retrieving UUID from " + url); - if (status != 200) throw new Exception("Error retrieving UUID from " + url); - - try (InputStreamReader reader = new InputStreamReader(connection.getInputStream())) { - JsonObject data = new Gson().fromJson(reader, JsonObject.class); - return new UUIDProfile(name, UUIDUtils.getUUID(data.get(fetcher.getKey()).getAsString())); - } - } finally { - connection.disconnect(); - } + JsonObject data = new Gson().fromJson(response.body(), JsonObject.class); + return new UUIDProfile(name, UUIDUtils.getUUID(data.get(fetcher.key()).getAsString())); } public static String getCurrentName(BanManagerPlugin plugin, UUID uuid) throws Exception { plugin.getLogger().info("Requesting name for " + uuid.toString()); - Fetcher fetcher = plugin.getConfig().getUuidFetcher().getIdToName(); - String url = fetcher.getUrl().replace("[uuid]", uuid.toString()); + Fetcher fetcher = plugin.getConfig().getUuidFetcher().idToName(); + String url = fetcher.url().replace("[uuid]", uuid.toString()); - HttpURLConnection connection = createConnection(url, "GET"); + HttpResponse response = sendBlocking(plugin.getHttpClient(), buildGet(url)); + int status = response.statusCode(); - try { - int status = connection.getResponseCode(); + plugin.getLogger().info(url + " " + status); - plugin.getLogger().info(url + " " + status); + if (status != 200) throw new Exception("Error retrieving name from " + url); - if (status != 200) throw new Exception("Error retrieving name from " + url); - - try (InputStreamReader reader = new InputStreamReader(connection.getInputStream())) { - JsonObject data = new Gson().fromJson(reader, JsonObject.class); - return data.get(fetcher.getKey()).getAsString(); - } - } finally { - connection.disconnect(); - } + JsonObject data = new Gson().fromJson(response.body(), JsonObject.class); + return data.get(fetcher.key()).getAsString(); } public static UUID createOfflineUUID(String name) { - try { - return UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes("UTF-8")); - } catch (UnsupportedEncodingException e) { - return null; - } + return UUID.nameUUIDFromBytes(("OfflinePlayer:" + name).getBytes(StandardCharsets.UTF_8)); + } + + private static HttpResponse sendBlocking(HttpClient client, HttpRequest request) throws Exception { + return client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); } } diff --git a/common/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider b/common/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider new file mode 100644 index 000000000..0d1832d1c --- /dev/null +++ b/common/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider @@ -0,0 +1 @@ +org.slf4j.impl.BanManagerSlf4jServiceProvider diff --git a/common/src/test/java/me/confuser/banmanager/common/BasePluginDbTest.java b/common/src/test/java/me/confuser/banmanager/common/BasePluginDbTest.java index 66a43460d..8caec5bd2 100644 --- a/common/src/test/java/me/confuser/banmanager/common/BasePluginDbTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/BasePluginDbTest.java @@ -2,9 +2,14 @@ import ch.vorburger.exec.ManagedProcessException; import ch.vorburger.mariadb4j.DB; -import com.github.javafaker.Faker; -import org.junit.*; -import org.junit.rules.TemporaryFolder; +import me.confuser.banmanager.common.commands.CommonCommand; +import net.datafaker.Faker; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.io.TempDir; import java.io.File; import java.nio.charset.StandardCharsets; @@ -15,8 +20,8 @@ import static org.mockito.Mockito.*; public abstract class BasePluginDbTest { - @Rule - public TemporaryFolder temporaryFolder = new TemporaryFolder(); + @TempDir + public File temporaryFolder; protected static String storageType = System.getenv("STORAGE_TYPE") != null ? System.getenv("STORAGE_TYPE") : "h2"; protected BanManagerPlugin plugin; protected Faker faker = new Faker(); @@ -25,7 +30,7 @@ public abstract class BasePluginDbTest { private static boolean configSetup = false; private static DB db; - @BeforeClass + @BeforeAll public static void dbSetup() throws ManagedProcessException { if (storageType.equals("mariadb")) { db = DB.newEmbeddedDB(0); @@ -33,10 +38,10 @@ public static void dbSetup() throws ManagedProcessException { } } - @Before + @BeforeEach public void setup() throws Exception { CommonLogger logger = new TestLogger(); - plugin = new BanManagerPlugin(BasePluginTest.setupConfigs(temporaryFolder), logger, temporaryFolder.getRoot(), new TestScheduler(), server, new TestMetrics()); + plugin = new BanManagerPlugin(BasePluginTest.setupConfigs(temporaryFolder), logger, temporaryFolder, new TestScheduler(), server, new TestMetrics()); testUtils = new TestUtils(plugin, faker); server.enable(plugin); @@ -47,13 +52,11 @@ public void setup() throws Exception { } catch (Exception e) { } - // Clean up any resources from the initial enable before reconfiguring plugin.disable(); setupConfig(); - // Recreate plugin with updated config - plugin = new BanManagerPlugin(BasePluginTest.setupConfigs(temporaryFolder), logger, temporaryFolder.getRoot(), new TestScheduler(), server, new TestMetrics()); + plugin = new BanManagerPlugin(BasePluginTest.setupConfigs(temporaryFolder), logger, temporaryFolder, new TestScheduler(), server, new TestMetrics()); testUtils = new TestUtils(plugin, faker); server.enable(plugin); } @@ -61,16 +64,16 @@ public void setup() throws Exception { plugin.enable(); } - @After + @AfterEach public void cleanup() { if (plugin != null) { plugin.disable(); } } - @AfterClass + @AfterAll public static void teardown() { - configSetup = false; // Reset for next test class + configSetup = false; if (db == null) return; @@ -81,8 +84,28 @@ public static void teardown() { } } + /** + * Locates a registered command by name and returns it cast to the requested + * type, or skips the calling test (via {@link Assumptions#assumeTrue}) if + * the command is not registered on this platform/profile. + * + *

Use from a {@code @BeforeEach} setup method so the assumption short- + * circuits the entire test class rather than each test method having to + * repeat the check.

+ */ + @SuppressWarnings("unchecked") + protected T requireCommand(String name) { + for (CommonCommand command : plugin.getCommands()) { + if (command.getCommandName().equals(name)) { + return (T) command; + } + } + Assumptions.assumeTrue(false, name + " command is not registered, skipping test class"); + return null; + } + private void setupConfig() throws Exception { - Path configFile = new File(temporaryFolder.getRoot(), "config.yml").toPath(); + Path configFile = new File(temporaryFolder, "config.yml").toPath(); List lines = Files.readAllLines(configFile, StandardCharsets.UTF_8); lines.set(5, " storageType: " + storageType); lines.set(6, " host: localhost"); diff --git a/common/src/test/java/me/confuser/banmanager/common/BasePluginTest.java b/common/src/test/java/me/confuser/banmanager/common/BasePluginTest.java index 720924bc2..d3d17ff4e 100644 --- a/common/src/test/java/me/confuser/banmanager/common/BasePluginTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/BasePluginTest.java @@ -3,22 +3,21 @@ import me.confuser.banmanager.common.configs.PluginInfo; import me.confuser.banmanager.common.configuration.ConfigurationSection; import me.confuser.banmanager.common.configuration.file.YamlConfiguration; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.io.TempDir; import java.io.*; public abstract class BasePluginTest { - @Rule - public TemporaryFolder temporaryFolder = new TemporaryFolder(); + @TempDir + public File temporaryFolder; protected BanManagerPlugin plugin; - @Before + @BeforeEach public void setup() { CommonLogger logger = new TestLogger(); - plugin = new BanManagerPlugin(setupConfigs(temporaryFolder), logger, temporaryFolder.getRoot(), new TestScheduler(), new TestServer(), new TestMetrics()); + plugin = new BanManagerPlugin(setupConfigs(temporaryFolder), logger, temporaryFolder, new TestScheduler(), new TestServer(), new TestMetrics()); try { plugin.enable(); @@ -26,14 +25,14 @@ public void setup() { } } - @After + @AfterEach public void cleanup() { if (plugin != null) { plugin.disable(); } } - public static PluginInfo setupConfigs(TemporaryFolder folder) { + public static PluginInfo setupConfigs(File folder) { String[] configs = new String[]{ "config.yml", "console.yml", @@ -47,7 +46,7 @@ public static PluginInfo setupConfigs(TemporaryFolder folder) { for (String name : configs) { try (InputStream in = BasePluginTest.class.getClassLoader().getResource(name).openStream(); - OutputStream out = new FileOutputStream(new File(folder.getRoot(), name))) { + OutputStream out = new FileOutputStream(new File(folder, name))) { byte[] buf = new byte[1024]; int len; while ((len = in.read(buf)) > 0) { @@ -58,7 +57,7 @@ public static PluginInfo setupConfigs(TemporaryFolder folder) { } } - File messagesDir = new File(folder.getRoot(), "messages"); + File messagesDir = new File(folder, "messages"); messagesDir.mkdirs(); try (InputStream in = BasePluginTest.class.getClassLoader().getResource("messages/messages_en.yml").openStream(); OutputStream out = new FileOutputStream(new File(messagesDir, "messages_en.yml"))) { @@ -71,7 +70,6 @@ public static PluginInfo setupConfigs(TemporaryFolder folder) { e.printStackTrace(); } - // Load plugin.yml PluginInfo pluginInfo = new PluginInfo(); try (InputStream in = BasePluginTest.class.getClassLoader().getResource("plugin.yml").openStream(); Reader defConfigStream = new InputStreamReader(in)) { diff --git a/common/src/test/java/me/confuser/banmanager/common/CommonLoggerTest.java b/common/src/test/java/me/confuser/banmanager/common/CommonLoggerTest.java index 851540f80..ab59e70d9 100644 --- a/common/src/test/java/me/confuser/banmanager/common/CommonLoggerTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/CommonLoggerTest.java @@ -1,13 +1,13 @@ package me.confuser.banmanager.common; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class CommonLoggerTest { @@ -72,7 +72,7 @@ public void defaultMethodsShouldNotWriteToStderr() { logger.severe("test", new RuntimeException("err")); logger.warning("test", new RuntimeException("err")); - assertEquals("No output should go to stderr", 0, baos.size()); + assertEquals(0, baos.size(), "No output should go to stderr"); } finally { System.setErr(originalErr); } diff --git a/common/src/test/java/me/confuser/banmanager/common/PluginTest.java b/common/src/test/java/me/confuser/banmanager/common/PluginTest.java index c0700dfb9..ee84db845 100644 --- a/common/src/test/java/me/confuser/banmanager/common/PluginTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/PluginTest.java @@ -1,8 +1,8 @@ package me.confuser.banmanager.common; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class PluginTest extends BasePluginTest { @Test diff --git a/common/src/test/java/me/confuser/banmanager/common/StderrRegressionTest.java b/common/src/test/java/me/confuser/banmanager/common/StderrRegressionTest.java index b9356f1d5..2f1a88174 100644 --- a/common/src/test/java/me/confuser/banmanager/common/StderrRegressionTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/StderrRegressionTest.java @@ -1,13 +1,13 @@ package me.confuser.banmanager.common; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.io.ByteArrayOutputStream; import java.io.PrintStream; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * Verifies that plugin startup does not write to System.err via @@ -18,7 +18,7 @@ public class StderrRegressionTest extends BasePluginDbTest { private PrintStream originalErr; private ByteArrayOutputStream errCapture; - @Before + @BeforeEach @Override public void setup() throws Exception { originalErr = System.err; @@ -28,7 +28,7 @@ public void setup() throws Exception { super.setup(); } - @After + @AfterEach @Override public void cleanup() { super.cleanup(); @@ -56,7 +56,7 @@ public void pluginStartupShouldNotWriteToStderr() { offendingLines.append(line).append("\n"); } } - assertEquals("No e.printStackTrace() output should appear during startup:\n" + offendingLines, 0, offendingLines.length()); + assertEquals(0, offendingLines.length(), "No e.printStackTrace() output should appear during startup:\n" + offendingLines); } } } diff --git a/common/src/test/java/me/confuser/banmanager/common/TestUtils.java b/common/src/test/java/me/confuser/banmanager/common/TestUtils.java index d6f35b1a0..9f9c07b2e 100644 --- a/common/src/test/java/me/confuser/banmanager/common/TestUtils.java +++ b/common/src/test/java/me/confuser/banmanager/common/TestUtils.java @@ -1,6 +1,6 @@ package me.confuser.banmanager.common; -import com.github.javafaker.Faker; +import net.datafaker.Faker; import lombok.AllArgsConstructor; import me.confuser.banmanager.common.data.PlayerBanData; import me.confuser.banmanager.common.data.PlayerData; @@ -35,7 +35,7 @@ public PlayerData createPlayerWithName(String name) { } public String createRandomPlayerName() { - String name = faker.name().username(); + String name = faker.credentials().username(); return name.substring(0, Math.min(name.length(), 16)); } diff --git a/common/src/test/java/me/confuser/banmanager/common/commands/AddNoteCommandTest.java b/common/src/test/java/me/confuser/banmanager/common/commands/AddNoteCommandTest.java index bb8173bb8..a9b7d4dc0 100644 --- a/common/src/test/java/me/confuser/banmanager/common/commands/AddNoteCommandTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/commands/AddNoteCommandTest.java @@ -3,19 +3,19 @@ import me.confuser.banmanager.common.BasePluginDbTest; import me.confuser.banmanager.common.CommonServer; import me.confuser.banmanager.common.data.PlayerData; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.sql.SQLException; import static org.awaitility.Awaitility.await; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; public class AddNoteCommandTest extends BasePluginDbTest { private AddNoteCommand cmd; - @Before + @BeforeEach public void setupCmd() { for (CommonCommand cmd : plugin.getCommands()) { if (cmd.getCommandName().equals("addnote")) { @@ -46,7 +46,7 @@ public void shouldFailIfPlayerNotFound() { @Test public void shouldAddNoteToPlayer() throws SQLException { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{player.getName(), "test note message"}; @@ -59,7 +59,7 @@ public void shouldAddNoteToPlayer() throws SQLException { @Test public void shouldAddNoteSilently() throws SQLException { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{player.getName(), "silent note", "-s"}; diff --git a/common/src/test/java/me/confuser/banmanager/common/commands/BanCommandTest.java b/common/src/test/java/me/confuser/banmanager/common/commands/BanCommandTest.java index fdc717d01..87bf75981 100644 --- a/common/src/test/java/me/confuser/banmanager/common/commands/BanCommandTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/commands/BanCommandTest.java @@ -6,20 +6,20 @@ import me.confuser.banmanager.common.TestPlayer; import me.confuser.banmanager.common.data.PlayerBanData; import me.confuser.banmanager.common.data.PlayerData; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.sql.SQLException; import java.util.UUID; import static org.awaitility.Awaitility.await; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; public class BanCommandTest extends BasePluginDbTest { private BanCommand cmd; - @Before + @BeforeEach public void setupCmd() { for (CommonCommand cmd : plugin.getCommands()) { if (cmd.getCommandName().equals("ban")) { @@ -154,7 +154,7 @@ public void shouldAllowPartialWhenNoExactCollision() { @Test public void shouldFailIfExempt() { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); CommonPlayer commonPlayer = spy(new TestPlayer(player.getUUID(), player.getName(), true)); this.server.setExactMatch(player.getName(), commonPlayer); @@ -179,7 +179,7 @@ public void shouldFailIfNotFound() { @Test public void shouldFailIfOfflineExempt() { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); CommonPlayer commonPlayer = spy(new TestPlayer(player.getUUID(), player.getName(), true)); this.server.setExactMatch(player.getName(), commonPlayer); @@ -195,7 +195,7 @@ public void shouldFailIfOfflineExempt() { @Test public void shouldBanPlayer() { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{player.getName(), "test"}; @@ -213,7 +213,7 @@ public void shouldBanPlayer() { @Test public void shouldBanPlayerSilently() { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{player.getName(), "test", "-s"}; diff --git a/common/src/test/java/me/confuser/banmanager/common/commands/BanIpCommandTest.java b/common/src/test/java/me/confuser/banmanager/common/commands/BanIpCommandTest.java index 1e9a4ef9e..92c7e90b6 100644 --- a/common/src/test/java/me/confuser/banmanager/common/commands/BanIpCommandTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/commands/BanIpCommandTest.java @@ -8,20 +8,20 @@ import me.confuser.banmanager.common.data.PlayerData; import me.confuser.banmanager.common.ipaddr.IPAddress; import me.confuser.banmanager.common.util.IPUtils; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.sql.SQLException; import static org.awaitility.Awaitility.await; -import static org.junit.Assert.*; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.*; public class BanIpCommandTest extends BasePluginDbTest { private BanIpCommand cmd; - @Before + @BeforeEach public void setupCmd() { for (CommonCommand cmd : plugin.getCommands()) { if (cmd.getCommandName().equals("banip")) { @@ -76,7 +76,7 @@ public void shouldFailIfAlreadyBanned() throws SQLException { @Test public void shouldFailIfExempt() { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); CommonPlayer commonPlayer = spy(server.getPlayer(player.getName())); String[] args = new String[]{player.getName(), "test"}; @@ -100,7 +100,7 @@ public void shouldFailIfNotFound() { @Test public void shouldFailIfOfflineExempt() { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); ExemptionsConfig config = spy(plugin.getExemptionsConfig()); String[] args = new String[]{player.getName(), "test"}; @@ -115,7 +115,7 @@ public void shouldFailIfOfflineExempt() { @Test public void shouldBanIp() { IPAddress ip = IPUtils.toIPAddress(faker.internet().ipV6Address()); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{ip.toString(), "test"}; @@ -133,7 +133,7 @@ public void shouldBanIp() { @Test public void shouldBanIpSilently() { IPAddress ip = IPUtils.toIPAddress(faker.internet().ipV6Address()); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{ip.toString(), "test", "-s"}; diff --git a/common/src/test/java/me/confuser/banmanager/common/commands/BanIpRangeCommandTest.java b/common/src/test/java/me/confuser/banmanager/common/commands/BanIpRangeCommandTest.java index bd66dd42c..4e0e88330 100644 --- a/common/src/test/java/me/confuser/banmanager/common/commands/BanIpRangeCommandTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/commands/BanIpRangeCommandTest.java @@ -7,21 +7,21 @@ import me.confuser.banmanager.common.ipaddr.IPAddressSeqRange; import me.confuser.banmanager.common.ipaddr.IPAddressString; import me.confuser.banmanager.common.util.IPUtils; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.sql.SQLException; import static org.awaitility.Awaitility.await; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.mockito.Mockito.*; public class BanIpRangeCommandTest extends BasePluginDbTest { private BanIpRangeCommand cmd; - @Before + @BeforeEach public void setupCmd() { for (CommonCommand cmd : plugin.getCommands()) { if (cmd.getCommandName().equals("baniprange")) { @@ -85,7 +85,7 @@ public void shouldBanRange() { IPAddress expectedFromIp = range.getLower(); IPAddress expectedToIp = range.getUpper(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{cidr, "test"}; diff --git a/common/src/test/java/me/confuser/banmanager/common/commands/BanNameCommandTest.java b/common/src/test/java/me/confuser/banmanager/common/commands/BanNameCommandTest.java index 020b30eba..196c868b1 100755 --- a/common/src/test/java/me/confuser/banmanager/common/commands/BanNameCommandTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/commands/BanNameCommandTest.java @@ -4,19 +4,19 @@ import me.confuser.banmanager.common.CommonServer; import me.confuser.banmanager.common.data.NameBanData; import me.confuser.banmanager.common.data.PlayerData; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.sql.SQLException; import static org.awaitility.Awaitility.await; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; public class BanNameCommandTest extends BasePluginDbTest { private BanNameCommand cmd; - @Before + @BeforeEach public void setupCmd() { for (CommonCommand cmd : plugin.getCommands()) { if (cmd.getCommandName().equals("banname")) { @@ -62,7 +62,7 @@ public void shouldFailIfAlreadyBanned() throws SQLException { @Test public void shouldBanPlayerName() { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{player.getName(), "test"}; @@ -80,7 +80,7 @@ public void shouldBanPlayerName() { @Test public void shouldBanNameSilently() { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{player.getName(), "test", "-s"}; diff --git a/common/src/test/java/me/confuser/banmanager/common/commands/CommandParserTest.java b/common/src/test/java/me/confuser/banmanager/common/commands/CommandParserTest.java index 6e8a381ca..3415951cf 100644 --- a/common/src/test/java/me/confuser/banmanager/common/commands/CommandParserTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/commands/CommandParserTest.java @@ -1,9 +1,9 @@ package me.confuser.banmanager.common.commands; import me.confuser.banmanager.common.BasePluginTest; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class CommandParserTest extends BasePluginTest { diff --git a/common/src/test/java/me/confuser/banmanager/common/commands/InfoCommandTest.java b/common/src/test/java/me/confuser/banmanager/common/commands/InfoCommandTest.java index 972ed0b9d..0e4a4300e 100644 --- a/common/src/test/java/me/confuser/banmanager/common/commands/InfoCommandTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/commands/InfoCommandTest.java @@ -4,19 +4,19 @@ import me.confuser.banmanager.common.CommonServer; import me.confuser.banmanager.common.data.*; import me.confuser.banmanager.common.util.parsers.InfoCommandParser; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.sql.SQLException; import static org.awaitility.Awaitility.await; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; public class InfoCommandTest extends BasePluginDbTest { private InfoCommand cmd; - @Before + @BeforeEach public void setupCmd() { for (CommonCommand cmd : plugin.getCommands()) { if (cmd.getCommandName().equals("bminfo")) { @@ -29,7 +29,7 @@ public void setupCmd() { @Test public void shouldShowPlayerInfo() { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{player.getName()}; @@ -42,7 +42,7 @@ public void shouldShowPlayerInfo() { public void shouldShowBanHistory() throws SQLException { PlayerData player = testUtils.createRandomPlayer(); PlayerData actor = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); // Create a ban and unban to create history @@ -59,7 +59,7 @@ public void shouldShowBanHistory() throws SQLException { public void shouldShowMuteHistory() throws SQLException { PlayerData player = testUtils.createRandomPlayer(); PlayerData actor = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); // Create a mute and unmute to create history @@ -76,7 +76,7 @@ public void shouldShowMuteHistory() throws SQLException { public void shouldShowWarnings() throws SQLException { PlayerData player = testUtils.createRandomPlayer(); PlayerData actor = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); // Create a warning @@ -93,7 +93,7 @@ public void shouldShowWarnings() throws SQLException { public void shouldShowNotes() throws SQLException { PlayerData player = testUtils.createRandomPlayer(); PlayerData actor = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); // Create a note @@ -119,7 +119,7 @@ public void shouldFailIfPlayerNotFound() { @Test public void shouldShowKnownNames() throws SQLException { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); // Create a session record (which includes name) @@ -136,7 +136,7 @@ public void shouldShowKnownNames() throws SQLException { @Test public void shouldHideNamesWithoutPermission() throws SQLException { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); // Create a session record (which includes name) @@ -154,7 +154,7 @@ public void shouldHideNamesWithoutPermission() throws SQLException { public void shouldShowStatsWithMultipleRecordTypes() throws SQLException { PlayerData player = testUtils.createRandomPlayer(); PlayerData actor = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); PlayerBanData ban = testUtils.createBan(player, actor, "test ban"); diff --git a/common/src/test/java/me/confuser/banmanager/common/commands/KickAllCommandTest.java b/common/src/test/java/me/confuser/banmanager/common/commands/KickAllCommandTest.java index 3922ebfe6..b6836dddb 100644 --- a/common/src/test/java/me/confuser/banmanager/common/commands/KickAllCommandTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/commands/KickAllCommandTest.java @@ -4,17 +4,17 @@ import me.confuser.banmanager.common.CommonPlayer; import me.confuser.banmanager.common.data.PlayerData; import me.confuser.banmanager.common.util.Message; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; public class KickAllCommandTest extends BasePluginDbTest { private KickAllCommand cmd; - @Before + @BeforeEach public void setupCmd() { for (CommonCommand cmd : plugin.getCommands()) { if (cmd.getCommandName().equals("kickall")) { diff --git a/common/src/test/java/me/confuser/banmanager/common/commands/KickCommandTest.java b/common/src/test/java/me/confuser/banmanager/common/commands/KickCommandTest.java index d1222145b..f0cf5ff0a 100644 --- a/common/src/test/java/me/confuser/banmanager/common/commands/KickCommandTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/commands/KickCommandTest.java @@ -4,17 +4,17 @@ import me.confuser.banmanager.common.CommonPlayer; import me.confuser.banmanager.common.data.PlayerData; import me.confuser.banmanager.common.util.Message; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; public class KickCommandTest extends BasePluginDbTest { private KickCommand cmd; - @Before + @BeforeEach public void setupCmd() { for (CommonCommand cmd : plugin.getCommands()) { if (cmd.getCommandName().equals("kick")) { diff --git a/common/src/test/java/me/confuser/banmanager/common/commands/LoglessKickAllCommandTest.java b/common/src/test/java/me/confuser/banmanager/common/commands/LoglessKickAllCommandTest.java index 12bc88f54..a86391be5 100644 --- a/common/src/test/java/me/confuser/banmanager/common/commands/LoglessKickAllCommandTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/commands/LoglessKickAllCommandTest.java @@ -4,17 +4,17 @@ import me.confuser.banmanager.common.CommonPlayer; import me.confuser.banmanager.common.data.PlayerData; import me.confuser.banmanager.common.util.Message; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; public class LoglessKickAllCommandTest extends BasePluginDbTest { private LoglessKickAllCommand cmd; - @Before + @BeforeEach public void setupCmd() { for (CommonCommand cmd : plugin.getCommands()) { if (cmd.getCommandName().equals("nlkickall")) { diff --git a/common/src/test/java/me/confuser/banmanager/common/commands/MuteCommandTest.java b/common/src/test/java/me/confuser/banmanager/common/commands/MuteCommandTest.java index 496419ba7..e350aa59d 100755 --- a/common/src/test/java/me/confuser/banmanager/common/commands/MuteCommandTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/commands/MuteCommandTest.java @@ -6,20 +6,20 @@ import me.confuser.banmanager.common.TestPlayer; import me.confuser.banmanager.common.data.PlayerMuteData; import me.confuser.banmanager.common.data.PlayerData; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.sql.SQLException; import java.util.UUID; import static org.awaitility.Awaitility.await; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; public class MuteCommandTest extends BasePluginDbTest { private MuteCommand cmd; - @Before + @BeforeEach public void setupCmd() { for (CommonCommand cmd : plugin.getCommands()) { if (cmd.getCommandName().equals("mute")) { @@ -130,7 +130,7 @@ public void shouldAllowPartialWhenNoExactCollision() { @Test public void shouldFailIfExempt() { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); CommonPlayer commonPlayer = spy(new TestPlayer(player.getUUID(), player.getName(), true)); this.server.setExactMatch(player.getName(), commonPlayer); @@ -155,7 +155,7 @@ public void shouldFailIfNotFound() { @Test public void shouldFailIfOfflineExempt() { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); CommonPlayer commonPlayer = spy(new TestPlayer(player.getUUID(), player.getName(), true)); this.server.setExactMatch(player.getName(), commonPlayer); @@ -171,7 +171,7 @@ public void shouldFailIfOfflineExempt() { @Test public void shouldMutePlayer() { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{player.getName(), "test"}; @@ -189,7 +189,7 @@ public void shouldMutePlayer() { @Test public void shouldMutePlayerSoftly() { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{player.getName(), "test", "-st"}; @@ -208,7 +208,7 @@ public void shouldMutePlayerSoftly() { @Test public void shouldMutePlayerSilently() { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{player.getName(), "test", "-s"}; diff --git a/common/src/test/java/me/confuser/banmanager/common/commands/MuteIpCommandTest.java b/common/src/test/java/me/confuser/banmanager/common/commands/MuteIpCommandTest.java index ffec0bac0..ed1a158df 100755 --- a/common/src/test/java/me/confuser/banmanager/common/commands/MuteIpCommandTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/commands/MuteIpCommandTest.java @@ -8,19 +8,19 @@ import me.confuser.banmanager.common.data.PlayerData; import me.confuser.banmanager.common.ipaddr.IPAddress; import me.confuser.banmanager.common.util.IPUtils; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.sql.SQLException; import static org.awaitility.Awaitility.await; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; public class MuteIpCommandTest extends BasePluginDbTest { private MuteIpCommand cmd; - @Before + @BeforeEach public void setupCmd() { for (CommonCommand cmd : plugin.getCommands()) { if (cmd.getCommandName().equals("muteip")) { @@ -75,7 +75,7 @@ public void shouldFailIfAlreadyBanned() throws SQLException { @Test public void shouldFailIfExempt() { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); CommonPlayer commonPlayer = spy(server.getPlayer(player.getName())); String[] args = new String[]{player.getName(), "test"}; @@ -99,7 +99,7 @@ public void shouldFailIfNotFound() { @Test public void shouldFailIfOfflineExempt() { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); ExemptionsConfig config = spy(plugin.getExemptionsConfig()); String[] args = new String[]{player.getName(), "test"}; @@ -114,7 +114,7 @@ public void shouldFailIfOfflineExempt() { @Test public void shouldMuteIp() { IPAddress ip = IPUtils.toIPAddress(faker.internet().ipV6Address()); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{ip.toString(), "test"}; @@ -132,7 +132,7 @@ public void shouldMuteIp() { @Test public void shouldMuteIpSilently() { IPAddress ip = IPUtils.toIPAddress(faker.internet().ipV6Address()); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{ip.toString(), "test", "-s"}; @@ -146,7 +146,7 @@ public void shouldMuteIpSilently() { @Test public void shouldMuteIpSoftly() { IPAddress ip = IPUtils.toIPAddress(faker.internet().ipV6Address()); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{ip.toString(), "test", "-st"}; diff --git a/common/src/test/java/me/confuser/banmanager/common/commands/NamesCommandTest.java b/common/src/test/java/me/confuser/banmanager/common/commands/NamesCommandTest.java index a89c51eb7..b7daab3db 100644 --- a/common/src/test/java/me/confuser/banmanager/common/commands/NamesCommandTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/commands/NamesCommandTest.java @@ -2,8 +2,8 @@ import me.confuser.banmanager.common.BasePluginDbTest; import me.confuser.banmanager.common.data.PlayerData; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.sql.SQLException; @@ -13,7 +13,7 @@ public class NamesCommandTest extends BasePluginDbTest { private NamesCommand cmd; - @Before + @BeforeEach public void setupCmd() { for (CommonCommand cmd : plugin.getCommands()) { if (cmd.getCommandName().equals("bmnames")) { diff --git a/common/src/test/java/me/confuser/banmanager/common/commands/ReportCommandTest.java b/common/src/test/java/me/confuser/banmanager/common/commands/ReportCommandTest.java index d8ee7dfca..0e4df47f0 100644 --- a/common/src/test/java/me/confuser/banmanager/common/commands/ReportCommandTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/commands/ReportCommandTest.java @@ -4,35 +4,28 @@ import me.confuser.banmanager.common.CommonServer; import me.confuser.banmanager.common.TestPlayer; import me.confuser.banmanager.common.data.PlayerData; -import org.junit.Assume; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.sql.SQLException; import java.util.UUID; import static org.awaitility.Awaitility.await; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; public class ReportCommandTest extends BasePluginDbTest { private ReportCommand cmd; - @Before + @BeforeEach public void setupCmd() { - for (CommonCommand command : plugin.getCommands()) { - if (command.getCommandName().equals("report")) { - this.cmd = (ReportCommand) command; - break; - } - } + cmd = requireCommand("report"); } @Test public void shouldCreateReport() throws SQLException { - Assume.assumeNotNull(cmd); PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{player.getName(), "test report reason"}; @@ -42,8 +35,7 @@ public void shouldCreateReport() throws SQLException { @Test public void shouldFailIfSelfReport() { - Assume.assumeNotNull(cmd); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); // Get sender's player data @@ -56,7 +48,6 @@ public void shouldFailIfSelfReport() { @Test public void shouldFailIfPlayerNotFound() { - Assume.assumeNotNull(cmd); CommonSender sender = spy(plugin.getServer().getConsoleSender()); String playerName = testUtils.createRandomPlayerName(); String[] args = new String[]{playerName, "test report"}; @@ -67,7 +58,6 @@ public void shouldFailIfPlayerNotFound() { @Test public void shouldFailIfNoReasonGiven() { - Assume.assumeNotNull(cmd); CommonSender sender = plugin.getServer().getConsoleSender(); String[] args = new String[]{"confuser"}; @@ -121,9 +111,8 @@ public void shouldAllowPartialWhenNoExactCollision() throws SQLException { @Test public void shouldNotifyStaff() throws SQLException { - Assume.assumeNotNull(cmd); PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{player.getName(), "test report notification"}; diff --git a/common/src/test/java/me/confuser/banmanager/common/commands/RollbackCommandTest.java b/common/src/test/java/me/confuser/banmanager/common/commands/RollbackCommandTest.java index 1db257eed..14d2076f6 100644 --- a/common/src/test/java/me/confuser/banmanager/common/commands/RollbackCommandTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/commands/RollbackCommandTest.java @@ -3,19 +3,19 @@ import me.confuser.banmanager.common.BasePluginDbTest; import me.confuser.banmanager.common.CommonServer; import me.confuser.banmanager.common.data.*; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.sql.SQLException; import static org.awaitility.Awaitility.await; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; public class RollbackCommandTest extends BasePluginDbTest { private RollbackCommand cmd; - @Before + @BeforeEach public void setupCmd() { for (CommonCommand cmd : plugin.getCommands()) { if (cmd.getCommandName().equals("bmrollback")) { @@ -65,7 +65,7 @@ public void shouldRollbackActiveBan() throws SQLException { assertTrue(plugin.getPlayerBanStorage().isBanned(victim.getUUID())); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{maliciousMod.getName(), "1d", "bans"}; @@ -107,7 +107,7 @@ public void shouldRollbackUnbanAndRestoreLegitBan() throws SQLException { .countOf(); assertEquals(1, recordCount); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{maliciousMod.getName(), "1d", "banrecords"}; @@ -156,7 +156,7 @@ public void shouldNotRestoreBanWhenBothActionsAreMalicious() throws SQLException .countOf(); assertEquals(1, recordCount); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{maliciousMod.getName(), "1d", "banrecords"}; @@ -200,7 +200,7 @@ public void shouldCleanupRecordsOfMaliciousBans() throws SQLException { .countOf(); assertEquals(1, recordCount); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); // Rolling back banrecords for malicious mod should clean up records where they were the original banner String[] args = new String[]{maliciousMod.getName(), "1d", "banrecords"}; @@ -242,7 +242,7 @@ public void shouldNotAffectBansOutsideTimeframe() throws SQLException, Interrupt // Wait a bit to ensure time difference Thread.sleep(100); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); // Rollback only last 1 second (ban was created more than 1 second ago) String[] args = new String[]{maliciousMod.getName(), "1s", "bans"}; @@ -269,7 +269,7 @@ public void shouldRollbackMutes() throws SQLException { assertTrue(plugin.getPlayerMuteStorage().isMuted(victim.getUUID())); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{maliciousMod.getName(), "1d", "mutes"}; @@ -304,7 +304,7 @@ public void shouldRollbackMuteRecordsAndRestoreLegitMute() throws SQLException { testUtils.unmutePlayer(mute, maliciousMod); assertFalse(plugin.getPlayerMuteStorage().isMuted(victim.getUUID())); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{maliciousMod.getName(), "1d", "muterecords"}; @@ -347,7 +347,7 @@ public void shouldNotRestoreMuteWhenBothActionsAreMalicious() throws SQLExceptio .countOf(); assertEquals(1, recordCount); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{maliciousMod.getName(), "1d", "muterecords"}; diff --git a/common/src/test/java/me/confuser/banmanager/common/commands/TabCompletionTest.java b/common/src/test/java/me/confuser/banmanager/common/commands/TabCompletionTest.java index 5b2792448..1f88d4264 100644 --- a/common/src/test/java/me/confuser/banmanager/common/commands/TabCompletionTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/commands/TabCompletionTest.java @@ -3,18 +3,18 @@ import me.confuser.banmanager.common.BasePluginDbTest; import me.confuser.banmanager.common.CommonPlayer; import me.confuser.banmanager.common.data.PlayerData; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.util.List; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; public class TabCompletionTest extends BasePluginDbTest { private CommonCommand cmd; - @Before + @BeforeEach public void setupCmd() { for (CommonCommand command : plugin.getCommands()) { if (command.getCommandName().equals("ban")) { @@ -36,9 +36,9 @@ public void shouldCompletePlayerNameFromStart() { List suggestions = cmd.handlePlayerNameTabComplete(sender, args); - assertTrue("Should suggest TestPlayer1", suggestions.contains("TestPlayer1")); - assertTrue("Should suggest TestPlayer2", suggestions.contains("TestPlayer2")); - assertFalse("Should not suggest OtherPlayer", suggestions.contains("OtherPlayer")); + assertTrue(suggestions.contains("TestPlayer1"), "Should suggest TestPlayer1"); + assertTrue(suggestions.contains("TestPlayer2"), "Should suggest TestPlayer2"); + assertFalse(suggestions.contains("OtherPlayer"), "Should not suggest OtherPlayer"); } @Test @@ -51,9 +51,9 @@ public void shouldCompleteEmptyArg() { List suggestions = cmd.handlePlayerNameTabComplete(sender, args); - assertTrue("Should include all players for empty input", suggestions.size() >= 2); - assertTrue("Should suggest Alpha", suggestions.contains("Alpha")); - assertTrue("Should suggest Beta", suggestions.contains("Beta")); + assertTrue(suggestions.size() >= 2, "Should include all players for empty input"); + assertTrue(suggestions.contains("Alpha"), "Should suggest Alpha"); + assertTrue(suggestions.contains("Beta"), "Should suggest Beta"); } @Test @@ -69,8 +69,8 @@ public void shouldCompleteMultiplePlayersWithComma() { List suggestions = cmd.handlePlayerNameTabComplete(sender, args); // Should complete with the full prefix including first player - assertTrue("Should suggest Alice,Alice", suggestions.stream().anyMatch(s -> s.startsWith("Alice,Alice"))); - assertTrue("Should suggest Alice,AliceAlt", suggestions.stream().anyMatch(s -> s.startsWith("Alice,AliceAlt"))); + assertTrue(suggestions.stream().anyMatch(s -> s.startsWith("Alice,Alice")), "Should suggest Alice,Alice"); + assertTrue(suggestions.stream().anyMatch(s -> s.startsWith("Alice,AliceAlt")), "Should suggest Alice,AliceAlt"); } @Test @@ -91,7 +91,7 @@ public void shouldReturnEmptyForDisabledTabCompletion() { String[] args = new String[]{"Test"}; List suggestions = noTabCmd.handlePlayerNameTabComplete(sender, args); - assertTrue("Should return empty list for disabled tab completion", suggestions.isEmpty()); + assertTrue(suggestions.isEmpty(), "Should return empty list for disabled tab completion"); } @Test @@ -106,7 +106,7 @@ public void shouldLimitResultsTo100() { List suggestions = cmd.handlePlayerNameTabComplete(sender, args); - assertTrue("Should limit results to 100 or less", suggestions.size() <= 100); + assertTrue(suggestions.size() <= 100, "Should limit results to 100 or less"); } @Test @@ -126,7 +126,7 @@ public void shouldBeCaseInsensitiveForOnlinePlayers() { // Offline auto-complete is case-sensitive by design (uses radix tree) // So this should not find CaseSensitive when searching for "casesens" // But let's verify behavior is consistent - assertNotNull("Should return a list", suggestions); + assertNotNull(suggestions, "Should return a list"); } @Test @@ -139,6 +139,6 @@ public void shouldNotSuggestForSecondArgWithoutHashtag() { List suggestions = cmd.handlePlayerNameTabComplete(sender, args); - assertTrue("Should return empty for non-hashtag second arg", suggestions.isEmpty()); + assertTrue(suggestions.isEmpty(), "Should return empty for non-hashtag second arg"); } } diff --git a/common/src/test/java/me/confuser/banmanager/common/commands/TempBanCommandTest.java b/common/src/test/java/me/confuser/banmanager/common/commands/TempBanCommandTest.java index 55ee5b112..e549ee01d 100644 --- a/common/src/test/java/me/confuser/banmanager/common/commands/TempBanCommandTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/commands/TempBanCommandTest.java @@ -6,20 +6,20 @@ import me.confuser.banmanager.common.TestPlayer; import me.confuser.banmanager.common.data.PlayerBanData; import me.confuser.banmanager.common.data.PlayerData; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.sql.SQLException; import java.util.UUID; import static org.awaitility.Awaitility.await; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; public class TempBanCommandTest extends BasePluginDbTest { private TempBanCommand cmd; - @Before + @BeforeEach public void setupCmd() { for (CommonCommand cmd : plugin.getCommands()) { if (cmd.getCommandName().equals("tempban")) { @@ -130,7 +130,7 @@ public void shouldAllowPartialWhenNoExactCollision() { @Test public void shouldFailIfExempt() { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); CommonPlayer commonPlayer = spy(new TestPlayer(player.getUUID(), player.getName(), true)); this.server.setExactMatch(player.getName(), commonPlayer); @@ -155,7 +155,7 @@ public void shouldFailIfNotFound() { @Test public void shouldFailIfOfflineExempt() { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); CommonPlayer commonPlayer = spy(new TestPlayer(player.getUUID(), player.getName(), true)); this.server.setExactMatch(player.getName(), commonPlayer); @@ -171,7 +171,7 @@ public void shouldFailIfOfflineExempt() { @Test public void shouldBanPlayer() { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{player.getName(), "30y", "test"}; @@ -189,7 +189,7 @@ public void shouldBanPlayer() { @Test public void shouldBanPlayerSilently() { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{player.getName(), "30y", "test", "-s"}; diff --git a/common/src/test/java/me/confuser/banmanager/common/commands/TempMuteCommandTest.java b/common/src/test/java/me/confuser/banmanager/common/commands/TempMuteCommandTest.java index 6ea1a58ca..124311dd2 100644 --- a/common/src/test/java/me/confuser/banmanager/common/commands/TempMuteCommandTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/commands/TempMuteCommandTest.java @@ -5,20 +5,20 @@ import me.confuser.banmanager.common.TestPlayer; import me.confuser.banmanager.common.data.PlayerData; import me.confuser.banmanager.common.data.PlayerMuteData; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.sql.SQLException; import java.util.UUID; import static org.awaitility.Awaitility.await; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; public class TempMuteCommandTest extends BasePluginDbTest { private TempMuteCommand cmd; - @Before + @BeforeEach public void setupCmd() { for (CommonCommand cmd : plugin.getCommands()) { if (cmd.getCommandName().equals("tempmute")) { @@ -48,7 +48,7 @@ public void shouldFailIfInvalidTimeFormat() { @Test public void shouldTempMutePlayer() { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{player.getName(), "1h", "test"}; @@ -75,7 +75,7 @@ public void shouldTempMuteWithVariousTimeFormats() { await().until(() -> plugin.getPlayerMuteStorage().isMuted(player.getUUID())); PlayerMuteData mute = plugin.getPlayerMuteStorage().getMute(player.getUUID()); - assertTrue("Expiry should be set for " + time, mute.getExpires() > 0); + assertTrue(mute.getExpires() > 0, "Expiry should be set for " + time); // Clean up plugin.getPlayerMuteStorage().removeMute(player.getUUID()); @@ -86,7 +86,7 @@ public void shouldTempMuteWithVariousTimeFormats() { public void shouldTempMuteWithLongDuration() { // Test that very long durations are accepted when time limits are not configured PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{player.getName(), "30y", "test"}; @@ -99,7 +99,7 @@ public void shouldTempMuteWithLongDuration() { @Test public void shouldFailWhenAlreadyMutedWithoutOverride() throws SQLException { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); // First mute the player @@ -122,7 +122,7 @@ public void shouldFailWhenAlreadyMutedWithoutOverride() throws SQLException { @Test public void shouldTempMuteSilently() { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{player.getName(), "1h", "test", "-s"}; @@ -136,7 +136,7 @@ public void shouldTempMuteSilently() { @Test public void shouldTempMuteSoftly() { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{player.getName(), "1h", "test", "-st"}; diff --git a/common/src/test/java/me/confuser/banmanager/common/commands/TempWarnCommandTest.java b/common/src/test/java/me/confuser/banmanager/common/commands/TempWarnCommandTest.java index 903361778..a2ed0ca0c 100644 --- a/common/src/test/java/me/confuser/banmanager/common/commands/TempWarnCommandTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/commands/TempWarnCommandTest.java @@ -6,20 +6,20 @@ import me.confuser.banmanager.common.data.PlayerData; import me.confuser.banmanager.common.data.PlayerWarnData; import me.confuser.banmanager.common.util.parsers.WarnCommandParser; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.sql.SQLException; import java.util.UUID; import static org.awaitility.Awaitility.await; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; public class TempWarnCommandTest extends BasePluginDbTest { private TempWarnCommand cmd; - @Before + @BeforeEach public void setupCmd() { for (CommonCommand cmd : plugin.getCommands()) { if (cmd.getCommandName().equals("tempwarn")) { @@ -32,7 +32,7 @@ public void setupCmd() { @Test public void shouldTempWarnPlayer() throws SQLException { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{player.getName(), "1h", "test warning"}; @@ -48,7 +48,7 @@ public void shouldTempWarnPlayer() throws SQLException { @Test public void shouldTempWarnWithPoints() throws SQLException { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{"-p", "5", player.getName(), "1h", "test warning"}; diff --git a/common/src/test/java/me/confuser/banmanager/common/commands/UnbanCommandTest.java b/common/src/test/java/me/confuser/banmanager/common/commands/UnbanCommandTest.java index 75397a2f1..88c873746 100644 --- a/common/src/test/java/me/confuser/banmanager/common/commands/UnbanCommandTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/commands/UnbanCommandTest.java @@ -5,20 +5,20 @@ import me.confuser.banmanager.common.data.PlayerBanData; import me.confuser.banmanager.common.data.PlayerData; import me.confuser.banmanager.common.util.parsers.UnbanCommandParser; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.sql.SQLException; import java.util.UUID; import static org.awaitility.Awaitility.await; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; public class UnbanCommandTest extends BasePluginDbTest { private UnbanCommand cmd; - @Before + @BeforeEach public void setupCmd() { for (CommonCommand cmd : plugin.getCommands()) { if (cmd.getCommandName().equals("unban")) { @@ -61,7 +61,7 @@ public void shouldFailIfInvalidUUID() { @Test public void shouldUnbanPlayer() throws SQLException { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); // First ban the player @@ -81,7 +81,7 @@ public void shouldUnbanPlayer() throws SQLException { @Test public void shouldUnbanByUUID() throws SQLException { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); PlayerBanData ban = testUtils.createBan(player, sender.getData(), "test ban"); @@ -97,7 +97,7 @@ public void shouldUnbanByUUID() throws SQLException { @Test public void shouldUnbanWithDeleteFlag() throws SQLException { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); when(sender.hasPermission(cmd.getPermission() + ".delete")).thenReturn(true); @@ -117,7 +117,7 @@ public void shouldUnbanWithDeleteFlag() throws SQLException { @Test public void shouldFailDeleteWithoutPermission() throws SQLException { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); when(sender.hasPermission(cmd.getPermission() + ".delete")).thenReturn(false); @@ -135,7 +135,7 @@ public void shouldFailDeleteWithoutPermission() throws SQLException { @Test public void shouldUnbanSilently() throws SQLException { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); PlayerBanData ban = testUtils.createBan(player, sender.getData(), "test ban"); @@ -154,7 +154,7 @@ public void shouldUnbanSilently() throws SQLException { public void shouldFailOwnUnbanWithoutPermission() throws SQLException { PlayerData player = testUtils.createRandomPlayer(); PlayerData otherMod = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); // Create ban by a different actor diff --git a/common/src/test/java/me/confuser/banmanager/common/commands/UnmuteCommandTest.java b/common/src/test/java/me/confuser/banmanager/common/commands/UnmuteCommandTest.java index 4a5b91fea..4820d4bea 100644 --- a/common/src/test/java/me/confuser/banmanager/common/commands/UnmuteCommandTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/commands/UnmuteCommandTest.java @@ -5,19 +5,19 @@ import me.confuser.banmanager.common.data.PlayerData; import me.confuser.banmanager.common.data.PlayerMuteData; import me.confuser.banmanager.common.util.parsers.UnbanCommandParser; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.sql.SQLException; import static org.awaitility.Awaitility.await; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; public class UnmuteCommandTest extends BasePluginDbTest { private UnmuteCommand cmd; - @Before + @BeforeEach public void setupCmd() { for (CommonCommand cmd : plugin.getCommands()) { if (cmd.getCommandName().equals("unmute")) { @@ -50,7 +50,7 @@ public void shouldFailIfPlayerNotFound() { @Test public void shouldUnmutePlayer() throws SQLException { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); // First mute the player @@ -70,7 +70,7 @@ public void shouldUnmutePlayer() throws SQLException { @Test public void shouldUnmuteByUUID() throws SQLException { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); PlayerMuteData mute = testUtils.createMute(player, sender.getData(), "test mute"); @@ -86,7 +86,7 @@ public void shouldUnmuteByUUID() throws SQLException { @Test public void shouldUnmuteWithDeleteFlag() throws SQLException { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); when(sender.hasPermission(cmd.getPermission() + ".delete")).thenReturn(true); @@ -106,7 +106,7 @@ public void shouldUnmuteWithDeleteFlag() throws SQLException { @Test public void shouldFailDeleteWithoutPermission() throws SQLException { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); when(sender.hasPermission(cmd.getPermission() + ".delete")).thenReturn(false); @@ -124,7 +124,7 @@ public void shouldFailDeleteWithoutPermission() throws SQLException { @Test public void shouldUnmuteSilently() throws SQLException { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); PlayerMuteData mute = testUtils.createMute(player, sender.getData(), "test mute"); @@ -143,7 +143,7 @@ public void shouldUnmuteSilently() throws SQLException { public void shouldFailOwnUnmuteWithoutPermission() throws SQLException { PlayerData player = testUtils.createRandomPlayer(); PlayerData otherMod = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); // Create mute by a different actor diff --git a/common/src/test/java/me/confuser/banmanager/common/commands/WarnCommandTest.java b/common/src/test/java/me/confuser/banmanager/common/commands/WarnCommandTest.java index 86665461a..7386fb85d 100644 --- a/common/src/test/java/me/confuser/banmanager/common/commands/WarnCommandTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/commands/WarnCommandTest.java @@ -10,8 +10,8 @@ import me.confuser.banmanager.common.data.PlayerWarnData; import me.confuser.banmanager.common.util.DateUtils; import me.confuser.banmanager.common.util.parsers.WarnCommandParser; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.sql.SQLException; import java.util.Arrays; @@ -20,7 +20,7 @@ import java.util.List; import java.util.UUID; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.*; @@ -30,7 +30,7 @@ public class WarnCommandTest extends BasePluginDbTest { private WarnCommand cmd; - @Before + @BeforeEach public void setupCmd() { for (CommonCommand cmd : plugin.getCommands()) { if (cmd.getCommandName().equals("warn")) { @@ -145,7 +145,7 @@ public void shouldFailIfPlayerExempt() { @Test public void shouldWarnPlayer() throws SQLException { PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{player.getName(), "test"}; diff --git a/common/src/test/java/me/confuser/banmanager/common/commands/global/BanAllCommandTest.java b/common/src/test/java/me/confuser/banmanager/common/commands/global/BanAllCommandTest.java index a167a8cb7..e7393123d 100644 --- a/common/src/test/java/me/confuser/banmanager/common/commands/global/BanAllCommandTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/commands/global/BanAllCommandTest.java @@ -3,49 +3,39 @@ import me.confuser.banmanager.common.BasePluginDbTest; import me.confuser.banmanager.common.CommonServer; import me.confuser.banmanager.common.commands.CommandParser; -import me.confuser.banmanager.common.commands.CommonCommand; import me.confuser.banmanager.common.commands.CommonSender; import me.confuser.banmanager.common.data.PlayerData; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.sql.SQLException; import static org.awaitility.Awaitility.await; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; -import org.junit.Assume; public class BanAllCommandTest extends BasePluginDbTest { private BanAllCommand cmd; - @Before + @BeforeEach public void setupCmd() { - for (CommonCommand command : plugin.getCommands()) { - if (command.getCommandName().equals("banall")) { - this.cmd = (BanAllCommand) command; - break; - } - } + cmd = requireCommand("banall"); } @Test public void shouldCreateGlobalBan() throws SQLException { - Assume.assumeNotNull(cmd); PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{player.getName(), "global ban test"}; assert (cmd.onCommand(sender, new CommandParser(plugin, args, 1))); - // Verify the command was executed (the global ban was created) await().untilAsserted(() -> verify(sender, atLeastOnce()).sendMessage(anyString())); } @Test public void shouldFailIfNoReasonGiven() { - Assume.assumeNotNull(cmd); CommonSender sender = plugin.getServer().getConsoleSender(); String[] args = new String[]{"confuser"}; @@ -54,7 +44,6 @@ public void shouldFailIfNoReasonGiven() { @Test public void shouldFailIfPlayerNotFound() { - Assume.assumeNotNull(cmd); CommonSender sender = spy(plugin.getServer().getConsoleSender()); String playerName = testUtils.createRandomPlayerName(); String[] args = new String[]{playerName, "test"}; @@ -65,9 +54,8 @@ public void shouldFailIfPlayerNotFound() { @Test public void shouldNotifyOnGlobalBan() throws SQLException { - Assume.assumeNotNull(cmd); PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{player.getName(), "global ban notification test"}; diff --git a/common/src/test/java/me/confuser/banmanager/common/commands/global/MuteAllCommandTest.java b/common/src/test/java/me/confuser/banmanager/common/commands/global/MuteAllCommandTest.java index b0062e468..79496c94a 100644 --- a/common/src/test/java/me/confuser/banmanager/common/commands/global/MuteAllCommandTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/commands/global/MuteAllCommandTest.java @@ -3,49 +3,39 @@ import me.confuser.banmanager.common.BasePluginDbTest; import me.confuser.banmanager.common.CommonServer; import me.confuser.banmanager.common.commands.CommandParser; -import me.confuser.banmanager.common.commands.CommonCommand; import me.confuser.banmanager.common.commands.CommonSender; import me.confuser.banmanager.common.data.PlayerData; -import org.junit.Assume; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.sql.SQLException; import static org.awaitility.Awaitility.await; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; public class MuteAllCommandTest extends BasePluginDbTest { private MuteAllCommand cmd; - @Before + @BeforeEach public void setupCmd() { - for (CommonCommand command : plugin.getCommands()) { - if (command.getCommandName().equals("muteall")) { - this.cmd = (MuteAllCommand) command; - break; - } - } + cmd = requireCommand("muteall"); } @Test public void shouldCreateGlobalMute() throws SQLException { - Assume.assumeNotNull(cmd); PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{player.getName(), "global mute test"}; assert (cmd.onCommand(sender, new CommandParser(plugin, args, 1))); - // Verify the command was executed await().untilAsserted(() -> verify(sender, atLeastOnce()).sendMessage(anyString())); } @Test public void shouldFailIfNoReasonGiven() { - Assume.assumeNotNull(cmd); CommonSender sender = plugin.getServer().getConsoleSender(); String[] args = new String[]{"confuser"}; @@ -54,7 +44,6 @@ public void shouldFailIfNoReasonGiven() { @Test public void shouldFailIfPlayerNotFound() { - Assume.assumeNotNull(cmd); CommonSender sender = spy(plugin.getServer().getConsoleSender()); String playerName = testUtils.createRandomPlayerName(); String[] args = new String[]{playerName, "test"}; @@ -65,9 +54,8 @@ public void shouldFailIfPlayerNotFound() { @Test public void shouldNotifyOnGlobalMute() throws SQLException { - Assume.assumeNotNull(cmd); PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{player.getName(), "global mute notification test"}; diff --git a/common/src/test/java/me/confuser/banmanager/common/commands/global/UnbanAllCommandTest.java b/common/src/test/java/me/confuser/banmanager/common/commands/global/UnbanAllCommandTest.java index 2e23ddcf6..2c884c0bb 100644 --- a/common/src/test/java/me/confuser/banmanager/common/commands/global/UnbanAllCommandTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/commands/global/UnbanAllCommandTest.java @@ -3,55 +3,44 @@ import me.confuser.banmanager.common.BasePluginDbTest; import me.confuser.banmanager.common.CommonServer; import me.confuser.banmanager.common.commands.CommandParser; -import me.confuser.banmanager.common.commands.CommonCommand; import me.confuser.banmanager.common.commands.CommonSender; import me.confuser.banmanager.common.data.PlayerData; import me.confuser.banmanager.common.data.global.GlobalPlayerBanData; -import org.junit.Assume; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.sql.SQLException; import static org.awaitility.Awaitility.await; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; public class UnbanAllCommandTest extends BasePluginDbTest { private UnbanAllCommand cmd; - @Before + @BeforeEach public void setupCmd() { - for (CommonCommand command : plugin.getCommands()) { - if (command.getCommandName().equals("unbanall")) { - this.cmd = (UnbanAllCommand) command; - break; - } - } + cmd = requireCommand("unbanall"); } @Test public void shouldRemoveGlobalBan() throws SQLException { - Assume.assumeNotNull(cmd); PlayerData player = testUtils.createRandomPlayer(); PlayerData actor = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); - // First create a global ban GlobalPlayerBanData ban = new GlobalPlayerBanData(player, actor, "test"); plugin.getGlobalPlayerBanStorage().create(ban); String[] args = new String[]{player.getName()}; assert (cmd.onCommand(sender, new CommandParser(plugin, args, 0))); - // Verify the command was executed await().untilAsserted(() -> verify(sender, atLeastOnce()).sendMessage(anyString())); } @Test public void shouldFailIfPlayerNotFound() { - Assume.assumeNotNull(cmd); CommonSender sender = spy(plugin.getServer().getConsoleSender()); String playerName = testUtils.createRandomPlayerName(); String[] args = new String[]{playerName}; @@ -62,9 +51,8 @@ public void shouldFailIfPlayerNotFound() { @Test public void shouldFailIfNotGloballyBanned() { - Assume.assumeNotNull(cmd); PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{player.getName()}; diff --git a/common/src/test/java/me/confuser/banmanager/common/commands/global/UnmuteAllCommandTest.java b/common/src/test/java/me/confuser/banmanager/common/commands/global/UnmuteAllCommandTest.java index 541ed2849..1b4fd1f12 100644 --- a/common/src/test/java/me/confuser/banmanager/common/commands/global/UnmuteAllCommandTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/commands/global/UnmuteAllCommandTest.java @@ -3,55 +3,44 @@ import me.confuser.banmanager.common.BasePluginDbTest; import me.confuser.banmanager.common.CommonServer; import me.confuser.banmanager.common.commands.CommandParser; -import me.confuser.banmanager.common.commands.CommonCommand; import me.confuser.banmanager.common.commands.CommonSender; import me.confuser.banmanager.common.data.PlayerData; import me.confuser.banmanager.common.data.global.GlobalPlayerMuteData; -import org.junit.Assume; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.sql.SQLException; import static org.awaitility.Awaitility.await; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; public class UnmuteAllCommandTest extends BasePluginDbTest { private UnmuteAllCommand cmd; - @Before + @BeforeEach public void setupCmd() { - for (CommonCommand command : plugin.getCommands()) { - if (command.getCommandName().equals("unmuteall")) { - this.cmd = (UnmuteAllCommand) command; - break; - } - } + cmd = requireCommand("unmuteall"); } @Test public void shouldRemoveGlobalMute() throws SQLException { - Assume.assumeNotNull(cmd); PlayerData player = testUtils.createRandomPlayer(); PlayerData actor = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); - // First create a global mute GlobalPlayerMuteData mute = new GlobalPlayerMuteData(player, actor, "test", false); plugin.getGlobalPlayerMuteStorage().create(mute); String[] args = new String[]{player.getName()}; assert (cmd.onCommand(sender, new CommandParser(plugin, args, 0))); - // Verify the command was executed await().untilAsserted(() -> verify(sender, atLeastOnce()).sendMessage(anyString())); } @Test public void shouldFailIfPlayerNotFound() { - Assume.assumeNotNull(cmd); CommonSender sender = spy(plugin.getServer().getConsoleSender()); String playerName = testUtils.createRandomPlayerName(); String[] args = new String[]{playerName}; @@ -62,9 +51,8 @@ public void shouldFailIfPlayerNotFound() { @Test public void shouldFailIfNotGloballyMuted() { - Assume.assumeNotNull(cmd); PlayerData player = testUtils.createRandomPlayer(); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = spy(server.getConsoleSender()); String[] args = new String[]{player.getName()}; diff --git a/common/src/test/java/me/confuser/banmanager/common/commands/report/AssignSubCommandTest.java b/common/src/test/java/me/confuser/banmanager/common/commands/report/AssignSubCommandTest.java index 5a1736a1b..c60d455e1 100644 --- a/common/src/test/java/me/confuser/banmanager/common/commands/report/AssignSubCommandTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/commands/report/AssignSubCommandTest.java @@ -3,11 +3,11 @@ import me.confuser.banmanager.common.BasePluginDbTest; import me.confuser.banmanager.common.data.PlayerData; import me.confuser.banmanager.common.data.PlayerReportData; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.sql.SQLException; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class AssignSubCommandTest extends BasePluginDbTest { diff --git a/common/src/test/java/me/confuser/banmanager/common/commands/report/CloseSubCommandTest.java b/common/src/test/java/me/confuser/banmanager/common/commands/report/CloseSubCommandTest.java index b6df4bb4c..85ccddceb 100644 --- a/common/src/test/java/me/confuser/banmanager/common/commands/report/CloseSubCommandTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/commands/report/CloseSubCommandTest.java @@ -3,11 +3,11 @@ import me.confuser.banmanager.common.BasePluginDbTest; import me.confuser.banmanager.common.data.PlayerData; import me.confuser.banmanager.common.data.PlayerReportData; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.sql.SQLException; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class CloseSubCommandTest extends BasePluginDbTest { diff --git a/common/src/test/java/me/confuser/banmanager/common/configs/ConfigReloadTest.java b/common/src/test/java/me/confuser/banmanager/common/configs/ConfigReloadTest.java index 040092529..a8cc0dfab 100644 --- a/common/src/test/java/me/confuser/banmanager/common/configs/ConfigReloadTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/configs/ConfigReloadTest.java @@ -4,37 +4,35 @@ import me.confuser.banmanager.common.CommonLogger; import me.confuser.banmanager.common.TestLogger; import me.confuser.banmanager.common.util.Message; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.nio.file.Files; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class ConfigReloadTest extends BasePluginTest { @Test public void configLoadReturnsFalseForInvalidYaml() throws IOException { - // Create a file with invalid YAML - File invalidFile = new File(temporaryFolder.getRoot(), "invalid.yml"); + File invalidFile = new File(temporaryFolder, "invalid.yml"); try (FileWriter writer = new FileWriter(invalidFile)) { writer.write("invalid: yaml: content: [unclosed"); } CommonLogger logger = new TestLogger(); - TestConfig config = new TestConfig(temporaryFolder.getRoot(), invalidFile, logger); + TestConfig config = new TestConfig(temporaryFolder, invalidFile, logger); boolean result = config.load(); - assertFalse("Config.load() should return false for invalid YAML", result); + assertFalse(result, "Config.load() should return false for invalid YAML"); } @Test public void configLoadReturnsTrueForValidYaml() throws IOException { - // Create a file with valid YAML - File validFile = new File(temporaryFolder.getRoot(), "valid.yml"); + File validFile = new File(temporaryFolder, "valid.yml"); try (FileWriter writer = new FileWriter(validFile)) { writer.write("key: value\n"); writer.write("nested:\n"); @@ -42,66 +40,58 @@ public void configLoadReturnsTrueForValidYaml() throws IOException { } CommonLogger logger = new TestLogger(); - TestConfig config = new TestConfig(temporaryFolder.getRoot(), validFile, logger); + TestConfig config = new TestConfig(temporaryFolder, validFile, logger); boolean result = config.load(); - assertTrue("Config.load() should return true for valid YAML", result); + assertTrue(result, "Config.load() should return true for valid YAML"); assertEquals("value", config.conf.getString("key")); assertEquals("data", config.conf.getString("nested.child")); } @Test public void configLoadReturnsFalseForMissingFile() { - File missingFile = new File(temporaryFolder.getRoot(), "nonexistent.yml"); + File missingFile = new File(temporaryFolder, "nonexistent.yml"); CommonLogger logger = new TestLogger(); - TestConfig config = new TestConfig(temporaryFolder.getRoot(), missingFile, logger); + TestConfig config = new TestConfig(temporaryFolder, missingFile, logger); boolean result = config.load(); - assertFalse("Config.load() should return false for missing file", result); + assertFalse(result, "Config.load() should return false for missing file"); } @Test public void setupConfigsPreservesPreviousSettingsOnReloadFailure() throws IOException { - // First, verify the initial config loaded successfully - assertNotNull("Initial config should be loaded", plugin.getConfig()); + assertNotNull(plugin.getConfig(), "Initial config should be loaded"); DefaultConfig originalConfig = plugin.getConfig(); - // Corrupt the config.yml file with invalid YAML - File configFile = new File(temporaryFolder.getRoot(), "config.yml"); + File configFile = new File(temporaryFolder, "config.yml"); try (FileWriter writer = new FileWriter(configFile)) { writer.write("invalid: yaml: [unclosed bracket"); } - // Reload configs plugin.setupConfigs(); - // The original config should still be in place - assertSame("Config should be preserved after failed reload", originalConfig, plugin.getConfig()); + assertSame(originalConfig, plugin.getConfig(), "Config should be preserved after failed reload"); } @Test public void setupConfigsReplacesConfigOnSuccessfulReload() throws IOException { - // Get the original config DefaultConfig originalConfig = plugin.getConfig(); - assertNotNull("Initial config should be loaded", originalConfig); + assertNotNull(originalConfig, "Initial config should be loaded"); - // Reload configs (file is still valid) plugin.setupConfigs(); - // A new config object should have been created - assertNotSame("Config should be replaced after successful reload", originalConfig, plugin.getConfig()); + assertNotSame(originalConfig, plugin.getConfig(), "Config should be replaced after successful reload"); } @Test public void messagesArePreservedWhenConfigFails() throws IOException { String originalMessage = Message.getString("configReloaded"); - assertNotNull("configReloaded message should exist", originalMessage); + assertNotNull(originalMessage, "configReloaded message should exist"); - // Corrupt the messages/messages_en.yml so reload yields no usable messages - File messagesEnFile = new File(temporaryFolder.getRoot(), "messages/messages_en.yml"); + File messagesEnFile = new File(temporaryFolder, "messages/messages_en.yml"); try (FileWriter writer = new FileWriter(messagesEnFile)) { writer.write("invalid: yaml: [unclosed"); } @@ -109,28 +99,24 @@ public void messagesArePreservedWhenConfigFails() throws IOException { plugin.setupConfigs(); String afterReloadMessage = Message.getString("configReloaded"); - assertEquals("Messages should be preserved after failed reload", originalMessage, afterReloadMessage); + assertEquals(originalMessage, afterReloadMessage, "Messages should be preserved after failed reload"); } @Test public void messagesAreUpdatedOnSuccessfulReload() throws IOException { - // Get the original message String originalMessage = Message.getString("configReloaded"); - assertNotNull("configReloaded message should exist", originalMessage); + assertNotNull(originalMessage, "configReloaded message should exist"); - // Modify the messages/messages_en.yml file with a new message value - File messagesFile = new File(temporaryFolder.getRoot(), "messages/messages_en.yml"); + File messagesFile = new File(temporaryFolder, "messages/messages_en.yml"); try (FileWriter writer = new FileWriter(messagesFile)) { writer.write("messages:\n"); writer.write(" configReloaded: \"New reload message\"\n"); } - // Reload configs plugin.setupConfigs(); - // Message should be updated String afterReloadMessage = Message.getString("configReloaded"); - assertEquals("Messages should be updated after successful reload", "New reload message", afterReloadMessage); + assertEquals("New reload message", afterReloadMessage, "Messages should be updated after successful reload"); } /** @@ -150,7 +136,6 @@ public void afterLoad() { @Override public void onSave() { - // No-op for testing } public boolean wasAfterLoadCalled() { @@ -160,102 +145,91 @@ public boolean wasAfterLoadCalled() { @Test public void afterLoadIsNotCalledOnFailure() throws IOException { - // Create a file with invalid YAML - File invalidFile = new File(temporaryFolder.getRoot(), "invalid.yml"); + File invalidFile = new File(temporaryFolder, "invalid.yml"); try (FileWriter writer = new FileWriter(invalidFile)) { writer.write("invalid: yaml: [unclosed"); } CommonLogger logger = new TestLogger(); - TestConfig config = new TestConfig(temporaryFolder.getRoot(), invalidFile, logger); + TestConfig config = new TestConfig(temporaryFolder, invalidFile, logger); config.load(); - assertFalse("afterLoad() should not be called when loading fails", config.wasAfterLoadCalled()); + assertFalse(config.wasAfterLoadCalled(), "afterLoad() should not be called when loading fails"); } @Test public void afterLoadIsCalledOnSuccess() throws IOException { - // Create a file with valid YAML - File validFile = new File(temporaryFolder.getRoot(), "valid.yml"); + File validFile = new File(temporaryFolder, "valid.yml"); try (FileWriter writer = new FileWriter(validFile)) { writer.write("key: value\n"); } CommonLogger logger = new TestLogger(); - TestConfig config = new TestConfig(temporaryFolder.getRoot(), validFile, logger); + TestConfig config = new TestConfig(temporaryFolder, validFile, logger); config.load(); - assertTrue("afterLoad() should be called when loading succeeds", config.wasAfterLoadCalled()); + assertTrue(config.wasAfterLoadCalled(), "afterLoad() should be called when loading succeeds"); } @Test public void saveDoesNotOverwriteFileAfterFailedReload() throws IOException { - // Create initial valid config - File configFile = new File(temporaryFolder.getRoot(), "saveable.yml"); + File configFile = new File(temporaryFolder, "saveable.yml"); try (FileWriter writer = new FileWriter(configFile)) { writer.write("key: originalValue\n"); } CommonLogger logger = new TestLogger(); - SaveableTestConfig config = new SaveableTestConfig(temporaryFolder.getRoot(), configFile, logger); - assertTrue("Initial load should succeed", config.load()); + SaveableTestConfig config = new SaveableTestConfig(temporaryFolder, configFile, logger); + assertTrue(config.load(), "Initial load should succeed"); - // User edits the file (makes it invalid) String invalidContent = "key: editedValue\ninvalid: yaml: [unclosed"; try (FileWriter writer = new FileWriter(configFile)) { writer.write(invalidContent); } - // Reload fails - assertFalse("Reload should fail for invalid YAML", config.load()); + assertFalse(config.load(), "Reload should fail for invalid YAML"); - // Now save is called (e.g., on plugin disable) config.save(); - // The file should NOT have been overwritten with old content String fileContent = new String(Files.readAllBytes(configFile.toPath())); - assertEquals("File should not be overwritten after failed reload", invalidContent, fileContent); + assertEquals(invalidContent, fileContent, "File should not be overwritten after failed reload"); } @Test public void saveOverwritesFileAfterSuccessfulReload() throws IOException { - // Create initial valid config - File configFile = new File(temporaryFolder.getRoot(), "saveable.yml"); + File configFile = new File(temporaryFolder, "saveable.yml"); try (FileWriter writer = new FileWriter(configFile)) { writer.write("key: originalValue\n"); } CommonLogger logger = new TestLogger(); - SaveableTestConfig config = new SaveableTestConfig(temporaryFolder.getRoot(), configFile, logger); - assertTrue("Initial load should succeed", config.load()); + SaveableTestConfig config = new SaveableTestConfig(temporaryFolder, configFile, logger); + assertTrue(config.load(), "Initial load should succeed"); - // Modify the in-memory config config.conf.set("key", "modifiedValue"); - // Save should work config.save(); - // Verify file was updated String fileContent = new String(Files.readAllBytes(configFile.toPath())); - assertTrue("File should contain modified value", fileContent.contains("modifiedValue")); + assertTrue(fileContent.contains("modifiedValue"), "File should contain modified value"); } @Test public void saveInAfterLoadPersistsChanges() throws IOException { - File configFile = new File(temporaryFolder.getRoot(), "self-save.yml"); + File configFile = new File(temporaryFolder, "self-save.yml"); try (FileWriter writer = new FileWriter(configFile)) { writer.write("key: originalValue\n"); } CommonLogger logger = new TestLogger(); - SelfSavingTestConfig config = new SelfSavingTestConfig(temporaryFolder.getRoot(), configFile, logger); + SelfSavingTestConfig config = new SelfSavingTestConfig(temporaryFolder, configFile, logger); - assertTrue("Load should succeed for valid YAML", config.load()); + assertTrue(config.load(), "Load should succeed for valid YAML"); String fileContent = new String(Files.readAllBytes(configFile.toPath())); - assertTrue("afterLoad() save should persist updated value", fileContent.contains("updatedInAfterLoad")); + assertTrue(fileContent.contains("updatedInAfterLoad"), "afterLoad() save should persist updated value"); } /** @@ -268,12 +242,10 @@ public SaveableTestConfig(File dataFolder, File file, CommonLogger logger) { @Override public void afterLoad() { - // No-op } @Override public void onSave() { - // No-op } } @@ -293,7 +265,6 @@ public void afterLoad() { @Override public void onSave() { - // No-op } } } diff --git a/common/src/test/java/me/confuser/banmanager/common/configs/ConsoleConfigTest.java b/common/src/test/java/me/confuser/banmanager/common/configs/ConsoleConfigTest.java index d3cf46cd1..3d06d6978 100644 --- a/common/src/test/java/me/confuser/banmanager/common/configs/ConsoleConfigTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/configs/ConsoleConfigTest.java @@ -1,10 +1,10 @@ package me.confuser.banmanager.common.configs; import me.confuser.banmanager.common.BasePluginTest; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class ConsoleConfigTest extends BasePluginTest { diff --git a/common/src/test/java/me/confuser/banmanager/common/configs/DefaultConfigTest.java b/common/src/test/java/me/confuser/banmanager/common/configs/DefaultConfigTest.java index 9e49f63c2..e8290b110 100644 --- a/common/src/test/java/me/confuser/banmanager/common/configs/DefaultConfigTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/configs/DefaultConfigTest.java @@ -1,9 +1,9 @@ package me.confuser.banmanager.common.configs; import me.confuser.banmanager.common.BasePluginDbTest; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class DefaultConfigTest extends BasePluginDbTest { diff --git a/common/src/test/java/me/confuser/banmanager/common/configs/GeoIpConfigTest.java b/common/src/test/java/me/confuser/banmanager/common/configs/GeoIpConfigTest.java index 14284a177..d6c959c50 100644 --- a/common/src/test/java/me/confuser/banmanager/common/configs/GeoIpConfigTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/configs/GeoIpConfigTest.java @@ -1,9 +1,9 @@ package me.confuser.banmanager.common.configs; import me.confuser.banmanager.common.BasePluginTest; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertFalse; +import static org.junit.jupiter.api.Assertions.assertFalse; public class GeoIpConfigTest extends BasePluginTest { diff --git a/common/src/test/java/me/confuser/banmanager/common/configs/NotificationsConfigTest.java b/common/src/test/java/me/confuser/banmanager/common/configs/NotificationsConfigTest.java index 171eac588..54fa15b02 100644 --- a/common/src/test/java/me/confuser/banmanager/common/configs/NotificationsConfigTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/configs/NotificationsConfigTest.java @@ -1,9 +1,9 @@ package me.confuser.banmanager.common.configs; import me.confuser.banmanager.common.BasePluginTest; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class NotificationsConfigTest extends BasePluginTest { diff --git a/common/src/test/java/me/confuser/banmanager/common/configs/SchedulesConfigTest.java b/common/src/test/java/me/confuser/banmanager/common/configs/SchedulesConfigTest.java index 8f4bcd73a..c9ee8dd8b 100644 --- a/common/src/test/java/me/confuser/banmanager/common/configs/SchedulesConfigTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/configs/SchedulesConfigTest.java @@ -1,9 +1,9 @@ package me.confuser.banmanager.common.configs; import me.confuser.banmanager.common.BasePluginTest; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class SchedulesConfigTest extends BasePluginTest { @Test diff --git a/common/src/test/java/me/confuser/banmanager/common/listeners/CommonJoinListenerTest.java b/common/src/test/java/me/confuser/banmanager/common/listeners/CommonJoinListenerTest.java index cd6f9d8c6..a334aefc8 100644 --- a/common/src/test/java/me/confuser/banmanager/common/listeners/CommonJoinListenerTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/listeners/CommonJoinListenerTest.java @@ -6,8 +6,8 @@ import me.confuser.banmanager.common.ipaddr.IPAddress; import me.confuser.banmanager.common.util.IPUtils; import me.confuser.banmanager.common.util.Message; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.net.InetAddress; import java.net.UnknownHostException; @@ -15,7 +15,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; public class CommonJoinListenerTest extends BasePluginDbTest { @@ -23,7 +23,7 @@ public class CommonJoinListenerTest extends BasePluginDbTest { private CommonJoinListener listener; private IPAddress testIp; - @Before + @BeforeEach public void setupListener() throws UnknownHostException { listener = new CommonJoinListener(plugin); testIp = IPUtils.toIPAddress(InetAddress.getByName("192.168.1.100")); @@ -59,7 +59,7 @@ public void banCheckShouldPopulateMultiaccountsCache() throws Exception { // The cache should have been populated - we can't directly access it, // but we can verify by calling onPlayerLogin and seeing if it uses the cached value // For now, verify that the method completed without error - assertFalse("Should not deny during banCheck for multiaccounts", denied.get()); + assertFalse(denied.get(), "Should not deny during banCheck for multiaccounts"); } @Test @@ -97,7 +97,7 @@ public void onPlayerLoginShouldDenyWhenCachedCountExceedsLimit() throws Exceptio testListener.onPlayerLogin(testPlayer, loginHandler); - assertTrue("Should deny login when multiaccounts count exceeds limit", loginDenied.get()); + assertTrue(loginDenied.get(), "Should deny login when multiaccounts count exceeds limit"); } @Test @@ -131,7 +131,7 @@ public void onPlayerLoginShouldAllowWhenPlayerHasExemptPermission() throws Excep AtomicBoolean loginDenied = new AtomicBoolean(false); testListener.onPlayerLogin(testPlayer, createHandler(loginDenied)); - assertFalse("Should allow login when player has exempt permission", loginDenied.get()); + assertFalse(loginDenied.get(), "Should allow login when player has exempt permission"); } @Test @@ -158,7 +158,7 @@ public void onPlayerLoginShouldAllowWhenCacheIsEmpty() throws Exception { AtomicBoolean loginDenied = new AtomicBoolean(false); testListener.onPlayerLogin(testPlayer, createHandler(loginDenied)); - assertFalse("Should allow login (fail-open) when cache is empty", loginDenied.get()); + assertFalse(loginDenied.get(), "Should allow login (fail-open) when cache is empty"); } @Test @@ -191,7 +191,7 @@ public void onPlayerLoginShouldAllowWhenCountIsWithinLimit() throws Exception { AtomicBoolean loginDenied = new AtomicBoolean(false); testListener.onPlayerLogin(testPlayer, createHandler(loginDenied)); - assertFalse("Should allow login when count is within limit", loginDenied.get()); + assertFalse(loginDenied.get(), "Should allow login when count is within limit"); } @Test @@ -206,13 +206,13 @@ public void shouldRecordSessionOnJoin() throws Exception { testListener.onPreJoin(existingPlayer.getUUID(), existingPlayer.getName(), testIp); // Session should be in DB and tracked in memory - assertTrue("Session should be active", plugin.getPlayerHistoryStorage().hasActiveSession(existingPlayer.getUUID())); + assertTrue(plugin.getPlayerHistoryStorage().hasActiveSession(existingPlayer.getUUID()), "Session should be active"); // Verify the session is in the database with correct data java.util.List sessions = plugin.getPlayerHistoryStorage().queryForAll(); - assertEquals("Should have one session", 1, sessions.size()); - assertEquals("Recorded name should match", existingPlayer.getName(), sessions.get(0).getName()); - assertNotNull("Session should have IP", sessions.get(0).getIp()); + assertEquals(1, sessions.size(), "Should have one session"); + assertEquals(existingPlayer.getName(), sessions.get(0).getName(), "Recorded name should match"); + assertNotNull(sessions.get(0).getIp(), "Session should have IP"); // Clean up - end the session plugin.getPlayerHistoryStorage().endSession(existingPlayer.getUUID()); @@ -236,13 +236,13 @@ public void shouldRecordSessionWithoutIpWhenLogIpsDisabled() throws Exception { testListener.onPreJoin(existingPlayer.getUUID(), existingPlayer.getName(), testIp); // Session should be active - assertTrue("Session should be active", plugin.getPlayerHistoryStorage().hasActiveSession(existingPlayer.getUUID())); + assertTrue(plugin.getPlayerHistoryStorage().hasActiveSession(existingPlayer.getUUID()), "Session should be active"); // Verify session has null IP in database java.util.List sessions = plugin.getPlayerHistoryStorage().queryForAll(); - assertEquals("Should have one session", 1, sessions.size()); - assertEquals("Recorded name should match", existingPlayer.getName(), sessions.get(0).getName()); - assertNull("Session should NOT have IP when logIps is disabled", sessions.get(0).getIp()); + assertEquals(1, sessions.size(), "Should have one session"); + assertEquals(existingPlayer.getName(), sessions.get(0).getName(), "Recorded name should match"); + assertNull(sessions.get(0).getIp(), "Session should NOT have IP when logIps is disabled"); // Clean up - end the session plugin.getPlayerHistoryStorage().endSession(existingPlayer.getUUID()); diff --git a/common/src/test/java/me/confuser/banmanager/common/listeners/NoteListenerTest.java b/common/src/test/java/me/confuser/banmanager/common/listeners/NoteListenerTest.java index 7f03dee52..bc9c6e8d9 100755 --- a/common/src/test/java/me/confuser/banmanager/common/listeners/NoteListenerTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/listeners/NoteListenerTest.java @@ -5,10 +5,10 @@ import me.confuser.banmanager.common.data.PlayerData; import me.confuser.banmanager.common.data.PlayerNoteData; import me.confuser.banmanager.common.util.Message; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; public class NoteListenerTest extends BasePluginDbTest { @@ -16,7 +16,7 @@ public class NoteListenerTest extends BasePluginDbTest { public void shouldBroadcast() { PlayerData player = testUtils.createRandomPlayer(); BanManagerPlugin plugin = spy(this.plugin); - CommonServer server = spy(plugin.getServer()); + CommonServer server = this.server; CommonSender sender = plugin.getServer().getConsoleSender(); PlayerNoteData data = new PlayerNoteData(player, sender.getData(), "test"); CommonPlayer testPlayer = spy(new TestPlayer(sender.getData().getUUID(), sender.getName(), false)); diff --git a/common/src/test/java/me/confuser/banmanager/common/runnables/BmRunnableTest.java b/common/src/test/java/me/confuser/banmanager/common/runnables/BmRunnableTest.java index 4fa6e28d5..8a30e2510 100644 --- a/common/src/test/java/me/confuser/banmanager/common/runnables/BmRunnableTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/runnables/BmRunnableTest.java @@ -1,9 +1,9 @@ package me.confuser.banmanager.common.runnables; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * Tests for BmRunnable schedule disable semantics. @@ -20,7 +20,7 @@ public void scheduleZeroDisablesExecution() { // Test the logic that schedule <= 0 means disabled int scheduleValue = 0; boolean shouldExecute = scheduleValue > 0; - assertFalse("Schedule 0 should disable execution", shouldExecute); + assertFalse(shouldExecute, "Schedule 0 should disable execution"); } /** @@ -30,7 +30,7 @@ public void scheduleZeroDisablesExecution() { public void scheduleNegativeDisablesExecution() { int scheduleValue = -1; boolean shouldExecute = scheduleValue > 0; - assertFalse("Negative schedule should disable execution", shouldExecute); + assertFalse(shouldExecute, "Negative schedule should disable execution"); } /** @@ -40,6 +40,6 @@ public void scheduleNegativeDisablesExecution() { public void schedulePositiveAllowsExecution() { int scheduleValue = 5; boolean scheduleAllowsExecution = scheduleValue > 0; - assertTrue("Positive schedule should allow execution (if other conditions met)", scheduleAllowsExecution); + assertTrue(scheduleAllowsExecution, "Positive schedule should allow execution (if other conditions met)"); } } diff --git a/common/src/test/java/me/confuser/banmanager/common/runnables/RollbackSyncTest.java b/common/src/test/java/me/confuser/banmanager/common/runnables/RollbackSyncTest.java index db93b1279..451191f3f 100644 --- a/common/src/test/java/me/confuser/banmanager/common/runnables/RollbackSyncTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/runnables/RollbackSyncTest.java @@ -5,17 +5,17 @@ import me.confuser.banmanager.common.data.PlayerData; import me.confuser.banmanager.common.data.PlayerMuteData; import me.confuser.banmanager.common.data.RollbackData; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.sql.SQLException; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class RollbackSyncTest extends BasePluginDbTest { private RollbackSync rollbackSync; - @Before + @BeforeEach public void setupSync() { rollbackSync = new RollbackSync(plugin); } diff --git a/common/src/test/java/me/confuser/banmanager/common/storage/IpBanStorageTest.java b/common/src/test/java/me/confuser/banmanager/common/storage/IpBanStorageTest.java index 4634a2859..351474dfe 100644 --- a/common/src/test/java/me/confuser/banmanager/common/storage/IpBanStorageTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/storage/IpBanStorageTest.java @@ -5,11 +5,11 @@ import me.confuser.banmanager.common.data.PlayerData; import me.confuser.banmanager.common.ipaddr.IPAddress; import me.confuser.banmanager.common.util.IPUtils; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.sql.SQLException; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class IpBanStorageTest extends BasePluginDbTest { diff --git a/common/src/test/java/me/confuser/banmanager/common/storage/IpRangeBanStorageTest.java b/common/src/test/java/me/confuser/banmanager/common/storage/IpRangeBanStorageTest.java index 3fc2c0679..f53bb1bab 100644 --- a/common/src/test/java/me/confuser/banmanager/common/storage/IpRangeBanStorageTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/storage/IpRangeBanStorageTest.java @@ -5,11 +5,11 @@ import me.confuser.banmanager.common.data.PlayerData; import me.confuser.banmanager.common.ipaddr.IPAddress; import me.confuser.banmanager.common.util.IPUtils; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.sql.SQLException; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class IpRangeBanStorageTest extends BasePluginDbTest { diff --git a/common/src/test/java/me/confuser/banmanager/common/storage/PlayerBanStorageTest.java b/common/src/test/java/me/confuser/banmanager/common/storage/PlayerBanStorageTest.java index 9766455f0..75f81a31b 100644 --- a/common/src/test/java/me/confuser/banmanager/common/storage/PlayerBanStorageTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/storage/PlayerBanStorageTest.java @@ -3,12 +3,12 @@ import me.confuser.banmanager.common.BasePluginDbTest; import me.confuser.banmanager.common.data.PlayerBanData; import me.confuser.banmanager.common.data.PlayerData; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.sql.SQLException; import java.util.UUID; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class PlayerBanStorageTest extends BasePluginDbTest { diff --git a/common/src/test/java/me/confuser/banmanager/common/storage/PlayerHistoryStorageTest.java b/common/src/test/java/me/confuser/banmanager/common/storage/PlayerHistoryStorageTest.java index 2878c3f5a..52eb1eb6a 100644 --- a/common/src/test/java/me/confuser/banmanager/common/storage/PlayerHistoryStorageTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/storage/PlayerHistoryStorageTest.java @@ -6,16 +6,16 @@ import me.confuser.banmanager.common.data.PlayerData; import me.confuser.banmanager.common.data.PlayerHistoryData; import me.confuser.banmanager.common.data.PlayerNameSummary; -import org.junit.After; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; import java.sql.SQLException; import java.util.List; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class PlayerHistoryStorageTest extends BasePluginDbTest { - @After + @AfterEach public void clear() throws SQLException { plugin.getPlayerHistoryStorage().updateRaw("TRUNCATE TABLE " + plugin.getPlayerHistoryStorage().getTableName()); } @@ -89,7 +89,7 @@ public void shouldCreateSessionWithNullIp() throws SQLException { // Fetch from DB to verify List sessions = storage.queryForAll(); assertEquals(1, sessions.size()); - assertNull("IP should be null when logIp=false", sessions.get(0).getIp()); + assertNull(sessions.get(0).getIp(), "IP should be null when logIp=false"); assertEquals(player.getName(), sessions.get(0).getName()); } @@ -102,7 +102,7 @@ public void shouldGetNamesSummary() throws SQLException { List summary = storage.getNamesSummary(player); assertEquals(1, summary.size()); - assertEquals(player.getName(), summary.get(0).getName()); + assertEquals(player.getName(), summary.get(0).name()); } @Test @@ -162,8 +162,8 @@ public void shouldSetDatabaseTimestampsOnSession() throws SQLException { assertEquals(1, sessions.size()); PlayerHistoryData session = sessions.get(0); - assertTrue("Join time should be set", session.getJoin() > 0); - assertTrue("Leave time should be set", session.getLeave() > 0); - assertTrue("Leave should be >= Join", session.getLeave() >= session.getJoin()); + assertTrue(session.getJoin() > 0, "Join time should be set"); + assertTrue(session.getLeave() > 0, "Leave time should be set"); + assertTrue(session.getLeave() >= session.getJoin(), "Leave should be >= Join"); } } diff --git a/common/src/test/java/me/confuser/banmanager/common/storage/PlayerMuteStorageTest.java b/common/src/test/java/me/confuser/banmanager/common/storage/PlayerMuteStorageTest.java index f44efa61c..92e7d3bd4 100644 --- a/common/src/test/java/me/confuser/banmanager/common/storage/PlayerMuteStorageTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/storage/PlayerMuteStorageTest.java @@ -5,12 +5,11 @@ import me.confuser.banmanager.common.data.PlayerMuteData; import me.confuser.banmanager.common.data.PlayerMuteRecord; import me.confuser.banmanager.common.ormlite.dao.CloseableIterator; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import java.io.IOException; import java.sql.SQLException; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class PlayerMuteStorageTest extends BasePluginDbTest { @@ -233,7 +232,7 @@ public void shouldNotConsiderPausedMuteExpired() throws SQLException { } @Test - public void shouldNotRestoreOnlineOnlyWhenNoRemainingTime() throws SQLException, IOException { + public void shouldNotRestoreOnlineOnlyWhenNoRemainingTime() throws Exception { PlayerData player = testUtils.createRandomPlayer(); PlayerData actor = testUtils.createRandomPlayer(); PlayerData unmuter = testUtils.createRandomPlayer(); @@ -268,7 +267,7 @@ public void shouldNotRestoreOnlineOnlyWhenNoRemainingTime() throws SQLException, } @Test - public void shouldRestoreOnlineOnlyWithRemainingTime() throws SQLException, IOException { + public void shouldRestoreOnlineOnlyWithRemainingTime() throws Exception { PlayerData player = testUtils.createRandomPlayer(); PlayerData actor = testUtils.createRandomPlayer(); PlayerData unmuter = testUtils.createRandomPlayer(); diff --git a/common/src/test/java/me/confuser/banmanager/common/storage/PlayerStorageTest.java b/common/src/test/java/me/confuser/banmanager/common/storage/PlayerStorageTest.java index 350fc450f..eb7ee4503 100644 --- a/common/src/test/java/me/confuser/banmanager/common/storage/PlayerStorageTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/storage/PlayerStorageTest.java @@ -3,12 +3,12 @@ import me.confuser.banmanager.common.BasePluginDbTest; import me.confuser.banmanager.common.data.PlayerData; import me.confuser.banmanager.common.ormlite.dao.Dao; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.sql.SQLException; import java.util.UUID; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class PlayerStorageTest extends BasePluginDbTest { diff --git a/common/src/test/java/me/confuser/banmanager/common/storage/PlayerWarnStorageTest.java b/common/src/test/java/me/confuser/banmanager/common/storage/PlayerWarnStorageTest.java index 6bf031b19..c7e994656 100644 --- a/common/src/test/java/me/confuser/banmanager/common/storage/PlayerWarnStorageTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/storage/PlayerWarnStorageTest.java @@ -4,11 +4,11 @@ import me.confuser.banmanager.common.data.PlayerData; import me.confuser.banmanager.common.data.PlayerWarnData; import me.confuser.banmanager.common.ormlite.dao.CloseableIterator; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.sql.SQLException; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class PlayerWarnStorageTest extends BasePluginDbTest { diff --git a/common/src/test/java/me/confuser/banmanager/common/storage/conversion/LiteBansImportTest.java b/common/src/test/java/me/confuser/banmanager/common/storage/conversion/LiteBansImportTest.java index de767113b..42dbbb6bd 100644 --- a/common/src/test/java/me/confuser/banmanager/common/storage/conversion/LiteBansImportTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/storage/conversion/LiteBansImportTest.java @@ -6,14 +6,13 @@ import me.confuser.banmanager.common.ipaddr.IPAddressString; import me.confuser.banmanager.common.ormlite.dao.CloseableIterator; import me.confuser.banmanager.common.ormlite.support.DatabaseConnection; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; -import java.io.IOException; import java.sql.SQLException; import java.util.UUID; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; /** * Tests for the LiteBans to BanManager import functionality. @@ -30,8 +29,8 @@ public class LiteBansImportTest extends BasePluginDbTest { private static final String PLAYER_UUID_3 = "550e8400-e29b-41d4-a716-446655440003"; private static final String ACTOR_UUID = "550e8400-e29b-41d4-a716-446655440099"; - @Before - public void setupLiteBansTables() throws SQLException, IOException { + @BeforeEach + public void setupLiteBansTables() throws Exception { createLiteBansSchema(); insertTestData(); } @@ -39,7 +38,7 @@ public void setupLiteBansTables() throws SQLException, IOException { /** * Creates the LiteBans table schema in the test database. */ - private void createLiteBansSchema() throws SQLException, IOException { + private void createLiteBansSchema() throws Exception { try (DatabaseConnection conn = plugin.getLocalConn().getReadWriteConnection("")) { // Create bans table conn.executeStatement( @@ -146,7 +145,7 @@ private void createLiteBansSchema() throws SQLException, IOException { /** * Inserts test data into the LiteBans tables. */ - private void insertTestData() throws SQLException, IOException { + private void insertTestData() throws Exception { long currentTime = System.currentTimeMillis(); long pastTime = currentTime - 86400000L; // 1 day ago @@ -254,12 +253,12 @@ public void shouldImportActivePlayerBan() throws SQLException { runImport(); UUID playerUuid = UUID.fromString(PLAYER_UUID_1); - assertTrue("Player should be banned", plugin.getPlayerBanStorage().isBanned(playerUuid)); + assertTrue(plugin.getPlayerBanStorage().isBanned(playerUuid), "Player should be banned"); PlayerBanData ban = plugin.getPlayerBanStorage().getBan(playerUuid); - assertNotNull("Ban data should exist", ban); + assertNotNull(ban, "Ban data should exist"); assertEquals("Test ban reason", ban.getReason()); - assertFalse("Ban should not be silent", ban.isSilent()); + assertFalse(ban.isSilent(), "Ban should not be silent"); } @Test @@ -269,7 +268,7 @@ public void shouldImportActiveIpBan() { // Check that IP ban was imported // The IP 10.0.0.1 should be banned IPAddress ip = new IPAddressString("10.0.0.1").getAddress(); - assertTrue("IP should be banned", plugin.getIpBanStorage().isBanned(ip)); + assertTrue(plugin.getIpBanStorage().isBanned(ip), "IP should be banned"); } @Test @@ -278,7 +277,7 @@ public void shouldImportActiveIpRangeBan() { // Check that an IP within the range 172.16.0.0/16 is banned IPAddress ip = new IPAddressString("172.16.1.1").getAddress(); - assertTrue("IP in range should be banned", plugin.getIpRangeBanStorage().isBanned(ip)); + assertTrue(plugin.getIpRangeBanStorage().isBanned(ip), "IP in range should be banned"); } @Test @@ -286,10 +285,10 @@ public void shouldImportActivePlayerMute() throws SQLException { runImport(); UUID playerUuid = UUID.fromString(PLAYER_UUID_1); - assertTrue("Player should be muted", plugin.getPlayerMuteStorage().isMuted(playerUuid)); + assertTrue(plugin.getPlayerMuteStorage().isMuted(playerUuid), "Player should be muted"); PlayerMuteData mute = plugin.getPlayerMuteStorage().getMute(playerUuid); - assertNotNull("Mute data should exist", mute); + assertNotNull(mute, "Mute data should exist"); assertEquals("Test mute reason", mute.getReason()); } @@ -298,11 +297,11 @@ public void shouldImportActiveIpMute() { runImport(); IPAddress ip = new IPAddressString("10.0.0.2").getAddress(); - assertTrue("IP should be muted", plugin.getIpMuteStorage().isMuted(ip)); + assertTrue(plugin.getIpMuteStorage().isMuted(ip), "IP should be muted"); } @Test - public void shouldImportWarningWithCorrectReadStatus() throws SQLException, IOException { + public void shouldImportWarningWithCorrectReadStatus() throws Exception { runImport(); // Check warnings for PLAYER_UUID_1 - should have warned=true (read=true) @@ -314,11 +313,11 @@ public void shouldImportWarningWithCorrectReadStatus() throws SQLException, IOEx while (warnings.hasNext()) { PlayerWarnData warn = warnings.next(); if (warn.getReason().equals("Test warning")) { - assertTrue("Warning should be marked as read", warn.isRead()); + assertTrue(warn.isRead(), "Warning should be marked as read"); foundReadWarning = true; } } - assertTrue("Should have found the read warning", foundReadWarning); + assertTrue(foundReadWarning, "Should have found the read warning"); } } @@ -331,7 +330,7 @@ public void shouldImportKick() throws SQLException { // Verify at least one kick was imported for this player long kickCount = plugin.getPlayerKickStorage().getCount(player); - assertTrue("Should have at least one kick", kickCount > 0); + assertTrue(kickCount > 0, "Should have at least one kick"); } @Test @@ -340,8 +339,7 @@ public void shouldSkipInvalidUuids() { runImport(); // Verify import completed successfully by checking valid entries exist - assertTrue("Valid player ban should exist", - plugin.getPlayerBanStorage().isBanned(UUID.fromString(PLAYER_UUID_1))); + assertTrue(plugin.getPlayerBanStorage().isBanned(UUID.fromString(PLAYER_UUID_1)), "Valid player ban should exist"); } @Test @@ -350,13 +348,11 @@ public void shouldHandleConsoleAsActor() { // The IP ban was created by CONSOLE, verify it imported correctly IPAddress ip = new IPAddressString("10.0.0.1").getAddress(); - assertTrue("IP ban by console should exist", plugin.getIpBanStorage().isBanned(ip)); + assertTrue(plugin.getIpBanStorage().isBanned(ip), "IP ban by console should exist"); IpBanData ban = plugin.getIpBanStorage().getBan(ip); - assertNotNull("Ban should exist", ban); - assertEquals("Actor should be console", - plugin.getPlayerStorage().getConsole().getUUID(), - ban.getActor().getUUID()); + assertNotNull(ban, "Ban should exist"); + assertEquals(plugin.getPlayerStorage().getConsole().getUUID(), ban.getActor().getUUID(), "Actor should be console"); } @Test @@ -367,13 +363,12 @@ public void shouldCreateBanRecordForRemovedBan() throws SQLException { UUID removedPlayerUuid = UUID.fromString("550e8400-e29b-41d4-a716-446655440004"); // Player should NOT be actively banned (it was removed) - assertFalse("Player should not be actively banned", - plugin.getPlayerBanStorage().isBanned(removedPlayerUuid)); + assertFalse(plugin.getPlayerBanStorage().isBanned(removedPlayerUuid), "Player should not be actively banned"); // But there should be a ban record PlayerData player = plugin.getPlayerStorage().createIfNotExists(removedPlayerUuid, "Unknown"); long recordCount = plugin.getPlayerBanRecordStorage().getCount(player); - assertTrue("Should have a ban record", recordCount > 0); + assertTrue(recordCount > 0, "Should have a ban record"); } /** diff --git a/common/src/test/java/me/confuser/banmanager/common/storage/migration/MigrationIntegrationTest.java b/common/src/test/java/me/confuser/banmanager/common/storage/migration/MigrationIntegrationTest.java index 466deb1b5..40836800d 100644 --- a/common/src/test/java/me/confuser/banmanager/common/storage/migration/MigrationIntegrationTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/storage/migration/MigrationIntegrationTest.java @@ -4,33 +4,33 @@ import me.confuser.banmanager.common.configs.PluginInfo; import me.confuser.banmanager.common.ormlite.dao.Dao; import me.confuser.banmanager.common.ormlite.dao.DaoManager; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import java.io.File; import java.util.List; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.spy; public class MigrationIntegrationTest { - @Rule - public TemporaryFolder temporaryFolder = new TemporaryFolder(); + @TempDir + public File temporaryFolder; private BanManagerPlugin plugin; private TestServer server = spy(new TestServer()); - @Before + @BeforeEach public void setup() throws Exception { CommonLogger logger = new TestLogger(); PluginInfo pluginInfo = BasePluginTest.setupConfigs(temporaryFolder); - plugin = new BanManagerPlugin(pluginInfo, logger, temporaryFolder.getRoot(), new TestScheduler(), server, new TestMetrics()); + plugin = new BanManagerPlugin(pluginInfo, logger, temporaryFolder, new TestScheduler(), server, new TestMetrics()); server.enable(plugin); } - @After + @AfterEach public void cleanup() { if (plugin != null) { plugin.disable(); @@ -44,18 +44,18 @@ public void freshInstall_marksLatestVersion() throws Exception { Dao dao = DaoManager.createDao(plugin.getLocalConn(), SchemaVersion.class); List versions = dao.queryForAll(); - assertFalse("Schema versions should have been recorded", versions.isEmpty()); + assertFalse(versions.isEmpty(), "Schema versions should have been recorded"); boolean hasLocal = false; for (SchemaVersion sv : versions) { if ("local".equals(sv.getScope())) { hasLocal = true; - assertEquals("Fresh install should mark at latest version", 2, sv.getVersion()); - assertTrue("Description should indicate fresh install", sv.getDescription().contains("fresh install")); + assertEquals(2, sv.getVersion(), "Fresh install should mark at latest version"); + assertTrue(sv.getDescription().contains("fresh install"), "Description should indicate fresh install"); } } - assertTrue("Should have a local scope version", hasLocal); + assertTrue(hasLocal, "Should have a local scope version"); } @Test @@ -65,7 +65,7 @@ public void secondEnable_isIdempotent() throws Exception { CommonLogger logger = new TestLogger(); PluginInfo pluginInfo = BasePluginTest.setupConfigs(temporaryFolder); - plugin = new BanManagerPlugin(pluginInfo, logger, temporaryFolder.getRoot(), new TestScheduler(), server, new TestMetrics()); + plugin = new BanManagerPlugin(pluginInfo, logger, temporaryFolder, new TestScheduler(), server, new TestMetrics()); server.enable(plugin); plugin.enable(); @@ -76,6 +76,6 @@ public void secondEnable_isIdempotent() throws Exception { ).getFirstResult(); int count = Integer.parseInt(rawResults[0]); - assertEquals("Should have exactly one local version row (no duplicates from second enable)", 1, count); + assertEquals(1, count, "Should have exactly one local version row (no duplicates from second enable)"); } } diff --git a/common/src/test/java/me/confuser/banmanager/common/storage/migration/MigrationRunnerTest.java b/common/src/test/java/me/confuser/banmanager/common/storage/migration/MigrationRunnerTest.java index 98e703726..6f94714a7 100644 --- a/common/src/test/java/me/confuser/banmanager/common/storage/migration/MigrationRunnerTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/storage/migration/MigrationRunnerTest.java @@ -1,10 +1,10 @@ package me.confuser.banmanager.common.storage.migration; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.List; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class MigrationRunnerTest { diff --git a/common/src/test/java/me/confuser/banmanager/common/util/ColorUtilsTest.java b/common/src/test/java/me/confuser/banmanager/common/util/ColorUtilsTest.java index 75b44a2ac..3597103da 100644 --- a/common/src/test/java/me/confuser/banmanager/common/util/ColorUtilsTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/util/ColorUtilsTest.java @@ -1,7 +1,7 @@ package me.confuser.banmanager.common.util; -import org.junit.Test; -import static org.junit.Assert.*; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; public class ColorUtilsTest { @@ -9,65 +9,65 @@ public class ColorUtilsTest { public void testHexColorParsing() { String input = "&#ff0000Red Text"; String legacy = ColorUtils.toDownsampledLegacy(input); - assertFalse("Output should not contain #", legacy.contains("#")); - assertTrue("Output should contain 'Red Text'", legacy.contains("Red Text")); + assertFalse(legacy.contains("#"), "Output should not contain #"); + assertTrue(legacy.contains("Red Text"), "Output should contain 'Red Text'"); } @Test public void testMixedColors() { String input = "&cRed �ff00Green &9Blue"; String legacy = ColorUtils.toDownsampledLegacy(input); - assertFalse("Output should not contain #", legacy.contains("#")); - assertTrue("Output should contain text", legacy.contains("Red")); - assertTrue("Output should contain text", legacy.contains("Green")); - assertTrue("Output should contain text", legacy.contains("Blue")); + assertFalse(legacy.contains("#"), "Output should not contain #"); + assertTrue(legacy.contains("Red"), "Output should contain text"); + assertTrue(legacy.contains("Green"), "Output should contain text"); + assertTrue(legacy.contains("Blue"), "Output should contain text"); } @Test public void testSpigotHexFormat() { String input = "&x&f&f&0&0&0&0Red"; String legacy = ColorUtils.toDownsampledLegacy(input); - assertFalse("Output should not contain &x", legacy.contains("&x")); - assertFalse("Output should not contain #", legacy.contains("#")); - assertTrue("Output should contain 'Red'", legacy.contains("Red")); + assertFalse(legacy.contains("&x"), "Output should not contain &x"); + assertFalse(legacy.contains("#"), "Output should not contain #"); + assertTrue(legacy.contains("Red"), "Output should contain 'Red'"); } @Test public void testDownsampledJsonNoHex() { String input = "&#ff5733Orange"; String json = ColorUtils.toDownsampledJson(input); - assertFalse("JSON should not contain hex code", json.contains("#ff5733")); - assertFalse("JSON should not contain hex code", json.contains("ff5733")); - assertTrue("JSON should contain text", json.contains("Orange")); + assertFalse(json.contains("#ff5733"), "JSON should not contain hex code"); + assertFalse(json.contains("ff5733"), "JSON should not contain hex code"); + assertTrue(json.contains("Orange"), "JSON should contain text"); } @Test public void testFullJsonHasHex() { String input = "&#ff5733Orange"; String json = ColorUtils.toJson(input); - assertTrue("JSON should contain text", json.contains("Orange")); + assertTrue(json.contains("Orange"), "JSON should contain text"); } @Test public void testNewlinePreservation() { String input = "Line1\\nLine2"; String legacy = ColorUtils.toDownsampledLegacy(input); - assertTrue("Should convert \\n to newline", legacy.contains("\n")); + assertTrue(legacy.contains("\n"), "Should convert \\n to newline"); } @Test public void testPlainTextUnchanged() { String input = "Hello World"; String legacy = ColorUtils.toDownsampledLegacy(input); - assertEquals("Plain text should be unchanged", "Hello World", legacy); + assertEquals("Hello World", legacy, "Plain text should be unchanged"); } @Test public void testLegacyColorsPreserved() { String input = "&cRed &aGreen"; String legacy = ColorUtils.toDownsampledLegacy(input); - assertTrue("Should contain & codes", legacy.contains("&")); - assertTrue("Should contain text", legacy.contains("Red")); - assertTrue("Should contain text", legacy.contains("Green")); + assertTrue(legacy.contains("&"), "Should contain & codes"); + assertTrue(legacy.contains("Red"), "Should contain text"); + assertTrue(legacy.contains("Green"), "Should contain text"); } } diff --git a/common/src/test/java/me/confuser/banmanager/common/util/DateUtilsTest.java b/common/src/test/java/me/confuser/banmanager/common/util/DateUtilsTest.java index 3ef5b2cd6..3072f75a0 100755 --- a/common/src/test/java/me/confuser/banmanager/common/util/DateUtilsTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/util/DateUtilsTest.java @@ -1,10 +1,10 @@ package me.confuser.banmanager.common.util; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.ZoneOffset; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class DateUtilsTest { diff --git a/common/src/test/java/me/confuser/banmanager/common/util/IPUtilsTest.java b/common/src/test/java/me/confuser/banmanager/common/util/IPUtilsTest.java index 1dce4991f..abfb3c5a2 100644 --- a/common/src/test/java/me/confuser/banmanager/common/util/IPUtilsTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/util/IPUtilsTest.java @@ -1,15 +1,15 @@ package me.confuser.banmanager.common.util; -import com.github.javafaker.Faker; +import net.datafaker.Faker; import me.confuser.banmanager.common.ipaddr.AddressStringException; import me.confuser.banmanager.common.ipaddr.IPAddress; import me.confuser.banmanager.common.ipaddr.IPAddressString; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.net.InetAddress; import java.net.UnknownHostException; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class IPUtilsTest { private Faker faker = new Faker(); @@ -105,16 +105,35 @@ public void bytesToIPAddress() { assertEquals(randomIpv4, reconstructed.toString()); } + // Curated, well-formed IPv6 addresses. We can't trust faker.internet().ipV6Address() + // alone because the previous test contained a "replaceAll(\"0\", \"\")" workaround that + // occasionally produced strings like ":::abc" which fail to parse - leaving the suite + // intermittently red without saying anything new about IPUtils. + private static final String[] SAMPLE_IPV6 = { + "2001:db8::1", + "fe80::1ff:fe23:4567:890a", + "::ffff:192.0.2.128", + "2001:0db8:85a3:0000:0000:8a2e:0370:7334", + "::", + "2607:f8b0:4005:805::200e" + }; + + private static String randomSampleIpv6() { + return SAMPLE_IPV6[(int) (Math.random() * SAMPLE_IPV6.length)]; + } + @Test public void stringToIPAddress() { assertEquals("127.0.0.1", IPUtils.toIPAddress("127.0.0.1").toString()); assertEquals("::1", IPUtils.toIPAddress("::1").toString()); String ipv4 = faker.internet().ipV4Address(); - String ipv6 = faker.internet().ipV6Address().replaceAll("0", ""); - assertEquals(ipv4, IPUtils.toIPAddress(ipv4).toString()); - assertEquals(ipv6, IPUtils.toIPAddress(ipv6).toString()); + + // Compare canonical forms because IPAddress normalises ("2001:0db8:..." → "2001:db8:..."). + String ipv6 = randomSampleIpv6(); + String canonicalInput = new IPAddressString(ipv6).getAddress().toString(); + assertEquals(canonicalInput, IPUtils.toIPAddress(ipv6).toString()); } @Test @@ -123,12 +142,12 @@ public void isValid() { assertTrue(IPUtils.isValid("::1")); String ipv4 = faker.internet().ipV4Address(); - String ipv6 = faker.internet().ipV6Address().replaceAll("0", ""); + assertTrue(IPUtils.isValid(ipv4), () -> "expected valid IPv4: " + ipv4); - assertTrue(IPUtils.isValid(ipv4)); - assertTrue(IPUtils.isValid(ipv6)); + for (String ipv6 : SAMPLE_IPV6) { + assertTrue(IPUtils.isValid(ipv6), () -> "expected valid IPv6: " + ipv6); + } - // Test invalid IPs assertFalse(IPUtils.isValid("not-an-ip")); assertFalse(IPUtils.isValid("256.256.256.256")); } diff --git a/common/src/test/java/me/confuser/banmanager/common/util/LocaleNormalisationTest.java b/common/src/test/java/me/confuser/banmanager/common/util/LocaleNormalisationTest.java index e88a7a93f..a495f4bb0 100644 --- a/common/src/test/java/me/confuser/banmanager/common/util/LocaleNormalisationTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/util/LocaleNormalisationTest.java @@ -1,8 +1,8 @@ package me.confuser.banmanager.common.util; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class LocaleNormalisationTest { diff --git a/common/src/test/java/me/confuser/banmanager/common/util/MessageDeferredTest.java b/common/src/test/java/me/confuser/banmanager/common/util/MessageDeferredTest.java index 39152fa04..4d97a94bd 100644 --- a/common/src/test/java/me/confuser/banmanager/common/util/MessageDeferredTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/util/MessageDeferredTest.java @@ -3,18 +3,18 @@ import me.confuser.banmanager.common.TestLogger; import me.confuser.banmanager.common.TestPlayer; import me.confuser.banmanager.common.kyori.text.Component; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; import java.util.UUID; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class MessageDeferredTest { - @Before + @BeforeEach public void setUp() { MessageRegistry registry = new MessageRegistry("en"); @@ -113,7 +113,7 @@ public void dynamicRegistrationWritesToDefaultLocale() { new Message("custom.key", "Custom message"); String result = Message.getString("custom.key"); - assertNotNull("Dynamic registration should produce a non-null result", result); + assertNotNull(result, "Dynamic registration should produce a non-null result"); assertEquals("Custom message", result); } diff --git a/common/src/test/java/me/confuser/banmanager/common/util/MessageLoadingTest.java b/common/src/test/java/me/confuser/banmanager/common/util/MessageLoadingTest.java index e2a2a96a0..9f42539fc 100644 --- a/common/src/test/java/me/confuser/banmanager/common/util/MessageLoadingTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/util/MessageLoadingTest.java @@ -4,11 +4,11 @@ import me.confuser.banmanager.common.TestLogger; import me.confuser.banmanager.common.configuration.file.YamlConfiguration; import me.confuser.banmanager.common.kyori.text.Component; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.StringReader; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class MessageLoadingTest extends BasePluginTest { @@ -67,7 +67,7 @@ public void noConsecutiveEmptyLinesInLoadedMessages() { String result = Message.get("test.msg").toString(); for (String line : result.split("\n", -1)) { - assertFalse("Empty lines should contain a space for chat rendering", line.isEmpty()); + assertFalse(line.isEmpty(), "Empty lines should contain a space for chat rendering"); } } } diff --git a/common/src/test/java/me/confuser/banmanager/common/util/MessageRegistryTest.java b/common/src/test/java/me/confuser/banmanager/common/util/MessageRegistryTest.java index 80a123c00..e0a7efa03 100644 --- a/common/src/test/java/me/confuser/banmanager/common/util/MessageRegistryTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/util/MessageRegistryTest.java @@ -1,19 +1,19 @@ package me.confuser.banmanager.common.util; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; import java.util.Set; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class MessageRegistryTest { private MessageRegistry registry; - @Before + @BeforeEach public void setUp() { registry = new MessageRegistry("en"); diff --git a/common/src/test/java/me/confuser/banmanager/common/util/MessageRendererTest.java b/common/src/test/java/me/confuser/banmanager/common/util/MessageRendererTest.java index c2d20aa2b..a04df5ffc 100644 --- a/common/src/test/java/me/confuser/banmanager/common/util/MessageRendererTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/util/MessageRendererTest.java @@ -1,25 +1,25 @@ package me.confuser.banmanager.common.util; import me.confuser.banmanager.common.kyori.text.Component; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class MessageRendererTest { private MessageRenderer renderer; - @Before + @BeforeEach public void setUp() { renderer = MessageRenderer.getInstance(); } - @After + @AfterEach public void tearDown() { renderer.loadStaticTokens(new HashMap<>()); } @@ -113,6 +113,6 @@ public void escapeTagsPreventsInjection() { String escaped = renderer.escapeTags(malicious); Component result = renderer.render("" + escaped); String plain = renderer.toPlainText(result); - assertTrue("Escaped tags should render as literal text", plain.contains("injected")); + assertTrue(plain.contains("injected"), "Escaped tags should render as literal text"); } } diff --git a/common/src/test/java/me/confuser/banmanager/common/util/MessagesYamlValidatorTest.java b/common/src/test/java/me/confuser/banmanager/common/util/MessagesYamlValidatorTest.java index 2182c1288..faa4d6c50 100644 --- a/common/src/test/java/me/confuser/banmanager/common/util/MessagesYamlValidatorTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/util/MessagesYamlValidatorTest.java @@ -5,9 +5,9 @@ import me.confuser.banmanager.common.kyori.text.Component; import me.confuser.banmanager.common.kyori.text.minimessage.tag.resolver.Placeholder; import me.confuser.banmanager.common.kyori.text.minimessage.tag.resolver.TagResolver; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.io.InputStream; import java.io.InputStreamReader; @@ -22,9 +22,9 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; /** * Validates that every message in the bundled messages_en.yml parses cleanly through MiniMessage. @@ -50,7 +50,7 @@ public class MessagesYamlValidatorTest { private MessageRenderer renderer; private List errors; - @Before + @BeforeEach public void setUp() { MessageRenderer.reset(); renderer = MessageRenderer.getInstance(); @@ -62,7 +62,7 @@ public void setUp() { errors = new ArrayList<>(); } - @After + @AfterEach public void tearDown() { renderer.loadStaticTokens(new HashMap<>()); MessageRenderer.reset(); @@ -72,7 +72,7 @@ public void tearDown() { public void everyMessageParsesWithoutError() throws Exception { YamlConfiguration conf = loadBundledMessages(); ConfigurationSection messages = conf.getConfigurationSection("messages"); - assertNotNull("messages section missing", messages); + assertNotNull(messages, "messages section missing"); TagResolver.Builder resolverBuilder = TagResolver.builder(); for (String token : KNOWN_DYNAMIC_TOKENS) { @@ -107,7 +107,7 @@ public void everyMessageParsesWithoutError() throws Exception { if (!errors.isEmpty()) { fail("Found " + errors.size() + " message issue(s):\n" + String.join("\n", errors)); } - assertTrue("Expected to parse at least 1 message", parsed > 0); + assertTrue(parsed > 0, "Expected to parse at least 1 message"); } @Test @@ -203,17 +203,14 @@ public void colorChoiceIsConsistentForActionButtons() throws Exception { String close = messages.getString("report.actions.close"); String tp = messages.getString("report.actions.tp"); - assertNotNull("report.actions.assign missing", assign); - assertNotNull("report.actions.close missing", close); - assertNotNull("report.actions.tp missing", tp); + assertNotNull(assign, "report.actions.assign missing"); + assertNotNull(close, "report.actions.close missing"); + assertNotNull(tp, "report.actions.tp missing"); String buttonColor = ""; - assertTrue("report.actions.assign should use " + buttonColor + " for visual consistency: " + assign, - assign.contains(buttonColor)); - assertTrue("report.actions.close should use " + buttonColor + " for visual consistency: " + close, - close.contains(buttonColor)); - assertTrue("report.actions.tp should use " + buttonColor + " for visual consistency: " + tp, - tp.contains(buttonColor)); + assertTrue(assign.contains(buttonColor), "report.actions.assign should use " + buttonColor + " for visual consistency: " + assign); + assertTrue(close.contains(buttonColor), "report.actions.close should use " + buttonColor + " for visual consistency: " + close); + assertTrue(tp.contains(buttonColor), "report.actions.tp should use " + buttonColor + " for visual consistency: " + tp); } @Test @@ -221,8 +218,7 @@ public void noTypoMissingNoun_addnoteall() throws Exception { YamlConfiguration conf = loadBundledMessages(); String value = conf.getString("messages.addnoteall.notify"); assertNotNull(value); - assertTrue("addnoteall.notify is missing the word 'note': " + value, - value.toLowerCase().contains("note")); + assertTrue(value.toLowerCase().contains("note"), "addnoteall.notify is missing the word 'note': " + value); } @Test @@ -230,8 +226,7 @@ public void noGrammarErrors_invalidReason() throws Exception { YamlConfiguration conf = loadBundledMessages(); String value = conf.getString("messages.sender.error.invalidReason"); assertNotNull(value); - assertTrue("invalidReason should say 'is not a valid reason' (was previously 'is no valid reason'): " + value, - value.contains("is not a valid")); + assertTrue(value.contains("is not a valid"), "invalidReason should say 'is not a valid reason' (was previously 'is no valid reason'): " + value); } @Test @@ -240,14 +235,12 @@ public void banPlayerKickAndDisallowedShareCriticalTokens() throws Exception { String disallowed = conf.getString("messages.ban.player.disallowed"); String kick = conf.getString("messages.ban.player.kick"); - assertNotNull("ban.player.disallowed missing", disallowed); - assertNotNull("ban.player.kick missing", kick); + assertNotNull(disallowed, "ban.player.disallowed missing"); + assertNotNull(kick, "ban.player.kick missing"); for (String token : new String[]{"", "", "", ""}) { - assertTrue("ban.player.disallowed should contain " + token + ": " + disallowed, - disallowed.contains(token)); - assertTrue("ban.player.kick should contain " + token + " for parity with disallowed: " + kick, - kick.contains(token)); + assertTrue(disallowed.contains(token), "ban.player.disallowed should contain " + token + ": " + disallowed); + assertTrue(kick.contains(token), "ban.player.kick should contain " + token + " for parity with disallowed: " + kick); } } @@ -257,14 +250,12 @@ public void tempbanPlayerKickAndDisallowedShareCriticalTokens() throws Exception String disallowed = conf.getString("messages.tempban.player.disallowed"); String kick = conf.getString("messages.tempban.player.kick"); - assertNotNull("tempban.player.disallowed missing", disallowed); - assertNotNull("tempban.player.kick missing", kick); + assertNotNull(disallowed, "tempban.player.disallowed missing"); + assertNotNull(kick, "tempban.player.kick missing"); for (String token : new String[]{"", "", "", "", ""}) { - assertTrue("tempban.player.disallowed should contain " + token + ": " + disallowed, - disallowed.contains(token)); - assertTrue("tempban.player.kick should contain " + token + " for parity with disallowed: " + kick, - kick.contains(token)); + assertTrue(disallowed.contains(token), "tempban.player.disallowed should contain " + token + ": " + disallowed); + assertTrue(kick.contains(token), "tempban.player.kick should contain " + token + " for parity with disallowed: " + kick); } } @@ -276,8 +267,7 @@ public void dashboardHeaderRendersStaffDashboardText() throws Exception { Component component = renderer.render(header); String plain = renderer.toPlainText(component); - assertTrue("dashboard.header plain text should contain 'Staff Dashboard': " + plain, - plain.contains("Staff Dashboard")); + assertTrue(plain.contains("Staff Dashboard"), "dashboard.header plain text should contain 'Staff Dashboard': " + plain); } @Test @@ -311,7 +301,7 @@ public void noLeftoverBracketTokens() throws Exception { private YamlConfiguration loadBundledMessages() throws Exception { InputStream in = getClass().getClassLoader().getResourceAsStream(MESSAGES_RESOURCE); - assertNotNull("Resource " + MESSAGES_RESOURCE + " not found on classpath", in); + assertNotNull(in, "Resource " + MESSAGES_RESOURCE + " not found on classpath"); try (InputStream stream = in; Reader reader = new InputStreamReader(stream)) { return YamlConfiguration.loadConfiguration(reader); diff --git a/common/src/test/java/me/confuser/banmanager/common/util/PaginatedViewTest.java b/common/src/test/java/me/confuser/banmanager/common/util/PaginatedViewTest.java index b79bac6d9..660c76182 100644 --- a/common/src/test/java/me/confuser/banmanager/common/util/PaginatedViewTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/util/PaginatedViewTest.java @@ -1,12 +1,12 @@ package me.confuser.banmanager.common.util; import me.confuser.banmanager.common.kyori.text.Component; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.List; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class PaginatedViewTest { diff --git a/common/src/test/java/me/confuser/banmanager/common/util/SchedulerTimeTest.java b/common/src/test/java/me/confuser/banmanager/common/util/SchedulerTimeTest.java index dfa5a97f7..5c9dd24b8 100644 --- a/common/src/test/java/me/confuser/banmanager/common/util/SchedulerTimeTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/util/SchedulerTimeTest.java @@ -1,10 +1,10 @@ package me.confuser.banmanager.common.util; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.time.Duration; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * Tests for SchedulerTime utility class. @@ -15,68 +15,68 @@ public class SchedulerTimeTest { @Test public void negativeDurationReturnsZero() { Duration negative = Duration.ofMillis(-1); - assertEquals("Negative duration should return 0 ticks", 0, SchedulerTime.durationToTicksCeil(negative)); + assertEquals(0, SchedulerTime.durationToTicksCeil(negative), "Negative duration should return 0 ticks"); } @Test public void zeroDurationReturnsZero() { Duration zero = Duration.ZERO; - assertEquals("Zero duration should return 0 ticks", 0, SchedulerTime.durationToTicksCeil(zero)); + assertEquals(0, SchedulerTime.durationToTicksCeil(zero), "Zero duration should return 0 ticks"); } @Test public void oneMillisecondReturnsOneTick() { // 1ms should ceil to 1 tick (since 1ms > 0, it needs at least 1 tick) Duration oneMs = Duration.ofMillis(1); - assertEquals("1ms should ceil to 1 tick", 1, SchedulerTime.durationToTicksCeil(oneMs)); + assertEquals(1, SchedulerTime.durationToTicksCeil(oneMs), "1ms should ceil to 1 tick"); } @Test public void fortyNineMillisecondsReturnsOneTick() { // 49ms should ceil to 1 tick Duration ms49 = Duration.ofMillis(49); - assertEquals("49ms should ceil to 1 tick", 1, SchedulerTime.durationToTicksCeil(ms49)); + assertEquals(1, SchedulerTime.durationToTicksCeil(ms49), "49ms should ceil to 1 tick"); } @Test public void fiftyMillisecondsReturnsOneTick() { // 50ms = exactly 1 tick Duration ms50 = Duration.ofMillis(50); - assertEquals("50ms should equal 1 tick", 1, SchedulerTime.durationToTicksCeil(ms50)); + assertEquals(1, SchedulerTime.durationToTicksCeil(ms50), "50ms should equal 1 tick"); } @Test public void fiftyOneMillisecondsReturnsTwoTicks() { // 51ms should ceil to 2 ticks Duration ms51 = Duration.ofMillis(51); - assertEquals("51ms should ceil to 2 ticks", 2, SchedulerTime.durationToTicksCeil(ms51)); + assertEquals(2, SchedulerTime.durationToTicksCeil(ms51), "51ms should ceil to 2 ticks"); } @Test public void nineHundredNinetyNineMillisecondsReturnsTwentyTicks() { // 999ms should ceil to 20 ticks (999 + 49 = 1048, 1048 / 50 = 20.96 -> 20) Duration ms999 = Duration.ofMillis(999); - assertEquals("999ms should ceil to 20 ticks", 20, SchedulerTime.durationToTicksCeil(ms999)); + assertEquals(20, SchedulerTime.durationToTicksCeil(ms999), "999ms should ceil to 20 ticks"); } @Test public void oneSecondReturnsTwentyTicks() { // 1000ms = exactly 20 ticks Duration oneSecond = Duration.ofSeconds(1); - assertEquals("1 second should equal 20 ticks", 20, SchedulerTime.durationToTicksCeil(oneSecond)); + assertEquals(20, SchedulerTime.durationToTicksCeil(oneSecond), "1 second should equal 20 ticks"); } @Test public void oneSecondAndOneMillisReturnsTwentyOneTicks() { // 1001ms should ceil to 21 ticks Duration ms1001 = Duration.ofMillis(1001); - assertEquals("1001ms should ceil to 21 ticks", 21, SchedulerTime.durationToTicksCeil(ms1001)); + assertEquals(21, SchedulerTime.durationToTicksCeil(ms1001), "1001ms should ceil to 21 ticks"); } @Test public void fiveSecondsReturnsOneHundredTicks() { // 5000ms = 100 ticks Duration fiveSeconds = Duration.ofSeconds(5); - assertEquals("5 seconds should equal 100 ticks", 100, SchedulerTime.durationToTicksCeil(fiveSeconds)); + assertEquals(100, SchedulerTime.durationToTicksCeil(fiveSeconds), "5 seconds should equal 100 ticks"); } } diff --git a/common/src/test/java/me/confuser/banmanager/common/util/StringUtilsTest.java b/common/src/test/java/me/confuser/banmanager/common/util/StringUtilsTest.java index f502f3330..7668d19fc 100755 --- a/common/src/test/java/me/confuser/banmanager/common/util/StringUtilsTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/util/StringUtilsTest.java @@ -1,8 +1,8 @@ package me.confuser.banmanager.common.util; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; // From apache commons lang3 public class StringUtilsTest { diff --git a/common/src/test/java/me/confuser/banmanager/common/util/parsers/InfoCommandParserTest.java b/common/src/test/java/me/confuser/banmanager/common/util/parsers/InfoCommandParserTest.java index f6f39c4a6..814fe4443 100644 --- a/common/src/test/java/me/confuser/banmanager/common/util/parsers/InfoCommandParserTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/util/parsers/InfoCommandParserTest.java @@ -1,9 +1,9 @@ package me.confuser.banmanager.common.util.parsers; import me.confuser.banmanager.common.BasePluginTest; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class InfoCommandParserTest extends BasePluginTest { diff --git a/common/src/test/java/me/confuser/banmanager/common/util/parsers/UnbanCommandParserTest.java b/common/src/test/java/me/confuser/banmanager/common/util/parsers/UnbanCommandParserTest.java index c36f4f512..e8ad97fcd 100644 --- a/common/src/test/java/me/confuser/banmanager/common/util/parsers/UnbanCommandParserTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/util/parsers/UnbanCommandParserTest.java @@ -1,9 +1,9 @@ package me.confuser.banmanager.common.util.parsers; import me.confuser.banmanager.common.BasePluginTest; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class UnbanCommandParserTest extends BasePluginTest { diff --git a/common/src/test/java/me/confuser/banmanager/common/util/parsers/WarnCommandParserTest.java b/common/src/test/java/me/confuser/banmanager/common/util/parsers/WarnCommandParserTest.java index e0b64c722..8144c0a7b 100644 --- a/common/src/test/java/me/confuser/banmanager/common/util/parsers/WarnCommandParserTest.java +++ b/common/src/test/java/me/confuser/banmanager/common/util/parsers/WarnCommandParserTest.java @@ -1,9 +1,9 @@ package me.confuser.banmanager.common.util.parsers; import me.confuser.banmanager.common.BasePluginTest; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class WarnCommandParserTest extends BasePluginTest { diff --git a/fabric/src/main/java/me/confuser/banmanager/fabric/listeners/WebhookListener.java b/fabric/src/main/java/me/confuser/banmanager/fabric/listeners/WebhookListener.java index 994ffed05..8b1d0fd7d 100644 --- a/fabric/src/main/java/me/confuser/banmanager/fabric/listeners/WebhookListener.java +++ b/fabric/src/main/java/me/confuser/banmanager/fabric/listeners/WebhookListener.java @@ -2,7 +2,7 @@ import me.confuser.banmanager.common.BanManagerPlugin; import me.confuser.banmanager.common.listeners.CommonWebhookListener; -import me.confuser.banmanager.common.listeners.CommonWebhookListener.WebhookData; +import me.confuser.banmanager.common.data.Webhook; import me.confuser.banmanager.common.util.Message; import me.confuser.banmanager.fabric.BanManagerEvents; import me.confuser.banmanager.common.data.*; @@ -28,54 +28,54 @@ public WebhookListener(BanManagerPlugin plugin) { } private void notifyOnBan(PlayerBanData banData, boolean silent, Message kickMessage) { - List webhooks = listener.notifyOnBan(banData); + List webhooks = listener.notifyOnBan(banData); sendAll(webhooks, silent); } private void notifyOnMute(PlayerMuteData muteData, boolean silent) { - List webhooks = listener.notifyOnMute(muteData); + List webhooks = listener.notifyOnMute(muteData); sendAll(webhooks, silent); } private void notifyOnBan(IpBanData banData, boolean silent) { - List webhooks = listener.notifyOnBan(banData); + List webhooks = listener.notifyOnBan(banData); sendAll(webhooks, silent); } private void notifyOnKick(PlayerKickData kickData, boolean silent) { - List webhooks = listener.notifyOnKick(kickData); + List webhooks = listener.notifyOnKick(kickData); sendAll(webhooks, silent); } private void notifyOnWarn(PlayerWarnData warnData, boolean silent) { - List webhooks = listener.notifyOnWarn(warnData); + List webhooks = listener.notifyOnWarn(warnData); sendAll(webhooks, silent); } private void notifyOnUnban(PlayerBanData banData, PlayerData actor, String reason, boolean silent) { - List webhooks = listener.notifyOnUnban(banData, actor, reason); + List webhooks = listener.notifyOnUnban(banData, actor, reason); sendAll(webhooks, silent); } private void notifyOnUnban(IpBanData banData, PlayerData actor, String reason, boolean silent) { - List webhooks = listener.notifyOnUnban(banData, actor, reason); + List webhooks = listener.notifyOnUnban(banData, actor, reason); sendAll(webhooks, silent); } private void notifyOnUnmute(PlayerMuteData muteData, PlayerData actor, String reason, boolean silent) { - List webhooks = listener.notifyOnUnmute(muteData, actor, reason); + List webhooks = listener.notifyOnUnmute(muteData, actor, reason); sendAll(webhooks, silent); } private void notifyOnReport(PlayerReportData reportData, boolean silent) { - List webhooks = listener.notifyOnReport(reportData, reportData.getActor(), reportData.getReason()); + List webhooks = listener.notifyOnReport(reportData, reportData.getActor(), reportData.getReason()); sendAll(webhooks, silent); } - private void sendAll(List webhooks, boolean isSilent) { - for (WebhookData data : webhooks) { - if (isSilent && data.ignoreSilent) continue; - if (data.url == null || data.payload == null || data.url.isEmpty() || data.payload.isEmpty()) continue; + private void sendAll(List webhooks, boolean isSilent) { + for (Webhook data : webhooks) { + if (isSilent && data.ignoreSilent()) continue; + if (data.url() == null || data.payload() == null || data.url().isEmpty() || data.payload().isEmpty()) continue; listener.sendAsync(data); } } diff --git a/gradle.properties b/gradle.properties index 79a0ef2cb..321afbf96 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,4 +3,4 @@ version=8.0.0-SNAPSHOT org.gradle.parallel=true description="The defacto plugin for Minecraft to manage punishments and moderate more effectively" -org.gradle.jvmargs=-Xmx2G +org.gradle.jvmargs=-Xmx2G -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+UseStringDeduplication diff --git a/libs/build.gradle.kts b/libs/build.gradle.kts index 47058d1e5..8178fe805 100644 --- a/libs/build.gradle.kts +++ b/libs/build.gradle.kts @@ -14,22 +14,22 @@ dependencies { "shade"("net.kyori:examination-string:1.3.0") "shade"("net.kyori:option:1.1.0") - "shade"("com.j256.ormlite:ormlite-core:5.1") - "shade"("com.j256.ormlite:ormlite-jdbc:5.1") + "shade"("com.j256.ormlite:ormlite-core:6.1") + "shade"("com.j256.ormlite:ormlite-jdbc:6.1") - "shade"("com.zaxxer:HikariCP:4.0.3") - "shade"("org.mariadb.jdbc:mariadb-java-client:2.7.4") - "shade"("mysql:mysql-connector-java:8.0.27") + "shade"("com.zaxxer:HikariCP:6.3.3") + "shade"("org.mariadb.jdbc:mariadb-java-client:3.5.7") + "shade"("com.mysql:mysql-connector-j:8.4.0") "shade"("com.googlecode.concurrent-trees:concurrent-trees:2.4.0") "shade"("com.maxmind.db:maxmind-db-gson:2.0.3") - "shade"("org.yaml:snakeyaml:1.29") - "shade"("com.google.code.gson:gson:2.3.1") + "shade"("org.yaml:snakeyaml:2.4") + "shade"("com.google.code.gson:gson:2.11.0") "shade"("com.github.spullara.cli-parser:cli-parser:1.1.5") - "shade"("com.google.guava:guava:21.0") + "shade"("com.google.guava:guava:33.4.8-jre") - "shade"("org.apache.commons:commons-compress:1.19") - "shade"("com.github.seancfoley:ipaddress:5.3.4") + "shade"("org.apache.commons:commons-compress:1.27.1") + "shade"("com.github.seancfoley:ipaddress:5.5.1") "shade"("com.h2database:h2:1.4.200") } @@ -74,8 +74,8 @@ tasks.named("jar") { } relocate("com.j256.ormlite", "me.confuser.banmanager.common.ormlite") { - include(dependency("com.j256.ormlite:ormlite-core:5.1")) - include(dependency("com.j256.ormlite:ormlite-jdbc:5.1")) + include(dependency("com.j256.ormlite:ormlite-core")) + include(dependency("com.j256.ormlite:ormlite-jdbc")) } relocate("com.zaxxer.hikari", "me.confuser.banmanager.common.hikari") { @@ -87,7 +87,7 @@ tasks.named("jar") { } relocate("com.mysql", "me.confuser.banmanager.common.mysql") { - include(dependency("mysql:mysql-connector-java")) + include(dependency("com.mysql:mysql-connector-j")) } relocate("com.google.gson", "me.confuser.banmanager.common.gson") { diff --git a/sponge/build.gradle.kts b/sponge/build.gradle.kts index c126d65da..34c51f23a 100644 --- a/sponge/build.gradle.kts +++ b/sponge/build.gradle.kts @@ -92,7 +92,7 @@ dependencies { api(project(":BanManagerCommon")) { isTransitive = true } - "shadeOnly"("org.bstats:bstats-sponge:3.0.2") + "shadeOnly"("org.bstats:bstats-sponge:3.2.1") } // Sponge API 11+ requires Java 21 diff --git a/sponge/src/main/java/me/confuser/banmanager/sponge/listeners/WebhookListener.java b/sponge/src/main/java/me/confuser/banmanager/sponge/listeners/WebhookListener.java index a2773905b..02b1d11cd 100644 --- a/sponge/src/main/java/me/confuser/banmanager/sponge/listeners/WebhookListener.java +++ b/sponge/src/main/java/me/confuser/banmanager/sponge/listeners/WebhookListener.java @@ -2,7 +2,7 @@ import me.confuser.banmanager.common.BanManagerPlugin; import me.confuser.banmanager.common.listeners.CommonWebhookListener; -import me.confuser.banmanager.common.listeners.CommonWebhookListener.WebhookData; +import me.confuser.banmanager.common.data.Webhook; import me.confuser.banmanager.sponge.api.events.*; import org.spongepowered.api.event.Listener; import org.spongepowered.api.event.Order; @@ -18,62 +18,62 @@ public WebhookListener(BanManagerPlugin plugin) { @Listener(order = Order.POST) public void notifyOnBan(PlayerBannedEvent event) { - List webhooks = listener.notifyOnBan(event.getBan()); + List webhooks = listener.notifyOnBan(event.getBan()); sendAll(webhooks, event.isSilent()); } @Listener(order = Order.POST) public void notifyOnMute(PlayerMutedEvent event) { - List webhooks = listener.notifyOnMute(event.getMute()); + List webhooks = listener.notifyOnMute(event.getMute()); sendAll(webhooks, event.isSilent()); } @Listener(order = Order.POST) public void notifyOnWarn(PlayerWarnedEvent event) { - List webhooks = listener.notifyOnWarn(event.getWarning()); + List webhooks = listener.notifyOnWarn(event.getWarning()); sendAll(webhooks, event.isSilent()); } @Listener(order = Order.POST) public void notifyOnBan(IpBannedEvent event) { - List webhooks = listener.notifyOnBan(event.getBan()); + List webhooks = listener.notifyOnBan(event.getBan()); sendAll(webhooks, event.isSilent()); } @Listener(order = Order.POST) public void notifyOnKick(PlayerKickedEvent event) { - List webhooks = listener.notifyOnKick(event.getKick()); + List webhooks = listener.notifyOnKick(event.getKick()); sendAll(webhooks, event.isSilent()); } @Listener(order = Order.POST) public void notifyOnUnban(PlayerUnbanEvent event) { - List webhooks = listener.notifyOnUnban(event.getBan(), event.getActor(), event.getReason()); + List webhooks = listener.notifyOnUnban(event.getBan(), event.getActor(), event.getReason()); sendAll(webhooks, event.isSilent()); } @Listener(order = Order.POST) public void notifyOnUnban(IpUnbanEvent event) { - List webhooks = listener.notifyOnUnban(event.getBan(), event.getActor(), event.getReason()); + List webhooks = listener.notifyOnUnban(event.getBan(), event.getActor(), event.getReason()); sendAll(webhooks, event.isSilent()); } @Listener(order = Order.POST) public void notifyOnUnmute(PlayerUnmuteEvent event) { - List webhooks = listener.notifyOnUnmute(event.getMute(), event.getActor(), event.getReason()); + List webhooks = listener.notifyOnUnmute(event.getMute(), event.getActor(), event.getReason()); sendAll(webhooks, event.isSilent()); } @Listener(order = Order.POST) public void notifyOnReport(PlayerReportedEvent event) { - List webhooks = listener.notifyOnReport(event.getReport(), event.getReport().getActor(), event.getReport().getReason()); + List webhooks = listener.notifyOnReport(event.getReport(), event.getReport().getActor(), event.getReport().getReason()); sendAll(webhooks, event.isSilent()); } - private void sendAll(List webhooks, boolean isSilent) { - for (WebhookData data : webhooks) { - if (isSilent && data.ignoreSilent) continue; - if (data.url == null || data.payload == null || data.url.isEmpty() || data.payload.isEmpty()) continue; + private void sendAll(List webhooks, boolean isSilent) { + for (Webhook data : webhooks) { + if (isSilent && data.ignoreSilent()) continue; + if (data.url() == null || data.payload() == null || data.url().isEmpty() || data.payload().isEmpty()) continue; listener.sendAsync(data); } } diff --git a/velocity/build.gradle.kts b/velocity/build.gradle.kts index 68c26abd7..1afc911c2 100644 --- a/velocity/build.gradle.kts +++ b/velocity/build.gradle.kts @@ -65,7 +65,7 @@ dependencies { api(project(":BanManagerCommon")) compileOnly("com.velocitypowered:velocity-api:3.1.0") annotationProcessor("com.velocitypowered:velocity-api:3.1.0") - "shadeOnly"("org.bstats:bstats-velocity:3.0.0") + "shadeOnly"("org.bstats:bstats-velocity:3.2.1") } tasks.named("processResources") { diff --git a/velocity/src/main/java/me/confuser/banmanager/velocity/listeners/WebhookListener.java b/velocity/src/main/java/me/confuser/banmanager/velocity/listeners/WebhookListener.java index 9d6f1f531..3e37b287b 100644 --- a/velocity/src/main/java/me/confuser/banmanager/velocity/listeners/WebhookListener.java +++ b/velocity/src/main/java/me/confuser/banmanager/velocity/listeners/WebhookListener.java @@ -4,7 +4,7 @@ import me.confuser.banmanager.velocity.Listener; import me.confuser.banmanager.common.BanManagerPlugin; import me.confuser.banmanager.common.listeners.CommonWebhookListener; -import me.confuser.banmanager.common.listeners.CommonWebhookListener.WebhookData; +import me.confuser.banmanager.common.data.Webhook; import com.velocitypowered.api.event.Subscribe; import com.velocitypowered.api.event.PostOrder; @@ -19,62 +19,62 @@ public WebhookListener(BanManagerPlugin plugin) { @Subscribe(order = PostOrder.LAST) public void notifyOnBan(PlayerBannedEvent event) { - List webhooks = listener.notifyOnBan(event.getBan()); + List webhooks = listener.notifyOnBan(event.getBan()); sendAll(webhooks, event.isSilent()); } @Subscribe(order = PostOrder.LAST) public void notifyOnMute(PlayerMutedEvent event) { - List webhooks = listener.notifyOnMute(event.getMute()); + List webhooks = listener.notifyOnMute(event.getMute()); sendAll(webhooks, event.isSilent()); } @Subscribe(order = PostOrder.LAST) public void notifyOnBan(IpBannedEvent event) { - List webhooks = listener.notifyOnBan(event.getBan()); + List webhooks = listener.notifyOnBan(event.getBan()); sendAll(webhooks, event.isSilent()); } @Subscribe(order = PostOrder.LAST) public void notifyOnKick(PlayerKickedEvent event) { - List webhooks = listener.notifyOnKick(event.getKick()); + List webhooks = listener.notifyOnKick(event.getKick()); sendAll(webhooks, event.isSilent()); } @Subscribe(order = PostOrder.LAST) public void notifyOnWarn(PlayerWarnedEvent event) { - List webhooks = listener.notifyOnWarn(event.getWarning()); + List webhooks = listener.notifyOnWarn(event.getWarning()); sendAll(webhooks, event.isSilent()); } @Subscribe(order = PostOrder.LAST) public void notifyOnUnban(PlayerUnbanEvent event) { - List webhooks = listener.notifyOnUnban(event.getBan(), event.getActor(), event.getReason()); + List webhooks = listener.notifyOnUnban(event.getBan(), event.getActor(), event.getReason()); sendAll(webhooks, event.isSilent()); } @Subscribe(order = PostOrder.LAST) public void notifyOnUnban(IpUnbanEvent event) { - List webhooks = listener.notifyOnUnban(event.getBan(), event.getActor(), event.getReason()); + List webhooks = listener.notifyOnUnban(event.getBan(), event.getActor(), event.getReason()); sendAll(webhooks, event.isSilent()); } @Subscribe(order = PostOrder.LAST) public void notifyOnUnmute(PlayerUnmuteEvent event) { - List webhooks = listener.notifyOnUnmute(event.getMute(), event.getActor(), event.getReason()); + List webhooks = listener.notifyOnUnmute(event.getMute(), event.getActor(), event.getReason()); sendAll(webhooks, event.isSilent()); } @Subscribe(order = PostOrder.LAST) public void notifyOnReport(PlayerReportedEvent event) { - List webhooks = listener.notifyOnReport(event.getReport(), event.getReport().getActor(), event.getReport().getReason()); + List webhooks = listener.notifyOnReport(event.getReport(), event.getReport().getActor(), event.getReport().getReason()); sendAll(webhooks, event.isSilent()); } - private void sendAll(List webhooks, boolean isSilent) { - for (WebhookData data : webhooks) { - if (isSilent && data.ignoreSilent) continue; - if (data.url == null || data.payload == null || data.url.isEmpty() || data.payload.isEmpty()) continue; + private void sendAll(List webhooks, boolean isSilent) { + for (Webhook data : webhooks) { + if (isSilent && data.ignoreSilent()) continue; + if (data.url() == null || data.payload() == null || data.url().isEmpty() || data.payload().isEmpty()) continue; listener.sendAsync(data); } } From 3c727c60cdf3737c3a03d0446231e3e1bf3dc882 Mon Sep 17 00:00:00 2001 From: James Mortemore Date: Sat, 18 Apr 2026 20:24:59 +0100 Subject: [PATCH 2/4] fix(build): preserve relocated META-INF/services in shaded plugin JARs The libs ShadowJar and every platform ShadowJar were excluding META-INF/services/** wholesale. That worked fine for MariaDB Connector/J 2.x and HikariCP 4.x, which registered codecs and drivers via static initialisers. After the modernisation bump to mariadb-java-client 3.5.7 those pieces are loaded via ServiceLoader and the missing META-INF/services entries surfaced at runtime as: java.sql.SQLException: Type java.lang.Boolean not supported type at me.confuser.banmanager.common.mariadb.BasePreparedStatement .trySetWithCodec(BasePreparedStatement.java:1331) (every INSERT against a MariaDB server failed because the BooleanCodec service entry was stripped from the relocated mariadb.plugin.Codec service file). Fix: - libs ShadowJar: drop the blanket META-INF/services/** exclusion and add mergeServiceFiles() so each shaded dep's service file is merged into a single relocated entry. Verified contents include the java.sql.Driver registrations for the three drivers and ~30 mariadb codec / auth / credential plugin entries, all rewritten to the me.confuser.banmanager.common.* package. - bukkit / bungee / velocity / sponge / fabric ShadowJars: drop the same exclusion, add mergeServiceFiles(), and exclude :BanManagerLibs from minimisation so the codec / driver classes referenced only by the merged service files are not pruned. (For bukkit the minimisation list now also names the JDBC drivers, HikariCP and ORMLite explicitly to match the bukkit-specific include list.) Local verification on all five plugin JARs: - META-INF/services/java.sql.Driver lists the three relocated driver classes. - META-INF/services/me.confuser.banmanager.common.mariadb.plugin.Codec carries the full codec list. - BooleanCodec.class is present (5441 bytes on JVM-only platforms, 5590 after Fabric remap). E2E: the previous run failed on every platform with the BooleanCodec error; this commit should restore the green state. --- bukkit/build.gradle.kts | 9 +++++++++ bungee/build.gradle.kts | 5 ++++- fabric/build.gradle.kts | 8 ++++++-- libs/build.gradle.kts | 8 +++++++- sponge/build.gradle.kts | 4 +++- velocity/build.gradle.kts | 5 ++++- 6 files changed, 33 insertions(+), 6 deletions(-) diff --git a/bukkit/build.gradle.kts b/bukkit/build.gradle.kts index 34796b7cf..ad83bdd25 100644 --- a/bukkit/build.gradle.kts +++ b/bukkit/build.gradle.kts @@ -145,6 +145,15 @@ tasks.named("shadowJar") { minimize { exclude(dependency("org.bstats:.*:.*")) exclude(dependency("org.slf4j:.*:.*")) + // JDBC drivers, HikariCP and ORMLite load codecs / dialects / drivers + // via ServiceLoader. Static analysis can't see those references, so + // exclude them from minimisation to avoid stripping classes that are + // listed in the merged META-INF/services entries. + exclude(dependency("org.mariadb.jdbc:mariadb-java-client:.*")) + exclude(dependency("com.mysql:mysql-connector-j:.*")) + exclude(dependency("com.zaxxer:HikariCP:.*")) + exclude(dependency("com.j256.ormlite:.*:.*")) + exclude(dependency("com.h2database:.*:.*")) } } diff --git a/bungee/build.gradle.kts b/bungee/build.gradle.kts index 0f503e056..be115d76e 100644 --- a/bungee/build.gradle.kts +++ b/bungee/build.gradle.kts @@ -99,10 +99,12 @@ tasks.named("shadowJar") { relocate("org.bstats", "me.confuser.banmanager.common.bstats") } + + mergeServiceFiles() + exclude("GradleStart**") exclude(".cache"); exclude("LICENSE*") - exclude("META-INF/services/**") exclude("META-INF/maven/**") exclude("org/intellij/**") exclude("org/jetbrains/**") @@ -110,6 +112,7 @@ tasks.named("shadowJar") { minimize { exclude(dependency("org.bstats:.*:.*")) + exclude(dependency(":BanManagerLibs")) } } diff --git a/fabric/build.gradle.kts b/fabric/build.gradle.kts index e86f86de7..84cbd29e9 100644 --- a/fabric/build.gradle.kts +++ b/fabric/build.gradle.kts @@ -169,16 +169,20 @@ tasks.named("shadowJar") { include(dependency(":BanManagerCommon")) include(dependency(":BanManagerLibs")) } + + mergeServiceFiles() + exclude("GradleStart**") exclude(".cache"); exclude("LICENSE*") - exclude("META-INF/services/**") exclude("META-INF/maven/**") exclude("org/intellij/**") exclude("org/jetbrains/**") exclude("/mappings/*") - minimize() + minimize { + exclude(dependency(":BanManagerLibs")) + } } tasks.named("remapJar") { diff --git a/libs/build.gradle.kts b/libs/build.gradle.kts index 8178fe805..cf3dcabc6 100644 --- a/libs/build.gradle.kts +++ b/libs/build.gradle.kts @@ -119,10 +119,16 @@ tasks.named("jar") { } } + // MariaDB Connector/J 3.x, MySQL Connector/J 8.x, HikariCP and others + // discover codecs / credential plugins / drivers via ServiceLoader. Keep + // META-INF/services and let the shadow task merge + relocate the entries + // alongside the package renames (e.g. org.mariadb.jdbc.plugin.codec.* -> + // me.confuser.banmanager.common.mariadb.plugin.codec.*). + mergeServiceFiles() + exclude("GradleStart**") exclude(".cache"); exclude("LICENSE*") - exclude("META-INF/services/**") exclude("META-INF/maven/**") exclude("org/intellij/**") exclude("org/jetbrains/**") diff --git a/sponge/build.gradle.kts b/sponge/build.gradle.kts index 34c51f23a..ada853a3e 100644 --- a/sponge/build.gradle.kts +++ b/sponge/build.gradle.kts @@ -138,10 +138,11 @@ tasks.named("shadowJar") { relocate("org.bstats", "me.confuser.banmanager.common.bstats") } + mergeServiceFiles() + exclude("GradleStart**") exclude(".cache"); exclude("LICENSE*") - exclude("META-INF/services/**") exclude("META-INF/maven/**") exclude("META-INF/versions/**") exclude("org/intellij/**") @@ -153,6 +154,7 @@ tasks.named("shadowJar") { minimize { exclude(dependency("org.bstats:.*:.*")) + exclude(dependency(":BanManagerLibs")) } } diff --git a/velocity/build.gradle.kts b/velocity/build.gradle.kts index 1afc911c2..15a6bf6c1 100644 --- a/velocity/build.gradle.kts +++ b/velocity/build.gradle.kts @@ -96,16 +96,19 @@ tasks.named("shadowJar") { relocate("org.bstats", "me.confuser.banmanager.common.bstats") } + + mergeServiceFiles() + exclude("GradleStart**") exclude(".cache") exclude("LICENSE*") - exclude("META-INF/services/**") exclude("META-INF/maven/**") exclude("org/intellij/**") exclude("org/jetbrains/**") minimize { exclude(dependency("org.bstats:.*:.*")) + exclude(dependency(":BanManagerLibs")) } } From 3d9a1dd5d2a86ba6e3ca09e9f8b5243ae281f46f Mon Sep 17 00:00:00 2001 From: James Mortemore Date: Sat, 18 Apr 2026 20:38:44 +0100 Subject: [PATCH 3/4] fix(build): drop bundled SLF4J provider entry on non-Bukkit platforms The BanManagerSlf4jServiceProvider implementation lives only in bukkit/src/main/java/org/slf4j/impl/ and is renamed to me.confuser.banmanager.common.slf4j.* by Bukkit's shadowJar relocation. Other platforms inherit the bundled META-INF/services/org.slf4j.spi.SLF4JServiceProvider entry from common (since bukkit/src/main/resources is a symlink to common/src/main/resources) without ever shipping the implementation class. On Fabric this crashes Minecraft's bootstrap when LogUtils initialises SLF4J because the registered provider class can't be loaded. Exclude org.slf4j.spi.SLF4JServiceProvider from the Fabric, Bungee, Velocity and Sponge shaded JARs so they fall back to the SLF4J 2.x implementation already provided by their host platform. --- bungee/build.gradle.kts | 4 ++++ fabric/build.gradle.kts | 5 +++++ sponge/build.gradle.kts | 4 ++++ velocity/build.gradle.kts | 4 ++++ 4 files changed, 17 insertions(+) diff --git a/bungee/build.gradle.kts b/bungee/build.gradle.kts index be115d76e..22c174db2 100644 --- a/bungee/build.gradle.kts +++ b/bungee/build.gradle.kts @@ -106,6 +106,10 @@ tasks.named("shadowJar") { exclude(".cache"); exclude("LICENSE*") exclude("META-INF/maven/**") + // BanManagerSlf4jServiceProvider only ships in (and is relocated by) Bukkit. + // BungeeCord provides its own SLF4J 2.x implementation, so removing the + // bundled provider entry avoids a ServiceConfigurationError at startup. + exclude("META-INF/services/org.slf4j.spi.SLF4JServiceProvider") exclude("org/intellij/**") exclude("org/jetbrains/**") exclude("velocity.yml") diff --git a/fabric/build.gradle.kts b/fabric/build.gradle.kts index 84cbd29e9..a4807145f 100644 --- a/fabric/build.gradle.kts +++ b/fabric/build.gradle.kts @@ -176,6 +176,11 @@ tasks.named("shadowJar") { exclude(".cache"); exclude("LICENSE*") exclude("META-INF/maven/**") + // BanManagerSlf4jServiceProvider only ships in (and is relocated by) Bukkit. + // Minecraft / Fabric provide their own SLF4J 2.x implementation, so removing + // the bundled provider entry avoids a ServiceConfigurationError at server + // bootstrap (LogUtils initialises SLF4J before BanManager's mod loads). + exclude("META-INF/services/org.slf4j.spi.SLF4JServiceProvider") exclude("org/intellij/**") exclude("org/jetbrains/**") exclude("/mappings/*") diff --git a/sponge/build.gradle.kts b/sponge/build.gradle.kts index ada853a3e..9e43cbe1f 100644 --- a/sponge/build.gradle.kts +++ b/sponge/build.gradle.kts @@ -144,6 +144,10 @@ tasks.named("shadowJar") { exclude(".cache"); exclude("LICENSE*") exclude("META-INF/maven/**") + // BanManagerSlf4jServiceProvider only ships in (and is relocated by) Bukkit. + // Sponge provides its own SLF4J 2.x implementation, so removing the + // bundled provider entry avoids a ServiceConfigurationError at startup. + exclude("META-INF/services/org.slf4j.spi.SLF4JServiceProvider") exclude("META-INF/versions/**") exclude("org/intellij/**") exclude("org/jetbrains/**") diff --git a/velocity/build.gradle.kts b/velocity/build.gradle.kts index 15a6bf6c1..8168bd78e 100644 --- a/velocity/build.gradle.kts +++ b/velocity/build.gradle.kts @@ -103,6 +103,10 @@ tasks.named("shadowJar") { exclude(".cache") exclude("LICENSE*") exclude("META-INF/maven/**") + // BanManagerSlf4jServiceProvider only ships in (and is relocated by) Bukkit. + // Velocity provides its own SLF4J 2.x implementation, so removing the + // bundled provider entry avoids a ServiceConfigurationError at startup. + exclude("META-INF/services/org.slf4j.spi.SLF4JServiceProvider") exclude("org/intellij/**") exclude("org/jetbrains/**") From b0884a4120938d95be34581c39537603d7c09c43 Mon Sep 17 00:00:00 2001 From: James Mortemore Date: Sat, 18 Apr 2026 21:03:19 +0100 Subject: [PATCH 4/4] chore: remove UPGRADE.md Not needed for this release. --- UPGRADE.md | 72 ------------------------------------------------------ 1 file changed, 72 deletions(-) delete mode 100644 UPGRADE.md diff --git a/UPGRADE.md b/UPGRADE.md deleted file mode 100644 index 79852f110..000000000 --- a/UPGRADE.md +++ /dev/null @@ -1,72 +0,0 @@ -# Upgrade Notes - -End-user-visible changes that may require attention when upgrading. Versioned -changes are listed newest-first. - -## Java 17 modernisation - -This release modernises the codebase from Java 8 to Java 17 and bumps a number -of bundled dependencies. Most installs upgrade transparently, but the items -below are worth checking. - -### Required runtime: Java 17 or newer - -- BanManager now requires **Java 17+** at runtime (Java 21 to build from - source). -- This matches the supported runtimes for Spigot/Paper 1.20+, Velocity 3.3+, - and Sponge API 8+, so no action is normally required for modern servers. -- If you are still running Java 8/11 you must update your JRE before installing - this version. - -### MariaDB JDBC driver upgraded to 3.x - -- The bundled `mariadb-java-client` is now `3.5.x`. The 3.x driver no longer - hijacks `jdbc:mysql://` URLs, and it warns about legacy parameters that the - 2.x line silently accepted. -- BanManager now builds a per-driver JDBC URL automatically based on - `storageType` in `config.yml`, so you should not see `WARN` lines about - unknown options like `autoReconnect`, `serverTimezone`, or - `verifyServerCertificate` after upgrading. -- `useSSL` and `verifyServerCertificate` are translated to MariaDB's - `sslMode` (`disable`, `trust`, or `verify-full`). No `config.yml` changes - are required. -- If you previously set `storageType: mysql` but pointed at a MariaDB server, - consider switching to `storageType: mariadb` so the correct driver is used. - -### MySQL Connector/J upgraded to 8.4.x - -- The shaded `mysql-connector-j` is now `8.4.0`, replacing the legacy - `mysql-connector-java` artifact. -- The legacy `&disableMariaDbDriver` URL fragment has been removed because the - modern MariaDB driver no longer needs to be opted out. - -### SnakeYAML upgraded to 2.x - -- The bundled SnakeYAML jumped from `1.29` to `2.4`. SnakeYAML 2.x flips a - handful of defaults that could otherwise break existing user-edited - configs. BanManager pre-configures the loader to keep the old behaviour: - - `allowDuplicateKeys` is forced back to `true` so a duplicate key in - `messages.yml` won't refuse to load (the last value wins, as before). - - `codePointLimit` is raised to 32 MB so very large translation files keep - loading. - - `nestingDepthLimit` is raised to 100 for deeply nested webhook payloads. -- If you intentionally relied on duplicate-key detection, consider linting - your YAML separately. - -### Bundled bStats / PlaceholderAPI upgrades - -- bStats was bumped to `3.2.1` across all platforms. -- PlaceholderAPI was bumped to `2.12.2` (Bukkit only, soft-dependency). - -### SLF4J upgraded to 2.x on Bukkit - -- The Bukkit module now ships an SLF4J 2.x service-provider implementation so - that ORMLite and HikariCP log through BanManager's own logger rather than - the generic console. -- `disableDatabaseLogging()` is now a no-op on Bukkit (the new provider - filters log levels itself). - -### Tests: JUnit 5 + Mockito 5 - -- Internal change only - the test suite migrated from JUnit 4/Mockito 3 to - JUnit 5/Mockito 5. No effect on the runtime jar.