diff --git a/common/build.gradle b/common/build.gradle index 2301e4bae..9db5a047f 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -4,7 +4,11 @@ plugins { } test { - useJUnitPlatform {} + useJUnitPlatform { + if (!project.hasProperty('dockerTests')) { + excludeTags 'docker' + } + } } jacocoTestReport { @@ -15,9 +19,14 @@ dependencies { 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-params:5.9.1' + testImplementation "org.testcontainers:junit-jupiter:1.19.8" testImplementation 'org.mockito:mockito-core:5.11.0' testImplementation 'org.mockito:mockito-junit-jupiter:5.11.0' testImplementation 'com.h2database:h2:2.1.214' + testImplementation 'org.mongodb:mongodb-driver-legacy:4.5.0' + testImplementation 'org.spongepowered:configurate-yaml:3.7.2' + testImplementation 'org.spongepowered:configurate-hocon:3.7.2' + testImplementation 'me.lucko.configurate:configurate-toml:3.7' api project(':api') api 'org.checkerframework:checker-qual:3.12.0' @@ -87,7 +96,7 @@ dependencies { compileOnly 'redis.clients:jedis:4.4.3' compileOnly 'io.nats:jnats:2.16.4' compileOnly 'com.rabbitmq:amqp-client:5.12.0' - api 'org.mongodb:mongodb-driver-legacy:4.5.0' + compileOnly 'org.mongodb:mongodb-driver-legacy:4.5.0' compileOnly 'org.postgresql:postgresql:42.6.0' compileOnly 'org.yaml:snakeyaml:1.28' } diff --git a/common/src/main/java/me/lucko/luckperms/common/actionlog/Log.java b/common/src/main/java/me/lucko/luckperms/common/actionlog/Log.java index f5c744988..002921047 100644 --- a/common/src/main/java/me/lucko/luckperms/common/actionlog/Log.java +++ b/common/src/main/java/me/lucko/luckperms/common/actionlog/Log.java @@ -42,6 +42,10 @@ public class Log { return new Builder(); } + public static Log of(List content) { + return content.isEmpty() ? EMPTY : new Log(content); + } + public static Log empty() { return EMPTY; } diff --git a/common/src/main/java/me/lucko/luckperms/common/actionlog/LogPage.java b/common/src/main/java/me/lucko/luckperms/common/actionlog/LogPage.java index 62b6feec4..4a7feec26 100644 --- a/common/src/main/java/me/lucko/luckperms/common/actionlog/LogPage.java +++ b/common/src/main/java/me/lucko/luckperms/common/actionlog/LogPage.java @@ -37,6 +37,10 @@ public class LogPage { return new LogPage.Builder(); } + public static LogPage of(List content) { + return content.isEmpty() ? EMPTY : new LogPage(content); + } + public static LogPage empty() { return EMPTY; } diff --git a/common/src/main/java/me/lucko/luckperms/common/actionlog/filter/ActionFilterMongoBuilder.java b/common/src/main/java/me/lucko/luckperms/common/actionlog/filter/ActionFilterMongoBuilder.java index 06d6b7c62..a866970cb 100644 --- a/common/src/main/java/me/lucko/luckperms/common/actionlog/filter/ActionFilterMongoBuilder.java +++ b/common/src/main/java/me/lucko/luckperms/common/actionlog/filter/ActionFilterMongoBuilder.java @@ -62,7 +62,7 @@ public final class ActionFilterMongoBuilder extends FilterMongoBuilder { if (value instanceof String | value instanceof UUID) { return value; } else if (value instanceof Action.Target.Type) { - return LoggedAction.getTypeString((Action.Target.Type) value); + return ((Action.Target.Type) value).name(); } else { throw new IllegalArgumentException("Don't know how to map value with type: " + value.getClass().getName()); } diff --git a/common/src/main/java/me/lucko/luckperms/common/filter/PageParameters.java b/common/src/main/java/me/lucko/luckperms/common/filter/PageParameters.java index 9f5cd52f4..51e509edd 100644 --- a/common/src/main/java/me/lucko/luckperms/common/filter/PageParameters.java +++ b/common/src/main/java/me/lucko/luckperms/common/filter/PageParameters.java @@ -25,6 +25,10 @@ package me.lucko.luckperms.common.filter; +import java.util.Collections; +import java.util.List; +import java.util.stream.Stream; + public class PageParameters { private final int pageSize; @@ -43,4 +47,18 @@ public class PageParameters { return this.pageNumber; } + public List paginate(List input) { + int fromIndex = this.pageSize * (this.pageNumber - 1); + if (fromIndex >= input.size()) { + return Collections.emptyList(); + } + + int toIndex = Math.min(fromIndex + this.pageSize, input.size()); + return input.subList(fromIndex, toIndex); + } + + public Stream paginate(Stream input) { + return input.skip((long) this.pageSize * (this.pageNumber - 1)).limit(this.pageSize); + } + } diff --git a/common/src/main/java/me/lucko/luckperms/common/filter/mongo/ConstraintMongoBuilder.java b/common/src/main/java/me/lucko/luckperms/common/filter/mongo/ConstraintMongoBuilder.java index 427d46d71..edcce0d52 100644 --- a/common/src/main/java/me/lucko/luckperms/common/filter/mongo/ConstraintMongoBuilder.java +++ b/common/src/main/java/me/lucko/luckperms/common/filter/mongo/ConstraintMongoBuilder.java @@ -76,7 +76,7 @@ public class ConstraintMongoBuilder { public static FindIterable page(PageParameters pageParameters, FindIterable iterable) { int pageSize = pageParameters.pageSize(); int pageNumber = pageParameters.pageNumber(); - return iterable.limit(pageNumber).skip((pageNumber - 1) * pageSize); + return iterable.limit(pageSize).skip((pageNumber - 1) * pageSize); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/filter/mongo/FilterMongoBuilder.java b/common/src/main/java/me/lucko/luckperms/common/filter/mongo/FilterMongoBuilder.java index e7224be38..93cd57267 100644 --- a/common/src/main/java/me/lucko/luckperms/common/filter/mongo/FilterMongoBuilder.java +++ b/common/src/main/java/me/lucko/luckperms/common/filter/mongo/FilterMongoBuilder.java @@ -25,12 +25,10 @@ package me.lucko.luckperms.common.filter.mongo; -import com.mongodb.client.FindIterable; import com.mongodb.client.model.Filters; import me.lucko.luckperms.common.filter.Filter; import me.lucko.luckperms.common.filter.FilterField; import me.lucko.luckperms.common.filter.FilterList; -import me.lucko.luckperms.common.filter.PageParameters; import org.bson.conversions.Bson; import java.util.List; @@ -64,10 +62,4 @@ public abstract class FilterMongoBuilder extends ConstraintMongoBuilder { return make(filters.operator(), filters); } - public static FindIterable page(PageParameters pageParameters, FindIterable iterable) { - int pageSize = pageParameters.pageSize(); - int pageNumber = pageParameters.pageNumber(); - return iterable.limit(pageNumber).skip((pageNumber - 1) * pageSize); - } - } diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/file/AbstractConfigurateStorage.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/file/AbstractConfigurateStorage.java index c4431620b..1b2ffa72f 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/file/AbstractConfigurateStorage.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/file/AbstractConfigurateStorage.java @@ -177,7 +177,7 @@ public abstract class AbstractConfigurateStorage implements StorageImplementatio @Override public LogPage getLogPage(FilterList filters, PageParameters page) throws Exception { - throw new UnsupportedOperationException(); + return this.actionLogger.getLogPage(filters, page); } @Override diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/file/FileActionLogger.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/file/FileActionLogger.java index 5064570ed..ad437ea55 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/file/FileActionLogger.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/file/FileActionLogger.java @@ -25,12 +25,14 @@ package me.lucko.luckperms.common.storage.implementation.file; +import com.google.common.collect.ImmutableList; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.stream.JsonReader; import me.lucko.luckperms.common.actionlog.ActionJsonSerializer; import me.lucko.luckperms.common.actionlog.Log; import me.lucko.luckperms.common.actionlog.LogPage; +import me.lucko.luckperms.common.actionlog.LoggedAction; import me.lucko.luckperms.common.cache.BufferedRequest; import me.lucko.luckperms.common.filter.FilterList; import me.lucko.luckperms.common.filter.PageParameters; @@ -45,11 +47,14 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; +import java.util.stream.Collectors; +import java.util.stream.Stream; public class FileActionLogger { @@ -133,36 +138,37 @@ public class FileActionLogger { } } - public Log getLog() throws IOException { + private Stream getRawLog() throws IOException { // if there is log content waiting to be written, flush immediately before trying to read if (this.saveBuffer.isEnqueued()) { this.saveBuffer.requestDirectly(); } if (!Files.exists(this.contentFile)) { - return Log.empty(); + return Stream.empty(); } - Log.Builder log = Log.builder(); - + Stream.Builder builder = Stream.builder(); try (BufferedReader reader = Files.newBufferedReader(this.contentFile, StandardCharsets.UTF_8)) { String line; while ((line = reader.readLine()) != null) { try { JsonElement parsed = GsonProvider.parser().parse(line); - log.add(ActionJsonSerializer.deserialize(parsed)); + builder.add(ActionJsonSerializer.deserialize(parsed)); } catch (Exception e) { e.printStackTrace(); } } } + return builder.build(); + } - return log.build(); + public Log getLog() throws IOException { + return Log.of(getRawLog().collect(Collectors.toList())); } public LogPage getLogPage(FilterList filters, PageParameters page) throws IOException { - - + return LogPage.of(page.paginate(getRawLog().filter(filters::evaluate).sorted(Comparator.reverseOrder())).collect(Collectors.toList())); } private final class SaveBuffer extends BufferedRequest { diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java index 0072110cc..df34b9a6a 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/mongodb/MongoStorage.java @@ -31,11 +31,13 @@ import com.mongodb.MongoClientOptions; import com.mongodb.MongoClientURI; import com.mongodb.MongoCredential; import com.mongodb.ServerAddress; +import com.mongodb.client.MongoClients; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoCursor; import com.mongodb.client.MongoDatabase; import com.mongodb.client.model.Filters; import com.mongodb.client.model.ReplaceOptions; +import com.mongodb.client.model.Sorts; import me.lucko.luckperms.common.actionlog.Log; import me.lucko.luckperms.common.actionlog.LogPage; import me.lucko.luckperms.common.actionlog.LoggedAction; @@ -70,6 +72,7 @@ import net.luckperms.api.node.Node; import net.luckperms.api.node.NodeBuilder; import org.bson.Document; import org.bson.UuidRepresentation; +import org.bson.conversions.Bson; import java.time.Instant; import java.util.ArrayList; @@ -193,7 +196,7 @@ public class MongoStorage implements StorageImplementation { public LogPage getLogPage(FilterList filters, PageParameters page) throws Exception { LogPage.Builder log = LogPage.builder(); MongoCollection c = this.database.getCollection(this.prefix + "action"); - try (MongoCursor cursor = FilterMongoBuilder.page(page, c.find(ActionFilterMongoBuilder.INSTANCE.make(filters))).iterator()) { + try (MongoCursor cursor = ConstraintMongoBuilder.page(page, c.find(ActionFilterMongoBuilder.INSTANCE.make(filters)).sort(Sorts.descending("timestamp"))).iterator()) { while (cursor.hasNext()) { log.add(actionFromDoc(cursor.next())); } diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java index 9bace8a47..195e69ecd 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/implementation/sql/SqlStorage.java @@ -268,7 +268,7 @@ public class SqlStorage implements StorageImplementation { ActionFilterSqlBuilder sqlBuilder = new ActionFilterSqlBuilder(); sqlBuilder.builder().append(ACTION_SELECT_ALL); sqlBuilder.visit(filter); - sqlBuilder.builder().append(" ORDER BY id DESC"); + sqlBuilder.builder().append(" ORDER BY time DESC"); sqlBuilder.visit(page); try (PreparedStatement ps = sqlBuilder.builder().build(c, this.statementProcessor)) { diff --git a/common/src/main/java/me/lucko/luckperms/common/storage/misc/StorageCredentials.java b/common/src/main/java/me/lucko/luckperms/common/storage/misc/StorageCredentials.java index 731d0d4f6..1545754c3 100644 --- a/common/src/main/java/me/lucko/luckperms/common/storage/misc/StorageCredentials.java +++ b/common/src/main/java/me/lucko/luckperms/common/storage/misc/StorageCredentials.java @@ -25,6 +25,8 @@ package me.lucko.luckperms.common.storage.misc; +import com.google.common.collect.ImmutableMap; + import java.util.Map; import java.util.Objects; @@ -54,6 +56,10 @@ public class StorageCredentials { this.properties = properties; } + public StorageCredentials(String address, String database, String username, String password) { + this(address, database, username, password, 10, 10, 1800000, 0, 5000, ImmutableMap.of()); + } + public String getAddress() { return Objects.requireNonNull(this.address, "address"); } diff --git a/common/src/test/java/me/lucko/luckperms/common/storage/AbstractStorageTest.java b/common/src/test/java/me/lucko/luckperms/common/storage/AbstractStorageTest.java new file mode 100644 index 000000000..4d3fea861 --- /dev/null +++ b/common/src/test/java/me/lucko/luckperms/common/storage/AbstractStorageTest.java @@ -0,0 +1,355 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * 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.common.storage; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import me.lucko.luckperms.common.actionlog.filter.ActionFilters; +import me.lucko.luckperms.common.actionlog.Log; +import me.lucko.luckperms.common.actionlog.LogPage; +import me.lucko.luckperms.common.actionlog.LoggedAction; +import me.lucko.luckperms.common.filter.PageParameters; +import me.lucko.luckperms.common.config.ConfigKeys; +import me.lucko.luckperms.common.config.LuckPermsConfiguration; +import me.lucko.luckperms.common.event.EventDispatcher; +import me.lucko.luckperms.common.model.Group; +import me.lucko.luckperms.common.model.PrimaryGroupHolder; +import me.lucko.luckperms.common.model.User; +import me.lucko.luckperms.common.model.manager.group.GroupManager; +import me.lucko.luckperms.common.model.manager.group.StandardGroupManager; +import me.lucko.luckperms.common.model.manager.user.StandardUserManager; +import me.lucko.luckperms.common.model.manager.user.UserManager; +import me.lucko.luckperms.common.node.types.Inheritance; +import me.lucko.luckperms.common.node.types.Permission; +import me.lucko.luckperms.common.plugin.LuckPermsPlugin; +import me.lucko.luckperms.common.plugin.bootstrap.LuckPermsBootstrap; +import me.lucko.luckperms.common.plugin.scheduler.SchedulerAdapter; +import me.lucko.luckperms.common.storage.implementation.StorageImplementation; +import net.luckperms.api.actionlog.Action; +import net.luckperms.api.model.PlayerSaveResult; +import net.luckperms.api.model.PlayerSaveResult.Outcome; +import net.luckperms.api.model.data.DataType; +import net.luckperms.api.node.Node; +import net.luckperms.api.node.types.InheritanceNode; +import net.luckperms.api.node.types.PermissionNode; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.time.Instant; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.AdditionalAnswers.answer; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public abstract class AbstractStorageTest { + + @Mock protected LuckPermsPlugin plugin; + @Mock protected LuckPermsBootstrap bootstrap; + @Mock protected LuckPermsConfiguration configuration; + + private StorageImplementation storage; + + @BeforeEach + public final void setupMocksAndStorage() throws Exception { + lenient().when(this.plugin.getBootstrap()).thenReturn(this.bootstrap); + lenient().when(this.plugin.getConfiguration()).thenReturn(this.configuration); + lenient().when(this.plugin.getEventDispatcher()).thenReturn(mock(EventDispatcher.class)); + lenient().when(this.bootstrap.getScheduler()).thenReturn(mock(SchedulerAdapter.class)); + lenient().when(this.configuration.get(ConfigKeys.PRIMARY_GROUP_CALCULATION)).thenReturn(PrimaryGroupHolder.AllParentsByWeight::new); + lenient().when(this.configuration.get(ConfigKeys.PRIMARY_GROUP_CALCULATION_METHOD)).thenReturn("parents-by-weight"); + lenient().when(this.bootstrap.getResourceStream(anyString())) + .then(answer((String path) -> AbstractStorageTest.class.getClassLoader().getResourceAsStream(path))); + lenient().when(this.plugin.getEventDispatcher()).thenReturn(mock(EventDispatcher.class)); + + this.storage = makeStorage(this.plugin); + this.storage.init(); + } + + protected abstract StorageImplementation makeStorage(LuckPermsPlugin plugin) throws Exception; + + protected void cleanupResources() { + // do nothing + } + + @AfterEach + public final void shutdownStorage() { + this.storage.shutdown(); + cleanupResources(); + } + + @Test + public void testActionLog() throws Exception { + LoggedAction action = LoggedAction.build() + .source(UUID.randomUUID()) + .sourceName("Test Source") + .targetType(Action.Target.Type.USER) + .target(UUID.randomUUID()) + .targetName("Test Target") + .description("hello 123 hello 123") + .build(); + + this.storage.logAction(action); + + Log log = this.storage.getLog(); + assertEquals(1, log.getContent().size()); + assertEquals(action, log.getContent().first()); + } + + @Test + public void testActionLogPage() throws Exception { + UUID sourceUuid = UUID.randomUUID(); + UUID targetUuid = UUID.randomUUID(); + + Instant baseTime = Instant.now(); + + Function mockAction = i -> LoggedAction.build() + .source(i % 2 == 0 ? sourceUuid : UUID.randomUUID()) + .sourceName("Test Source") + .targetType(Action.Target.Type.USER) + .target(targetUuid) + .targetName("Test Target") + .description("hello " + i) + .timestamp(baseTime.plusSeconds(i)) + .build(); + + for (int i = 0; i < 100; i++) { + this.storage.logAction(mockAction.apply(-i)); + } + for (int i = 0; i < 100; i++) { + this.storage.logAction(mockAction.apply(i)); + } + for (int i = 100; i < 200; i++) { + this.storage.logAction(mockAction.apply(-i)); + } + + for (int i = 0; i < 10; i++) { + this.storage.logAction(LoggedAction.build() + .source(UUID.randomUUID()) + .sourceName("Test Source") + .targetType(Action.Target.Type.GROUP) + .targetName(i % 2 == 0 ? "test_group" : "dummy") + .description("group test " + i) + .build()); + } + + for (int i = 0; i < 10; i++) { + this.storage.logAction(LoggedAction.build() + .source(UUID.randomUUID()) + .sourceName("Test Source") + .targetType(Action.Target.Type.TRACK) + .targetName(i % 2 == 0 ? "test_track" : "dummy") + .description("track test " + i) + .build()); + } + + LogPage page = this.storage.getLogPage(ActionFilters.source(sourceUuid), new PageParameters(5, 1)); + assertEquals(ImmutableList.of( + mockAction.apply(98), + mockAction.apply(96), + mockAction.apply(94), + mockAction.apply(92), + mockAction.apply(90) + ), page.getContent()); + + + page = this.storage.getLogPage(ActionFilters.source(sourceUuid), new PageParameters(5, 3)); + assertEquals(ImmutableList.of( + mockAction.apply(78), + mockAction.apply(76), + mockAction.apply(74), + mockAction.apply(72), + mockAction.apply(70) + ), page.getContent()); + + page = this.storage.getLogPage(ActionFilters.source(sourceUuid), new PageParameters(500, 1)); + assertEquals(150, page.getContent().size()); + + page = this.storage.getLogPage(ActionFilters.all(), new PageParameters(500, 1)); + assertEquals(320, page.getContent().size()); + + page = this.storage.getLogPage(ActionFilters.user(targetUuid), new PageParameters(500, 1)); + assertEquals(300, page.getContent().size()); + + page = this.storage.getLogPage(ActionFilters.group("test_group"), new PageParameters(10, 1)); + assertEquals(5, page.getContent().size()); + + page = this.storage.getLogPage(ActionFilters.track("test_track"), new PageParameters(10, 1)); + assertEquals(5, page.getContent().size()); + + page = this.storage.getLogPage(ActionFilters.search("hello"), new PageParameters(500, 1)); + assertEquals(300, page.getContent().size()); + } + + @Test + public void testSavePlayerData() throws Exception { + UUID uniqueId = UUID.randomUUID(); + + // clean insert + PlayerSaveResult r1 = this.storage.savePlayerData(uniqueId, "Player1"); + assertEquals(ImmutableSet.of(Outcome.CLEAN_INSERT), r1.getOutcomes()); + assertNull(r1.getOtherUniqueIds()); + assertNull(r1.getPreviousUsername()); + + // no change expected + PlayerSaveResult r2 = this.storage.savePlayerData(uniqueId, "Player1"); + assertEquals(ImmutableSet.of(Outcome.NO_CHANGE), r2.getOutcomes()); + assertNull(r2.getOtherUniqueIds()); + assertNull(r2.getPreviousUsername()); + + // changed username + PlayerSaveResult r3 = this.storage.savePlayerData(uniqueId, "Player2"); + assertEquals(ImmutableSet.of(Outcome.USERNAME_UPDATED), r3.getOutcomes()); + assertNull(r3.getOtherUniqueIds()); + assertTrue("Player1".equalsIgnoreCase(r3.getPreviousUsername())); + + // changed uuid + UUID newUniqueId = UUID.randomUUID(); + PlayerSaveResult r4 = this.storage.savePlayerData(newUniqueId, "Player2"); + assertEquals(ImmutableSet.of(Outcome.CLEAN_INSERT, Outcome.OTHER_UNIQUE_IDS_PRESENT_FOR_USERNAME), r4.getOutcomes()); + assertNotNull(r4.getOtherUniqueIds()); + assertEquals(ImmutableSet.of(uniqueId), r4.getOtherUniqueIds()); + assertNull(r2.getPreviousUsername()); + } + + @Test + public void testGetPlayerUniqueIdAndName() throws Exception { + UUID uniqueId = UUID.randomUUID(); + String username = "Player1"; + + this.storage.savePlayerData(uniqueId, username); + + assertEquals(uniqueId, this.storage.getPlayerUniqueId("Player1")); + assertTrue(username.equalsIgnoreCase(this.storage.getPlayerName(uniqueId))); + } + + @Test + public void testGetPlayerUniqueIdAndNameNull() throws Exception { + assertNull(this.storage.getPlayerUniqueId("Player1")); + assertNull(this.storage.getPlayerName(UUID.randomUUID())); + } + + @Test + public void testSaveAndLoadGroup() throws Exception { + StandardGroupManager groupManager = new StandardGroupManager(this.plugin); + + //noinspection unchecked,rawtypes + lenient().when(this.plugin.getGroupManager()).thenReturn((GroupManager) groupManager); + + Group group = this.storage.createAndLoadGroup("test"); + + group.normalData().add(Permission.builder() + .permission("test.1") + .withContext("server", "test") + .build() + ); + group.normalData().add(Permission.builder() + .permission("test.2") + .withContext("world", "test") + .build() + ); + group.normalData().add(Permission.builder() + .permission("test.3") + .expiry(1, TimeUnit.HOURS) + .withContext("server", "test") + .withContext("world", "test") + .withContext("hello", "test") + .build() + ); + + Set nodes = group.normalData().asSet(); + assertEquals(3, nodes.size()); + + this.storage.saveGroup(group); + groupManager.unload("test"); + + Group loaded = this.storage.loadGroup("test").orElse(null); + assertNotNull(loaded); + assertNotSame(group, loaded); + assertEquals(nodes, loaded.normalData().asSet()); + } + + @Test + public void testSaveAndDeleteUser() throws Exception { + StandardUserManager userManager = new StandardUserManager(this.plugin); + + //noinspection unchecked,rawtypes + when(this.plugin.getUserManager()).thenReturn((UserManager) userManager); + + UUID exampleUniqueId = UUID.fromString("069a79f4-44e9-4726-a5be-fca90e38aaf5"); + String exampleUsername = "Notch"; + PermissionNode examplePermission = Permission.builder() + .permission("test.1") + .withContext("server", "test") + .build(); + InheritanceNode defaultGroupNode = Inheritance.builder(GroupManager.DEFAULT_GROUP_NAME).build(); + + // create a default user, assert that is doesn't appear in unique users list + this.storage.savePlayerData(exampleUniqueId, exampleUsername); + assertFalse(this.storage.getUniqueUsers().contains(exampleUniqueId)); + + // give the user a node, assert that it does appear in unique users list + User user = this.storage.loadUser(exampleUniqueId, exampleUsername); + user.setNode(DataType.NORMAL, examplePermission, true); + this.storage.saveUser(user); + assertTrue(this.storage.getUniqueUsers().contains(exampleUniqueId)); + + // clear all nodes (reset to default) and assert that it does not appear in unique users list + user.clearNodes(DataType.NORMAL, null, true); + this.storage.saveUser(user); + assertFalse(this.storage.getUniqueUsers().contains(exampleUniqueId)); + assertEquals(ImmutableSet.of(defaultGroupNode), user.normalData().asSet()); + + // give it a node again, assert that it shows as a unique user + user.setNode(DataType.NORMAL, examplePermission, true); + this.storage.saveUser(user); + assertTrue(this.storage.getUniqueUsers().contains(exampleUniqueId)); + assertEquals(ImmutableSet.of(defaultGroupNode, examplePermission), user.normalData().asSet()); + + // reload user data from the db and assert that it is unchanged + user = this.storage.loadUser(exampleUniqueId, exampleUsername); + assertEquals(ImmutableSet.of(defaultGroupNode, examplePermission), user.normalData().asSet()); + } + +} diff --git a/common/src/test/java/me/lucko/luckperms/common/storage/ConfigurateStorageTest.java b/common/src/test/java/me/lucko/luckperms/common/storage/ConfigurateStorageTest.java new file mode 100644 index 000000000..45038a4ba --- /dev/null +++ b/common/src/test/java/me/lucko/luckperms/common/storage/ConfigurateStorageTest.java @@ -0,0 +1,116 @@ +package me.lucko.luckperms.common.storage; + +import me.lucko.luckperms.common.plugin.LuckPermsPlugin; +import me.lucko.luckperms.common.storage.implementation.StorageImplementation; +import me.lucko.luckperms.common.storage.implementation.file.CombinedConfigurateStorage; +import me.lucko.luckperms.common.storage.implementation.file.SeparatedConfigurateStorage; +import me.lucko.luckperms.common.storage.implementation.file.loader.HoconLoader; +import me.lucko.luckperms.common.storage.implementation.file.loader.JsonLoader; +import me.lucko.luckperms.common.storage.implementation.file.loader.TomlLoader; +import me.lucko.luckperms.common.storage.implementation.file.loader.YamlLoader; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.io.TempDir; + +import java.nio.file.Path; + +import static org.mockito.Mockito.lenient; + +public class ConfigurateStorageTest { + + @Nested + class SeparatedYaml extends AbstractStorageTest { + @TempDir + private Path directory; + + @Override + protected StorageImplementation makeStorage(LuckPermsPlugin plugin) throws Exception { + lenient().when(this.bootstrap.getDataDirectory()).thenReturn(this.directory); + return new SeparatedConfigurateStorage(plugin, "YAML", new YamlLoader(), ".yml", "yaml-storage"); + } + } + + @Nested + class SeparatedJson extends AbstractStorageTest { + @TempDir + private Path directory; + + @Override + protected StorageImplementation makeStorage(LuckPermsPlugin plugin) throws Exception { + lenient().when(this.bootstrap.getDataDirectory()).thenReturn(this.directory); + return new SeparatedConfigurateStorage(plugin, "JSON", new JsonLoader(), ".json", "json-storage"); + } + } + + @Nested + class SeparatedHocon extends AbstractStorageTest { + @TempDir + private Path directory; + + @Override + protected StorageImplementation makeStorage(LuckPermsPlugin plugin) throws Exception { + lenient().when(this.bootstrap.getDataDirectory()).thenReturn(this.directory); + return new SeparatedConfigurateStorage(plugin, "HOCON", new HoconLoader(), ".conf", "hocon-storage"); + } + } + + @Nested + class SeparatedToml extends AbstractStorageTest { + @TempDir + private Path directory; + + @Override + protected StorageImplementation makeStorage(LuckPermsPlugin plugin) throws Exception { + lenient().when(this.bootstrap.getDataDirectory()).thenReturn(this.directory); + return new SeparatedConfigurateStorage(plugin, "TOML", new TomlLoader(), ".toml", "toml-storage"); + } + } + + @Nested + class CombinedYaml extends AbstractStorageTest { + @TempDir + private Path directory; + + @Override + protected StorageImplementation makeStorage(LuckPermsPlugin plugin) throws Exception { + lenient().when(this.bootstrap.getDataDirectory()).thenReturn(this.directory); + return new CombinedConfigurateStorage(plugin, "YAML", new YamlLoader(), ".yml", "yaml-storage"); + } + } + + @Nested + class CombinedJson extends AbstractStorageTest { + @TempDir + private Path directory; + + @Override + protected StorageImplementation makeStorage(LuckPermsPlugin plugin) throws Exception { + lenient().when(this.bootstrap.getDataDirectory()).thenReturn(this.directory); + return new CombinedConfigurateStorage(plugin, "JSON", new JsonLoader(), ".json", "json-storage"); + } + } + + @Nested + class CombinedHocon extends AbstractStorageTest { + @TempDir + private Path directory; + + @Override + protected StorageImplementation makeStorage(LuckPermsPlugin plugin) throws Exception { + lenient().when(this.bootstrap.getDataDirectory()).thenReturn(this.directory); + return new CombinedConfigurateStorage(plugin, "HOCON", new HoconLoader(), ".conf", "hocon-storage"); + } + } + + @Nested + class CombinedToml extends AbstractStorageTest { + @TempDir + private Path directory; + + @Override + protected StorageImplementation makeStorage(LuckPermsPlugin plugin) throws Exception { + lenient().when(this.bootstrap.getDataDirectory()).thenReturn(this.directory); + return new CombinedConfigurateStorage(plugin, "TOML", new TomlLoader(), ".toml", "toml-storage"); + } + } + +} diff --git a/common/src/test/java/me/lucko/luckperms/common/storage/MongoStorageTest.java b/common/src/test/java/me/lucko/luckperms/common/storage/MongoStorageTest.java new file mode 100644 index 000000000..ebabb5fbf --- /dev/null +++ b/common/src/test/java/me/lucko/luckperms/common/storage/MongoStorageTest.java @@ -0,0 +1,36 @@ +package me.lucko.luckperms.common.storage; + +import me.lucko.luckperms.common.plugin.LuckPermsPlugin; +import me.lucko.luckperms.common.storage.implementation.StorageImplementation; +import me.lucko.luckperms.common.storage.implementation.mongodb.MongoStorage; +import me.lucko.luckperms.common.storage.misc.StorageCredentials; +import org.junit.jupiter.api.Tag; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.utility.DockerImageName; + +//@Tag("docker") +public class MongoStorageTest extends AbstractStorageTest { + + private final GenericContainer container = new GenericContainer<>(DockerImageName.parse("mongo")) + .withExposedPorts(27017); + + @Override + protected StorageImplementation makeStorage(LuckPermsPlugin plugin) throws Exception { + this.container.start(); + String host = this.container.getHost(); + Integer port = this.container.getFirstMappedPort(); + + StorageCredentials credentials = new StorageCredentials( + host + ":" + port, + "minecraft", + "", + "" + ); + return new MongoStorage(plugin, credentials, "", ""); + } + + @Override + protected void cleanupResources() { + this.container.stop(); + } +} diff --git a/common/src/test/java/me/lucko/luckperms/common/storage/SqlStorageTest.java b/common/src/test/java/me/lucko/luckperms/common/storage/SqlStorageTest.java index f144f405d..f098c75ab 100644 --- a/common/src/test/java/me/lucko/luckperms/common/storage/SqlStorageTest.java +++ b/common/src/test/java/me/lucko/luckperms/common/storage/SqlStorageTest.java @@ -25,316 +25,22 @@ package me.lucko.luckperms.common.storage; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import me.lucko.luckperms.common.actionlog.filter.ActionFilters; -import me.lucko.luckperms.common.actionlog.Log; -import me.lucko.luckperms.common.actionlog.LogPage; -import me.lucko.luckperms.common.actionlog.LoggedAction; -import me.lucko.luckperms.common.filter.PageParameters; -import me.lucko.luckperms.common.config.ConfigKeys; -import me.lucko.luckperms.common.config.LuckPermsConfiguration; -import me.lucko.luckperms.common.event.EventDispatcher; -import me.lucko.luckperms.common.model.Group; -import me.lucko.luckperms.common.model.PrimaryGroupHolder; -import me.lucko.luckperms.common.model.User; -import me.lucko.luckperms.common.model.manager.group.GroupManager; -import me.lucko.luckperms.common.model.manager.group.StandardGroupManager; -import me.lucko.luckperms.common.model.manager.user.StandardUserManager; -import me.lucko.luckperms.common.model.manager.user.UserManager; -import me.lucko.luckperms.common.node.types.Inheritance; -import me.lucko.luckperms.common.node.types.Permission; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; -import me.lucko.luckperms.common.plugin.bootstrap.LuckPermsBootstrap; -import me.lucko.luckperms.common.plugin.scheduler.SchedulerAdapter; +import me.lucko.luckperms.common.storage.implementation.StorageImplementation; import me.lucko.luckperms.common.storage.implementation.sql.SqlStorage; import me.lucko.luckperms.common.storage.implementation.sql.connection.ConnectionFactory; import me.lucko.luckperms.common.storage.implementation.sql.connection.file.NonClosableConnection; -import net.luckperms.api.actionlog.Action; -import net.luckperms.api.model.PlayerSaveResult; -import net.luckperms.api.model.PlayerSaveResult.Outcome; -import net.luckperms.api.model.data.DataType; -import net.luckperms.api.node.Node; -import net.luckperms.api.node.types.InheritanceNode; -import net.luckperms.api.node.types.PermissionNode; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.TimeUnit; import java.util.function.Function; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNotSame; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.AdditionalAnswers.answer; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.lenient; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +public class SqlStorageTest extends AbstractStorageTest { -@ExtendWith(MockitoExtension.class) -public class SqlStorageTest { - - @Mock private LuckPermsPlugin plugin; - @Mock private LuckPermsBootstrap bootstrap; - @Mock private LuckPermsConfiguration configuration; - - private SqlStorage storage; - - @BeforeEach - public void setupMocksAndDatabase() throws Exception { - lenient().when(this.plugin.getBootstrap()).thenReturn(this.bootstrap); - lenient().when(this.plugin.getConfiguration()).thenReturn(this.configuration); - lenient().when(this.plugin.getEventDispatcher()).thenReturn(mock(EventDispatcher.class)); - lenient().when(this.bootstrap.getScheduler()).thenReturn(mock(SchedulerAdapter.class)); - lenient().when(this.configuration.get(ConfigKeys.PRIMARY_GROUP_CALCULATION)).thenReturn(PrimaryGroupHolder.AllParentsByWeight::new); - lenient().when(this.configuration.get(ConfigKeys.PRIMARY_GROUP_CALCULATION_METHOD)).thenReturn("parents-by-weight"); - lenient().when(this.bootstrap.getResourceStream(anyString())) - .then(answer((String path) -> SqlStorageTest.class.getClassLoader().getResourceAsStream(path))); - lenient().when(this.plugin.getEventDispatcher()).thenReturn(mock(EventDispatcher.class)); - - this.storage = new SqlStorage(this.plugin, new TestH2ConnectionFactory(), "luckperms_"); - this.storage.init(); - } - - @AfterEach - public void shutdownDatabase() { - this.storage.shutdown(); - } - - @Test - public void testActionLog() throws Exception { - LoggedAction action = LoggedAction.build() - .source(UUID.randomUUID()) - .sourceName("Test Source") - .targetType(Action.Target.Type.USER) - .target(UUID.randomUUID()) - .targetName("Test Target") - .description("hello 123 hello 123") - .build(); - - this.storage.logAction(action); - - Log log = this.storage.getLog(); - assertEquals(1, log.getContent().size()); - assertEquals(action, log.getContent().first()); - } - - @Test - public void testActionLogPage() throws Exception { - UUID sourceUuid = UUID.randomUUID(); - UUID targetUuid = UUID.randomUUID(); - - Function mockAction = i -> LoggedAction.build() - .source(i % 2 == 0 ? sourceUuid : UUID.randomUUID()) - .sourceName("Test Source") - .targetType(Action.Target.Type.USER) - .target(targetUuid) - .targetName("Test Target") - .description("hello " + i) - .build(); - - for (int i = 0; i < 100; i++) { - this.storage.logAction(mockAction.apply(i)); - } - - for (int i = 0; i < 10; i++) { - this.storage.logAction(LoggedAction.build() - .source(UUID.randomUUID()) - .sourceName("Test Source") - .targetType(Action.Target.Type.GROUP) - .targetName(i % 2 == 0 ? "test_group" : "dummy") - .description("group test " + i) - .build()); - } - - for (int i = 0; i < 10; i++) { - this.storage.logAction(LoggedAction.build() - .source(UUID.randomUUID()) - .sourceName("Test Source") - .targetType(Action.Target.Type.TRACK) - .targetName(i % 2 == 0 ? "test_track" : "dummy") - .description("track test " + i) - .build()); - } - - LogPage page = this.storage.getLogPage(ActionFilters.source(sourceUuid), new PageParameters(5, 1)); - assertEquals(ImmutableList.of( - mockAction.apply(98), - mockAction.apply(96), - mockAction.apply(94), - mockAction.apply(92), - mockAction.apply(90) - ), page.getContent()); - - page = this.storage.getLogPage(ActionFilters.source(sourceUuid), new PageParameters(5, 3)); - assertEquals(ImmutableList.of( - mockAction.apply(78), - mockAction.apply(76), - mockAction.apply(74), - mockAction.apply(72), - mockAction.apply(70) - ), page.getContent()); - - page = this.storage.getLogPage(ActionFilters.source(sourceUuid), new PageParameters(200, 1)); - assertEquals(50, page.getContent().size()); - - page = this.storage.getLogPage(ActionFilters.all(), new PageParameters(200, 1)); - assertEquals(120, page.getContent().size()); - - page = this.storage.getLogPage(ActionFilters.user(targetUuid), new PageParameters(200, 1)); - assertEquals(100, page.getContent().size()); - - page = this.storage.getLogPage(ActionFilters.group("test_group"), new PageParameters(10, 1)); - assertEquals(5, page.getContent().size()); - - page = this.storage.getLogPage(ActionFilters.track("test_track"), new PageParameters(10, 1)); - assertEquals(5, page.getContent().size()); - - page = this.storage.getLogPage(ActionFilters.search("hello"), new PageParameters(200, 1)); - assertEquals(100, page.getContent().size()); - } - - @Test - public void testSavePlayerData() throws Exception { - UUID uniqueId = UUID.randomUUID(); - - // clean insert - PlayerSaveResult r1 = this.storage.savePlayerData(uniqueId, "Player1"); - assertEquals(ImmutableSet.of(Outcome.CLEAN_INSERT), r1.getOutcomes()); - assertNull(r1.getOtherUniqueIds()); - assertNull(r1.getPreviousUsername()); - - // no change expected - PlayerSaveResult r2 = this.storage.savePlayerData(uniqueId, "Player1"); - assertEquals(ImmutableSet.of(Outcome.NO_CHANGE), r2.getOutcomes()); - assertNull(r2.getOtherUniqueIds()); - assertNull(r2.getPreviousUsername()); - - // changed username - PlayerSaveResult r3 = this.storage.savePlayerData(uniqueId, "Player2"); - assertEquals(ImmutableSet.of(Outcome.USERNAME_UPDATED), r3.getOutcomes()); - assertNull(r3.getOtherUniqueIds()); - assertTrue("Player1".equalsIgnoreCase(r3.getPreviousUsername())); - - // changed uuid - UUID newUniqueId = UUID.randomUUID(); - PlayerSaveResult r4 = this.storage.savePlayerData(newUniqueId, "Player2"); - assertEquals(ImmutableSet.of(Outcome.CLEAN_INSERT, Outcome.OTHER_UNIQUE_IDS_PRESENT_FOR_USERNAME), r4.getOutcomes()); - assertNotNull(r4.getOtherUniqueIds()); - assertEquals(ImmutableSet.of(uniqueId), r4.getOtherUniqueIds()); - assertNull(r2.getPreviousUsername()); - } - - @Test - public void testGetPlayerUniqueIdAndName() throws Exception { - UUID uniqueId = UUID.randomUUID(); - String username = "Player1"; - - this.storage.savePlayerData(uniqueId, username); - - assertEquals(uniqueId, this.storage.getPlayerUniqueId("Player1")); - assertTrue(username.equalsIgnoreCase(this.storage.getPlayerName(uniqueId))); - } - - @Test - public void testGetPlayerUniqueIdAndNameNull() throws Exception { - assertNull(this.storage.getPlayerUniqueId("Player1")); - assertNull(this.storage.getPlayerName(UUID.randomUUID())); - } - - @Test - public void testSaveAndLoadGroup() throws Exception { - StandardGroupManager groupManager = new StandardGroupManager(this.plugin); - - //noinspection unchecked,rawtypes - lenient().when(this.plugin.getGroupManager()).thenReturn((GroupManager) groupManager); - - Group group = this.storage.createAndLoadGroup("test"); - - group.normalData().add(Permission.builder() - .permission("test.1") - .withContext("server", "test") - .build() - ); - group.normalData().add(Permission.builder() - .permission("test.2") - .withContext("world", "test") - .build() - ); - group.normalData().add(Permission.builder() - .permission("test.3") - .expiry(1, TimeUnit.HOURS) - .withContext("server", "test") - .withContext("world", "test") - .withContext("hello", "test") - .build() - ); - - Set nodes = group.normalData().asSet(); - assertEquals(3, nodes.size()); - - this.storage.saveGroup(group); - groupManager.unload("test"); - - Group loaded = this.storage.loadGroup("test").orElse(null); - assertNotNull(loaded); - assertNotSame(group, loaded); - assertEquals(nodes, loaded.normalData().asSet()); - } - - @Test - public void testSaveAndDeleteUser() throws SQLException { - StandardUserManager userManager = new StandardUserManager(this.plugin); - - //noinspection unchecked,rawtypes - when(this.plugin.getUserManager()).thenReturn((UserManager) userManager); - - UUID exampleUniqueId = UUID.fromString("069a79f4-44e9-4726-a5be-fca90e38aaf5"); - String exampleUsername = "Notch"; - PermissionNode examplePermission = Permission.builder() - .permission("test.1") - .withContext("server", "test") - .build(); - InheritanceNode defaultGroupNode = Inheritance.builder(GroupManager.DEFAULT_GROUP_NAME).build(); - - // create a default user, assert that is doesn't appear in unique users list - this.storage.savePlayerData(exampleUniqueId, exampleUsername); - assertFalse(this.storage.getUniqueUsers().contains(exampleUniqueId)); - - // give the user a node, assert that it does appear in unique users list - User user = this.storage.loadUser(exampleUniqueId, exampleUsername); - user.setNode(DataType.NORMAL, examplePermission, true); - this.storage.saveUser(user); - assertTrue(this.storage.getUniqueUsers().contains(exampleUniqueId)); - - // clear all nodes (reset to default) and assert that it does not appear in unique users list - user.clearNodes(DataType.NORMAL, null, true); - this.storage.saveUser(user); - assertFalse(this.storage.getUniqueUsers().contains(exampleUniqueId)); - assertEquals(ImmutableSet.of(defaultGroupNode), user.normalData().asSet()); - - // give it a node again, assert that it shows as a unique user - user.setNode(DataType.NORMAL, examplePermission, true); - this.storage.saveUser(user); - assertTrue(this.storage.getUniqueUsers().contains(exampleUniqueId)); - assertEquals(ImmutableSet.of(defaultGroupNode, examplePermission), user.normalData().asSet()); - - // reload user data from the db and assert that it is unchanged - user = this.storage.loadUser(exampleUniqueId, exampleUsername); - assertEquals(ImmutableSet.of(defaultGroupNode, examplePermission), user.normalData().asSet()); + @Override + protected StorageImplementation makeStorage(LuckPermsPlugin plugin) throws Exception { + return new SqlStorage(plugin, new TestH2ConnectionFactory(), "luckperms_"); } private static class TestH2ConnectionFactory implements ConnectionFactory { @@ -379,5 +85,4 @@ public class SqlStorageTest { this.connection.shutdown(); } } - }