From e268c3a624b0aba4169ab569acd7d000dc0feb8d Mon Sep 17 00:00:00 2001 From: sgdc3 Date: Wed, 4 Oct 2017 20:12:53 +0200 Subject: [PATCH 1/2] Start working on the changepassword api method TODO: fix unit testing --- .../fr/xephi/authme/api/v3/AuthMeApi.java | 10 +++++ .../authme/ChangePasswordAdminCommand.java | 35 +++------------ .../fr/xephi/authme/process/Management.java | 4 ++ .../changepassword/AsyncChangePassword.java | 44 ++++++++++++++++++- .../ChangePasswordAdminCommandTest.java | 8 ++-- 5 files changed, 65 insertions(+), 36 deletions(-) diff --git a/src/main/java/fr/xephi/authme/api/v3/AuthMeApi.java b/src/main/java/fr/xephi/authme/api/v3/AuthMeApi.java index a23883c6..0e1e099e 100644 --- a/src/main/java/fr/xephi/authme/api/v3/AuthMeApi.java +++ b/src/main/java/fr/xephi/authme/api/v3/AuthMeApi.java @@ -285,6 +285,16 @@ public class AuthMeApi { management.performUnregisterByAdmin(null, name, Bukkit.getPlayer(name)); } + /** + * Change a user's password + * + * @param name the user name + * @param newPassword the new password + */ + public void changePassword(String name, String newPassword) { + management.performPasswordChangeAsAdmin(null, name, newPassword); + } + /** * Get all the registered names (lowercase) * diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommand.java index e12f4d38..26054241 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommand.java @@ -1,12 +1,10 @@ package fr.xephi.authme.command.executable.authme; -import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; -import fr.xephi.authme.message.MessageKey; +import fr.xephi.authme.process.Management; import fr.xephi.authme.security.PasswordSecurity; -import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.ValidationService; @@ -39,6 +37,9 @@ public class ChangePasswordAdminCommand implements ExecutableCommand { @Inject private CommonService commonService; + @Inject + private Management management; + @Override public void executeCommand(final CommandSender sender, List arguments) { // Get the player and password @@ -53,32 +54,6 @@ public class ChangePasswordAdminCommand implements ExecutableCommand { } // Set the password - bukkitService.runTaskOptionallyAsync(() -> changePassword(playerName.toLowerCase(), playerPass, sender)); - } - - /** - * Changes the password of the given player to the given password. - * - * @param nameLowercase the name of the player - * @param password the password to set - * @param sender the sender initiating the password change - */ - private void changePassword(String nameLowercase, String password, CommandSender sender) { - if (!isNameRegistered(nameLowercase)) { - commonService.send(sender, MessageKey.UNKNOWN_USER); - return; - } - - HashedPassword hashedPassword = passwordSecurity.computeHash(password, nameLowercase); - if (dataSource.updatePassword(nameLowercase, hashedPassword)) { - commonService.send(sender, MessageKey.PASSWORD_CHANGED_SUCCESS); - ConsoleLogger.info(sender.getName() + " changed password of " + nameLowercase); - } else { - commonService.send(sender, MessageKey.ERROR); - } - } - - private boolean isNameRegistered(String nameLowercase) { - return playerCache.isAuthenticated(nameLowercase) || dataSource.isAuthAvailable(nameLowercase); + management.performPasswordChangeAsAdmin(sender, playerName, playerPass); } } diff --git a/src/main/java/fr/xephi/authme/process/Management.java b/src/main/java/fr/xephi/authme/process/Management.java index d2707c52..4f0743ed 100644 --- a/src/main/java/fr/xephi/authme/process/Management.java +++ b/src/main/java/fr/xephi/authme/process/Management.java @@ -94,6 +94,10 @@ public class Management { runTask(() -> asyncChangePassword.changePassword(player, oldPassword, newPassword)); } + public void performPasswordChangeAsAdmin(CommandSender sender, String playerName, String newPassword) { + runTask(() -> asyncChangePassword.changePasswordAsAdmin(sender, playerName, newPassword)); + } + private void runTask(Runnable runnable) { bukkitService.runTaskOptionallyAsync(runnable); } diff --git a/src/main/java/fr/xephi/authme/process/changepassword/AsyncChangePassword.java b/src/main/java/fr/xephi/authme/process/changepassword/AsyncChangePassword.java index 2a3fa175..0a7d11fe 100644 --- a/src/main/java/fr/xephi/authme/process/changepassword/AsyncChangePassword.java +++ b/src/main/java/fr/xephi/authme/process/changepassword/AsyncChangePassword.java @@ -9,6 +9,7 @@ import fr.xephi.authme.process.AsynchronousProcess; import fr.xephi.authme.service.CommonService; import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.security.crypts.HashedPassword; +import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import javax.inject.Inject; @@ -30,7 +31,13 @@ public class AsyncChangePassword implements AsynchronousProcess { AsyncChangePassword() { } - + /** + * Change password for an online player + * + * @param player the player + * @param oldPassword the old password used by the player + * @param newPassword the new password chosen by the player + */ public void changePassword(final Player player, String oldPassword, String newPassword) { final String name = player.getName().toLowerCase(); PlayerAuth auth = playerCache.getAuth(name); @@ -50,5 +57,38 @@ public class AsyncChangePassword implements AsynchronousProcess { commonService.send(player, MessageKey.WRONG_PASSWORD); } } -} + /** + * Change a user's password as an administrator, without asking for the previous one + * + * @param sender who is performing the operation, null if called by other plugins + * @param playerName the player name + * @param newPassword the new password chosen for the player + */ + public void changePasswordAsAdmin(CommandSender sender, final String playerName, String newPassword) { + final String lowerCaseName = playerName.toLowerCase(); + if (!(playerCache.isAuthenticated(lowerCaseName) || dataSource.isAuthAvailable(lowerCaseName))) { + if(sender == null) { + ConsoleLogger.warning("Tried to change password for user " + lowerCaseName + " but it doesn't exist!"); + } else { + commonService.send(sender, MessageKey.UNKNOWN_USER); + } + return; + } + + HashedPassword hashedPassword = passwordSecurity.computeHash(newPassword, lowerCaseName); + if (dataSource.updatePassword(lowerCaseName, hashedPassword)) { + if(sender != null) { + commonService.send(sender, MessageKey.PASSWORD_CHANGED_SUCCESS); + ConsoleLogger.info(sender.getName() + " changed password of " + lowerCaseName); + } else { + ConsoleLogger.info("Changed password of " + lowerCaseName); + } + } else { + if(sender != null) { + commonService.send(sender, MessageKey.ERROR); + } + ConsoleLogger.warning("An error occurred while changing password for user " + lowerCaseName + "!"); + } + } +} diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommandTest.java index 9e957a62..4bcfec99 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommandTest.java @@ -5,6 +5,7 @@ import fr.xephi.authme.data.auth.PlayerAuth; import fr.xephi.authme.data.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; +import fr.xephi.authme.process.Management; import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.service.BukkitService; @@ -56,6 +57,9 @@ public class ChangePasswordAdminCommandTest { @Mock private ValidationService validationService; + @Mock + private Management management; + @BeforeClass public static void setUpLogger() { TestHelper.setupLogger(); @@ -88,7 +92,6 @@ public class ChangePasswordAdminCommandTest { // when command.executeCommand(sender, Arrays.asList(player, password)); - runOptionallyAsyncTask(bukkitService); // then verify(service).send(sender, MessageKey.UNKNOWN_USER); @@ -110,7 +113,6 @@ public class ChangePasswordAdminCommandTest { // when command.executeCommand(sender, Arrays.asList(player, password)); - runOptionallyAsyncTask(bukkitService); // then verify(validationService).validatePassword(password, player); @@ -135,7 +137,6 @@ public class ChangePasswordAdminCommandTest { // when command.executeCommand(sender, Arrays.asList(player, password)); - runOptionallyAsyncTask(bukkitService); // then verify(validationService).validatePassword(password, player); @@ -159,7 +160,6 @@ public class ChangePasswordAdminCommandTest { // when command.executeCommand(sender, Arrays.asList(player, password)); - runOptionallyAsyncTask(bukkitService); // then verify(validationService).validatePassword(password, player); From 867b32194b0495abc3ee1ed73bcba5b11ead2a22 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Thu, 5 Oct 2017 23:44:16 +0200 Subject: [PATCH 2/2] #1347 Adapt tests for new change password architecture --- .../authme/ChangePasswordAdminCommand.java | 24 +-- .../changepassword/AsyncChangePassword.java | 6 +- .../fr/xephi/authme/api/v3/AuthMeApiTest.java | 13 ++ .../ChangePasswordAdminCommandTest.java | 146 +++--------------- .../AsyncChangePasswordTest.java | 127 +++++++++++++++ 5 files changed, 170 insertions(+), 146 deletions(-) create mode 100644 src/test/java/fr/xephi/authme/process/changepassword/AsyncChangePasswordTest.java diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommand.java index 26054241..3c6264cc 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommand.java @@ -1,11 +1,7 @@ package fr.xephi.authme.command.executable.authme; import fr.xephi.authme.command.ExecutableCommand; -import fr.xephi.authme.data.auth.PlayerCache; -import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.process.Management; -import fr.xephi.authme.security.PasswordSecurity; -import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.ValidationService; import fr.xephi.authme.service.ValidationService.ValidationResult; @@ -19,18 +15,6 @@ import java.util.List; */ public class ChangePasswordAdminCommand implements ExecutableCommand { - @Inject - private PasswordSecurity passwordSecurity; - - @Inject - private PlayerCache playerCache; - - @Inject - private DataSource dataSource; - - @Inject - private BukkitService bukkitService; - @Inject private ValidationService validationService; @@ -41,7 +25,7 @@ public class ChangePasswordAdminCommand implements ExecutableCommand { private Management management; @Override - public void executeCommand(final CommandSender sender, List arguments) { + public void executeCommand(CommandSender sender, List arguments) { // Get the player and password final String playerName = arguments.get(0); final String playerPass = arguments.get(1); @@ -50,10 +34,8 @@ public class ChangePasswordAdminCommand implements ExecutableCommand { ValidationResult validationResult = validationService.validatePassword(playerPass, playerName); if (validationResult.hasError()) { commonService.send(sender, validationResult.getMessageKey(), validationResult.getArgs()); - return; + } else { + management.performPasswordChangeAsAdmin(sender, playerName, playerPass); } - - // Set the password - management.performPasswordChangeAsAdmin(sender, playerName, playerPass); } } diff --git a/src/main/java/fr/xephi/authme/process/changepassword/AsyncChangePassword.java b/src/main/java/fr/xephi/authme/process/changepassword/AsyncChangePassword.java index 0a7d11fe..27e84d24 100644 --- a/src/main/java/fr/xephi/authme/process/changepassword/AsyncChangePassword.java +++ b/src/main/java/fr/xephi/authme/process/changepassword/AsyncChangePassword.java @@ -68,7 +68,7 @@ public class AsyncChangePassword implements AsynchronousProcess { public void changePasswordAsAdmin(CommandSender sender, final String playerName, String newPassword) { final String lowerCaseName = playerName.toLowerCase(); if (!(playerCache.isAuthenticated(lowerCaseName) || dataSource.isAuthAvailable(lowerCaseName))) { - if(sender == null) { + if (sender == null) { ConsoleLogger.warning("Tried to change password for user " + lowerCaseName + " but it doesn't exist!"); } else { commonService.send(sender, MessageKey.UNKNOWN_USER); @@ -78,14 +78,14 @@ public class AsyncChangePassword implements AsynchronousProcess { HashedPassword hashedPassword = passwordSecurity.computeHash(newPassword, lowerCaseName); if (dataSource.updatePassword(lowerCaseName, hashedPassword)) { - if(sender != null) { + if (sender != null) { commonService.send(sender, MessageKey.PASSWORD_CHANGED_SUCCESS); ConsoleLogger.info(sender.getName() + " changed password of " + lowerCaseName); } else { ConsoleLogger.info("Changed password of " + lowerCaseName); } } else { - if(sender != null) { + if (sender != null) { commonService.send(sender, MessageKey.ERROR); } ConsoleLogger.warning("An error occurred while changing password for user " + lowerCaseName + "!"); diff --git a/src/test/java/fr/xephi/authme/api/v3/AuthMeApiTest.java b/src/test/java/fr/xephi/authme/api/v3/AuthMeApiTest.java index 0fadd3f1..d1ddaf73 100644 --- a/src/test/java/fr/xephi/authme/api/v3/AuthMeApiTest.java +++ b/src/test/java/fr/xephi/authme/api/v3/AuthMeApiTest.java @@ -280,6 +280,19 @@ public class AuthMeApiTest { verify(management).performUnregisterByAdmin(null, name, player); } + @Test + public void shouldChangePassword() { + // given + String name = "Bobby12"; + String password = "resetPw!"; + + // when + api.changePassword(name, password); + + // then + verify(management).performPasswordChangeAsAdmin(null, name, password); + } + private static Player mockPlayerWithName(String name) { Player player = mock(Player.class); given(player.getName()).willReturn(name); diff --git a/src/test/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommandTest.java index 4bcfec99..b10b9c45 100644 --- a/src/test/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommandTest.java @@ -1,19 +1,11 @@ package fr.xephi.authme.command.executable.authme; -import fr.xephi.authme.TestHelper; -import fr.xephi.authme.data.auth.PlayerAuth; -import fr.xephi.authme.data.auth.PlayerCache; -import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.message.MessageKey; import fr.xephi.authme.process.Management; -import fr.xephi.authme.security.PasswordSecurity; -import fr.xephi.authme.security.crypts.HashedPassword; -import fr.xephi.authme.service.BukkitService; import fr.xephi.authme.service.CommonService; import fr.xephi.authme.service.ValidationService; import fr.xephi.authme.service.ValidationService.ValidationResult; import org.bukkit.command.CommandSender; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; @@ -22,11 +14,8 @@ import org.mockito.junit.MockitoJUnitRunner; import java.util.Arrays; -import static fr.xephi.authme.TestHelper.runOptionallyAsyncTask; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; @@ -40,19 +29,7 @@ public class ChangePasswordAdminCommandTest { private ChangePasswordAdminCommand command; @Mock - private CommonService service; - - @Mock - private PasswordSecurity passwordSecurity; - - @Mock - private DataSource dataSource; - - @Mock - private PlayerCache playerCache; - - @Mock - private BukkitService bukkitService; + private CommonService commonService; @Mock private ValidationService validationService; @@ -60,112 +37,37 @@ public class ChangePasswordAdminCommandTest { @Mock private Management management; - @BeforeClass - public static void setUpLogger() { - TestHelper.setupLogger(); + @Test + public void shouldForwardRequestToManagement() { + // given + String name = "theUser"; + String pass = "newPassword"; + given(validationService.validatePassword(pass, name)).willReturn(new ValidationResult()); + CommandSender sender = mock(CommandSender.class); + + // when + command.executeCommand(sender, Arrays.asList(name, pass)); + + // then + verify(validationService).validatePassword(pass, name); + verify(management).performPasswordChangeAsAdmin(sender, name, pass); } @Test - public void shouldRejectInvalidPassword() { + public void shouldSendErrorToCommandSender() { // given + String name = "theUser"; + String pass = "newPassword"; + given(validationService.validatePassword(pass, name)).willReturn( + new ValidationResult(MessageKey.INVALID_PASSWORD_LENGTH, "7")); CommandSender sender = mock(CommandSender.class); - given(validationService.validatePassword("Bobby", "bobby")).willReturn( - new ValidationResult(MessageKey.PASSWORD_IS_USERNAME_ERROR)); // when - command.executeCommand(sender, Arrays.asList("bobby", "Bobby")); + command.executeCommand(sender, Arrays.asList(name, pass)); // then - verify(validationService).validatePassword("Bobby", "bobby"); - verify(service).send(sender, MessageKey.PASSWORD_IS_USERNAME_ERROR, new String[0]); - verifyZeroInteractions(dataSource); + verify(validationService).validatePassword(pass, name); + verify(commonService).send(sender, MessageKey.INVALID_PASSWORD_LENGTH, "7"); + verifyZeroInteractions(management); } - - @Test - public void shouldRejectCommandForUnknownUser() { - // given - CommandSender sender = mock(CommandSender.class); - String player = "player"; - String password = "password"; - given(playerCache.isAuthenticated(player)).willReturn(false); - given(validationService.validatePassword(password, player)).willReturn(new ValidationResult()); - - // when - command.executeCommand(sender, Arrays.asList(player, password)); - - // then - verify(service).send(sender, MessageKey.UNKNOWN_USER); - verify(dataSource, never()).updatePassword(any(PlayerAuth.class)); - } - - @Test - public void shouldUpdatePasswordOfLoggedInUser() { - // given - CommandSender sender = mock(CommandSender.class); - String player = "my_user12"; - String password = "passPass"; - given(playerCache.isAuthenticated(player)).willReturn(true); - - HashedPassword hashedPassword = mock(HashedPassword.class); - given(passwordSecurity.computeHash(password, player)).willReturn(hashedPassword); - given(dataSource.updatePassword(player, hashedPassword)).willReturn(true); - given(validationService.validatePassword(password, player)).willReturn(new ValidationResult()); - - // when - command.executeCommand(sender, Arrays.asList(player, password)); - - // then - verify(validationService).validatePassword(password, player); - verify(service).send(sender, MessageKey.PASSWORD_CHANGED_SUCCESS); - verify(passwordSecurity).computeHash(password, player); - verify(dataSource).updatePassword(player, hashedPassword); - } - - @Test - public void shouldUpdatePasswordOfOfflineUser() { - // given - CommandSender sender = mock(CommandSender.class); - String player = "my_user12"; - String password = "passPass"; - given(playerCache.isAuthenticated(player)).willReturn(false); - given(dataSource.isAuthAvailable(player)).willReturn(true); - given(validationService.validatePassword(password, player)).willReturn(new ValidationResult()); - - HashedPassword hashedPassword = mock(HashedPassword.class); - given(passwordSecurity.computeHash(password, player)).willReturn(hashedPassword); - given(dataSource.updatePassword(player, hashedPassword)).willReturn(true); - - // when - command.executeCommand(sender, Arrays.asList(player, password)); - - // then - verify(validationService).validatePassword(password, player); - verify(service).send(sender, MessageKey.PASSWORD_CHANGED_SUCCESS); - verify(passwordSecurity).computeHash(password, player); - verify(dataSource).updatePassword(player, hashedPassword); - } - - @Test - public void shouldReportWhenSaveFailed() { - // given - CommandSender sender = mock(CommandSender.class); - String player = "my_user12"; - String password = "passPass"; - given(playerCache.isAuthenticated(player)).willReturn(true); - given(validationService.validatePassword(password, player)).willReturn(new ValidationResult()); - - HashedPassword hashedPassword = mock(HashedPassword.class); - given(passwordSecurity.computeHash(password, player)).willReturn(hashedPassword); - given(dataSource.updatePassword(player, hashedPassword)).willReturn(false); - - // when - command.executeCommand(sender, Arrays.asList(player, password)); - - // then - verify(validationService).validatePassword(password, player); - verify(service).send(sender, MessageKey.ERROR); - verify(passwordSecurity).computeHash(password, player); - verify(dataSource).updatePassword(player, hashedPassword); - } - } diff --git a/src/test/java/fr/xephi/authme/process/changepassword/AsyncChangePasswordTest.java b/src/test/java/fr/xephi/authme/process/changepassword/AsyncChangePasswordTest.java new file mode 100644 index 00000000..76633544 --- /dev/null +++ b/src/test/java/fr/xephi/authme/process/changepassword/AsyncChangePasswordTest.java @@ -0,0 +1,127 @@ +package fr.xephi.authme.process.changepassword; + +import fr.xephi.authme.TestHelper; +import fr.xephi.authme.data.auth.PlayerCache; +import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.message.MessageKey; +import fr.xephi.authme.security.PasswordSecurity; +import fr.xephi.authme.security.crypts.HashedPassword; +import fr.xephi.authme.service.CommonService; +import org.bukkit.command.CommandSender; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.only; +import static org.mockito.Mockito.verify; + +/** + * Test for {@link AsyncChangePassword}. + */ +@RunWith(MockitoJUnitRunner.class) +public class AsyncChangePasswordTest { + + @InjectMocks + private AsyncChangePassword asyncChangePassword; + + @Mock + private CommonService commonService; + @Mock + private DataSource dataSource; + @Mock + private PlayerCache playerCache; + @Mock + private PasswordSecurity passwordSecurity; + + @Before + public void setUpLogger() { + TestHelper.setupLogger(); + } + + @Test + public void shouldRejectCommandForUnknownUser() { + // given + CommandSender sender = mock(CommandSender.class); + String player = "player"; + String password = "password"; + given(playerCache.isAuthenticated(player)).willReturn(false); + given(dataSource.isAuthAvailable(player)).willReturn(false); + + // when + asyncChangePassword.changePasswordAsAdmin(sender, player, password); + + // then + verify(commonService).send(sender, MessageKey.UNKNOWN_USER); + verify(dataSource, only()).isAuthAvailable(player); + } + + @Test + public void shouldUpdatePasswordOfLoggedInUser() { + // given + CommandSender sender = mock(CommandSender.class); + String player = "my_user12"; + String password = "passPass"; + given(playerCache.isAuthenticated(player)).willReturn(true); + + HashedPassword hashedPassword = mock(HashedPassword.class); + given(passwordSecurity.computeHash(password, player)).willReturn(hashedPassword); + given(dataSource.updatePassword(player, hashedPassword)).willReturn(true); + + // when + asyncChangePassword.changePasswordAsAdmin(sender, player, password); + + // then + verify(commonService).send(sender, MessageKey.PASSWORD_CHANGED_SUCCESS); + verify(passwordSecurity).computeHash(password, player); + verify(dataSource).updatePassword(player, hashedPassword); + } + + @Test + public void shouldUpdatePasswordOfOfflineUser() { + // given + CommandSender sender = mock(CommandSender.class); + String player = "my_user12"; + String password = "passPass"; + given(playerCache.isAuthenticated(player)).willReturn(false); + given(dataSource.isAuthAvailable(player)).willReturn(true); + + HashedPassword hashedPassword = mock(HashedPassword.class); + given(passwordSecurity.computeHash(password, player)).willReturn(hashedPassword); + given(dataSource.updatePassword(player, hashedPassword)).willReturn(true); + + // when + asyncChangePassword.changePasswordAsAdmin(sender, player, password); + + // then + verify(commonService).send(sender, MessageKey.PASSWORD_CHANGED_SUCCESS); + verify(passwordSecurity).computeHash(password, player); + verify(dataSource).updatePassword(player, hashedPassword); + } + + @Test + public void shouldReportWhenSaveFailed() { + // given + CommandSender sender = mock(CommandSender.class); + String player = "my_user12"; + String password = "passPass"; + given(playerCache.isAuthenticated(player)).willReturn(true); + + HashedPassword hashedPassword = mock(HashedPassword.class); + given(passwordSecurity.computeHash(password, player)).willReturn(hashedPassword); + given(dataSource.updatePassword(player, hashedPassword)).willReturn(false); + + // when + asyncChangePassword.changePasswordAsAdmin(sender, player, password); + + // then + verify(commonService).send(sender, MessageKey.ERROR); + verify(passwordSecurity).computeHash(password, player); + verify(dataSource).updatePassword(player, hashedPassword); + } + +}