From 68123ed58e302b7c8aa3b0803045945c2522b019 Mon Sep 17 00:00:00 2001 From: Antoine Vigneau Date: Mon, 24 Jul 2023 17:15:22 +0200 Subject: [PATCH] SONAR-19963 Add tests for API v2 users controller and refactor existing tests --- server/sonar-webserver-webapi-v2/build.gradle | 4 +- .../controller/DefaultUserControllerIT.java | 135 --------- .../v2/config/MockConfigForControllers.java | 102 ------- .../org/sonar/server/v2/WebApiEndpoints.java | 2 - .../sonar/server/v2/api/model/RestPage.java | 4 +- .../DefaultLivenessController.java} | 10 +- .../system}/controller/HealthController.java | 2 +- .../controller/LivenessController.java | 2 +- .../system}/controller/package-info.java | 2 +- .../v2/config/PlatformLevel4WebConfig.java | 8 +- .../server/v2/config/SafeModeWebConfig.java | 8 +- .../server/v2/api/ControllerTester.java} | 32 +- .../DefaultLivenessControllerTest.java} | 45 ++- .../controller/HealthControllerTest.java} | 98 +++---- .../controller/DefaultUserControllerTest.java | 276 ++++++++++++++++++ 15 files changed, 365 insertions(+), 365 deletions(-) delete mode 100644 server/sonar-webserver-webapi-v2/src/it/java/org/sonar/server/v2/api/user/controller/DefaultUserControllerIT.java delete mode 100644 server/sonar-webserver-webapi-v2/src/it/java/org/sonar/server/v2/config/MockConfigForControllers.java rename server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/{controller/DefautLivenessController.java => api/system/controller/DefaultLivenessController.java} (82%) rename server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/{ => api/system}/controller/HealthController.java (98%) rename server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/{ => api/system}/controller/LivenessController.java (98%) rename server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/{ => api/system}/controller/package-info.java (94%) rename server/sonar-webserver-webapi-v2/src/{it/java/org/sonar/server/v2/common/ControllerIT.java => test/java/org/sonar/server/v2/api/ControllerTester.java} (51%) rename server/sonar-webserver-webapi-v2/src/{it/java/org/sonar/server/v2/controller/DefaultLivenessControllerIT.java => test/java/org/sonar/server/v2/api/system/controller/DefaultLivenessControllerTest.java} (71%) rename server/sonar-webserver-webapi-v2/src/{it/java/org/sonar/server/v2/controller/HealthControllerIT.java => test/java/org/sonar/server/v2/api/system/controller/HealthControllerTest.java} (55%) create mode 100644 server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/user/controller/DefaultUserControllerTest.java diff --git a/server/sonar-webserver-webapi-v2/build.gradle b/server/sonar-webserver-webapi-v2/build.gradle index 42a33235113..04d0150bf86 100644 --- a/server/sonar-webserver-webapi-v2/build.gradle +++ b/server/sonar-webserver-webapi-v2/build.gradle @@ -20,9 +20,9 @@ dependencies { testImplementation 'org.mockito:mockito-core' testImplementation 'org.springframework:spring-test' testImplementation 'org.skyscreamer:jsonassert:1.5.1' + testImplementation project(':sonar-testing-harness') testImplementation testFixtures(project(':server:sonar-server-common')) - - testImplementation project(':sonar-testing-harness') + testImplementation testFixtures(project(':server:sonar-webserver-auth')) } diff --git a/server/sonar-webserver-webapi-v2/src/it/java/org/sonar/server/v2/api/user/controller/DefaultUserControllerIT.java b/server/sonar-webserver-webapi-v2/src/it/java/org/sonar/server/v2/api/user/controller/DefaultUserControllerIT.java deleted file mode 100644 index 5298c355060..00000000000 --- a/server/sonar-webserver-webapi-v2/src/it/java/org/sonar/server/v2/api/user/controller/DefaultUserControllerIT.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.server.v2.api.user.controller; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; -import org.sonar.db.DbClient; -import org.sonar.db.user.UserDao; -import org.sonar.server.common.user.service.UserService; -import org.sonar.server.exceptions.BadRequestException; -import org.sonar.server.exceptions.NotFoundException; -import org.sonar.server.exceptions.UnauthorizedException; -import org.sonar.server.user.AbstractUserSession; -import org.sonar.server.user.UserSession; -import org.sonar.server.v2.common.ControllerIT; - -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.sonar.server.v2.WebApiEndpoints.USER_ENDPOINT; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -public class DefaultUserControllerIT extends ControllerIT { - - @After - public void resetUsedMocks() { - Mockito.reset(webAppContext.getBean(UserService.class)); - Mockito.reset(webAppContext.getBean(UserSession.class)); - } - - @Before - public void setUp() { - UserSession userSession = webAppContext.getBean(UserSession.class); - when(userSession.checkLoggedIn()).thenReturn(userSession); - } - - @Test - public void deactivate_whenUserIsNotLoggedIn_shouldReturnForbidden() throws Exception { - when(webAppContext.getBean(UserSession.class).checkLoggedIn()).thenThrow(new UnauthorizedException("unauthorized")); - mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete")) - .andExpectAll( - status().isUnauthorized(), - content().string("{\"message\":\"unauthorized\"}")); - } - - @Test - public void deactivate_whenUserIsNotAdministrator_shouldReturnForbidden() throws Exception { - when(webAppContext.getBean(UserSession.class).checkIsSystemAdministrator()).thenThrow(AbstractUserSession.insufficientPrivilegesException()); - mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete")) - .andExpectAll( - status().isForbidden(), - content().json("{\"message\":\"Insufficient privileges\"}")); - } - - @Test - public void deactivate_whenUserServiceThrowsNotFoundException_shouldReturnNotFound() throws Exception { - doThrow(new NotFoundException("User not found.")).when(webAppContext.getBean(UserService.class)).deactivate("userToDelete", false); - mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete")) - .andExpectAll( - status().isNotFound(), - content().json("{\"message\":\"User not found.\"}")); - } - - @Test - public void deactivate_whenUserServiceThrowsBadRequestException_shouldReturnBadRequest() throws Exception { - doThrow(BadRequestException.create("Not allowed")).when(webAppContext.getBean(UserService.class)).deactivate("userToDelete", false); - mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete")) - .andExpectAll( - status().isBadRequest(), - content().json("{\"message\":\"Not allowed\"}")); - } - - @Test - public void deactivate_whenUserTryingToDeactivateThemself_shouldReturnBadRequest() throws Exception { - when(webAppContext.getBean(DbClient.class).userDao()).thenReturn(mock(UserDao.class)); - when(webAppContext.getBean(UserSession.class).getLogin()).thenReturn("userToDelete"); - mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete")) - .andExpectAll( - status().isBadRequest(), - content().json("{\"message\":\"Self-deactivation is not possible\"}")); - } - - @Test - public void deactivate_whenAnonymizeParameterIsNotBoolean_shouldReturnBadRequest() throws Exception { - mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete?anonymize=maybe")) - .andExpect( - status().isBadRequest()); - } - - @Test - public void deactivate_whenAnonymizeIsNotSpecified_shouldDeactivateUserWithoutAnonymization() throws Exception { - mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete")) - .andExpect(status().isNoContent()); - - verify(webAppContext.getBean(UserService.class)).deactivate("userToDelete", false); - } - - @Test - public void deactivate_whenAnonymizeFalse_shouldDeactivateUserWithoutAnonymization() throws Exception { - mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete?anonymize=false")) - .andExpect(status().isNoContent()); - - verify(webAppContext.getBean(UserService.class)).deactivate("userToDelete", false); - } - - @Test - public void deactivate_whenAnonymizeTrue_shouldDeactivateUserWithAnonymization() throws Exception { - mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete?anonymize=true")) - .andExpect(status().isNoContent()); - - verify(webAppContext.getBean(UserService.class)).deactivate("userToDelete", true); - } -} diff --git a/server/sonar-webserver-webapi-v2/src/it/java/org/sonar/server/v2/config/MockConfigForControllers.java b/server/sonar-webserver-webapi-v2/src/it/java/org/sonar/server/v2/config/MockConfigForControllers.java deleted file mode 100644 index cee83db8349..00000000000 --- a/server/sonar-webserver-webapi-v2/src/it/java/org/sonar/server/v2/config/MockConfigForControllers.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.server.v2.config; - -import org.sonar.db.DbClient; -import org.sonar.server.common.user.service.UserService; -import org.sonar.server.health.CeStatusNodeCheck; -import org.sonar.server.health.DbConnectionNodeCheck; -import org.sonar.server.health.EsStatusNodeCheck; -import org.sonar.server.health.HealthChecker; -import org.sonar.server.health.WebServerStatusNodeCheck; -import org.sonar.server.platform.NodeInformation; -import org.sonar.server.platform.ws.LivenessChecker; -import org.sonar.server.user.SystemPasscode; -import org.sonar.server.user.UserSession; -import org.sonar.server.user.UserUpdater; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import static org.mockito.Mockito.mock; - -@Configuration -public class MockConfigForControllers { - - @Bean - public DbClient dbClient() { - return mock(DbClient.class); - } - - @Bean - public DbConnectionNodeCheck dbConnectionNodeCheck() { - return mock(DbConnectionNodeCheck.class); - } - - @Bean - public WebServerStatusNodeCheck webServerStatusNodeCheck() { - return mock(WebServerStatusNodeCheck.class); - } - - @Bean - public CeStatusNodeCheck ceStatusNodeCheck() { - return mock(CeStatusNodeCheck.class); - } - - @Bean - public EsStatusNodeCheck esStatusNodeCheck() { - return mock(EsStatusNodeCheck.class); - } - - @Bean - public LivenessChecker livenessChecker() { - return mock(LivenessChecker.class); - } - - @Bean - public HealthChecker healthChecker() { - return mock(HealthChecker.class); - } - - @Bean - public SystemPasscode systemPasscode() { - return mock(SystemPasscode.class); - } - - @Bean - public NodeInformation nodeInformation() { - return mock(NodeInformation.class); - } - - @Bean - public UserSession userSession() { - return mock(UserSession.class); - } - - @Bean - UserUpdater userUpdater() { - return mock(UserUpdater.class); - } - - @Bean - UserService userService() { - return mock(UserService.class); - } - -} diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/WebApiEndpoints.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/WebApiEndpoints.java index 4870e06292f..87ef45e672c 100644 --- a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/WebApiEndpoints.java +++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/WebApiEndpoints.java @@ -21,9 +21,7 @@ package org.sonar.server.v2; public class WebApiEndpoints { private static final String SYSTEM_ENDPOINTS = "/system"; - public static final String LIVENESS_ENDPOINT = SYSTEM_ENDPOINTS + "/liveness"; - public static final String HEALTH_ENDPOINT = SYSTEM_ENDPOINTS + "/health"; public static final String USER_ENDPOINT = "/users"; diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/model/RestPage.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/model/RestPage.java index ec374ec5f36..ecd31afbc55 100644 --- a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/model/RestPage.java +++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/model/RestPage.java @@ -42,9 +42,9 @@ public record RestPage( ) { @VisibleForTesting - static final String DEFAULT_PAGE_SIZE = "50"; + public static final String DEFAULT_PAGE_SIZE = "50"; @VisibleForTesting - static final String DEFAULT_PAGE_INDEX = "1"; + public static final String DEFAULT_PAGE_INDEX = "1"; public RestPage(@Nullable Integer pageSize, @Nullable Integer pageIndex) { this.pageSize = pageSize == null ? Integer.valueOf(DEFAULT_PAGE_SIZE) : pageSize; diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/controller/DefautLivenessController.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/system/controller/DefaultLivenessController.java similarity index 82% rename from server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/controller/DefautLivenessController.java rename to server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/system/controller/DefaultLivenessController.java index d609c9e1110..da32653b58a 100644 --- a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/controller/DefautLivenessController.java +++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/system/controller/DefaultLivenessController.java @@ -17,24 +17,21 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.server.v2.controller; +package org.sonar.server.v2.api.system.controller; import javax.annotation.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.platform.ws.LivenessChecker; import org.sonar.server.user.SystemPasscode; import org.sonar.server.user.UserSession; -public class DefautLivenessController implements LivenessController { +public class DefaultLivenessController implements LivenessController { - private static final Logger LOGGER = LoggerFactory.getLogger(DefautLivenessController.class); private final LivenessChecker livenessChecker; private final UserSession userSession; private final SystemPasscode systemPasscode; - public DefautLivenessController(LivenessChecker livenessChecker, SystemPasscode systemPasscode, @Nullable UserSession userSession) { + public DefaultLivenessController(LivenessChecker livenessChecker, SystemPasscode systemPasscode, @Nullable UserSession userSession) { this.livenessChecker = livenessChecker; this.userSession = userSession; this.systemPasscode = systemPasscode; @@ -57,4 +54,5 @@ public class DefautLivenessController implements LivenessController { } return userSession.isSystemAdministrator(); } + } diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/controller/HealthController.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/system/controller/HealthController.java similarity index 98% rename from server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/controller/HealthController.java rename to server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/system/controller/HealthController.java index 302e79a3831..09d45219ecb 100644 --- a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/controller/HealthController.java +++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/system/controller/HealthController.java @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.server.v2.controller; +package org.sonar.server.v2.api.system.controller; import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.exceptions.ServerException; diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/controller/LivenessController.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/system/controller/LivenessController.java similarity index 98% rename from server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/controller/LivenessController.java rename to server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/system/controller/LivenessController.java index 9433a6e1b48..adce1544462 100644 --- a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/controller/LivenessController.java +++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/system/controller/LivenessController.java @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.server.v2.controller; +package org.sonar.server.v2.api.system.controller; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/controller/package-info.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/system/controller/package-info.java similarity index 94% rename from server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/controller/package-info.java rename to server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/system/controller/package-info.java index 26d2752b71a..b4ec4b380c6 100644 --- a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/controller/package-info.java +++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/system/controller/package-info.java @@ -18,6 +18,6 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ @ParametersAreNonnullByDefault -package org.sonar.server.v2.controller; +package org.sonar.server.v2.api.system.controller; import javax.annotation.ParametersAreNonnullByDefault; diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/PlatformLevel4WebConfig.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/PlatformLevel4WebConfig.java index fc29d481951..74b5c8a6fdf 100644 --- a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/PlatformLevel4WebConfig.java +++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/PlatformLevel4WebConfig.java @@ -31,12 +31,12 @@ import org.sonar.server.platform.ws.LivenessChecker; import org.sonar.server.platform.ws.LivenessCheckerImpl; import org.sonar.server.user.SystemPasscode; import org.sonar.server.user.UserSession; +import org.sonar.server.v2.api.system.controller.DefaultLivenessController; +import org.sonar.server.v2.api.system.controller.HealthController; +import org.sonar.server.v2.api.system.controller.LivenessController; import org.sonar.server.v2.api.user.controller.DefaultUserController; import org.sonar.server.v2.api.user.controller.UserController; import org.sonar.server.v2.api.user.converter.UsersSearchRestResponseGenerator; -import org.sonar.server.v2.controller.DefautLivenessController; -import org.sonar.server.v2.controller.HealthController; -import org.sonar.server.v2.controller.LivenessController; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -53,7 +53,7 @@ public class PlatformLevel4WebConfig { @Bean public LivenessController livenessController(LivenessChecker livenessChecker, UserSession userSession, SystemPasscode systemPasscode) { - return new DefautLivenessController(livenessChecker, systemPasscode, userSession); + return new DefaultLivenessController(livenessChecker, systemPasscode, userSession); } @Bean diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/SafeModeWebConfig.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/SafeModeWebConfig.java index 897cc07abb9..5c2671d016b 100644 --- a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/SafeModeWebConfig.java +++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/SafeModeWebConfig.java @@ -24,9 +24,9 @@ import org.sonar.server.health.HealthChecker; import org.sonar.server.platform.ws.LivenessChecker; import org.sonar.server.platform.ws.SafeModeLivenessCheckerImpl; import org.sonar.server.user.SystemPasscode; -import org.sonar.server.v2.controller.DefautLivenessController; -import org.sonar.server.v2.controller.HealthController; -import org.sonar.server.v2.controller.LivenessController; +import org.sonar.server.v2.api.system.controller.DefaultLivenessController; +import org.sonar.server.v2.api.system.controller.HealthController; +import org.sonar.server.v2.api.system.controller.LivenessController; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -44,7 +44,7 @@ public class SafeModeWebConfig { @Bean public LivenessController livenessController(LivenessChecker livenessChecker, SystemPasscode systemPasscode) { - return new DefautLivenessController(livenessChecker, systemPasscode, null); + return new DefaultLivenessController(livenessChecker, systemPasscode, null); } @Bean diff --git a/server/sonar-webserver-webapi-v2/src/it/java/org/sonar/server/v2/common/ControllerIT.java b/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/ControllerTester.java similarity index 51% rename from server/sonar-webserver-webapi-v2/src/it/java/org/sonar/server/v2/common/ControllerIT.java rename to server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/ControllerTester.java index 5985d9827d9..3b209c36ffe 100644 --- a/server/sonar-webserver-webapi-v2/src/it/java/org/sonar/server/v2/common/ControllerIT.java +++ b/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/ControllerTester.java @@ -17,33 +17,17 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.server.v2.common; +package org.sonar.server.v2.api; -import org.junit.Before; -import org.junit.runner.RunWith; -import org.sonar.server.v2.config.MockConfigForControllers; -import org.sonar.server.v2.config.PlatformLevel4WebConfig; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.context.web.WebAppConfiguration; +import org.sonar.server.v2.common.RestResponseEntityExceptionHandler; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.WebApplicationContext; -@WebAppConfiguration -@ContextConfiguration( - classes = {PlatformLevel4WebConfig.class, MockConfigForControllers.class} -) -@RunWith(SpringRunner.class) -public abstract class ControllerIT { - @Autowired - protected WebApplicationContext webAppContext; - - protected MockMvc mockMvc; - - @Before - public void setup() { - mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext).build(); +public class ControllerTester { + public static MockMvc getMockMvc(Object... controllers) { + return MockMvcBuilders + .standaloneSetup(controllers) + .setControllerAdvice(new RestResponseEntityExceptionHandler()) + .build(); } } diff --git a/server/sonar-webserver-webapi-v2/src/it/java/org/sonar/server/v2/controller/DefaultLivenessControllerIT.java b/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/system/controller/DefaultLivenessControllerTest.java similarity index 71% rename from server/sonar-webserver-webapi-v2/src/it/java/org/sonar/server/v2/controller/DefaultLivenessControllerIT.java rename to server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/system/controller/DefaultLivenessControllerTest.java index 3e773445335..6cd65d5d67b 100644 --- a/server/sonar-webserver-webapi-v2/src/it/java/org/sonar/server/v2/controller/DefaultLivenessControllerIT.java +++ b/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/system/controller/DefaultLivenessControllerTest.java @@ -17,17 +17,17 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.server.v2.controller; +package org.sonar.server.v2.api.system.controller; -import org.junit.After; -import org.junit.Before; +import org.junit.Rule; import org.junit.Test; -import org.mockito.Mockito; import org.sonar.server.platform.ws.LivenessChecker; +import org.sonar.server.tester.UserSessionRule; import org.sonar.server.user.SystemPasscode; -import org.sonar.server.user.UserSession; -import org.sonar.server.v2.common.ControllerIT; +import org.sonar.server.v2.api.ControllerTester; +import org.springframework.test.web.servlet.MockMvc; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.sonar.server.user.SystemPasscodeImpl.PASSCODE_HTTP_HEADER; import static org.sonar.server.v2.WebApiEndpoints.LIVENESS_ENDPOINT; @@ -35,26 +35,21 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -public class DefaultLivenessControllerIT extends ControllerIT { +public class DefaultLivenessControllerTest { private static final String VALID_PASSCODE = "valid_passcode"; private static final String INVALID_PASSCODE = "invalid_passcode"; - @Before - public void setup() { - super.setup(); - when(webAppContext.getBean(LivenessChecker.class).liveness()).thenReturn(true); - } - - @After - public void resetUsedMocks() { - Mockito.reset(webAppContext.getBean(SystemPasscode.class)); - Mockito.reset(webAppContext.getBean(UserSession.class)); - } + private final LivenessChecker livenessChecker = mock(LivenessChecker.class); + private final SystemPasscode systemPasscode = mock(SystemPasscode.class); + @Rule + public UserSessionRule userSession = UserSessionRule.standalone(); + private final MockMvc mockMvc = ControllerTester.getMockMvc(new DefaultLivenessController(livenessChecker, systemPasscode, userSession)); @Test public void getSystemLiveness_whenValidPasscode_shouldSucceed() throws Exception { - when(webAppContext.getBean(SystemPasscode.class).isValidPasscode(VALID_PASSCODE)).thenReturn(true); + when(systemPasscode.isValidPasscode(VALID_PASSCODE)).thenReturn(true); + when(livenessChecker.liveness()).thenReturn(true); mockMvc.perform(get(LIVENESS_ENDPOINT).header(PASSCODE_HTTP_HEADER, VALID_PASSCODE)) .andExpect(status().isNoContent()); @@ -62,7 +57,8 @@ public class DefaultLivenessControllerIT extends ControllerIT { @Test public void getSystemLiveness_whenAdminCredential_shouldSucceed() throws Exception { - when(webAppContext.getBean(UserSession.class).isSystemAdministrator()).thenReturn(true); + userSession.logIn().setSystemAdministrator(); + when(livenessChecker.liveness()).thenReturn(true); mockMvc.perform(get(LIVENESS_ENDPOINT)) .andExpect(status().isNoContent()); @@ -78,8 +74,8 @@ public class DefaultLivenessControllerIT extends ControllerIT { @Test public void getSystemLiveness_whenInvalidPasscodeAndNoAdminCredentials_shouldReturnForbidden() throws Exception { - when(webAppContext.getBean(SystemPasscode.class).isValidPasscode(INVALID_PASSCODE)).thenReturn(false); - when(webAppContext.getBean(UserSession.class).isSystemAdministrator()).thenReturn(false); + when(systemPasscode.isValidPasscode(INVALID_PASSCODE)).thenReturn(false); + userSession.logIn(); mockMvc.perform(get(LIVENESS_ENDPOINT).header(PASSCODE_HTTP_HEADER, INVALID_PASSCODE)) .andExpectAll( @@ -89,12 +85,13 @@ public class DefaultLivenessControllerIT extends ControllerIT { @Test public void getSystemLiveness_whenLivenessCheckFails_shouldReturnServerError() throws Exception { - when(webAppContext.getBean(SystemPasscode.class).isValidPasscode(VALID_PASSCODE)).thenReturn(true); - when(webAppContext.getBean(LivenessChecker.class).liveness()).thenReturn(false); + when(systemPasscode.isValidPasscode(VALID_PASSCODE)).thenReturn(true); + when(livenessChecker.liveness()).thenReturn(false); mockMvc.perform(get(LIVENESS_ENDPOINT).header(PASSCODE_HTTP_HEADER, VALID_PASSCODE)) .andExpectAll( status().isInternalServerError(), content().json("{\"message\":\"Liveness check failed\"}")); } + } diff --git a/server/sonar-webserver-webapi-v2/src/it/java/org/sonar/server/v2/controller/HealthControllerIT.java b/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/system/controller/HealthControllerTest.java similarity index 55% rename from server/sonar-webserver-webapi-v2/src/it/java/org/sonar/server/v2/controller/HealthControllerIT.java rename to server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/system/controller/HealthControllerTest.java index 9fb213f9090..947187b1de9 100644 --- a/server/sonar-webserver-webapi-v2/src/it/java/org/sonar/server/v2/controller/HealthControllerIT.java +++ b/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/system/controller/HealthControllerTest.java @@ -17,21 +17,23 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package org.sonar.server.v2.controller; +package org.sonar.server.v2.api.system.controller; -import org.junit.After; -import org.junit.Before; +import com.google.gson.Gson; +import org.junit.Rule; import org.junit.Test; -import org.mockito.Mockito; -import org.sonar.server.exceptions.UnauthorizedException; import org.sonar.server.health.Health; import org.sonar.server.health.HealthChecker; import org.sonar.server.platform.NodeInformation; +import org.sonar.server.tester.UserSessionRule; import org.sonar.server.user.SystemPasscode; -import org.sonar.server.user.UserSession; -import org.sonar.server.v2.common.ControllerIT; +import org.sonar.server.v2.api.ControllerTester; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; import static java.net.HttpURLConnection.HTTP_NOT_IMPLEMENTED; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.sonar.server.user.SystemPasscodeImpl.PASSCODE_HTTP_HEADER; import static org.sonar.server.v2.WebApiEndpoints.HEALTH_ENDPOINT; @@ -40,7 +42,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -public class HealthControllerIT extends ControllerIT { +public class HealthControllerTest { private static final String VALID_PASSCODE = "valid_passcode"; private static final String INVALID_PASSCODE = "invalid_passcode"; @@ -49,51 +51,42 @@ public class HealthControllerIT extends ControllerIT { .addCause("One cause") .build(); - @Before - public void setup() { - super.setup(); - when(webAppContext.getBean(HealthChecker.class).checkNode()).thenReturn(HEALTH_RESULT); - } + private final HealthChecker healthChecker = mock(HealthChecker.class); + private final SystemPasscode systemPasscode = mock(SystemPasscode.class); + private final NodeInformation nodeInformation = mock(NodeInformation.class); + @Rule + public UserSessionRule userSession = UserSessionRule.standalone(); + private final MockMvc mockMvc = ControllerTester.getMockMvc(new HealthController(healthChecker, systemPasscode, nodeInformation,userSession)); - @After - public void resetUsedMocks() { - Mockito.reset(webAppContext.getBean(SystemPasscode.class)); - Mockito.reset(webAppContext.getBean(NodeInformation.class)); - Mockito.reset(webAppContext.getBean(UserSession.class)); - } + + private static final Gson gson = new Gson(); @Test public void getSystemHealth_whenValidPasscodeAndStandaloneMode_shouldSucceed() throws Exception { - when(webAppContext.getBean(SystemPasscode.class).isValidPasscode(VALID_PASSCODE)).thenReturn(true); - when(webAppContext.getBean(NodeInformation.class).isStandalone()).thenReturn(true); + when(systemPasscode.isValidPasscode(VALID_PASSCODE)).thenReturn(true); + when(nodeInformation.isStandalone()).thenReturn(true); + when(healthChecker.checkNode()).thenReturn(HEALTH_RESULT); - mockMvc.perform(get(HEALTH_ENDPOINT).header(PASSCODE_HTTP_HEADER, VALID_PASSCODE)) - .andExpectAll( - status().isOk(), - content().json(""" - { - "status":"YELLOW", - "causes":[ - "One cause" - ] - }""")); + MvcResult mvcResult = mockMvc.perform(get(HEALTH_ENDPOINT).header(PASSCODE_HTTP_HEADER, VALID_PASSCODE)) + .andExpect(status().isOk()) + .andReturn(); + + Health actualHealth = gson.fromJson(mvcResult.getResponse().getContentAsString(), Health.class); + assertThat(actualHealth).isEqualTo(HEALTH_RESULT); } @Test public void getSystemHealth_whenAdminCredentialAndStandaloneMode_shouldSucceed() throws Exception { - when(webAppContext.getBean(UserSession.class).isSystemAdministrator()).thenReturn(true); - when(webAppContext.getBean(NodeInformation.class).isStandalone()).thenReturn(true); + userSession.logIn().setSystemAdministrator(); + when(nodeInformation.isStandalone()).thenReturn(true); + when(healthChecker.checkNode()).thenReturn(HEALTH_RESULT); - mockMvc.perform(get(HEALTH_ENDPOINT)) - .andExpectAll( - status().isOk(), - content().json(""" - { - "status":"YELLOW", - "causes":[ - "One cause" - ] - }""")); + MvcResult mvcResult = mockMvc.perform(get(HEALTH_ENDPOINT)) + .andExpect(status().isOk()) + .andReturn(); + + Health actualHealth = gson.fromJson(mvcResult.getResponse().getContentAsString(), Health.class); + assertThat(actualHealth).isEqualTo(HEALTH_RESULT); } @Test @@ -106,8 +99,8 @@ public class HealthControllerIT extends ControllerIT { @Test public void getSystemHealth_whenInvalidPasscodeAndNoAdminCredentials_shouldReturnForbidden() throws Exception { - when(webAppContext.getBean(SystemPasscode.class).isValidPasscode(INVALID_PASSCODE)).thenReturn(false); - when(webAppContext.getBean(UserSession.class).isSystemAdministrator()).thenReturn(false); + userSession.logIn(); + when(systemPasscode.isValidPasscode(INVALID_PASSCODE)).thenReturn(false); mockMvc.perform(get(HEALTH_ENDPOINT).header(PASSCODE_HTTP_HEADER, INVALID_PASSCODE)) .andExpectAll( @@ -115,24 +108,15 @@ public class HealthControllerIT extends ControllerIT { content().json("{\"message\":\"Insufficient privileges\"}")); } - @Test - public void getSystemHealth_whenUnauthorizedExceptionThrown_shouldReturnUnauthorized() throws Exception { - when(webAppContext.getBean(UserSession.class).isSystemAdministrator()).thenThrow(new UnauthorizedException("UnauthorizedException")); - - mockMvc.perform(get(HEALTH_ENDPOINT)) - .andExpectAll( - status().isUnauthorized(), - content().json("{\"message\":\"UnauthorizedException\"}")); - } - @Test public void getSystemHealth_whenValidPasscodeAndClusterMode_shouldReturnNotImplemented() throws Exception { - when(webAppContext.getBean(SystemPasscode.class).isValidPasscode(VALID_PASSCODE)).thenReturn(true); - when(webAppContext.getBean(NodeInformation.class).isStandalone()).thenReturn(false); + when(systemPasscode.isValidPasscode(VALID_PASSCODE)).thenReturn(true); + when(nodeInformation.isStandalone()).thenReturn(false); mockMvc.perform(get(HEALTH_ENDPOINT).header(PASSCODE_HTTP_HEADER, VALID_PASSCODE)) .andExpectAll( status().is(HTTP_NOT_IMPLEMENTED), content().json("{\"message\":\"Unsupported in cluster mode\"}")); } + } diff --git a/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/user/controller/DefaultUserControllerTest.java b/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/user/controller/DefaultUserControllerTest.java new file mode 100644 index 00000000000..a143aeae2ac --- /dev/null +++ b/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/user/controller/DefaultUserControllerTest.java @@ -0,0 +1,276 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.v2.api.user.controller; + +import com.google.gson.Gson; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.IntStream; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.sonar.db.user.UserDto; +import org.sonar.server.common.SearchResults; +import org.sonar.server.common.user.service.UserSearchResult; +import org.sonar.server.common.user.service.UserService; +import org.sonar.server.common.user.service.UsersSearchRequest; +import org.sonar.server.exceptions.BadRequestException; +import org.sonar.server.exceptions.NotFoundException; +import org.sonar.server.tester.UserSessionRule; +import org.sonar.server.v2.api.ControllerTester; +import org.sonar.server.v2.api.user.converter.UsersSearchRestResponseGenerator; +import org.sonar.server.v2.api.user.model.RestUser; +import org.sonar.server.v2.api.user.response.UsersSearchRestResponse; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.sonar.api.utils.DateUtils.formatDateTime; +import static org.sonar.server.v2.WebApiEndpoints.USER_ENDPOINT; +import static org.sonar.server.v2.api.model.RestPage.DEFAULT_PAGE_INDEX; +import static org.sonar.server.v2.api.model.RestPage.DEFAULT_PAGE_SIZE; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +public class DefaultUserControllerTest { + + @Rule + public UserSessionRule userSession = UserSessionRule.standalone(); + private final UserService userService = mock(UserService.class); + private final MockMvc mockMvc = ControllerTester.getMockMvc(new DefaultUserController(userSession, userService, new UsersSearchRestResponseGenerator(userSession))); + + private static final Gson gson = new Gson(); + + @Test + public void search_whenNoParameters_shouldUseDefaultAndForwardToUserService() throws Exception { + when(userService.findUsers(any())).thenReturn(new SearchResults<>(List.of(), 0)); + + mockMvc.perform(get(USER_ENDPOINT)) + .andExpect(status().isOk()) + .andReturn(); + + ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(UsersSearchRequest.class); + verify(userService).findUsers(requestCaptor.capture()); + assertThat(requestCaptor.getValue().getPageSize()).isEqualTo(Integer.valueOf(DEFAULT_PAGE_SIZE)); + assertThat(requestCaptor.getValue().getPage()).isEqualTo(Integer.valueOf(DEFAULT_PAGE_INDEX)); + assertThat(requestCaptor.getValue().isDeactivated()).isFalse(); + } + + @Test + public void search_whenParametersUsed_shouldForwardWithParameters() throws Exception { + when(userService.findUsers(any())).thenReturn(new SearchResults<>(List.of(), 0)); + userSession.logIn().setSystemAdministrator(); + + mockMvc.perform(get(USER_ENDPOINT) + .param("active", "false") + .param("managed", "true") + .param("q", "q") + .param("sonarQubeLastConnectionDateFrom", "2020-01-01T00:00:00+0100") + .param("sonarQubeLastConnectionDateTo", "2020-01-01T00:00:00+0100") + .param("sonarLintLastConnectionDateFrom", "2020-01-01T00:00:00+0100") + .param("sonarLintLastConnectionDateTo", "2020-01-01T00:00:00+0100") + .param("pageSize", "100") + .param("pageIndex", "2")) + .andExpect(status().isOk()) + .andReturn(); + + ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(UsersSearchRequest.class); + verify(userService).findUsers(requestCaptor.capture()); + assertThat(requestCaptor.getValue().getPageSize()).isEqualTo(100); + assertThat(requestCaptor.getValue().getPage()).isEqualTo(2); + assertThat(requestCaptor.getValue().isDeactivated()).isTrue(); + } + + @Test + public void search_whenAdminParametersUsedButNotAdmin_shouldFail() throws Exception { + mockMvc.perform(get(USER_ENDPOINT) + .param("sonarQubeLastConnectionDateFrom", "2020-01-01T00:00:00+0100")) + .andExpectAll( + status().isForbidden(), + content().string("{\"message\":\"parameter sonarQubeLastConnectionDateFrom requires Administer System permission.\"}")); + + mockMvc.perform(get(USER_ENDPOINT) + .param("sonarQubeLastConnectionDateTo", "2020-01-01T00:00:00+0100")) + .andExpectAll( + status().isForbidden(), + content().string("{\"message\":\"parameter sonarQubeLastConnectionDateTo requires Administer System permission.\"}")); + + mockMvc.perform(get(USER_ENDPOINT) + .param("sonarLintLastConnectionDateFrom", "2020-01-01T00:00:00+0100")) + .andExpectAll( + status().isForbidden(), + content().string("{\"message\":\"parameter sonarLintLastConnectionDateFrom requires Administer System permission.\"}")); + + mockMvc.perform(get(USER_ENDPOINT) + .param("sonarLintLastConnectionDateTo", "2020-01-01T00:00:00+0100")) + .andExpectAll( + status().isForbidden(), + content().string("{\"message\":\"parameter sonarLintLastConnectionDateTo requires Administer System permission.\"}")); + } + + @Test + public void search_whenUserServiceReturnUsers_shouldReturnThem() throws Exception { + UserSearchResult user1 = generateUserSearchResult("user1", true, true, false, 2, 3); + UserSearchResult user2 = generateUserSearchResult("user2", true, false, false, 3, 0); + UserSearchResult user3 = generateUserSearchResult("user3", true, false, true, 1, 1); + UserSearchResult user4 = generateUserSearchResult("user4", false, true, false, 0, 0); + List users = List.of(user1, user2, user3, user4); + when(userService.findUsers(any())).thenReturn(new SearchResults<>(users, users.size())); + userSession.logIn().setSystemAdministrator(); + + MvcResult mvcResult = mockMvc.perform(get(USER_ENDPOINT)) + .andExpect(status().isOk()) + .andReturn(); + + UsersSearchRestResponse actualUsersSearchRestResponse = gson.fromJson(mvcResult.getResponse().getContentAsString(), UsersSearchRestResponse.class); + assertThat(actualUsersSearchRestResponse.users()) + .containsExactlyInAnyOrder(toRestUser(user1), toRestUser(user2), toRestUser(user3), toRestUser(user4)); + assertThat(actualUsersSearchRestResponse.pageRestResponse().total()).isEqualTo(users.size()); + + } + + private UserSearchResult generateUserSearchResult(String id, boolean active, boolean local, boolean managed, int groupsCount, int tokensCount) { + UserDto userDto = new UserDto() + .setLogin("login_" + id) + .setUuid("uuid_" + id) + .setName("name_" + id) + .setEmail(id + "@email.com") + .setActive(active) + .setLocal(local) + .setExternalLogin("externalLogin_" + id) + .setExternalId("externalId_" + id) + .setExternalIdentityProvider("externalIdentityProvider_" + id) + .setLastConnectionDate(0L) + .setLastSonarlintConnectionDate(1L); + + List groups = new ArrayList<>(); + IntStream.range(1, groupsCount).forEach(i -> groups.add("group" + i)); + + return new UserSearchResult(userDto, managed, Optional.of("avatar_" + id), groups, tokensCount); + } + + private RestUser toRestUser(UserSearchResult userSearchResult) { + return new RestUser( + userSearchResult.userDto().getLogin(), + userSearchResult.userDto().getLogin(), + userSearchResult.userDto().getName(), + userSearchResult.userDto().getEmail(), + userSearchResult.userDto().isActive(), + userSearchResult.userDto().isLocal(), + userSearchResult.managed(), + userSearchResult.userDto().getExternalLogin(), + userSearchResult.userDto().getExternalIdentityProvider(), + userSearchResult.avatar().orElse(""), + formatDateTime(userSearchResult.userDto().getLastConnectionDate()), + formatDateTime(userSearchResult.userDto().getLastSonarlintConnectionDate()), + userSearchResult.groups().size(), + userSearchResult.tokensCount() + ); + } + + @Test + public void deactivate_whenUserIsNotAdministrator_shouldReturnForbidden() throws Exception { + userSession.logIn().setNonSystemAdministrator(); + + mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete")) + .andExpectAll( + status().isForbidden(), + content().json("{\"message\":\"Insufficient privileges\"}")); + } + + @Test + public void deactivate_whenUserServiceThrowsNotFoundException_shouldReturnNotFound() throws Exception { + userSession.logIn().setSystemAdministrator(); + doThrow(new NotFoundException("User not found.")).when(userService).deactivate("userToDelete", false); + + mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete")) + .andExpectAll( + status().isNotFound(), + content().json("{\"message\":\"User not found.\"}")); + } + + @Test + public void deactivate_whenUserServiceThrowsBadRequestException_shouldReturnBadRequest() throws Exception { + userSession.logIn().setSystemAdministrator(); + doThrow(BadRequestException.create("Not allowed")).when(userService).deactivate("userToDelete", false); + + mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete")) + .andExpectAll( + status().isBadRequest(), + content().json("{\"message\":\"Not allowed\"}")); + } + + @Test + public void deactivate_whenUserTryingToDeactivateThemself_shouldReturnBadRequest() throws Exception { + userSession.logIn("userToDelete").setSystemAdministrator(); + + mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete")) + .andExpectAll( + status().isBadRequest(), + content().json("{\"message\":\"Self-deactivation is not possible\"}")); + } + + @Test + public void deactivate_whenAnonymizeParameterIsNotBoolean_shouldReturnBadRequest() throws Exception { + userSession.logIn().setSystemAdministrator(); + + mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete?anonymize=maybe")) + .andExpect( + status().isBadRequest()); + } + + @Test + public void deactivate_whenAnonymizeIsNotSpecified_shouldDeactivateUserWithoutAnonymization() throws Exception { + userSession.logIn().setSystemAdministrator(); + + mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete")) + .andExpect(status().isNoContent()); + + verify(userService).deactivate("userToDelete", false); + } + + @Test + public void deactivate_whenAnonymizeFalse_shouldDeactivateUserWithoutAnonymization() throws Exception { + userSession.logIn().setSystemAdministrator(); + + mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete").param("anonymize", "false")) + .andExpect(status().isNoContent()); + + verify(userService).deactivate("userToDelete", false); + } + + @Test + public void deactivate_whenAnonymizeTrue_shouldDeactivateUserWithAnonymization() throws Exception { + userSession.logIn().setSystemAdministrator(); + + mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete").param("anonymize", "true")) + .andExpect(status().isNoContent()); + + verify(userService).deactivate("userToDelete", true); + } +} -- 2.39.5