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..9a0bb15 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 @@ -190,6 +190,19 @@ void updateTable_updatesToVersion1_whenVersionIsZero() throws SQLException { verify(updateStatement).setInt(3, 1); verify(updateStatement).executeUpdate(); } + + @Test + @DisplayName("Given the table is at a negative version, when updateTable is called, then it does not attempt version 0 migration") + void updateTable_skipsVersion0Migration_whenVersionIsNegative() throws SQLException { + try (MockedStatic tvhStatic = mockStatic(TableVersionHistoryDAO.class)) { + tvhStatic.when(() -> TableVersionHistoryDAO.getLatestVersion(any(), anyString())).thenReturn(-1); + + PlayerSettingDAO.updateTable(mockConnection); + + tvhStatic.verify(() -> TableVersionHistoryDAO.setTableVersion(any(), anyString(), eq(1)), + org.mockito.Mockito.never()); + } + } } @Nested @@ -273,6 +286,7 @@ void getPlayerSettings_returnsPartialResults_whenSqlExceptionOccurs() throws SQL assertNotNull(settings); assertTrue(settings.isEmpty()); } + } @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..81648e1 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 @@ -3,6 +3,8 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Nested; + import java.util.LinkedHashSet; import java.util.Set; @@ -121,4 +123,112 @@ void emptyBrackets_returnsEmptySet() { Set result = PlayerStatisticDAO.deserializeStringSet("[]"); assertTrue(result.isEmpty()); } + + @Nested + @DisplayName("deserializeStringSet branch coverage") + class DeserializeStringSetBranchCoverage { + + @DisplayName("Given whitespace between quoted elements, when deserialized, then elements are parsed correctly") + @Test + void whitespace_betweenQuotedElements_parsedCorrectly() { + Set result = PlayerStatisticDAO.deserializeStringSet("[\"alpha\", \"beta\"]"); + assertEquals(2, result.size()); + assertTrue(result.contains("alpha")); + assertTrue(result.contains("beta")); + } + + @DisplayName("Given leading whitespace inside brackets, when deserialized, then elements are parsed correctly") + @Test + void leadingWhitespace_parsedCorrectly() { + Set result = PlayerStatisticDAO.deserializeStringSet("[ \"hello\"]"); + assertEquals(1, result.size()); + assertTrue(result.contains("hello")); + } + + @DisplayName("Given only whitespace inside brackets, when deserialized, then returns empty set") + @Test + void whitespaceOnly_returnsEmptySet() { + Set result = PlayerStatisticDAO.deserializeStringSet("[ ]"); + assertTrue(result.isEmpty()); + } + + @DisplayName("Given unquoted single element, when deserialized, then element is parsed") + @Test + void unquotedSingleElement_parsed() { + Set result = PlayerStatisticDAO.deserializeStringSet("[foo]"); + assertEquals(1, result.size()); + assertTrue(result.contains("foo")); + } + + @DisplayName("Given unquoted elements with comma separator, when deserialized, then all elements are parsed") + @Test + void unquotedElements_withComma_allParsed() { + Set result = PlayerStatisticDAO.deserializeStringSet("[foo,bar,baz]"); + assertEquals(3, result.size()); + assertTrue(result.contains("foo")); + assertTrue(result.contains("bar")); + assertTrue(result.contains("baz")); + } + + @DisplayName("Given unquoted elements with whitespace, when deserialized, then elements are trimmed") + @Test + void unquotedElements_withWhitespace_trimmed() { + Set result = PlayerStatisticDAO.deserializeStringSet("[ foo , bar ]"); + assertEquals(2, result.size()); + assertTrue(result.contains("foo")); + assertTrue(result.contains("bar")); + } + + @DisplayName("Given unknown escape sequence in quoted element, when deserialized, then both characters are preserved") + @Test + void unknownEscapeSequence_bothCharsPreserved() { + Set result = PlayerStatisticDAO.deserializeStringSet("[\"hello\\nworld\"]"); + assertEquals(1, result.size()); + assertTrue(result.contains("hello\\nworld")); + } + + @DisplayName("Given backslash at end of input inside quoted element, when deserialized, then backslash is included") + @Test + void backslashAtEndOfInput_included() { + // Inner content is: "test\ — backslash is the very last char of inner, + // so i+1 >= inner.length() and the backslash falls to the else branch + Set result = PlayerStatisticDAO.deserializeStringSet("[\"test\\]"); + assertEquals(1, result.size()); + assertTrue(result.contains("test\\")); + } + + @DisplayName("Given whitespace and comma after quoted element, when deserialized, then separator is skipped") + @Test + void whitespaceAndCommaAfterElement_separatorSkipped() { + Set result = PlayerStatisticDAO.deserializeStringSet("[\"a\" , \"b\"]"); + assertEquals(2, result.size()); + assertTrue(result.contains("a")); + assertTrue(result.contains("b")); + } + + @DisplayName("Given mixed quoted and unquoted elements, when deserialized, then both types are parsed") + @Test + void mixedQuotedAndUnquoted_bothParsed() { + Set result = PlayerStatisticDAO.deserializeStringSet("[\"quoted\",unquoted]"); + assertEquals(2, result.size()); + assertTrue(result.contains("quoted")); + assertTrue(result.contains("unquoted")); + } + + @DisplayName("Given multiple unknown escape sequences, when deserialized, then all are preserved as-is") + @Test + void multipleUnknownEscapes_allPreserved() { + Set result = PlayerStatisticDAO.deserializeStringSet("[\"\\a\\b\\c\"]"); + assertEquals(1, result.size()); + assertTrue(result.contains("\\a\\b\\c")); + } + + @DisplayName("Given trailing whitespace after last quoted element, when deserialized, then parses correctly") + @Test + void trailingWhitespaceAfterLastElement_parsesCorrectly() { + Set result = PlayerStatisticDAO.deserializeStringSet("[\"only\" ]"); + assertEquals(1, result.size()); + assertTrue(result.contains("only")); + } + } } 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..3bc52f0 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 @@ -157,6 +157,19 @@ void updatesToVersion1_whenVersion0Stored() { tvhStatic.verify(() -> TableVersionHistoryDAO.setTableVersion(eq(connection), anyString(), eq(1))); } } + + @Test + @DisplayName("Given negative version stored, when updateTable, then does not perform version 0 migration") + void doesNotPerformVersion0Migration_whenVersionIsNegative() { + try (MockedStatic tvhStatic = mockStatic(TableVersionHistoryDAO.class)) { + tvhStatic.when(() -> TableVersionHistoryDAO.getLatestVersion(any(), anyString())).thenReturn(-1); + + PlayerStatisticDAO.updateTable(connection); + + tvhStatic.verify(() -> TableVersionHistoryDAO.setTableVersion(any(), anyString(), eq(1)), + org.mockito.Mockito.never()); + } + } } @Nested