Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,24 @@ public int compare(VoteSite v1, VoteSite v2) {
return voteSites;
}

/**
* Gets configured vote-site section keys without filtering by Enabled or
* validating the site's settings.
*
* @return the configured vote-site section keys
*/
public ArrayList<String> getRawVoteSiteNames() {
ArrayList<String> siteNames = new ArrayList<>();

if (!getData().isConfigurationSection("VoteSites")) {
return siteNames;
}

siteNames = ArrayUtils.convert(getData().getConfigurationSection("VoteSites").getKeys(false));
siteNames.removeIf(siteName -> !getData().isConfigurationSection("VoteSites." + siteName));
return siteNames;
}

/**
* Gets the names of vote sites.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ public void run() {
}

String voteSiteNameStr = plugin.getVoteSiteManager().getVoteSiteName(false, voteSite, matchSite);
boolean createSite = !plugin.getVoteSiteManager().hasVoteSite(voteSiteNameStr);
boolean createSite = !plugin.getVoteSiteManager().hasVoteSite(voteSiteNameStr)
&& !plugin.getVoteSiteManager().hasConfiguredVoteSite(voteSiteNameStr);

String serviceSite = voteSite;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,60 @@ public String normalizeVoteSiteKey(String name) {
return name.replaceAll("[\\.\\s]+", "_");
}

/**
* Resolves identifiers against every configured vote-site section, including
* disabled sites.
*
* @param identifiers vote-site keys, service sites, or display names
* @return the configured vote-site key, or null when no configuration matches
*/
private String getConfiguredVoteSiteName(String... identifiers) {
if (identifiers == null) {
return null;
}

ArrayList<String> configuredSites = plugin.getConfigVoteSites().getRawVoteSiteNames();
if (configuredSites == null || configuredSites.isEmpty()) {
return null;
}

for (String identifier : identifiers) {
if (identifier == null || identifier.isEmpty()) {
continue;
}

String normalizedIdentifier = normalizeVoteSiteKey(identifier);
for (String siteName : configuredSites) {
if (siteName == null) {
continue;
}

String serviceSite = plugin.getConfigVoteSites().getServiceSite(siteName);
String displayName = plugin.getConfigVoteSites().getDisplayName(siteName);
if (siteName.equalsIgnoreCase(identifier) || siteName.equalsIgnoreCase(normalizedIdentifier)
|| (serviceSite != null && !serviceSite.isEmpty()
&& serviceSite.equalsIgnoreCase(identifier))
|| (displayName != null && !displayName.isEmpty()
&& displayName.equalsIgnoreCase(identifier))) {
return siteName;
}
}
}

return null;
}

/**
* Checks whether an identifier belongs to any configured vote site, including
* a disabled site.
*
* @param identifiers vote-site identifiers
* @return true when a configured vote-site section matches
*/
public boolean hasConfiguredVoteSite(String... identifiers) {
return getConfiguredVoteSiteName(identifiers) != null;
}

/**
* Attempts to map a URL, display name, or key to the configured vote site key.
*
Expand All @@ -91,6 +145,10 @@ public String normalizeVoteSiteKey(String name) {
* found
*/
public String getVoteSiteName(boolean checkEnabled, String... urls) {
if (urls == null) {
return null;
}

for (String url : urls) {
if (url == null) {
return null;
Expand Down Expand Up @@ -119,6 +177,13 @@ public String getVoteSiteName(boolean checkEnabled, String... urls) {
}
}

if (!checkEnabled) {
String configuredSiteName = getConfiguredVoteSiteName(urls);
if (configuredSiteName != null) {
return configuredSiteName;
}
}

for (String url : urls) {
return url;
}
Expand Down Expand Up @@ -147,7 +212,8 @@ public VoteSite getVoteSite(String site, boolean checkEnabled) {
}
}

if (plugin.getConfigFile().isAutoCreateVoteSites() && !hasVoteSite(siteName)) {
if (plugin.getConfigFile().isAutoCreateVoteSites() && !hasVoteSite(siteName)
&& !hasConfiguredVoteSite(siteName)) {
if (!ServiceSiteValidator.isValid(siteName)) {
plugin.getLogger().warning("Unable to auto-create vote site with unsupported name '"
+ ServiceSiteValidator.sanitizeForLog(siteName) + "'");
Expand Down Expand Up @@ -214,6 +280,9 @@ public String getVoteSiteServiceSite(String name) {
*/
public boolean hasVoteSite(String site) {
String siteName = getVoteSiteName(false, site);
if (siteName == null) {
return false;
}

for (VoteSite voteSite : getVoteSites()) {
if (voteSite.getKey().equalsIgnoreCase(siteName)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package com.bencodez.votingplugin.tests.listeners;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ScheduledExecutorService;
import java.util.logging.Logger;

import org.bukkit.Server;
import org.bukkit.plugin.PluginManager;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import com.bencodez.votingplugin.VotingPluginMain;
import com.bencodez.votingplugin.config.ConfigVoteSites;
import com.bencodez.votingplugin.events.PlayerVoteEvent;
import com.bencodez.votingplugin.listeners.VotiferEvent;
import com.bencodez.votingplugin.votesites.VoteSiteManager;
import com.vexsoftware.votifier.model.Vote;

/**
* Tests Votifier ingestion when a received service belongs to a disabled
* configured vote site.
*/
public class VotiferEventDisabledVoteSiteTest {

private static final String SERVICE_SITE = "disabled.example.com";

private VotingPluginMain plugin;

private ConfigVoteSites configVoteSites;

private VoteSiteManager voteSiteManager;

private ScheduledExecutorService voteTimer;

private PluginManager pluginManager;

private VotiferEvent listener;

@BeforeEach
public void setUp() {
plugin = mock(VotingPluginMain.class, RETURNS_DEEP_STUBS);
configVoteSites = mock(ConfigVoteSites.class);
voteSiteManager = mock(VoteSiteManager.class);
voteTimer = mock(ScheduledExecutorService.class);

Server server = mock(Server.class);
pluginManager = mock(PluginManager.class);

when(plugin.getLogger()).thenReturn(Logger.getLogger("VotiferEventDisabledVoteSiteTest"));
when(plugin.getConfigVoteSites()).thenReturn(configVoteSites);
when(plugin.getVoteSiteManager()).thenReturn(voteSiteManager);
when(plugin.getVoteTimer()).thenReturn(voteTimer);
when(plugin.getServer()).thenReturn(server);
when(server.getPluginManager()).thenReturn(pluginManager);

when(plugin.getOptions().getBedrockPlayerPrefix()).thenReturn(".");
when(plugin.getBungeeSettings().isUseBungeecoord()).thenReturn(false);
when(plugin.getConfigFile().isAdvancedServiceSiteHandling()).thenReturn(false);
when(plugin.getTimeChecker().isActiveProcessing()).thenReturn(false);

// Execute submitted vote work immediately so assertions do not need a real
// executor thread.
doAnswer(invocation -> {
Runnable task = invocation.getArgument(0);
task.run();
return CompletableFuture.completedFuture(null);
}).when(voteTimer).submit(any(Runnable.class));

listener = new VotiferEvent(plugin);
}

/**
* Creates a mocked NuVotifier event.
*
* @param serviceSite service site supplied by NuVotifier
* @return the event
*/
private com.vexsoftware.votifier.model.VotifierEvent createVoteEvent(String serviceSite) {
Vote vote = mock(Vote.class);
when(vote.getServiceName()).thenReturn(serviceSite);
when(vote.getAddress()).thenReturn("127.0.0.1");
when(vote.getUsername()).thenReturn("Steve");

com.vexsoftware.votifier.model.VotifierEvent event =
mock(com.vexsoftware.votifier.model.VotifierEvent.class);
when(event.getVote()).thenReturn(vote);
return event;
}

@Test
public void testDisabledConfiguredSiteIsNotGeneratedByVotifierPath() {
when(plugin.getConfigFile().isAutoCreateVoteSites()).thenReturn(true);

when(voteSiteManager.getVoteSiteName(false, SERVICE_SITE, "")).thenReturn("DisabledSite");
when(voteSiteManager.hasVoteSite("DisabledSite")).thenReturn(false);
when(voteSiteManager.hasConfiguredVoteSite("DisabledSite")).thenReturn(true);

when(voteSiteManager.getVoteSiteName(true, SERVICE_SITE, "")).thenReturn(SERVICE_SITE);
when(voteSiteManager.getVoteSite(SERVICE_SITE, true)).thenReturn(null);

listener.onVotiferEvent(createVoteEvent(SERVICE_SITE));

verify(configVoteSites, never()).tryGenerateVoteSite(anyString());

// Proves the submitted task continued through vote resolution rather than
// passing only because processing stopped before the generation decision.
verify(pluginManager).callEvent(any(PlayerVoteEvent.class));
}

@Test
public void testUnknownSiteStillAttemptsGenerationByVotifierPath() {
when(plugin.getConfigFile().isAutoCreateVoteSites()).thenReturn(true);

when(voteSiteManager.getVoteSiteName(false, SERVICE_SITE, "")).thenReturn(SERVICE_SITE);
when(voteSiteManager.hasVoteSite(SERVICE_SITE)).thenReturn(false);
when(voteSiteManager.hasConfiguredVoteSite(SERVICE_SITE)).thenReturn(false);

// Return false to avoid depending on reload behavior. This test only needs to
// prove that a genuinely unknown site still attempts generation.
when(configVoteSites.tryGenerateVoteSite(SERVICE_SITE)).thenReturn(false);

when(voteSiteManager.getVoteSiteName(true, SERVICE_SITE, "")).thenReturn(SERVICE_SITE);
when(voteSiteManager.getVoteSite(SERVICE_SITE, true)).thenReturn(null);

listener.onVotiferEvent(createVoteEvent(SERVICE_SITE));

verify(configVoteSites).tryGenerateVoteSite(SERVICE_SITE);
verify(pluginManager).callEvent(any(PlayerVoteEvent.class));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.bencodez.votingplugin.tests.votesite;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;

import java.util.ArrayList;

import org.bukkit.configuration.file.YamlConfiguration;
import org.junit.jupiter.api.Test;

import com.bencodez.votingplugin.config.ConfigVoteSites;

/**
* Tests the unfiltered configured vote-site section lookup.
*/
public class ConfigVoteSitesRawNamesTest {

/**
* Creates a partially real ConfigVoteSites mock whose data comes from the
* supplied in-memory YAML configuration.
*
* @param data in-memory configuration data
* @return the configuration object
*/
private ConfigVoteSites configWithData(YamlConfiguration data) {
ConfigVoteSites config = mock(ConfigVoteSites.class, CALLS_REAL_METHODS);
doReturn(data).when(config).getData();
return config;
}

@Test
public void testRawNamesIncludesEnabledAndDisabledSections() {
YamlConfiguration data = new YamlConfiguration();

data.createSection("VoteSites.EnabledSite");
data.set("VoteSites.EnabledSite.Enabled", true);
data.set("VoteSites.EnabledSite.ServiceSite", "enabled.example.com");

data.createSection("VoteSites.DisabledSite");
data.set("VoteSites.DisabledSite.Enabled", false);
data.set("VoteSites.DisabledSite.ServiceSite", "disabled.example.com");

ArrayList<String> names = configWithData(data).getRawVoteSiteNames();

assertEquals(2, names.size());
assertTrue(names.contains("EnabledSite"));
assertTrue(names.contains("DisabledSite"),
"Disabled sections must remain visible to existence checks");
}

@Test
public void testRawNamesIgnoresMalformedScalarChildren() {
YamlConfiguration data = new YamlConfiguration();

data.createSection("VoteSites.RealSite");
data.set("VoteSites.RealSite.Enabled", false);
data.set("VoteSites.MalformedSite", "this is a scalar and not a vote-site section");

ArrayList<String> names = configWithData(data).getRawVoteSiteNames();

assertEquals(1, names.size());
assertEquals("RealSite", names.get(0));
}

@Test
public void testRawNamesReturnsEmptyWhenVoteSitesSectionIsMissing() {
YamlConfiguration data = new YamlConfiguration();

assertTrue(configWithData(data).getRawVoteSiteNames().isEmpty());
}

@Test
public void testRawNamesReturnsEmptyWhenVoteSitesValueIsMalformed() {
YamlConfiguration data = new YamlConfiguration();
data.set("VoteSites", "not a configuration section");

assertTrue(configWithData(data).getRawVoteSiteNames().isEmpty());
}
}
Loading
Loading