#1037 Improve architecture of registration methods (#236)

* #1037 Improve architecture of registration methods
- Introduce parameters classes for each registration method
- RegistrationMethod has constants with the required parameters class -> no longer need to have a RegistrationExecutorProvider class that needs to be injected everywhere, let AsyncRegister be the only one to worry about which class will perform the registration
- Fix inheritance of password registration types - previously two-factor auth registration inherited from password registration directly

* Create matcher which checks for equality via reflections
- Allow to perform equality check on objects with default equals() method
This commit is contained in:
ljacqu
2017-03-20 08:19:52 +01:00
committed by GitHub
parent 7f16e80442
commit 9a9d0974f8
31 changed files with 820 additions and 393 deletions
@@ -9,6 +9,7 @@ import fr.xephi.authme.command.CommandHandler;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.initialization.DataFolder;
import fr.xephi.authme.initialization.factory.FactoryDependencyHandler;
import fr.xephi.authme.initialization.factory.SingletonStoreDependencyHandler;
import fr.xephi.authme.listener.BlockListener;
import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.process.Management;
@@ -93,7 +94,7 @@ public class AuthMeInitializationTest {
new Settings(dataFolder, mock(PropertyResource.class), null, buildConfigurationData());
Injector injector = new InjectorBuilder()
.addHandlers(new FactoryDependencyHandler())
.addHandlers(new FactoryDependencyHandler(), new SingletonStoreDependencyHandler())
.addDefaultHandlers("fr.xephi.authme")
.create();
injector.provide(DataFolder.class, dataFolder);
@@ -25,7 +25,7 @@ public final class ReflectionTestUtils {
*/
public static <T> void setField(Class<T> clazz, T instance, String fieldName, Object value) {
try {
Field field = getField(clazz, instance, fieldName);
Field field = getField(clazz, fieldName);
field.set(instance, value);
} catch (IllegalAccessException e) {
throw new UnsupportedOperationException(
@@ -34,24 +34,30 @@ public final class ReflectionTestUtils {
}
}
private static <T> Field getField(Class<T> clazz, T instance, String fieldName) {
private static <T> Field getField(Class<T> clazz, String fieldName) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) {
throw new UnsupportedOperationException(format("Could not get field '%s' for instance '%s' of class '%s'",
fieldName, instance, clazz.getName()), e);
throw new UnsupportedOperationException(format("Could not get field '%s' from class '%s'",
fieldName, clazz.getName()), e);
}
}
@SuppressWarnings("unchecked")
public static <T, V> V getFieldValue(Class<T> clazz, T instance, String fieldName) {
Field field = getField(clazz, instance, fieldName);
Field field = getField(clazz, fieldName);
return getFieldValue(field, instance);
}
@SuppressWarnings("unchecked")
public static <V> V getFieldValue(Field field, Object instance) {
field.setAccessible(true);
try {
return (V) field.get(instance);
} catch (IllegalAccessException e) {
throw new UnsupportedOperationException("Could not get value of field '" + fieldName + "'", e);
throw new UnsupportedOperationException("Could not get value of field '" + field.getName() + "'", e);
}
}
@@ -1,13 +1,17 @@
package fr.xephi.authme.command.executable.register;
import fr.xephi.authme.ReflectionTestUtils;
import fr.xephi.authme.TestHelper;
import fr.xephi.authme.mail.EmailService;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.process.Management;
import fr.xephi.authme.process.register.RegisterSecondaryArgument;
import fr.xephi.authme.process.register.RegistrationType;
import fr.xephi.authme.process.register.executors.RegistrationExecutor;
import fr.xephi.authme.process.register.executors.RegistrationExecutorProvider;
import fr.xephi.authme.process.register.executors.EmailRegisterParams;
import fr.xephi.authme.process.register.executors.PasswordRegisterParams;
import fr.xephi.authme.process.register.executors.RegistrationMethod;
import fr.xephi.authme.process.register.executors.RegistrationParameters;
import fr.xephi.authme.process.register.executors.TwoFactorRegisterParams;
import fr.xephi.authme.security.HashAlgorithm;
import fr.xephi.authme.service.CommonService;
import fr.xephi.authme.service.ValidationService;
@@ -16,6 +20,9 @@ import fr.xephi.authme.settings.properties.SecuritySettings;
import org.bukkit.command.BlockCommandSender;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -24,10 +31,16 @@ import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
@@ -57,9 +70,6 @@ public class RegisterCommandTest {
@Mock
private ValidationService validationService;
@Mock
private RegistrationExecutorProvider registrationExecutorProvider;
@BeforeClass
public static void setup() {
TestHelper.setupLogger();
@@ -90,14 +100,13 @@ public class RegisterCommandTest {
// given
given(commonService.getProperty(SecuritySettings.PASSWORD_HASH)).willReturn(HashAlgorithm.TWO_FACTOR);
Player player = mock(Player.class);
RegistrationExecutor executor = mock(RegistrationExecutor.class);
given(registrationExecutorProvider.getTwoFactorRegisterExecutor(player)).willReturn(executor);
// when
command.executeCommand(player, Collections.emptyList());
// then
verify(management).performRegister(player, executor);
verify(management).performRegister(eq(RegistrationMethod.TWO_FACTOR_REGISTRATION),
argThat(isEqualTo(TwoFactorRegisterParams.of(player))));
verifyZeroInteractions(emailService);
}
@@ -208,8 +217,6 @@ public class RegisterCommandTest {
given(commonService.getProperty(RegistrationSettings.REGISTER_SECOND_ARGUMENT)).willReturn(RegisterSecondaryArgument.CONFIRMATION);
given(emailService.hasAllInformation()).willReturn(true);
Player player = mock(Player.class);
RegistrationExecutor executor = mock(RegistrationExecutor.class);
given(registrationExecutorProvider.getEmailRegisterExecutor(player, playerMail)).willReturn(executor);
// when
command.executeCommand(player, Arrays.asList(playerMail, playerMail));
@@ -217,7 +224,8 @@ public class RegisterCommandTest {
// then
verify(validationService).validateEmail(playerMail);
verify(emailService).hasAllInformation();
verify(management).performRegister(player, executor);
verify(management).performRegister(eq(RegistrationMethod.EMAIL_REGISTRATION),
argThat(isEqualTo(EmailRegisterParams.of(player, playerMail))));
}
@Test
@@ -239,14 +247,13 @@ public class RegisterCommandTest {
public void shouldPerformPasswordRegistration() {
// given
Player player = mock(Player.class);
RegistrationExecutor executor = mock(RegistrationExecutor.class);
given(registrationExecutorProvider.getPasswordRegisterExecutor(player, "myPass", null)).willReturn(executor);
// when
command.executeCommand(player, Collections.singletonList("myPass"));
// then
verify(management).performRegister(player, executor);
verify(management).performRegister(eq(RegistrationMethod.PASSWORD_REGISTRATION),
argThat(isEqualTo(PasswordRegisterParams.of(player, "myPass", null))));
}
@Test
@@ -257,15 +264,14 @@ public class RegisterCommandTest {
String email = "email@example.org";
given(validationService.validateEmail(email)).willReturn(true);
Player player = mock(Player.class);
RegistrationExecutor executor = mock(RegistrationExecutor.class);
given(registrationExecutorProvider.getPasswordRegisterExecutor(player, "myPass", email)).willReturn(executor);
// when
command.executeCommand(player, Arrays.asList("myPass", email));
// then
verify(validationService).validateEmail(email);
verify(management).performRegister(player, executor);
verify(management).performRegister(eq(RegistrationMethod.PASSWORD_REGISTRATION),
argThat(isEqualTo(PasswordRegisterParams.of(player, "myPass", email))));
}
@Test
@@ -292,14 +298,64 @@ public class RegisterCommandTest {
given(commonService.getProperty(RegistrationSettings.REGISTRATION_TYPE)).willReturn(RegistrationType.PASSWORD);
given(commonService.getProperty(RegistrationSettings.REGISTER_SECOND_ARGUMENT)).willReturn(RegisterSecondaryArgument.EMAIL_OPTIONAL);
Player player = mock(Player.class);
RegistrationExecutor executor = mock(RegistrationExecutor.class);
given(registrationExecutorProvider.getPasswordRegisterExecutor(eq(player), anyString(), eq(null))).willReturn(executor);
// when
command.executeCommand(player, Collections.singletonList("myPass"));
// then
verify(registrationExecutorProvider).getPasswordRegisterExecutor(player, "myPass", null);
verify(management).performRegister(player, executor);
verify(management).performRegister(eq(RegistrationMethod.PASSWORD_REGISTRATION),
argThat(isEqualTo(PasswordRegisterParams.of(player, "myPass", null))));
}
// TODO ljacqu 20170317: Document and extract as util
private static <P extends RegistrationParameters> Matcher<P> isEqualTo(P expected) {
return new TypeSafeMatcher<P>() {
@Override
protected boolean matchesSafely(RegistrationParameters item) {
assertAreParamsEqual(expected, item);
return true;
}
@Override
public void describeTo(Description description) {
description.appendText("parameters " + expected);
}
};
}
private static void assertAreParamsEqual(RegistrationParameters lhs, RegistrationParameters rhs) {
if (lhs.getClass() != rhs.getClass()) {
fail("Params classes don't match, got " + lhs.getClass().getSimpleName()
+ " and " + rhs.getClass().getSimpleName());
}
List<Field> fieldsToCheck = getFields(lhs);
for (Field field : fieldsToCheck) {
Object lhsValue = ReflectionTestUtils.getFieldValue(field, lhs);
Object rhsValue = ReflectionTestUtils.getFieldValue(field, rhs);
if (!Objects.equals(lhsValue, rhsValue)) {
fail("Field '" + field.getName() + "' does not have same value: '"
+ lhsValue + "' vs. '" + rhsValue + "'");
}
}
}
private static List<Field> getFields(RegistrationParameters params) {
List<Field> fields = new ArrayList<>();
Class<?> currentClass = params.getClass();
while (currentClass != null) {
for (Field f : currentClass.getDeclaredFields()) {
if (!Modifier.isStatic(f.getModifiers())) {
fields.add(f);
}
}
if (currentClass == RegistrationParameters.class) {
break;
}
currentClass = currentClass.getSuperclass();
}
return fields;
}
}
@@ -3,9 +3,13 @@ package fr.xephi.authme.process.register;
import fr.xephi.authme.TestHelper;
import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.initialization.factory.SingletonStore;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.process.register.executors.PasswordRegisterParams;
import fr.xephi.authme.process.register.executors.RegistrationExecutor;
import fr.xephi.authme.process.register.executors.RegistrationMethod;
import fr.xephi.authme.process.register.executors.TwoFactorRegisterParams;
import fr.xephi.authme.service.CommonService;
import fr.xephi.authme.settings.properties.RegistrationSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings;
@@ -16,6 +20,7 @@ import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.only;
@@ -39,6 +44,8 @@ public class AsyncRegisterTest {
private CommonService commonService;
@Mock
private DataSource dataSource;
@Mock
private SingletonStore<RegistrationExecutor> registrationExecutorStore;
@Test
public void shouldDetectAlreadyLoggedInPlayer() {
@@ -47,9 +54,10 @@ public class AsyncRegisterTest {
Player player = mockPlayerWithName(name);
given(playerCache.isAuthenticated(name)).willReturn(true);
RegistrationExecutor executor = mock(RegistrationExecutor.class);
singletonStoreWillReturn(registrationExecutorStore, executor);
// when
asyncRegister.register(player, executor);
asyncRegister.register(RegistrationMethod.PASSWORD_REGISTRATION, PasswordRegisterParams.of(player, "abc", null));
// then
verify(commonService).send(player, MessageKey.ALREADY_LOGGED_IN_ERROR);
@@ -64,9 +72,10 @@ public class AsyncRegisterTest {
given(playerCache.isAuthenticated(name)).willReturn(false);
given(commonService.getProperty(RegistrationSettings.IS_ENABLED)).willReturn(false);
RegistrationExecutor executor = mock(RegistrationExecutor.class);
singletonStoreWillReturn(registrationExecutorStore, executor);
// when
asyncRegister.register(player, executor);
asyncRegister.register(RegistrationMethod.TWO_FACTOR_REGISTRATION, TwoFactorRegisterParams.of(player));
// then
verify(commonService).send(player, MessageKey.REGISTRATION_DISABLED);
@@ -82,9 +91,10 @@ public class AsyncRegisterTest {
given(commonService.getProperty(RegistrationSettings.IS_ENABLED)).willReturn(true);
given(dataSource.isAuthAvailable(name)).willReturn(true);
RegistrationExecutor executor = mock(RegistrationExecutor.class);
singletonStoreWillReturn(registrationExecutorStore, executor);
// when
asyncRegister.register(player, executor);
asyncRegister.register(RegistrationMethod.TWO_FACTOR_REGISTRATION, TwoFactorRegisterParams.of(player));
// then
verify(commonService).send(player, MessageKey.NAME_ALREADY_REGISTERED);
@@ -93,6 +103,7 @@ public class AsyncRegisterTest {
}
@Test
@SuppressWarnings("unchecked")
public void shouldStopForFailedExecutorCheck() {
// given
String name = "edbert";
@@ -103,14 +114,16 @@ public class AsyncRegisterTest {
given(commonService.getProperty(RestrictionSettings.MAX_REGISTRATION_PER_IP)).willReturn(0);
given(dataSource.isAuthAvailable(name)).willReturn(false);
RegistrationExecutor executor = mock(RegistrationExecutor.class);
given(executor.isRegistrationAdmitted()).willReturn(false);
TwoFactorRegisterParams params = TwoFactorRegisterParams.of(player);
given(executor.isRegistrationAdmitted(params)).willReturn(false);
singletonStoreWillReturn(registrationExecutorStore, executor);
// when
asyncRegister.register(player, executor);
asyncRegister.register(RegistrationMethod.TWO_FACTOR_REGISTRATION, params);
// then
verify(dataSource, only()).isAuthAvailable(name);
verify(executor, only()).isRegistrationAdmitted();
verify(executor, only()).isRegistrationAdmitted(params);
}
private static Player mockPlayerWithName(String name) {
@@ -118,4 +131,10 @@ public class AsyncRegisterTest {
given(player.getName()).willReturn(name);
return player;
}
@SuppressWarnings("unchecked")
private static void singletonStoreWillReturn(SingletonStore<RegistrationExecutor> store,
RegistrationExecutor mock) {
given(store.getSingleton(any(Class.class))).willReturn(mock);
}
}
@@ -1,6 +1,5 @@
package fr.xephi.authme.process.register.executors;
import fr.xephi.authme.ReflectionTestUtils;
import fr.xephi.authme.TestHelper;
import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource;
@@ -34,13 +33,13 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
/**
* Test for {@link EmailRegisterExecutorProvider}.
* Test for {@link EmailRegisterExecutor}.
*/
@RunWith(MockitoJUnitRunner.class)
public class EmailRegisterExecutorProviderTest {
@InjectMocks
private EmailRegisterExecutorProvider emailRegisterExecutorProvider;
private EmailRegisterExecutor executor;
@Mock
private PermissionsManager permissionsManager;
@@ -62,10 +61,10 @@ public class EmailRegisterExecutorProviderTest {
String email = "test@example.com";
given(dataSource.countAuthsByEmail(email)).willReturn(4);
Player player = mock(Player.class);
RegistrationExecutor executor = emailRegisterExecutorProvider.new EmailRegisterExecutor(player, email);
EmailRegisterParams params = EmailRegisterParams.of(player, email);
// when
boolean result = executor.isRegistrationAdmitted();
boolean result = executor.isRegistrationAdmitted(params);
// then
assertThat(result, equalTo(false));
@@ -80,10 +79,10 @@ public class EmailRegisterExecutorProviderTest {
given(commonService.getProperty(EmailSettings.MAX_REG_PER_EMAIL)).willReturn(3);
Player player = mock(Player.class);
given(permissionsManager.hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS)).willReturn(true);
RegistrationExecutor executor = emailRegisterExecutorProvider.new EmailRegisterExecutor(player, "test@example.com");
EmailRegisterParams params = EmailRegisterParams.of(player, "test@example.com");
// when
boolean result = executor.isRegistrationAdmitted();
boolean result = executor.isRegistrationAdmitted(params);
// then
assertThat(result, equalTo(true));
@@ -97,10 +96,10 @@ public class EmailRegisterExecutorProviderTest {
String email = "test@example.com";
given(dataSource.countAuthsByEmail(email)).willReturn(0);
Player player = mock(Player.class);
RegistrationExecutor executor = emailRegisterExecutorProvider.new EmailRegisterExecutor(player, "test@example.com");
EmailRegisterParams params = EmailRegisterParams.of(player, "test@example.com");
// when
boolean result = executor.isRegistrationAdmitted();
boolean result = executor.isRegistrationAdmitted(params);
// then
assertThat(result, equalTo(true));
@@ -120,10 +119,10 @@ public class EmailRegisterExecutorProviderTest {
World world = mock(World.class);
given(world.getName()).willReturn("someWorld");
given(player.getLocation()).willReturn(new Location(world, 48, 96, 144));
RegistrationExecutor executor = emailRegisterExecutorProvider.new EmailRegisterExecutor(player, "test@example.com");
EmailRegisterParams params = EmailRegisterParams.of(player, "test@example.com");
// when
PlayerAuth auth = executor.buildPlayerAuth();
PlayerAuth auth = executor.buildPlayerAuth(params);
// then
assertThat(auth, hasAuthBasicData("veronica", "Veronica", "test@example.com", "123.45.67.89"));
@@ -132,18 +131,17 @@ public class EmailRegisterExecutorProviderTest {
}
@Test
@SuppressWarnings("unchecked")
public void shouldPerformActionAfterDataSourceSave() {
// given
given(emailService.sendPasswordMail(anyString(), anyString(), anyString())).willReturn(true);
Player player = mock(Player.class);
given(player.getName()).willReturn("Laleh");
RegistrationExecutor executor = emailRegisterExecutorProvider.new EmailRegisterExecutor(player, "test@example.com");
EmailRegisterParams params = EmailRegisterParams.of(player, "test@example.com");
String password = "A892C#@";
ReflectionTestUtils.setField((Class) executor.getClass(), executor, "password", password);
params.setPassword(password);
// when
executor.executePostPersistAction();
executor.executePostPersistAction(params);
// then
verify(emailService).sendPasswordMail("Laleh", "test@example.com", password);
@@ -151,18 +149,17 @@ public class EmailRegisterExecutorProviderTest {
}
@Test
@SuppressWarnings("unchecked")
public void shouldHandleEmailSendingFailure() {
// given
given(emailService.sendPasswordMail(anyString(), anyString(), anyString())).willReturn(false);
Player player = mock(Player.class);
given(player.getName()).willReturn("Laleh");
RegistrationExecutor executor = emailRegisterExecutorProvider.new EmailRegisterExecutor(player, "test@example.com");
EmailRegisterParams params = EmailRegisterParams.of(player, "test@example.com");
String password = "A892C#@";
ReflectionTestUtils.setField((Class) executor.getClass(), executor, "password", password);
params.setPassword(password);
// when
executor.executePostPersistAction();
executor.executePostPersistAction(params);
// then
verify(emailService).sendPasswordMail("Laleh", "test@example.com", password);
@@ -34,13 +34,13 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
/**
* Test for {@link PasswordRegisterExecutorProvider}.
* Test for {@link PasswordRegisterExecutor}.
*/
@RunWith(MockitoJUnitRunner.class)
public class PasswordRegisterExecutorProviderTest {
public class PasswordRegisterExecutorTest {
@InjectMocks
private PasswordRegisterExecutorProvider passwordRegisterExecutorProvider;
private PasswordRegisterExecutor executor;
@Mock
private ValidationService validationService;
@@ -62,10 +62,10 @@ public class PasswordRegisterExecutorProviderTest {
String name = "player040";
given(validationService.validatePassword(password, name)).willReturn(new ValidationResult());
Player player = mockPlayerWithName(name);
RegistrationExecutor executor = passwordRegisterExecutorProvider.new PasswordRegisterExecutor(player, password, null);
PasswordRegisterParams params = PasswordRegisterParams.of(player, password, null);
// when
boolean result = executor.isRegistrationAdmitted();
boolean result = executor.isRegistrationAdmitted(params);
// then
assertThat(result, equalTo(true));
@@ -80,10 +80,10 @@ public class PasswordRegisterExecutorProviderTest {
given(validationService.validatePassword(password, name)).willReturn(
new ValidationResult(MessageKey.PASSWORD_CHARACTERS_ERROR, "[a-z]"));
Player player = mockPlayerWithName(name);
RegistrationExecutor executor = passwordRegisterExecutorProvider.new PasswordRegisterExecutor(player, password, null);
PasswordRegisterParams params = PasswordRegisterParams.of(player, password, null);
// when
boolean result = executor.isRegistrationAdmitted();
boolean result = executor.isRegistrationAdmitted(params);
// then
assertThat(result, equalTo(false));
@@ -101,10 +101,10 @@ public class PasswordRegisterExecutorProviderTest {
World world = mock(World.class);
given(world.getName()).willReturn("someWorld");
given(player.getLocation()).willReturn(new Location(world, 48, 96, 144));
RegistrationExecutor executor = passwordRegisterExecutorProvider.new PasswordRegisterExecutor(player, "pass", "mail@example.org");
PasswordRegisterParams params = PasswordRegisterParams.of(player, "pass", "mail@example.org");
// when
PlayerAuth auth = executor.buildPlayerAuth();
PlayerAuth auth = executor.buildPlayerAuth(params);
// then
assertThat(auth, hasAuthBasicData("s1m0n", "S1m0N", "mail@example.org", "123.45.67.89"));
@@ -118,10 +118,10 @@ public class PasswordRegisterExecutorProviderTest {
given(commonService.getProperty(RegistrationSettings.FORCE_LOGIN_AFTER_REGISTER)).willReturn(false);
given(commonService.getProperty(PluginSettings.USE_ASYNC_TASKS)).willReturn(false);
Player player = mock(Player.class);
RegistrationExecutor executor = passwordRegisterExecutorProvider.new PasswordRegisterExecutor(player, "pass", "mail@example.org");
PasswordRegisterParams params = PasswordRegisterParams.of(player, "pass", "mail@example.org");
// when
executor.executePostPersistAction();
executor.executePostPersistAction(params);
// then
TestHelper.runSyncDelayedTaskWithDelay(bukkitService);
@@ -134,10 +134,10 @@ public class PasswordRegisterExecutorProviderTest {
// given
given(commonService.getProperty(RegistrationSettings.FORCE_LOGIN_AFTER_REGISTER)).willReturn(true);
Player player = mock(Player.class);
RegistrationExecutor executor = passwordRegisterExecutorProvider.new PasswordRegisterExecutor(player, "pass", "mail@example.org");
PasswordRegisterParams params = PasswordRegisterParams.of(player, "pass", "mail@example.org");
// when
executor.executePostPersistAction();
executor.executePostPersistAction(params);
// then
verifyZeroInteractions(bukkitService, asynchronousLogin);