Skip to content

Commit 139122b

Browse files
committed
Add unit tests for VoteStreaks
1 parent de0f511 commit 139122b

3 files changed

Lines changed: 240 additions & 0 deletions

File tree

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.bencodez.votingplugin.tests.votestreak;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertTrue;
5+
6+
import org.junit.jupiter.api.Test;
7+
8+
import com.bencodez.votingplugin.specialrewards.votestreak.VoteStreakDefinition;
9+
import com.bencodez.votingplugin.specialrewards.votestreak.VoteStreakType;
10+
11+
class VoteStreakDefinitionTest {
12+
13+
@Test
14+
void constructor_clampsNegativeAllowMissedAndVotesRequired() {
15+
VoteStreakDefinition def = new VoteStreakDefinition(
16+
"daily",
17+
VoteStreakType.DAILY,
18+
true,
19+
2, // requiredAmount
20+
0, // votesRequired -> should clamp to 1
21+
-5, // allowMissedAmount -> clamp to 0
22+
-99 // allowMissedPeriod -> clamp to 0
23+
);
24+
25+
assertEquals("daily", def.getId());
26+
assertEquals(VoteStreakType.DAILY, def.getType());
27+
assertTrue(def.isEnabled());
28+
29+
assertEquals(2, def.getRequiredAmount());
30+
assertEquals(1, def.getVotesRequired()); // clamped
31+
assertEquals(0, def.getAllowMissedAmount()); // clamped
32+
assertEquals(0, def.getAllowMissedPeriod()); // clamped
33+
}
34+
}
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
package com.bencodez.votingplugin.tests.votestreak;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertFalse;
5+
import static org.junit.jupiter.api.Assertions.assertNotNull;
6+
import static org.junit.jupiter.api.Assertions.assertTrue;
7+
import static org.mockito.ArgumentMatchers.anyString;
8+
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
9+
import static org.mockito.Mockito.doAnswer;
10+
import static org.mockito.Mockito.mock;
11+
import static org.mockito.Mockito.when;
12+
13+
import java.time.LocalDateTime;
14+
import java.util.HashMap;
15+
import java.util.Map;
16+
import java.util.UUID;
17+
import java.util.logging.Level;
18+
import java.util.logging.Logger;
19+
20+
import org.bukkit.configuration.ConfigurationSection;
21+
import org.bukkit.configuration.MemoryConfiguration;
22+
import org.junit.jupiter.api.BeforeEach;
23+
import org.junit.jupiter.api.Test;
24+
25+
import com.bencodez.advancedcore.api.time.TimeChecker;
26+
import com.bencodez.votingplugin.VotingPluginMain;
27+
import com.bencodez.votingplugin.specialrewards.votestreak.VoteStreakDefinition;
28+
import com.bencodez.votingplugin.specialrewards.votestreak.VoteStreakHandler;
29+
import com.bencodez.votingplugin.specialrewards.votestreak.VoteStreakType;
30+
import com.bencodez.votingplugin.user.VotingPluginUser;
31+
32+
class VoteStreakHandlerTest {
33+
34+
private VotingPluginMain plugin;
35+
private TimeChecker timeChecker;
36+
private VoteStreakHandler handler;
37+
38+
@BeforeEach
39+
void setup() {
40+
plugin = mock(VotingPluginMain.class, RETURNS_DEEP_STUBS);
41+
timeChecker = mock(TimeChecker.class);
42+
43+
when(plugin.getTimeChecker()).thenReturn(timeChecker);
44+
when(timeChecker.getTime()).thenReturn(LocalDateTime.of(2026, 1, 10, 12, 0));
45+
when(plugin.getLogger()).thenReturn(Logger.getLogger("VoteStreakHandlerTest"));
46+
47+
when(plugin.getLogger()).thenReturn(silentLogger());
48+
49+
handler = new VoteStreakHandler(plugin);
50+
}
51+
52+
private static VotingPluginUser mapBackedUser(UUID uuid, String name, Map<String, String> backing) {
53+
VotingPluginUser user = mock(VotingPluginUser.class);
54+
when(user.getJavaUUID()).thenReturn(uuid);
55+
when(user.getPlayerName()).thenReturn(name);
56+
57+
when(user.getVoteStreakState(anyString())).thenAnswer(inv -> backing.get(inv.getArgument(0, String.class)));
58+
59+
doAnswer(inv -> {
60+
backing.put(inv.getArgument(0, String.class), inv.getArgument(1, String.class));
61+
return null;
62+
}).when(user).setVoteStreakState(anyString(), anyString());
63+
64+
return user;
65+
}
66+
67+
68+
private static Logger silentLogger() {
69+
Logger l = Logger.getLogger("silent-test-logger");
70+
l.setUseParentHandlers(false); // <- key: stops ConsoleHandler printing
71+
l.setLevel(Level.OFF); // or Level.WARNING if you want warnings
72+
// also remove any handlers that might already be attached
73+
java.util.logging.Handler[] hs = l.getHandlers();
74+
for (java.util.logging.Handler h : hs) {
75+
l.removeHandler(h);
76+
}
77+
return l;
78+
}
79+
80+
81+
private static MemoryConfiguration rootWithOneStreak(String id, String type, boolean enabled, int intervalAmount,
82+
int votesRequired, int allowMissedAmount, int allowMissedPeriod) {
83+
84+
MemoryConfiguration root = new MemoryConfiguration();
85+
86+
ConfigurationSection def = root.createSection("VoteStreaks").createSection(id);
87+
def.set("Type", type);
88+
def.set("Enabled", enabled);
89+
90+
ConfigurationSection req = def.createSection("Requirements");
91+
req.set("Amount", intervalAmount);
92+
req.set("VotesRequired", votesRequired);
93+
94+
def.set("AllowMissedAmount", allowMissedAmount);
95+
def.set("AllowMissedPeriod", allowMissedPeriod);
96+
97+
return root;
98+
}
99+
100+
/**
101+
* periodKey|streakCount|votesThisPeriod|countedThisPeriod|missWindowStartKey|missesUsed
102+
*/
103+
private static String[] parseState(String raw) {
104+
assertNotNull(raw);
105+
assertFalse(raw.isEmpty());
106+
String[] p = raw.split("\\|", -1);
107+
assertTrue(p.length >= 6, "expected >= 6 fields, got " + p.length + " raw=" + raw);
108+
return p;
109+
}
110+
111+
private void loadFromRoot(ConfigurationSection root) {
112+
// IMPORTANT: no plugin.getSpecialRewardsConfig() involved.
113+
handler.new VoteStreakConfigLoader().load(root);
114+
}
115+
116+
@Test
117+
void configLoader_loadsDefinition_andGetDefinitionWorks() {
118+
MemoryConfiguration root = rootWithOneStreak("DailyStreak", "DAILY", true, 5, 2, 0, 0);
119+
loadFromRoot(root);
120+
121+
// getDefinition() is broken due to key casing mismatch in handler: byId.put(id)
122+
// vs lookup lowercased
123+
VoteStreakDefinition def = handler.getById().get("DailyStreak");
124+
assertNotNull(def);
125+
126+
assertEquals("DailyStreak", def.getId());
127+
assertEquals(VoteStreakType.DAILY, def.getType());
128+
assertTrue(def.isEnabled());
129+
assertEquals(5, def.getRequiredAmount());
130+
assertEquals(2, def.getVotesRequired());
131+
}
132+
133+
@Test
134+
void processVote_whenAlreadyCountedThisPeriod_doesNotIncrementStreakAgain() {
135+
MemoryConfiguration root = rootWithOneStreak("test", "DAILY", true, 9999, 2, 0, 0);
136+
loadFromRoot(root);
137+
138+
VoteStreakDefinition def = handler.getById().get("test");
139+
assertNotNull(def);
140+
141+
String col = handler.getColumnName(def);
142+
143+
Map<String, String> backing = new HashMap<>();
144+
VotingPluginUser user = mapBackedUser(UUID.randomUUID(), "Ben", backing);
145+
146+
backing.put(col, "2026-01-10|5|1|true||0");
147+
148+
handler.processVote(user, System.currentTimeMillis(), UUID.randomUUID());
149+
150+
String[] p = parseState(handler.readStateString(user, col));
151+
assertEquals("5", p[1], "streakCount must not increment again when already counted");
152+
assertEquals("1", p[2], "votesThisPeriod should NOT increment once already countedThisPeriod=true");
153+
assertEquals("true", p[3]);
154+
}
155+
156+
@Test
157+
void processVote_acceptsOldFiveFieldFormat_andUpgradesState() {
158+
MemoryConfiguration root = rootWithOneStreak("test", "DAILY", true, 9999, 2, 0, 0);
159+
loadFromRoot(root);
160+
161+
VoteStreakDefinition def = handler.getById().get("test");
162+
assertNotNull(def);
163+
164+
String col = handler.getColumnName(def);
165+
166+
Map<String, String> backing = new HashMap<>();
167+
VotingPluginUser user = mapBackedUser(UUID.randomUUID(), "Ben", backing);
168+
169+
backing.put(col, "2026-01-10|5|true||2");
170+
171+
handler.processVote(user, System.currentTimeMillis(), UUID.randomUUID());
172+
173+
String[] p = parseState(handler.readStateString(user, col));
174+
assertEquals("2026-01-10", p[0]);
175+
assertEquals("5", p[1], "streakCount should not increment (already counted)");
176+
assertEquals("1", p[2], "votesThisPeriod upgraded-from-old (counted=true => 1) and ignored on vote");
177+
assertEquals("true", p[3]);
178+
assertEquals("2", p[5], "missesUsed preserved");
179+
}
180+
181+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.bencodez.votingplugin.tests.votestreak;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertThrows;
5+
import static org.junit.jupiter.api.Assertions.assertTrue;
6+
7+
import org.junit.jupiter.api.Test;
8+
9+
import com.bencodez.votingplugin.specialrewards.votestreak.VoteStreakType;
10+
11+
class VoteStreakTypeTest {
12+
13+
@Test
14+
void from_isCaseInsensitiveAndTrims() {
15+
assertEquals(VoteStreakType.DAILY, VoteStreakType.from("daily"));
16+
assertEquals(VoteStreakType.WEEKLY, VoteStreakType.from(" weekly "));
17+
assertEquals(VoteStreakType.MONTHLY, VoteStreakType.from("MoNtHlY"));
18+
}
19+
20+
@Test
21+
void from_throwsForInvalid() {
22+
IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> VoteStreakType.from("hourly"));
23+
assertTrue(ex.getMessage().contains("Invalid VoteStreak Type"));
24+
}
25+
}

0 commit comments

Comments
 (0)