mirror of
https://github.com/LuckPerms/LuckPerms.git
synced 2025-01-28 11:11:52 +01:00
Add integration tests for storage and messaging
This commit is contained in:
parent
6523e708a1
commit
4068c71d5a
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@ -32,13 +32,15 @@ jobs:
|
|||||||
uses: gradle/gradle-build-action@v2
|
uses: gradle/gradle-build-action@v2
|
||||||
|
|
||||||
- name: Run build and tests with Gradle wrapper
|
- name: Run build and tests with Gradle wrapper
|
||||||
run: ./gradlew test build
|
run: ./gradlew test build -PdockerTests
|
||||||
|
|
||||||
- name: Publish test report
|
- name: Publish test report
|
||||||
uses: mikepenz/action-junit-report@v3
|
uses: mikepenz/action-junit-report@v3
|
||||||
if: success() || failure()
|
if: success() || failure()
|
||||||
with:
|
with:
|
||||||
report_paths: '**/build/test-results/test/TEST-*.xml'
|
report_paths: '**/build/test-results/test/TEST-*.xml'
|
||||||
|
annotate_notice: true
|
||||||
|
detailed_summary: true
|
||||||
|
|
||||||
- name: Upload all artifacts
|
- name: Upload all artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
|
@ -36,6 +36,12 @@ cd LuckPerms/
|
|||||||
|
|
||||||
You can find the output jars in the `loader/build/libs` or `build/libs` directories.
|
You can find the output jars in the `loader/build/libs` or `build/libs` directories.
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
There are some automated tests which run during each build.
|
||||||
|
|
||||||
|
* Unit tests are defined in [`common/src/test`](https://github.com/LuckPerms/LuckPerms/tree/master/common/src/test)
|
||||||
|
* Integration tests are defined in [`standalone/src/test`](https://github.com/LuckPerms/LuckPerms/tree/master/standalone/src/test).
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
#### Pull Requests
|
#### Pull Requests
|
||||||
If you make any changes or improvements to the plugin which you think would be beneficial to others, please consider making a pull request to merge your changes back into the upstream project. (especially if your changes are bug fixes!)
|
If you make any changes or improvements to the plugin which you think would be beneficial to others, please consider making a pull request to merge your changes back into the upstream project. (especially if your changes are bug fixes!)
|
||||||
|
@ -706,6 +706,11 @@ public final class ConfigKeys {
|
|||||||
*/
|
*/
|
||||||
public static final ConfigKey<String> RABBITMQ_PASSWORD = notReloadable(stringKey("rabbitmq.password", "guest"));
|
public static final ConfigKey<String> RABBITMQ_PASSWORD = notReloadable(stringKey("rabbitmq.password", "guest"));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the editor key should be generated lazily (only when needed)
|
||||||
|
*/
|
||||||
|
public static final ConfigKey<Boolean> EDITOR_LAZILY_GENERATE_KEY = booleanKey("editor-lazily-generate-key", false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The URL of the bytebin instance used to upload data
|
* The URL of the bytebin instance used to upload data
|
||||||
*/
|
*/
|
||||||
|
@ -37,6 +37,7 @@ import com.rabbitmq.client.DeliverCallback;
|
|||||||
import com.rabbitmq.client.Delivery;
|
import com.rabbitmq.client.Delivery;
|
||||||
|
|
||||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||||
|
import me.lucko.luckperms.common.plugin.scheduler.SchedulerTask;
|
||||||
|
|
||||||
import net.luckperms.api.messenger.IncomingMessageConsumer;
|
import net.luckperms.api.messenger.IncomingMessageConsumer;
|
||||||
import net.luckperms.api.messenger.Messenger;
|
import net.luckperms.api.messenger.Messenger;
|
||||||
@ -44,6 +45,8 @@ import net.luckperms.api.messenger.message.OutgoingMessage;
|
|||||||
|
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An implementation of {@link Messenger} using RabbitMQ.
|
* An implementation of {@link Messenger} using RabbitMQ.
|
||||||
*/
|
*/
|
||||||
@ -62,6 +65,7 @@ public class RabbitMQMessenger implements Messenger {
|
|||||||
private Connection connection;
|
private Connection connection;
|
||||||
private Channel channel;
|
private Channel channel;
|
||||||
private Subscription sub;
|
private Subscription sub;
|
||||||
|
private SchedulerTask checkConnectionTask;
|
||||||
|
|
||||||
public RabbitMQMessenger(LuckPermsPlugin plugin, IncomingMessageConsumer consumer) {
|
public RabbitMQMessenger(LuckPermsPlugin plugin, IncomingMessageConsumer consumer) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
@ -81,7 +85,8 @@ public class RabbitMQMessenger implements Messenger {
|
|||||||
this.connectionFactory.setPassword(password);
|
this.connectionFactory.setPassword(password);
|
||||||
|
|
||||||
this.sub = new Subscription();
|
this.sub = new Subscription();
|
||||||
this.plugin.getBootstrap().getScheduler().executeAsync(this.sub);
|
checkAndReopenConnection(true);
|
||||||
|
this.checkConnectionTask = this.plugin.getBootstrap().getScheduler().asyncRepeating(() -> checkAndReopenConnection(false), 5, TimeUnit.SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -100,7 +105,7 @@ public class RabbitMQMessenger implements Messenger {
|
|||||||
try {
|
try {
|
||||||
this.channel.close();
|
this.channel.close();
|
||||||
this.connection.close();
|
this.connection.close();
|
||||||
this.sub.isClosed = true;
|
this.checkConnectionTask.cancel();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
@ -159,30 +164,7 @@ public class RabbitMQMessenger implements Messenger {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Subscription implements Runnable, DeliverCallback {
|
private class Subscription implements DeliverCallback {
|
||||||
private boolean isClosed = false;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
boolean firstStartup = true;
|
|
||||||
while (!Thread.interrupted() && !this.isClosed) {
|
|
||||||
try {
|
|
||||||
if (!checkAndReopenConnection(firstStartup)) {
|
|
||||||
// Sleep for 5 seconds to prevent massive spam in console
|
|
||||||
Thread.sleep(5000);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check connection life every every 30 seconds
|
|
||||||
Thread.sleep(30_000);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
} finally {
|
|
||||||
firstStartup = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(String consumerTag, Delivery message) {
|
public void handle(String consumerTag, Delivery message) {
|
||||||
try {
|
try {
|
||||||
|
@ -25,6 +25,10 @@
|
|||||||
|
|
||||||
package me.lucko.luckperms.common.webeditor.store;
|
package me.lucko.luckperms.common.webeditor.store;
|
||||||
|
|
||||||
|
import com.google.common.base.Supplier;
|
||||||
|
import com.google.common.base.Suppliers;
|
||||||
|
|
||||||
|
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||||
import me.lucko.luckperms.common.webeditor.socket.CryptographyUtils;
|
import me.lucko.luckperms.common.webeditor.socket.CryptographyUtils;
|
||||||
|
|
||||||
@ -35,17 +39,29 @@ import java.util.concurrent.CompletableFuture;
|
|||||||
* Contains a store of known web editor sessions and provides a lookup function for
|
* Contains a store of known web editor sessions and provides a lookup function for
|
||||||
* trusted editor public keys.
|
* trusted editor public keys.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("Guava")
|
||||||
public class WebEditorStore {
|
public class WebEditorStore {
|
||||||
private final WebEditorSessionMap sessions;
|
private final WebEditorSessionMap sessions;
|
||||||
private final WebEditorSocketMap sockets;
|
private final WebEditorSocketMap sockets;
|
||||||
private final WebEditorKeystore keystore;
|
private final WebEditorKeystore keystore;
|
||||||
private final CompletableFuture<KeyPair> keyPair;
|
private final Supplier<CompletableFuture<KeyPair>> keyPair;
|
||||||
|
|
||||||
public WebEditorStore(LuckPermsPlugin plugin) {
|
public WebEditorStore(LuckPermsPlugin plugin) {
|
||||||
this.sessions = new WebEditorSessionMap();
|
this.sessions = new WebEditorSessionMap();
|
||||||
this.sockets = new WebEditorSocketMap();
|
this.sockets = new WebEditorSocketMap();
|
||||||
this.keystore = new WebEditorKeystore(plugin.getBootstrap().getConfigDirectory().resolve("editor-keystore.json"));
|
this.keystore = new WebEditorKeystore(plugin.getBootstrap().getConfigDirectory().resolve("editor-keystore.json"));
|
||||||
this.keyPair = CompletableFuture.supplyAsync(CryptographyUtils::generateKeyPair, plugin.getBootstrap().getScheduler().async());
|
|
||||||
|
Supplier<CompletableFuture<KeyPair>> keyPair = () -> CompletableFuture.supplyAsync(
|
||||||
|
CryptographyUtils::generateKeyPair,
|
||||||
|
plugin.getBootstrap().getScheduler().async()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (plugin.getConfiguration().get(ConfigKeys.EDITOR_LAZILY_GENERATE_KEY)) {
|
||||||
|
this.keyPair = Suppliers.memoize(keyPair);
|
||||||
|
} else {
|
||||||
|
CompletableFuture<KeyPair> future = keyPair.get();
|
||||||
|
this.keyPair = () -> future;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public WebEditorSessionMap sessions() {
|
public WebEditorSessionMap sessions() {
|
||||||
@ -61,10 +77,10 @@ public class WebEditorStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public KeyPair keyPair() {
|
public KeyPair keyPair() {
|
||||||
if (!this.keyPair.isDone()) {
|
if (!this.keyPair.get().isDone()) {
|
||||||
throw new IllegalStateException("Web editor keypair has not been generated yet! Has the server just started?");
|
throw new IllegalStateException("Web editor keypair has not been generated yet! Has the server just started?");
|
||||||
}
|
}
|
||||||
return this.keyPair.join();
|
return this.keyPair.get().join();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -130,6 +130,10 @@ public class LuckPermsApplication implements AutoCloseable {
|
|||||||
this.healthReporter = healthReporter;
|
this.healthReporter = healthReporter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LuckPerms getApi() {
|
||||||
|
return this.luckPermsApi;
|
||||||
|
}
|
||||||
|
|
||||||
public CommandExecutor getCommandExecutor() {
|
public CommandExecutor getCommandExecutor() {
|
||||||
return this.commandExecutor;
|
return this.commandExecutor;
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,11 @@ sourceCompatibility = 17
|
|||||||
targetCompatibility = 17
|
targetCompatibility = 17
|
||||||
|
|
||||||
test {
|
test {
|
||||||
useJUnitPlatform {}
|
useJUnitPlatform {
|
||||||
|
if (!project.hasProperty('dockerTests')) {
|
||||||
|
excludeTags 'docker'
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@ -19,9 +23,18 @@ dependencies {
|
|||||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.1'
|
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.1'
|
||||||
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.9.1'
|
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.9.1'
|
||||||
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.1'
|
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.1'
|
||||||
|
testImplementation "org.testcontainers:junit-jupiter:1.17.6"
|
||||||
testImplementation 'org.mockito:mockito-core:4.11.0'
|
testImplementation 'org.mockito:mockito-core:4.11.0'
|
||||||
testImplementation 'org.mockito:mockito-junit-jupiter:4.11.0'
|
testImplementation 'org.mockito:mockito-junit-jupiter:4.11.0'
|
||||||
|
|
||||||
testImplementation 'com.h2database:h2:2.1.214'
|
testImplementation 'com.h2database:h2:2.1.214'
|
||||||
|
testImplementation 'mysql:mysql-connector-java:8.0.23'
|
||||||
|
testImplementation 'org.mariadb.jdbc:mariadb-java-client:2.7.2'
|
||||||
|
testImplementation 'org.postgresql:postgresql:42.2.19'
|
||||||
|
testImplementation 'org.mongodb:mongodb-driver-sync:4.5.0'
|
||||||
|
testImplementation 'me.lucko.configurate:configurate-toml:3.7'
|
||||||
|
testImplementation 'org.spongepowered:configurate-hocon:3.7.2'
|
||||||
|
|
||||||
testImplementation project(':standalone:app')
|
testImplementation project(':standalone:app')
|
||||||
testImplementation project(':common:loader-utils')
|
testImplementation project(':common:loader-utils')
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,7 @@ public class LPStandaloneBootstrap implements LuckPermsBootstrap, LoaderBootstra
|
|||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
LPStandaloneBootstrap(LuckPermsApplication loader, ClassPathAppender classPathAppender) {
|
protected LPStandaloneBootstrap(LuckPermsApplication loader, ClassPathAppender classPathAppender) {
|
||||||
this.loader = loader;
|
this.loader = loader;
|
||||||
|
|
||||||
this.logger = new Log4jPluginLogger(LuckPermsApplication.LOGGER);
|
this.logger = new Log4jPluginLogger(LuckPermsApplication.LOGGER);
|
||||||
@ -82,7 +82,7 @@ public class LPStandaloneBootstrap implements LuckPermsBootstrap, LoaderBootstra
|
|||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
LPStandalonePlugin createTestPlugin() {
|
protected LPStandalonePlugin createTestPlugin() {
|
||||||
return new LPStandalonePlugin(this);
|
return new LPStandalonePlugin(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,9 +28,9 @@ package me.lucko.luckperms.standalone;
|
|||||||
import me.lucko.luckperms.common.config.ConfigKeys;
|
import me.lucko.luckperms.common.config.ConfigKeys;
|
||||||
import me.lucko.luckperms.common.model.Group;
|
import me.lucko.luckperms.common.model.Group;
|
||||||
import me.lucko.luckperms.common.node.types.Permission;
|
import me.lucko.luckperms.common.node.types.Permission;
|
||||||
import me.lucko.luckperms.standalone.app.LuckPermsApplication;
|
|
||||||
import me.lucko.luckperms.standalone.app.integration.CommandExecutor;
|
import me.lucko.luckperms.standalone.app.integration.CommandExecutor;
|
||||||
import me.lucko.luckperms.standalone.app.integration.HealthReporter;
|
import me.lucko.luckperms.standalone.app.integration.HealthReporter;
|
||||||
|
import me.lucko.luckperms.standalone.utils.TestPluginProvider;
|
||||||
|
|
||||||
import net.luckperms.api.model.data.DataType;
|
import net.luckperms.api.model.data.DataType;
|
||||||
import net.luckperms.api.node.NodeEqualityPredicate;
|
import net.luckperms.api.node.NodeEqualityPredicate;
|
||||||
@ -49,13 +49,11 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
|||||||
/**
|
/**
|
||||||
* A set of 'integration tests' for the standalone LuckPerms app.
|
* A set of 'integration tests' for the standalone LuckPerms app.
|
||||||
*/
|
*/
|
||||||
public class StandaloneIntegrationTests {
|
public class IntegrationTest {
|
||||||
|
|
||||||
private @TempDir Path tempDir;
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLoadEnableDisable() {
|
public void testLoadEnableDisable(@TempDir Path tempDir) {
|
||||||
useTestPlugin((app, bootstrap, plugin) -> {
|
TestPluginProvider.use(tempDir, (app, bootstrap, plugin) -> {
|
||||||
HealthReporter.Health health = app.getHealthReporter().poll();
|
HealthReporter.Health health = app.getHealthReporter().poll();
|
||||||
assertNotNull(health);
|
assertNotNull(health);
|
||||||
assertTrue(health.isUp());
|
assertTrue(health.isUp());
|
||||||
@ -63,8 +61,8 @@ public class StandaloneIntegrationTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRunCommand() {
|
public void testRunCommand(@TempDir Path tempDir) {
|
||||||
useTestPlugin((app, bootstrap, plugin) -> {
|
TestPluginProvider.use(tempDir, (app, bootstrap, plugin) -> {
|
||||||
CommandExecutor commandExecutor = app.getCommandExecutor();
|
CommandExecutor commandExecutor = app.getCommandExecutor();
|
||||||
commandExecutor.execute("group default permission set test").join();
|
commandExecutor.execute("group default permission set test").join();
|
||||||
|
|
||||||
@ -75,15 +73,15 @@ public class StandaloneIntegrationTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReloadConfig() throws IOException {
|
public void testReloadConfig(@TempDir Path tempDir) throws IOException {
|
||||||
useTestPlugin((app, bootstrap, plugin) -> {
|
TestPluginProvider.use(tempDir, (app, bootstrap, plugin) -> {
|
||||||
String server = plugin.getConfiguration().get(ConfigKeys.SERVER);
|
String server = plugin.getConfiguration().get(ConfigKeys.SERVER);
|
||||||
assertEquals("global", server);
|
assertEquals("global", server);
|
||||||
|
|
||||||
Integer syncTime = plugin.getConfiguration().get(ConfigKeys.SYNC_TIME);
|
Integer syncTime = plugin.getConfiguration().get(ConfigKeys.SYNC_TIME);
|
||||||
assertEquals(-1, syncTime);
|
assertEquals(-1, syncTime);
|
||||||
|
|
||||||
Path config = this.tempDir.resolve("config.yml");
|
Path config = tempDir.resolve("config.yml");
|
||||||
assertTrue(Files.exists(config));
|
assertTrue(Files.exists(config));
|
||||||
|
|
||||||
String configString = Files.readString(config)
|
String configString = Files.readString(config)
|
||||||
@ -101,22 +99,4 @@ public class StandaloneIntegrationTests {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private <E extends Throwable> void useTestPlugin(TestPluginConsumer<E> consumer) throws E {
|
|
||||||
LuckPermsApplication app = new LuckPermsApplication(() -> {});
|
|
||||||
LPStandaloneTestBootstrap bootstrap = new LPStandaloneTestBootstrap(app, this.tempDir);
|
|
||||||
|
|
||||||
bootstrap.onLoad();
|
|
||||||
bootstrap.onEnable();
|
|
||||||
|
|
||||||
try {
|
|
||||||
consumer.accept(app, bootstrap, bootstrap.getPlugin());
|
|
||||||
} finally {
|
|
||||||
bootstrap.onDisable();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TestPluginConsumer<E extends Throwable> {
|
|
||||||
void accept(LuckPermsApplication app, LPStandaloneTestBootstrap bootstrap, LPStandalonePlugin plugin) throws E;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,184 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of LuckPerms, licensed under the MIT License.
|
||||||
|
*
|
||||||
|
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||||
|
* Copyright (c) contributors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package me.lucko.luckperms.standalone;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
|
||||||
|
import me.lucko.luckperms.common.messaging.InternalMessagingService;
|
||||||
|
import me.lucko.luckperms.standalone.app.integration.HealthReporter;
|
||||||
|
import me.lucko.luckperms.standalone.utils.TestPluginProvider;
|
||||||
|
|
||||||
|
import net.luckperms.api.event.sync.PreNetworkSyncEvent;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Nested;
|
||||||
|
import org.junit.jupiter.api.Tag;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
import org.testcontainers.containers.GenericContainer;
|
||||||
|
import org.testcontainers.junit.jupiter.Container;
|
||||||
|
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||||
|
import org.testcontainers.utility.DockerImageName;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
@Testcontainers
|
||||||
|
@Tag("docker")
|
||||||
|
public class MessagingIntegrationTest {
|
||||||
|
|
||||||
|
private static void testMessaging(Map<String, String> config, Path tempDirA, Path tempDirB) throws InterruptedException {
|
||||||
|
try (TestPluginProvider.Plugin pluginA = TestPluginProvider.create(tempDirA, config);
|
||||||
|
TestPluginProvider.Plugin pluginB = TestPluginProvider.create(tempDirB, config)) {
|
||||||
|
|
||||||
|
// check the plugins are healthy
|
||||||
|
HealthReporter.Health healthA = pluginA.app().getHealthReporter().poll();
|
||||||
|
assertNotNull(healthA);
|
||||||
|
assertTrue(healthA.isUp());
|
||||||
|
|
||||||
|
HealthReporter.Health healthB = pluginB.app().getHealthReporter().poll();
|
||||||
|
assertNotNull(healthB);
|
||||||
|
assertTrue(healthB.isUp());
|
||||||
|
|
||||||
|
InternalMessagingService messagingServiceA = pluginA.plugin().getMessagingService().orElse(null);
|
||||||
|
InternalMessagingService messagingServiceB = pluginB.plugin().getMessagingService().orElse(null);
|
||||||
|
assertNotNull(messagingServiceA);
|
||||||
|
assertNotNull(messagingServiceB);
|
||||||
|
|
||||||
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
pluginB.app().getApi().getEventBus().subscribe(PreNetworkSyncEvent.class, e -> latch.countDown());
|
||||||
|
|
||||||
|
// send a message from plugin A to plugin B and wait for the message to be received
|
||||||
|
messagingServiceA.pushUpdate();
|
||||||
|
assertTrue(latch.await(30, TimeUnit.SECONDS));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class Sql {
|
||||||
|
|
||||||
|
@Container
|
||||||
|
private final GenericContainer<?> container = new GenericContainer<>(DockerImageName.parse("mysql:8"))
|
||||||
|
.withEnv("MYSQL_DATABASE", "minecraft")
|
||||||
|
.withEnv("MYSQL_ROOT_PASSWORD", "passw0rd")
|
||||||
|
.withExposedPorts(3306);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMySql(@TempDir Path tempDirA, @TempDir Path tempDirB) throws InterruptedException {
|
||||||
|
assertTrue(this.container.isRunning());
|
||||||
|
|
||||||
|
String host = this.container.getHost();
|
||||||
|
Integer port = this.container.getFirstMappedPort();
|
||||||
|
|
||||||
|
Map<String, String> config = ImmutableMap.<String, String>builder()
|
||||||
|
.put("storage-method", "mysql")
|
||||||
|
.put("data.address", host + ":" + port)
|
||||||
|
.put("data.database", "minecraft")
|
||||||
|
.put("data.username", "root")
|
||||||
|
.put("data.password", "passw0rd")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
testMessaging(config, tempDirA, tempDirB);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class Redis {
|
||||||
|
|
||||||
|
@Container
|
||||||
|
private final GenericContainer<?> container = new GenericContainer<>(DockerImageName.parse("redis"))
|
||||||
|
.withExposedPorts(6379);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRedis(@TempDir Path tempDirA, @TempDir Path tempDirB) throws InterruptedException {
|
||||||
|
assertTrue(this.container.isRunning());
|
||||||
|
|
||||||
|
String host = this.container.getHost();
|
||||||
|
Integer port = this.container.getFirstMappedPort();
|
||||||
|
|
||||||
|
Map<String, String> config = ImmutableMap.<String, String>builder()
|
||||||
|
.put("messaging-service", "redis")
|
||||||
|
.put("redis.enabled", "true")
|
||||||
|
.put("redis.address", host + ":" + port)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
testMessaging(config, tempDirA, tempDirB);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class RabbitMq {
|
||||||
|
|
||||||
|
@Container
|
||||||
|
private final GenericContainer<?> container = new GenericContainer<>(DockerImageName.parse("rabbitmq"))
|
||||||
|
.withExposedPorts(5672);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRabbitMq(@TempDir Path tempDirA, @TempDir Path tempDirB) throws InterruptedException {
|
||||||
|
assertTrue(this.container.isRunning());
|
||||||
|
|
||||||
|
String host = this.container.getHost();
|
||||||
|
Integer port = this.container.getFirstMappedPort();
|
||||||
|
|
||||||
|
Map<String, String> config = ImmutableMap.<String, String>builder()
|
||||||
|
.put("messaging-service", "rabbitmq")
|
||||||
|
.put("rabbitmq.enabled", "true")
|
||||||
|
.put("rabbitmq.address", host + ":" + port)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
testMessaging(config, tempDirA, tempDirB);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class Nats {
|
||||||
|
|
||||||
|
@Container
|
||||||
|
private final GenericContainer<?> container = new GenericContainer<>(DockerImageName.parse("nats"))
|
||||||
|
.withExposedPorts(4222);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNats(@TempDir Path tempDirA, @TempDir Path tempDirB) throws InterruptedException {
|
||||||
|
assertTrue(this.container.isRunning());
|
||||||
|
|
||||||
|
String host = this.container.getHost();
|
||||||
|
Integer port = this.container.getFirstMappedPort();
|
||||||
|
|
||||||
|
Map<String, String> config = ImmutableMap.<String, String>builder()
|
||||||
|
.put("messaging-service", "nats")
|
||||||
|
.put("nats.enabled", "true")
|
||||||
|
.put("nats.address", host + ":" + port)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
testMessaging(config, tempDirA, tempDirB);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,260 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of LuckPerms, licensed under the MIT License.
|
||||||
|
*
|
||||||
|
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||||
|
* Copyright (c) contributors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package me.lucko.luckperms.standalone;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
|
||||||
|
import me.lucko.luckperms.common.model.Group;
|
||||||
|
import me.lucko.luckperms.common.node.types.Inheritance;
|
||||||
|
import me.lucko.luckperms.common.node.types.Permission;
|
||||||
|
import me.lucko.luckperms.standalone.app.LuckPermsApplication;
|
||||||
|
import me.lucko.luckperms.standalone.app.integration.HealthReporter;
|
||||||
|
import me.lucko.luckperms.standalone.utils.TestPluginBootstrap;
|
||||||
|
import me.lucko.luckperms.standalone.utils.TestPluginBootstrap.TestPlugin;
|
||||||
|
import me.lucko.luckperms.standalone.utils.TestPluginProvider;
|
||||||
|
|
||||||
|
import net.luckperms.api.event.cause.CreationCause;
|
||||||
|
import net.luckperms.api.model.data.DataType;
|
||||||
|
import net.luckperms.api.node.Node;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Nested;
|
||||||
|
import org.junit.jupiter.api.Tag;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
import org.testcontainers.containers.GenericContainer;
|
||||||
|
import org.testcontainers.junit.jupiter.Container;
|
||||||
|
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||||
|
import org.testcontainers.utility.DockerImageName;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
@Testcontainers
|
||||||
|
@Tag("docker")
|
||||||
|
public class StorageIntegrationTest {
|
||||||
|
|
||||||
|
private static final Node TEST_PERMISSION = Permission.builder()
|
||||||
|
.permission("test")
|
||||||
|
.value(false)
|
||||||
|
.expiry(1, TimeUnit.HOURS)
|
||||||
|
.withContext("server", "foo")
|
||||||
|
.withContext("world", "bar")
|
||||||
|
.withContext("test", "test")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
private static final Node TEST_GROUP = Inheritance.builder()
|
||||||
|
.group("default")
|
||||||
|
.value(false)
|
||||||
|
.expiry(1, TimeUnit.HOURS)
|
||||||
|
.withContext("server", "foo")
|
||||||
|
.withContext("world", "bar")
|
||||||
|
.withContext("test", "test")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
|
||||||
|
private static void testStorage(LuckPermsApplication app, TestPluginBootstrap bootstrap, TestPlugin plugin) {
|
||||||
|
// check the plugin is healthy
|
||||||
|
HealthReporter.Health health = app.getHealthReporter().poll();
|
||||||
|
assertNotNull(health);
|
||||||
|
assertTrue(health.isUp());
|
||||||
|
|
||||||
|
// try to create / save a group
|
||||||
|
Group group = plugin.getStorage().createAndLoadGroup("test", CreationCause.INTERNAL).join();
|
||||||
|
group.setNode(DataType.NORMAL, TEST_PERMISSION, true);
|
||||||
|
group.setNode(DataType.NORMAL, TEST_GROUP, true);
|
||||||
|
plugin.getStorage().saveGroup(group).join();
|
||||||
|
|
||||||
|
plugin.getStorage().loadAllGroups().join();
|
||||||
|
|
||||||
|
Group testGroup = plugin.getGroupManager().getIfLoaded("test");
|
||||||
|
assertNotNull(testGroup);
|
||||||
|
|
||||||
|
assertEquals(ImmutableSet.of(TEST_PERMISSION, TEST_GROUP), testGroup.normalData().asSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class MySql {
|
||||||
|
|
||||||
|
@Container
|
||||||
|
private final GenericContainer<?> container = new GenericContainer<>(DockerImageName.parse("mysql:8"))
|
||||||
|
.withEnv("MYSQL_DATABASE", "minecraft")
|
||||||
|
.withEnv("MYSQL_ROOT_PASSWORD", "passw0rd")
|
||||||
|
.withExposedPorts(3306);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMySql(@TempDir Path tempDir) {
|
||||||
|
assertTrue(this.container.isRunning());
|
||||||
|
|
||||||
|
String host = this.container.getHost();
|
||||||
|
Integer port = this.container.getFirstMappedPort();
|
||||||
|
|
||||||
|
Map<String, String> config = ImmutableMap.<String, String>builder()
|
||||||
|
.put("storage-method", "mysql")
|
||||||
|
.put("data.address", host + ":" + port)
|
||||||
|
.put("data.database", "minecraft")
|
||||||
|
.put("data.username", "root")
|
||||||
|
.put("data.password", "passw0rd")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
TestPluginProvider.use(tempDir, config, StorageIntegrationTest::testStorage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class MariaDb {
|
||||||
|
|
||||||
|
@Container
|
||||||
|
private final GenericContainer<?> container = new GenericContainer<>(DockerImageName.parse("mariadb"))
|
||||||
|
.withEnv("MARIADB_USER", "minecraft")
|
||||||
|
.withEnv("MARIADB_PASSWORD", "passw0rd")
|
||||||
|
.withEnv("MARIADB_ROOT_PASSWORD", "rootpassw0rd")
|
||||||
|
.withEnv("MARIADB_DATABASE", "minecraft")
|
||||||
|
.withExposedPorts(3306);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMariaDb(@TempDir Path tempDir) {
|
||||||
|
assertTrue(this.container.isRunning());
|
||||||
|
|
||||||
|
String host = this.container.getHost();
|
||||||
|
Integer port = this.container.getFirstMappedPort();
|
||||||
|
|
||||||
|
Map<String, String> config = ImmutableMap.<String, String>builder()
|
||||||
|
.put("storage-method", "mariadb")
|
||||||
|
.put("data.address", host + ":" + port)
|
||||||
|
.put("data.database", "minecraft")
|
||||||
|
.put("data.username", "minecraft")
|
||||||
|
.put("data.password", "passw0rd")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
TestPluginProvider.use(tempDir, config, StorageIntegrationTest::testStorage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class Postgres {
|
||||||
|
|
||||||
|
@Container
|
||||||
|
private final GenericContainer<?> container = new GenericContainer<>(DockerImageName.parse("postgres"))
|
||||||
|
.withEnv("POSTGRES_PASSWORD", "passw0rd")
|
||||||
|
.withExposedPorts(5432);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPostgres(@TempDir Path tempDir) {
|
||||||
|
assertTrue(this.container.isRunning());
|
||||||
|
|
||||||
|
String host = this.container.getHost();
|
||||||
|
Integer port = this.container.getFirstMappedPort();
|
||||||
|
|
||||||
|
Map<String, String> config = ImmutableMap.<String, String>builder()
|
||||||
|
.put("storage-method", "postgresql")
|
||||||
|
.put("data.address", host + ":" + port)
|
||||||
|
.put("data.database", "postgres")
|
||||||
|
.put("data.username", "postgres")
|
||||||
|
.put("data.password", "passw0rd")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
TestPluginProvider.use(tempDir, config, StorageIntegrationTest::testStorage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class MongoDb {
|
||||||
|
|
||||||
|
@Container
|
||||||
|
private final GenericContainer<?> container = new GenericContainer<>(DockerImageName.parse("mongo"))
|
||||||
|
.withExposedPorts(27017);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMongo(@TempDir Path tempDir) {
|
||||||
|
assertTrue(this.container.isRunning());
|
||||||
|
|
||||||
|
String host = this.container.getHost();
|
||||||
|
Integer port = this.container.getFirstMappedPort();
|
||||||
|
|
||||||
|
Map<String, String> config = ImmutableMap.<String, String>builder()
|
||||||
|
.put("storage-method", "mongodb")
|
||||||
|
.put("data.address", host + ":" + port)
|
||||||
|
.put("data.database", "minecraft")
|
||||||
|
.put("data.username", "")
|
||||||
|
.put("data.password", "")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
TestPluginProvider.use(tempDir, config, StorageIntegrationTest::testStorage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class FlatFile {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testYaml(@TempDir Path tempDir) {
|
||||||
|
TestPluginProvider.use(tempDir, ImmutableMap.of("storage-method", "yaml"), StorageIntegrationTest::testStorage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testJson(@TempDir Path tempDir) {
|
||||||
|
TestPluginProvider.use(tempDir, ImmutableMap.of("storage-method", "json"), StorageIntegrationTest::testStorage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHocon(@TempDir Path tempDir) {
|
||||||
|
TestPluginProvider.use(tempDir, ImmutableMap.of("storage-method", "hocon"), StorageIntegrationTest::testStorage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testToml(@TempDir Path tempDir) {
|
||||||
|
TestPluginProvider.use(tempDir, ImmutableMap.of("storage-method", "toml"), StorageIntegrationTest::testStorage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testYamlCombined(@TempDir Path tempDir) {
|
||||||
|
TestPluginProvider.use(tempDir, ImmutableMap.of("storage-method", "yaml-combined"), StorageIntegrationTest::testStorage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testJsonCombined(@TempDir Path tempDir) {
|
||||||
|
TestPluginProvider.use(tempDir, ImmutableMap.of("storage-method", "json-combined"), StorageIntegrationTest::testStorage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHoconCombined(@TempDir Path tempDir) {
|
||||||
|
TestPluginProvider.use(tempDir, ImmutableMap.of("storage-method", "hocon-combined"), StorageIntegrationTest::testStorage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTomlCombined(@TempDir Path tempDir) {
|
||||||
|
TestPluginProvider.use(tempDir, ImmutableMap.of("storage-method", "toml-combined"), StorageIntegrationTest::testStorage);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -23,12 +23,14 @@
|
|||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package me.lucko.luckperms.standalone;
|
package me.lucko.luckperms.standalone.utils;
|
||||||
|
|
||||||
import me.lucko.luckperms.common.dependencies.Dependency;
|
import me.lucko.luckperms.common.dependencies.Dependency;
|
||||||
import me.lucko.luckperms.common.dependencies.DependencyManager;
|
import me.lucko.luckperms.common.dependencies.DependencyManager;
|
||||||
import me.lucko.luckperms.common.plugin.classpath.ClassPathAppender;
|
import me.lucko.luckperms.common.plugin.classpath.ClassPathAppender;
|
||||||
import me.lucko.luckperms.common.storage.StorageType;
|
import me.lucko.luckperms.common.storage.StorageType;
|
||||||
|
import me.lucko.luckperms.standalone.LPStandaloneBootstrap;
|
||||||
|
import me.lucko.luckperms.standalone.LPStandalonePlugin;
|
||||||
import me.lucko.luckperms.standalone.app.LuckPermsApplication;
|
import me.lucko.luckperms.standalone.app.LuckPermsApplication;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
@ -45,18 +47,18 @@ import java.util.Set;
|
|||||||
* </ul>
|
* </ul>
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public final class LPStandaloneTestBootstrap extends LPStandaloneBootstrap {
|
public final class TestPluginBootstrap extends LPStandaloneBootstrap {
|
||||||
private static final ClassPathAppender NOOP_APPENDER = file -> {};
|
private static final ClassPathAppender NOOP_APPENDER = file -> {};
|
||||||
|
|
||||||
private final Path dataDirectory;
|
private final Path dataDirectory;
|
||||||
private LPStandaloneTestPlugin plugin;
|
private TestPlugin plugin;
|
||||||
|
|
||||||
LPStandaloneTestBootstrap(LuckPermsApplication app, Path dataDirectory) {
|
TestPluginBootstrap(LuckPermsApplication app, Path dataDirectory) {
|
||||||
super(app, NOOP_APPENDER);
|
super(app, NOOP_APPENDER);
|
||||||
this.dataDirectory = dataDirectory;
|
this.dataDirectory = dataDirectory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LPStandaloneTestPlugin getPlugin() {
|
public TestPlugin getPlugin() {
|
||||||
return this.plugin;
|
return this.plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,14 +68,13 @@ public final class LPStandaloneTestBootstrap extends LPStandaloneBootstrap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
LPStandalonePlugin createTestPlugin() {
|
protected LPStandalonePlugin createTestPlugin() {
|
||||||
System.setProperty("luckperms.auto-install-translations", "false");
|
this.plugin = new TestPlugin(this);
|
||||||
this.plugin = new LPStandaloneTestPlugin(this);
|
|
||||||
return this.plugin;
|
return this.plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class LPStandaloneTestPlugin extends LPStandalonePlugin {
|
public static final class TestPlugin extends LPStandalonePlugin {
|
||||||
LPStandaloneTestPlugin(LPStandaloneBootstrap bootstrap) {
|
TestPlugin(LPStandaloneBootstrap bootstrap) {
|
||||||
super(bootstrap);
|
super(bootstrap);
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of LuckPerms, licensed under the MIT License.
|
||||||
|
*
|
||||||
|
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||||
|
* Copyright (c) contributors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package me.lucko.luckperms.standalone.utils;
|
||||||
|
|
||||||
|
import me.lucko.luckperms.standalone.app.LuckPermsApplication;
|
||||||
|
import me.lucko.luckperms.standalone.utils.TestPluginBootstrap.TestPlugin;
|
||||||
|
|
||||||
|
import org.testcontainers.shaded.com.google.common.collect.ImmutableMap;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public final class TestPluginProvider {
|
||||||
|
private TestPluginProvider() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a test LuckPerms plugin instance, loads/enables it, and returns it.
|
||||||
|
*
|
||||||
|
* @param tempDir the temporary directory to run the plugin in
|
||||||
|
* @param config the config to set
|
||||||
|
* @return the plugin
|
||||||
|
*/
|
||||||
|
public static Plugin create(Path tempDir, Map<String, String> config) {
|
||||||
|
Map<String, String> props = new HashMap<>(config);
|
||||||
|
props.putIfAbsent("auto-install-translations", "false");
|
||||||
|
props.putIfAbsent("editor-lazily-generate-key", "true");
|
||||||
|
|
||||||
|
props.forEach((k, v) -> System.setProperty("luckperms." + k, v));
|
||||||
|
|
||||||
|
LuckPermsApplication app = new LuckPermsApplication(() -> {});
|
||||||
|
TestPluginBootstrap bootstrap = new TestPluginBootstrap(app, tempDir);
|
||||||
|
|
||||||
|
bootstrap.onLoad();
|
||||||
|
bootstrap.onEnable();
|
||||||
|
|
||||||
|
props.keySet().forEach((k) -> System.clearProperty("luckperms." + k));
|
||||||
|
|
||||||
|
return new Plugin(app, bootstrap, bootstrap.getPlugin());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a test LuckPerms plugin instance, loads/enables it, and returns it.
|
||||||
|
*
|
||||||
|
* @param tempDir the temporary directory to run the plugin in
|
||||||
|
* @return the plugin
|
||||||
|
*/
|
||||||
|
public static Plugin create(Path tempDir) {
|
||||||
|
return create(tempDir, ImmutableMap.of());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a test LuckPerms plugin instance, loads/enables it, runs the consumer, then disables it.
|
||||||
|
*
|
||||||
|
* @param tempDir the temporary directory to run the plugin in
|
||||||
|
* @param config the config to set
|
||||||
|
* @param consumer the consumer
|
||||||
|
* @param <E> the exception class thrown by the consumer
|
||||||
|
* @throws E exception
|
||||||
|
*/
|
||||||
|
public static <E extends Throwable> void use(Path tempDir, Map<String, String> config, Consumer<E> consumer) throws E {
|
||||||
|
try (Plugin plugin = create(tempDir, config)) {
|
||||||
|
consumer.accept(plugin.app, plugin.bootstrap, plugin.plugin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a test LuckPerms plugin instance, loads/enables it, runs the consumer, then disables it.
|
||||||
|
*
|
||||||
|
* @param tempDir the temporary directory to run the plugin in
|
||||||
|
* @param consumer the consumer
|
||||||
|
* @param <E> the exception class thrown by the consumer
|
||||||
|
* @throws E exception
|
||||||
|
*/
|
||||||
|
public static <E extends Throwable> void use(Path tempDir, Consumer<E> consumer) throws E {
|
||||||
|
use(tempDir, ImmutableMap.of(), consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Consumer<E extends Throwable> {
|
||||||
|
void accept(LuckPermsApplication app, TestPluginBootstrap bootstrap, TestPlugin plugin) throws E;
|
||||||
|
}
|
||||||
|
|
||||||
|
public record Plugin(LuckPermsApplication app, TestPluginBootstrap bootstrap, TestPlugin plugin) implements AutoCloseable {
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
this.bootstrap.onDisable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user