diff --git a/src/test/java/com/diamonddagger590/mccore/database/table/impl/PlayerSettingDAOTest.java b/src/test/java/com/diamonddagger590/mccore/database/table/impl/PlayerSettingDAOTest.java index 05ca199..b181525 100644 --- a/src/test/java/com/diamonddagger590/mccore/database/table/impl/PlayerSettingDAOTest.java +++ b/src/test/java/com/diamonddagger590/mccore/database/table/impl/PlayerSettingDAOTest.java @@ -102,6 +102,45 @@ public Optional fromString(@NotNull String setting) { } } + private enum AnotherTestSetting implements PlayerSetting { + ENABLED, + DISABLED; + + private static final NamespacedKey KEY = new NamespacedKey("mccore", "another_test_setting"); + + @Override + @NotNull + public NamespacedKey getSettingKey() { + return KEY; + } + + @Override + @NotNull + public LinkedNode getFirstSetting() { + return new LinkedNode<>(ENABLED, new LinkedNode<>(DISABLED, null)); + } + + @Override + @NotNull + public LinkedNode getNextSetting() { + return this == ENABLED ? new LinkedNode<>(DISABLED, null) : new LinkedNode<>(ENABLED, null); + } + + @Override + public void onSettingChange(@NotNull CorePlayer player, @NotNull Optional oldSetting) { + } + + @Override + @NotNull + public Optional fromString(@NotNull String setting) { + try { + return Optional.of(AnotherTestSetting.valueOf(setting)); + } catch (IllegalArgumentException e) { + return Optional.empty(); + } + } + } + @Nested @DisplayName("attemptCreateTable") class AttemptCreateTable { @@ -273,6 +312,25 @@ void getPlayerSettings_returnsPartialResults_whenSqlExceptionOccurs() throws SQL assertNotNull(settings); assertTrue(settings.isEmpty()); } + + @Test + @DisplayName("Given multiple settings registered, when getPlayerSettings is called, then returns one result per registered setting") + void getPlayerSettings_returnsAllSettings_whenMultipleRegistered() throws SQLException { + PlayerSettingRegistry registry = RegistryAccess.registryAccess().registry(RegistryKey.PLAYER_SETTING); + registry.register(AnotherTestSetting.ENABLED); + + when(mockConnection.prepareStatement(anyString())).thenReturn(mockStatement); + // Both settings default (no saved values) - iteration order doesn't matter + when(mockStatement.executeQuery()).thenReturn(mockResultSet); + when(mockResultSet.next()).thenReturn(false); + + Set settings = PlayerSettingDAO.getPlayerSettings(mockConnection, TEST_UUID); + + assertEquals(2, settings.size()); + // Both should return their defaults: ON for TestSetting, ENABLED for AnotherTestSetting + assertTrue(settings.stream().anyMatch(s -> s.name().equals("ON"))); + assertTrue(settings.stream().anyMatch(s -> s.name().equals("ENABLED"))); + } } @Nested diff --git a/src/test/java/com/diamonddagger590/mccore/database/table/impl/PlayerStatisticDAOSerializationTest.java b/src/test/java/com/diamonddagger590/mccore/database/table/impl/PlayerStatisticDAOSerializationTest.java index 418a2df..3298bd4 100644 --- a/src/test/java/com/diamonddagger590/mccore/database/table/impl/PlayerStatisticDAOSerializationTest.java +++ b/src/test/java/com/diamonddagger590/mccore/database/table/impl/PlayerStatisticDAOSerializationTest.java @@ -121,4 +121,62 @@ void emptyBrackets_returnsEmptySet() { Set result = PlayerStatisticDAO.deserializeStringSet("[]"); assertTrue(result.isEmpty()); } + + @DisplayName("Given unquoted elements, when deserializing, then parses them correctly") + @Test + void unquotedElements_parsedCorrectly() { + Set result = PlayerStatisticDAO.deserializeStringSet("[hello,world]"); + assertEquals(2, result.size()); + assertTrue(result.contains("hello")); + assertTrue(result.contains("world")); + } + + @DisplayName("Given single unquoted element, when deserializing, then parses it correctly") + @Test + void singleUnquotedElement_parsedCorrectly() { + Set result = PlayerStatisticDAO.deserializeStringSet("[hello]"); + assertEquals(1, result.size()); + assertTrue(result.contains("hello")); + } + + @DisplayName("Given backslash followed by non-special character, when deserializing, then preserves both characters") + @Test + void unknownEscapeSequence_preservesBothCharacters() { + Set result = PlayerStatisticDAO.deserializeStringSet("[\"hello\\nworld\"]"); + assertEquals(1, result.size()); + assertTrue(result.contains("hello\\nworld")); + } + + @DisplayName("Given elements with leading whitespace, when deserializing, then trims whitespace") + @Test + void elementsWithWhitespace_trimmedCorrectly() { + Set result = PlayerStatisticDAO.deserializeStringSet("[\"alpha\", \"beta\"]"); + assertEquals(2, result.size()); + assertTrue(result.contains("alpha")); + assertTrue(result.contains("beta")); + } + + @DisplayName("Given brackets with only whitespace, when deserializing, then returns empty set") + @Test + void bracketsWithWhitespace_returnsEmptySet() { + Set result = PlayerStatisticDAO.deserializeStringSet("[ ]"); + assertTrue(result.isEmpty()); + } + + @DisplayName("Given unquoted elements with whitespace, when deserializing, then trims values") + @Test + void unquotedElementsWithWhitespace_trimmed() { + Set result = PlayerStatisticDAO.deserializeStringSet("[ alpha , beta ]"); + assertEquals(2, result.size()); + assertTrue(result.contains("alpha")); + assertTrue(result.contains("beta")); + } + + @DisplayName("Given backslash at end of quoted element, when deserializing, then treats as literal backslash") + @Test + void backslashAtEndOfQuotedElement_treatedAsLiteralEnd() { + Set result = PlayerStatisticDAO.deserializeStringSet("[\"test\\\\\"]"); + assertEquals(1, result.size()); + assertTrue(result.contains("test\\")); + } } diff --git a/src/test/java/com/diamonddagger590/mccore/database/table/impl/PlayerStatisticDAOTest.java b/src/test/java/com/diamonddagger590/mccore/database/table/impl/PlayerStatisticDAOTest.java index 0b30977..1606aed 100644 --- a/src/test/java/com/diamonddagger590/mccore/database/table/impl/PlayerStatisticDAOTest.java +++ b/src/test/java/com/diamonddagger590/mccore/database/table/impl/PlayerStatisticDAOTest.java @@ -397,6 +397,125 @@ void returnsStatistic_whenExists() throws SQLException { assertEquals(77, result.get().value()); } + @Test + @DisplayName("Given LONG statistic exists, when getPlayerStatistic, then returns long value") + void returnsLongStatistic_whenExists() throws SQLException { + when(connection.prepareStatement(anyString())).thenReturn(statement); + when(statement.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(true); + when(resultSet.getString("stat_type")).thenReturn("LONG"); + when(resultSet.getLong("long_value")).thenReturn(123456789L); + + Optional result = PlayerStatisticDAO.getPlayerStatistic(connection, playerUUID, key); + + assertTrue(result.isPresent()); + assertEquals(StatisticType.LONG, result.get().type()); + assertEquals(123456789L, result.get().value()); + } + + @Test + @DisplayName("Given DOUBLE statistic exists, when getPlayerStatistic, then returns double value") + void returnsDoubleStatistic_whenExists() throws SQLException { + when(connection.prepareStatement(anyString())).thenReturn(statement); + when(statement.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(true); + when(resultSet.getString("stat_type")).thenReturn("DOUBLE"); + when(resultSet.getDouble("double_value")).thenReturn(2.718); + + Optional result = PlayerStatisticDAO.getPlayerStatistic(connection, playerUUID, key); + + assertTrue(result.isPresent()); + assertEquals(StatisticType.DOUBLE, result.get().type()); + assertEquals(2.718, result.get().value()); + } + + @Test + @DisplayName("Given STRING statistic exists, when getPlayerStatistic, then returns string value") + void returnsStringStatistic_whenExists() throws SQLException { + when(connection.prepareStatement(anyString())).thenReturn(statement); + when(statement.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(true); + when(resultSet.getString("stat_type")).thenReturn("STRING"); + when(resultSet.getString("string_value")).thenReturn("test_value"); + + Optional result = PlayerStatisticDAO.getPlayerStatistic(connection, playerUUID, key); + + assertTrue(result.isPresent()); + assertEquals(StatisticType.STRING, result.get().type()); + assertEquals("test_value", result.get().value()); + } + + @Test + @DisplayName("Given STRING statistic with null value, when getPlayerStatistic, then returns empty string") + void returnsEmptyString_whenStringValueIsNull() throws SQLException { + when(connection.prepareStatement(anyString())).thenReturn(statement); + when(statement.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(true); + when(resultSet.getString("stat_type")).thenReturn("STRING"); + when(resultSet.getString("string_value")).thenReturn(null); + + Optional result = PlayerStatisticDAO.getPlayerStatistic(connection, playerUUID, key); + + assertTrue(result.isPresent()); + assertEquals(StatisticType.STRING, result.get().type()); + assertEquals("", result.get().value()); + } + + @Test + @DisplayName("Given TIMESTAMP statistic exists, when getPlayerStatistic, then returns Instant value") + void returnsTimestampStatistic_whenExists() throws SQLException { + long epochMillis = 1718000000000L; + when(connection.prepareStatement(anyString())).thenReturn(statement); + when(statement.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(true); + when(resultSet.getString("stat_type")).thenReturn("TIMESTAMP"); + when(resultSet.getLong("timestamp_value")).thenReturn(epochMillis); + + Optional result = PlayerStatisticDAO.getPlayerStatistic(connection, playerUUID, key); + + assertTrue(result.isPresent()); + assertEquals(StatisticType.TIMESTAMP, result.get().type()); + assertEquals(Instant.ofEpochMilli(epochMillis), result.get().value()); + } + + @Test + @DisplayName("Given SET_STRING statistic exists, when getPlayerStatistic, then returns deserialized set") + void returnsSetStringStatistic_whenExists() throws SQLException { + when(connection.prepareStatement(anyString())).thenReturn(statement); + when(statement.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(true); + when(resultSet.getString("stat_type")).thenReturn("SET_STRING"); + when(resultSet.getString("string_value")).thenReturn("[\"one\",\"two\"]"); + + Optional result = PlayerStatisticDAO.getPlayerStatistic(connection, playerUUID, key); + + assertTrue(result.isPresent()); + assertEquals(StatisticType.SET_STRING, result.get().type()); + @SuppressWarnings("unchecked") + Set set = (Set) result.get().value(); + assertEquals(2, set.size()); + assertTrue(set.contains("one")); + assertTrue(set.contains("two")); + } + + @Test + @DisplayName("Given SET_STRING statistic with null value, when getPlayerStatistic, then returns empty set") + void returnsEmptySet_whenSetStringValueIsNull() throws SQLException { + when(connection.prepareStatement(anyString())).thenReturn(statement); + when(statement.executeQuery()).thenReturn(resultSet); + when(resultSet.next()).thenReturn(true); + when(resultSet.getString("stat_type")).thenReturn("SET_STRING"); + when(resultSet.getString("string_value")).thenReturn(null); + + Optional result = PlayerStatisticDAO.getPlayerStatistic(connection, playerUUID, key); + + assertTrue(result.isPresent()); + assertEquals(StatisticType.SET_STRING, result.get().type()); + @SuppressWarnings("unchecked") + Set set = (Set) result.get().value(); + assertTrue(set.isEmpty()); + } + @Test @DisplayName("Given statistic does not exist, when getPlayerStatistic, then returns empty") void returnsEmpty_whenNotExists() throws SQLException {