AuthMeReloaded/src/test/java/fr/xephi/authme/ConsoleLoggerTest.java

334 lines
12 KiB
Java

package fr.xephi.authme;
import fr.xephi.authme.output.LogLevel;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.PluginSettings;
import fr.xephi.authme.settings.properties.SecuritySettings;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.entity.Player;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.List;
import java.util.logging.Logger;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.verifyNoMoreInteractions;
/**
* Test for {@link ConsoleLogger}.
*/
@RunWith(MockitoJUnitRunner.class)
public class ConsoleLoggerTest {
private ConsoleLogger consoleLogger;
@Mock
private Logger logger;
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
private File logFile;
@Before
public void setMockLogger() throws IOException {
File folder = temporaryFolder.newFolder();
File logFile = new File(folder, "authme.log");
if (!logFile.createNewFile()) {
throw new IOException("Could not create file '" + logFile.getPath() + "'");
}
ConsoleLogger.initialize(logger, logFile);
this.logFile = logFile;
this.consoleLogger = new ConsoleLogger("test");
}
@After
public void closeFileHandlers() {
ConsoleLogger.closeFileWriter();
}
/**
* Resets the ConsoleLogger back to its defaults after running all tests. Especially important
* is that we no longer enable logging to a file as the log file we've supplied will no longer
* be around after this test class has finished.
*/
@AfterClass
public static void resetConsoleToDefault() {
ConsoleLogger.initializeSharedSettings(newSettings(false, LogLevel.INFO));
}
@Test
public void shouldLogToFile() throws IOException {
// given
Settings settings = newSettings(true, LogLevel.FINE);
ConsoleLogger.initializeSharedSettings(settings);
consoleLogger.initializeSettings(settings);
// when
consoleLogger.fine("Logging a FINE message");
consoleLogger.debug("Logging a DEBUG message");
consoleLogger.info("This is an INFO message");
// then
verify(logger, times(2)).info(anyString());
verifyNoMoreInteractions(logger);
List<String> loggedLines = Files.readAllLines(logFile.toPath(), StandardCharsets.UTF_8);
assertThat(loggedLines, hasSize(2));
assertThat(loggedLines.get(0), containsString("[FINE] Logging a FINE message"));
assertThat(loggedLines.get(1), containsString("[INFO] This is an INFO message"));
}
@Test
public void shouldNotLogToFile() {
// given
Settings settings = newSettings(false, LogLevel.DEBUG);
ConsoleLogger.initializeSharedSettings(settings);
consoleLogger.initializeSettings(settings);
// when
consoleLogger.debug("Created test");
consoleLogger.warning("Encountered a warning");
// then
verify(logger).info("[DEBUG] Created test");
verify(logger).warning("Encountered a warning");
verifyNoMoreInteractions(logger);
assertThat(logFile.length(), equalTo(0L));
}
@Test
public void shouldLogStackTraceToFile() throws IOException {
// given
Settings settings = newSettings(true, LogLevel.INFO);
ConsoleLogger.initializeSharedSettings(settings);
Exception e = new IllegalStateException("Test exception message");
// when
consoleLogger.info("Info text");
consoleLogger.debug("Debug message");
consoleLogger.fine("Fine-level message");
consoleLogger.logException("Exception occurred:", e);
// then
verify(logger).info("Info text");
verify(logger).warning("Exception occurred: [IllegalStateException]: Test exception message");
verifyNoMoreInteractions(logger);
List<String> loggedLines = Files.readAllLines(logFile.toPath(), StandardCharsets.UTF_8);
assertThat(loggedLines.size(), greaterThan(3));
assertThat(loggedLines.get(0), containsString("[INFO] Info text"));
assertThat(loggedLines.get(1),
containsString("[WARN] Exception occurred: [IllegalStateException]: Test exception message"));
// Check that we have this class' full name somewhere in the file -> stacktrace of Exception e
assertThat(String.join("", loggedLines), containsString(getClass().getCanonicalName()));
}
@SuppressWarnings("unchecked")
@Test
public void shouldSupportVariousDebugMethods() throws IOException {
// given
Settings settings = newSettings(true, LogLevel.DEBUG);
ConsoleLogger.initializeSharedSettings(settings);
consoleLogger.initializeSettings(settings);
// when
consoleLogger.debug("Got {0} entries", 17);
consoleLogger.debug("Player `{0}` is in world `{1}`", "Bobby", new WorldDummy("world"));
consoleLogger.debug("{0} quick {1} jump over {2} lazy {3} (reason: {4})", 5, "foxes", 3, "dogs", null);
consoleLogger.debug(() -> "Too little too late");
// then
verify(logger).info("[DEBUG] Got 17 entries");
verify(logger).info("[DEBUG] Player `Bobby` is in world `w[world]`");
verify(logger).info("[DEBUG] 5 quick foxes jump over 3 lazy dogs (reason: null)");
verify(logger).info("[DEBUG] Too little too late");
verifyNoMoreInteractions(logger);
List<String> loggedLines = Files.readAllLines(logFile.toPath(), StandardCharsets.UTF_8);
assertThat(loggedLines, contains(
containsString("[DEBUG] Got 17 entries"),
containsString("[DEBUG] Player `Bobby` is in world `w[world]`"),
containsString("[DEBUG] 5 quick foxes jump over 3 lazy dogs (reason: null)"),
containsString("[DEBUG] Too little too late")));
}
@Test
public void shouldCloseFileWriterDespiteExceptionOnFlush() throws IOException {
// given
FileWriter fileWriter = mock(FileWriter.class);
doThrow(new IOException("Error during flush")).when(fileWriter).flush();
ReflectionTestUtils.setField(ConsoleLogger.class, null, "fileWriter", fileWriter);
// when
ConsoleLogger.closeFileWriter();
// then
verify(fileWriter).flush();
verify(fileWriter).close();
assertThat(ReflectionTestUtils.getFieldValue(ConsoleLogger.class, null, "fileWriter"), nullValue());
}
@Test
public void shouldHandleExceptionOnFileWriterClose() throws IOException {
// given
FileWriter fileWriter = mock(FileWriter.class);
doThrow(new IOException("Cannot close")).when(fileWriter).close();
ReflectionTestUtils.setField(ConsoleLogger.class, null, "fileWriter", fileWriter);
// when
ConsoleLogger.closeFileWriter();
// then
verify(fileWriter).flush();
verify(fileWriter).close();
assertThat(ReflectionTestUtils.getFieldValue(ConsoleLogger.class, null, "fileWriter"), nullValue());
}
@Test
public void shouldLogAndSendMessage() {
// given
Settings settings = newSettings(false, LogLevel.INFO);
ConsoleLogger.initializeSharedSettings(settings);
consoleLogger.initializeSettings(settings);
Player player = mock(Player.class);
String message = "Finished adding foo to the bar";
// when
consoleLogger.logAndSendMessage(player, message);
// then
verify(logger).info(message);
verify(player).sendMessage(message);
}
@Test
public void shouldHandleNullAsCommandSender() {
// given
Settings settings = newSettings(false, LogLevel.INFO);
ConsoleLogger.initializeSharedSettings(settings);
consoleLogger.initializeSettings(settings);
String message = "Test test, test.";
// when
consoleLogger.logAndSendMessage(null, message);
// then
verify(logger).info(message);
}
@Test
public void shouldNotSendToCommandSenderTwice() {
// given
Settings settings = newSettings(false, LogLevel.INFO);
ConsoleLogger.initializeSharedSettings(settings);
consoleLogger.initializeSettings(settings);
CommandSender sender = mock(ConsoleCommandSender.class);
String message = "Test test, test.";
// when
consoleLogger.logAndSendMessage(sender, message);
// then
verify(logger).info(message);
verifyNoInteractions(sender);
}
@Test
public void shouldLogAndSendWarning() {
// given
Settings settings = newSettings(false, LogLevel.INFO);
ConsoleLogger.initializeSharedSettings(settings);
consoleLogger.initializeSettings(settings);
String message = "Error while performing action";
CommandSender sender = mock(CommandSender.class);
// when
consoleLogger.logAndSendWarning(sender, message);
// then
verify(logger).warning(message);
verify(sender).sendMessage(ChatColor.RED + message);
}
@Test
public void shouldLogWarningAndNotSendToConsoleSender() {
// given
Settings settings = newSettings(false, LogLevel.INFO);
ConsoleLogger.initializeSharedSettings(settings);
consoleLogger.initializeSettings(settings);
String message = "Error while performing action";
CommandSender sender = mock(ConsoleCommandSender.class);
// when
consoleLogger.logAndSendWarning(sender, message);
// then
verify(logger).warning(message);
verifyNoInteractions(sender);
}
@SuppressWarnings("ConstantConditions")
@Test
public void shouldLogWarningAndHandleNullCommandSender() {
// given
Settings settings = newSettings(false, LogLevel.INFO);
ConsoleLogger.initializeSharedSettings(settings);
consoleLogger.initializeSettings(settings);
String message = "Error while performing action";
CommandSender sender = null;
// when
consoleLogger.logAndSendWarning(sender, message);
// then
verify(logger).warning(message);
}
private static Settings newSettings(boolean logToFile, LogLevel logLevel) {
Settings settings = mock(Settings.class);
given(settings.getProperty(SecuritySettings.USE_LOGGING)).willReturn(logToFile);
given(settings.getProperty(PluginSettings.LOG_LEVEL)).willReturn(logLevel);
return settings;
}
private static final class WorldDummy {
private final String name;
WorldDummy(String name) {
this.name = name;
}
@Override
public String toString() {
return "w[" + name + "]";
}
}
}