]> source.dussan.org Git - sonarqube.git/blob
d3d4b4a675f863a50d5cc2d92ae02c07043a1f38
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2024 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 3 of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20 package org.sonar.server.v2.api.user.controller;
21
22 import com.google.gson.Gson;
23 import com.google.gson.GsonBuilder;
24 import com.google.gson.TypeAdapter;
25 import com.google.gson.stream.JsonReader;
26 import com.google.gson.stream.JsonWriter;
27 import java.util.ArrayList;
28 import java.util.List;
29 import java.util.Optional;
30 import java.util.stream.IntStream;
31 import org.junit.Rule;
32 import org.junit.Test;
33 import org.mockito.ArgumentCaptor;
34 import org.sonar.db.user.UserDto;
35 import org.sonar.server.common.SearchResults;
36 import org.sonar.server.common.user.service.UserInformation;
37 import org.sonar.server.common.user.service.UserService;
38 import org.sonar.server.common.user.service.UsersSearchRequest;
39 import org.sonar.server.exceptions.BadRequestException;
40 import org.sonar.server.exceptions.NotFoundException;
41 import org.sonar.server.tester.UserSessionRule;
42 import org.sonar.server.user.UpdateUser;
43 import org.sonar.server.v2.api.ControllerTester;
44 import org.sonar.server.v2.api.model.RestError;
45 import org.sonar.server.v2.api.response.PageRestResponse;
46 import org.sonar.server.v2.api.user.converter.UsersSearchRestResponseGenerator;
47 import org.sonar.server.v2.api.user.response.UserRestResponse;
48 import org.sonar.server.v2.api.user.response.UserRestResponseForAdmins;
49 import org.sonar.server.v2.api.user.request.UserCreateRestRequest;
50 import org.sonar.server.v2.api.user.response.UsersSearchRestResponse;
51 import org.springframework.http.MediaType;
52 import org.springframework.test.web.servlet.MockMvc;
53 import org.springframework.test.web.servlet.MvcResult;
54
55 import static org.assertj.core.api.Assertions.assertThat;
56 import static org.mockito.ArgumentMatchers.any;
57 import static org.mockito.ArgumentMatchers.eq;
58 import static org.mockito.Mockito.doThrow;
59 import static org.mockito.Mockito.mock;
60 import static org.mockito.Mockito.verify;
61 import static org.mockito.Mockito.when;
62 import static org.sonar.api.utils.DateUtils.formatDateTime;
63 import static org.sonar.api.utils.DateUtils.parseOffsetDateTime;
64 import static org.sonar.server.v2.WebApiEndpoints.JSON_MERGE_PATCH_CONTENT_TYPE;
65 import static org.sonar.server.v2.WebApiEndpoints.USER_ENDPOINT;
66 import static org.sonar.server.v2.api.model.RestPage.DEFAULT_PAGE_INDEX;
67 import static org.sonar.server.v2.api.model.RestPage.DEFAULT_PAGE_SIZE;
68 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
69 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
70 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
71 import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
72 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
73 import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
74
75 public class DefaultUserControllerTest {
76
77   @Rule
78   public UserSessionRule userSession = UserSessionRule.standalone();
79   private final UserService userService = mock(UserService.class);
80   private final UsersSearchRestResponseGenerator responseGenerator = mock(UsersSearchRestResponseGenerator.class);
81   private final MockMvc mockMvc = ControllerTester.getMockMvc(new DefaultUserController(userSession, userService, responseGenerator));
82
83   private static final Gson gson = new GsonBuilder().registerTypeAdapter(UserRestResponse.class, new RestUserDeserializer()).create();
84
85   @Test
86   public void search_whenNoParameters_shouldUseDefaultAndForwardToUserService() throws Exception {
87     when(userService.findUsers(any())).thenReturn(new SearchResults<>(List.of(), 0));
88
89     mockMvc.perform(get(USER_ENDPOINT))
90       .andExpect(status().isOk());
91
92     ArgumentCaptor<UsersSearchRequest> requestCaptor = ArgumentCaptor.forClass(UsersSearchRequest.class);
93     verify(userService).findUsers(requestCaptor.capture());
94     assertThat(requestCaptor.getValue().getPageSize()).isEqualTo(Integer.valueOf(DEFAULT_PAGE_SIZE));
95     assertThat(requestCaptor.getValue().getPage()).isEqualTo(Integer.valueOf(DEFAULT_PAGE_INDEX));
96     assertThat(requestCaptor.getValue().isDeactivated()).isFalse();
97   }
98
99   @Test
100   public void search_whenParametersUsed_shouldForwardWithParameters() throws Exception {
101     when(userService.findUsers(any())).thenReturn(new SearchResults<>(List.of(), 0));
102     userSession.logIn().setSystemAdministrator();
103
104     mockMvc.perform(get(USER_ENDPOINT)
105         .param("active", "false")
106         .param("managed", "true")
107         .param("q", "q")
108         .param("externalIdentity", "externalIdentity")
109         .param("sonarQubeLastConnectionDateFrom", "2020-01-01T00:00:00+0100")
110         .param("sonarQubeLastConnectionDateTo", "2020-01-01T00:00:00+0100")
111         .param("sonarLintLastConnectionDateFrom", "2020-01-01T00:00:00+0100")
112         .param("sonarLintLastConnectionDateTo", "2020-01-01T00:00:00+0100")
113         .param("groupId", "groupId1")
114         .param("groupId!", "groupId2")
115         .param("pageSize", "100")
116         .param("pageIndex", "2"))
117       .andExpect(status().isOk());
118
119     ArgumentCaptor<UsersSearchRequest> requestCaptor = ArgumentCaptor.forClass(UsersSearchRequest.class);
120     verify(userService).findUsers(requestCaptor.capture());
121
122     assertThat(requestCaptor.getValue().isDeactivated()).isTrue();
123     assertThat(requestCaptor.getValue().isManaged()).isTrue();
124     assertThat(requestCaptor.getValue().getQuery()).isEqualTo("q");
125     assertThat(requestCaptor.getValue().getExternalLogin()).contains("externalIdentity");
126     assertThat(requestCaptor.getValue().getLastConnectionDateFrom()).contains(parseOffsetDateTime("2020-01-01T00:00:00+0100"));
127     assertThat(requestCaptor.getValue().getLastConnectionDateTo()).contains(parseOffsetDateTime("2020-01-01T00:00:00+0100"));
128     assertThat(requestCaptor.getValue().getSonarLintLastConnectionDateFrom()).contains(parseOffsetDateTime("2020-01-01T00:00:00+0100"));
129     assertThat(requestCaptor.getValue().getSonarLintLastConnectionDateTo()).contains(parseOffsetDateTime("2020-01-01T00:00:00+0100"));
130     assertThat(requestCaptor.getValue().getGroupUuid()).contains("groupId1");
131     assertThat(requestCaptor.getValue().getExcludedGroupUuid()).contains("groupId2");
132     assertThat(requestCaptor.getValue().getPageSize()).isEqualTo(100);
133     assertThat(requestCaptor.getValue().getPage()).isEqualTo(2);
134   }
135
136   @Test
137   public void search_whenAdminParametersUsedButNotAdmin_shouldFail() throws Exception {
138     mockMvc.perform(get(USER_ENDPOINT)
139       .param("sonarQubeLastConnectionDateFrom", "2020-01-01T00:00:00+0100"))
140       .andExpectAll(
141         status().isForbidden(),
142         content().string("{\"message\":\"Parameter sonarQubeLastConnectionDateFrom requires Administer System permission.\"}"));
143
144     mockMvc.perform(get(USER_ENDPOINT)
145       .param("sonarQubeLastConnectionDateTo", "2020-01-01T00:00:00+0100"))
146       .andExpectAll(
147         status().isForbidden(),
148         content().string("{\"message\":\"Parameter sonarQubeLastConnectionDateTo requires Administer System permission.\"}"));
149
150     mockMvc.perform(get(USER_ENDPOINT)
151       .param("sonarLintLastConnectionDateFrom", "2020-01-01T00:00:00+0100"))
152       .andExpectAll(
153         status().isForbidden(),
154         content().string("{\"message\":\"Parameter sonarLintLastConnectionDateFrom requires Administer System permission.\"}"));
155
156     mockMvc.perform(get(USER_ENDPOINT)
157       .param("sonarLintLastConnectionDateTo", "2020-01-01T00:00:00+0100"))
158       .andExpectAll(
159         status().isForbidden(),
160         content().string("{\"message\":\"Parameter sonarLintLastConnectionDateTo requires Administer System permission.\"}"));
161
162     mockMvc.perform(get(USER_ENDPOINT)
163         .param("externalIdentity", "externalIdentity"))
164       .andExpectAll(
165         status().isForbidden(),
166         content().string("{\"message\":\"Parameter externalIdentity requires Administer System permission.\"}"));
167
168     mockMvc.perform(get(USER_ENDPOINT)
169         .param("groupId", "groupId"))
170       .andExpectAll(
171         status().isForbidden(),
172         content().string("{\"message\":\"Parameter groupId requires Administer System permission.\"}"));
173
174     mockMvc.perform(get(USER_ENDPOINT)
175         .param("groupId!", "groupId"))
176       .andExpectAll(
177         status().isForbidden(),
178         content().string("{\"message\":\"Parameter groupId! requires Administer System permission.\"}"));
179   }
180
181   @Test
182   public void search_whenUserServiceReturnUsers_shouldReturnThem() throws Exception {
183     UserInformation user1 = generateUserSearchResult("user1", true, true, false, 2, 3);
184     UserInformation user2 = generateUserSearchResult("user2", true, false, false, 3, 0);
185     UserInformation user3 = generateUserSearchResult("user3", true, false, true, 1, 1);
186     UserInformation user4 = generateUserSearchResult("user4", false, true, false, 0, 0);
187     List<UserInformation> users = List.of(user1, user2, user3, user4);
188     SearchResults<UserInformation> searchResult = new SearchResults<>(users, users.size());
189     when(userService.findUsers(any())).thenReturn(searchResult);
190     List<UserRestResponse> restUserForAdmins = List.of(toRestUser(user1), toRestUser(user2), toRestUser(user3), toRestUser(user4));
191     when(responseGenerator.toUsersForResponse(eq(searchResult.searchResults()), any())).thenReturn(new UsersSearchRestResponse(restUserForAdmins, new PageRestResponse(1, 50, 4)));
192     userSession.logIn().setSystemAdministrator();
193
194     MvcResult mvcResult = mockMvc.perform(get(USER_ENDPOINT))
195       .andExpect(status().isOk())
196       .andReturn();
197
198     UsersSearchRestResponse actualUsersSearchRestResponse = gson.fromJson(mvcResult.getResponse().getContentAsString(), UsersSearchRestResponse.class);
199     assertThat(actualUsersSearchRestResponse.users())
200       .containsExactlyElementsOf(restUserForAdmins);
201     assertThat(actualUsersSearchRestResponse.page().total()).isEqualTo(users.size());
202
203   }
204
205   static class RestUserDeserializer extends TypeAdapter<UserRestResponse> {
206
207     @Override
208     public void write(JsonWriter out, UserRestResponse value) {
209       throw new IllegalStateException("not implemented");
210     }
211
212     @Override
213     public UserRestResponse read(JsonReader reader) {
214       return gson.fromJson(reader, UserRestResponseForAdmins.class);
215     }
216   }
217
218   private UserInformation generateUserSearchResult(String id, boolean active, boolean local, boolean managed, int groupsCount, int tokensCount) {
219     UserDto userDto = new UserDto()
220       .setLogin("login_" + id)
221       .setUuid("uuid_" + id)
222       .setName("name_" + id)
223       .setEmail(id + "@email.com")
224       .setActive(active)
225       .setLocal(local)
226       .setExternalLogin("externalLogin_" + id)
227       .setExternalId("externalId_" + id)
228       .setExternalIdentityProvider("externalIdentityProvider_" + id)
229       .setLastConnectionDate(0L)
230       .setLastSonarlintConnectionDate(1L);
231
232     List<String> groups = new ArrayList<>();
233     IntStream.range(1, groupsCount).forEach(i -> groups.add("group" + i));
234
235     return new UserInformation(userDto, managed, Optional.of("avatar_" + id), groups, tokensCount);
236   }
237
238   private UserRestResponseForAdmins toRestUser(UserInformation userInformation) {
239     return new UserRestResponseForAdmins(
240       userInformation.userDto().getLogin(),
241       userInformation.userDto().getLogin(),
242       userInformation.userDto().getName(),
243       userInformation.userDto().getEmail(),
244       userInformation.userDto().isActive(),
245       userInformation.userDto().isLocal(),
246       userInformation.managed(),
247       userInformation.userDto().getExternalLogin(),
248       userInformation.userDto().getExternalIdentityProvider(),
249       userInformation.avatar().orElse(""),
250       formatDateTime(userInformation.userDto().getLastConnectionDate()),
251       formatDateTime(userInformation.userDto().getLastSonarlintConnectionDate()),
252       userInformation.userDto().getSortedScmAccounts());
253   }
254
255   @Test
256   public void deactivate_whenUserIsNotAdministrator_shouldReturnForbidden() throws Exception {
257     userSession.logIn().setNonSystemAdministrator();
258
259     mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete"))
260       .andExpectAll(
261         status().isForbidden(),
262         content().json("{\"message\":\"Insufficient privileges\"}"));
263   }
264
265   @Test
266   public void deactivate_whenUserServiceThrowsNotFoundException_shouldReturnNotFound() throws Exception {
267     userSession.logIn().setSystemAdministrator();
268     doThrow(new NotFoundException("User not found.")).when(userService).deactivate("userToDelete", false);
269
270     mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete"))
271       .andExpectAll(
272         status().isNotFound(),
273         content().json("{\"message\":\"User not found.\"}"));
274   }
275
276   @Test
277   public void deactivate_whenUserServiceThrowsBadRequestException_shouldReturnBadRequest() throws Exception {
278     userSession.logIn().setSystemAdministrator();
279     doThrow(BadRequestException.create("Not allowed")).when(userService).deactivate("userToDelete", false);
280
281     mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete"))
282       .andExpectAll(
283         status().isBadRequest(),
284         content().json("{\"message\":\"Not allowed\"}"));
285   }
286
287   @Test
288   public void deactivate_whenUserTryingToDeactivateThemself_shouldReturnBadRequest() throws Exception {
289     UserSessionRule userToDelete = userSession.logIn("userToDelete").setSystemAdministrator();
290
291     mockMvc.perform(delete(USER_ENDPOINT + "/" + userToDelete.getUuid()))
292       .andExpectAll(
293         status().isBadRequest(),
294         content().json("{\"message\":\"Self-deactivation is not possible\"}"));
295   }
296
297   @Test
298   public void deactivate_whenAnonymizeParameterIsNotBoolean_shouldReturnBadRequest() throws Exception {
299     userSession.logIn().setSystemAdministrator();
300
301     mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete").param("anonymize", "maybe"))
302       .andExpect(
303         status().isBadRequest());
304   }
305
306   @Test
307   public void deactivate_whenAnonymizeIsNotSpecified_shouldDeactivateUserWithoutAnonymization() throws Exception {
308     userSession.logIn().setSystemAdministrator();
309
310     mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete"))
311       .andExpect(status().isNoContent());
312
313     verify(userService).deactivate("userToDelete", false);
314   }
315
316   @Test
317   public void deactivate_whenAnonymizeFalse_shouldDeactivateUserWithoutAnonymization() throws Exception {
318     userSession.logIn().setSystemAdministrator();
319
320     mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete").param("anonymize", "false"))
321       .andExpect(status().isNoContent());
322
323     verify(userService).deactivate("userToDelete", false);
324   }
325
326   @Test
327   public void deactivate_whenAnonymizeTrue_shouldDeactivateUserWithAnonymization() throws Exception {
328     userSession.logIn().setSystemAdministrator();
329
330     mockMvc.perform(delete(USER_ENDPOINT + "/userToDelete").param("anonymize", "true"))
331       .andExpect(status().isNoContent());
332
333     verify(userService).deactivate("userToDelete", true);
334   }
335
336   @Test
337   public void fetchUser_whenUserServiceThrowsNotFoundException_returnsNotFound() throws Exception {
338     when(userService.fetchUser("userLogin")).thenThrow(new NotFoundException("Not found"));
339     mockMvc.perform(get(USER_ENDPOINT + "/userLogin"))
340       .andExpectAll(
341         status().isNotFound(),
342         content().json("{\"message\":\"Not found\"}")
343       );
344
345   }
346
347   @Test
348   public void fetchUser_whenUserExists_shouldReturnUser() throws Exception {
349     UserInformation user = generateUserSearchResult("user1", true, true, false, 2, 3);
350     UserRestResponseForAdmins restUserForAdmins = toRestUser(user);
351     when(userService.fetchUser("userLogin")).thenReturn(user);
352     when(responseGenerator.toRestUser(user)).thenReturn(restUserForAdmins);
353     MvcResult mvcResult = mockMvc.perform(get(USER_ENDPOINT + "/userLogin"))
354       .andExpect(status().isOk())
355       .andReturn();
356     UserRestResponseForAdmins responseUser = gson.fromJson(mvcResult.getResponse().getContentAsString(), UserRestResponseForAdmins.class);
357     assertThat(responseUser).isEqualTo(restUserForAdmins);
358   }
359
360   @Test
361   public void create_whenNotAnAdmin_shouldReturnForbidden() throws Exception {
362     userSession.logIn().setNonSystemAdministrator();
363
364     mockMvc.perform(
365       post(USER_ENDPOINT)
366         .contentType(MediaType.APPLICATION_JSON_VALUE)
367         .content(gson.toJson(new UserCreateRestRequest(null, null, "login", "name", null, null))))
368       .andExpectAll(
369         status().isForbidden(),
370         content().json("{\"message\":\"Insufficient privileges\"}"));
371   }
372
373   @Test
374   public void create_whenNoLogin_shouldReturnBadRequest() throws Exception {
375     userSession.logIn().setSystemAdministrator();
376
377     mockMvc.perform(
378       post(USER_ENDPOINT)
379         .contentType(MediaType.APPLICATION_JSON_VALUE)
380         .content(gson.toJson(new UserCreateRestRequest(null, null, null, "name", null, null))))
381       .andExpectAll(
382         status().isBadRequest(),
383         content().json("{\"message\":\"Value {} for field login was rejected. Error: must not be null.\"}"));
384   }
385
386   @Test
387   public void create_whenNoName_shouldReturnBadRequest() throws Exception {
388     userSession.logIn().setSystemAdministrator();
389
390     mockMvc.perform(
391       post(USER_ENDPOINT)
392         .contentType(MediaType.APPLICATION_JSON_VALUE)
393         .content(gson.toJson(new UserCreateRestRequest(null, null, "login", null, null, null))))
394       .andExpectAll(
395         status().isBadRequest(),
396         content().json("{\"message\":\"Value {} for field name was rejected. Error: must not be null.\"}"));
397   }
398
399   @Test
400   public void create_whenUserServiceThrow_shouldReturnServerError() throws Exception {
401     userSession.logIn().setSystemAdministrator();
402     when(userService.createUser(any())).thenThrow(new IllegalArgumentException("IllegalArgumentException"));
403
404     mockMvc.perform(
405       post(USER_ENDPOINT)
406         .contentType(MediaType.APPLICATION_JSON_VALUE)
407         .content(gson.toJson(new UserCreateRestRequest("e@mail.com", true, "login", "name", "password", List.of("scm")))))
408       .andExpectAll(
409         status().isBadRequest(),
410         content().json("{\"message\":\"IllegalArgumentException\"}"));
411   }
412
413   @Test
414   public void create_whenUserServiceReturnUser_shouldReturnIt() throws Exception {
415     userSession.logIn().setSystemAdministrator();
416     UserInformation userInformation = generateUserSearchResult("1", true, true, false, 1, 2);
417     UserDto userDto = userInformation.userDto();
418     when(userService.createUser(any())).thenReturn(userInformation);
419     when(responseGenerator.toRestUser(userInformation)).thenReturn(toRestUser(userInformation));
420
421     MvcResult mvcResult = mockMvc.perform(
422       post(USER_ENDPOINT)
423         .contentType(MediaType.APPLICATION_JSON_VALUE)
424         .content(gson.toJson(new UserCreateRestRequest(
425           userDto.getEmail(), userDto.isLocal(), userDto.getLogin(), userDto.getName(), "password", userDto.getSortedScmAccounts()))))
426       .andExpect(status().isOk())
427       .andReturn();
428     UserRestResponseForAdmins responseUser = gson.fromJson(mvcResult.getResponse().getContentAsString(), UserRestResponseForAdmins.class);
429     assertThat(responseUser).isEqualTo(toRestUser(userInformation));
430   }
431
432   @Test
433   public void updateUser_whenUserDoesntExist_shouldReturnNotFound() throws Exception {
434     userSession.logIn().setSystemAdministrator();
435     when(userService.updateUser(eq("userLogin"), any(UpdateUser.class))).thenThrow(new NotFoundException("Not found"));
436     mockMvc.perform(patch(USER_ENDPOINT + "/userLogin")
437       .contentType(JSON_MERGE_PATCH_CONTENT_TYPE)
438       .content("{}"))
439       .andExpectAll(
440         status().isNotFound(),
441         content().json("{\"message\":\"Not found\"}"));
442   }
443
444   @Test
445   public void updateUser_whenCallerIsNotAdmin_shouldReturnForbidden() throws Exception {
446     userSession.logIn().setNonSystemAdministrator();
447
448     mockMvc.perform(
449       patch(USER_ENDPOINT + "/userLogin")
450         .contentType(JSON_MERGE_PATCH_CONTENT_TYPE)
451         .content("{}"))
452       .andExpectAll(
453         status().isForbidden(),
454         content().json("{\"message\":\"Insufficient privileges\"}"));
455   }
456
457   @Test
458   public void updateUser_whenEmailIsProvided_shouldUpdateUserAndReturnUpdatedValue() throws Exception {
459     UpdateUser userUpdate = performPatchCallAndVerifyResponse("{\"email\":\"newemail@example.com\"}");
460     assertThat(userUpdate.email()).isEqualTo("newemail@example.com");
461     assertThat(userUpdate.name()).isNull();
462     assertThat(userUpdate.scmAccounts()).isNull();
463   }
464
465   @Test
466   public void updateUser_whenNameIsProvided_shouldUpdateUserAndReturnUpdatedValue() throws Exception {
467     UpdateUser userUpdate = performPatchCallAndVerifyResponse("{\"name\":\"new name\"}");
468     assertThat(userUpdate.email()).isNull();
469     assertThat(userUpdate.name()).isEqualTo("new name");
470     assertThat(userUpdate.scmAccounts()).isNull();
471   }
472
473   @Test
474   public void updateUser_whenScmAccountsAreProvided_shouldUpdateUserAndReturnUpdatedValue() throws Exception {
475     UpdateUser userUpdate = performPatchCallAndVerifyResponse("{\"scmAccounts\":[\"account1\",\"account2\"]}");
476     assertThat(userUpdate.email()).isNull();
477     assertThat(userUpdate.name()).isNull();
478     assertThat(userUpdate.scmAccounts()).containsExactly("account1", "account2");
479   }
480
481   @Test
482   public void updateUser_whenEmailIsInvalid_shouldReturnBadRequest() throws Exception {
483     performPatchCallAndExpectBadRequest("{\"email\":\"notavalidemail\"}", "Value notavalidemail for field email was rejected. Error: must be a well-formed email address.");
484   }
485
486   @Test
487   public void updateUser_whenEmailIsEmpty_shouldReturnBadRequest() throws Exception {
488     performPatchCallAndExpectBadRequest("{\"email\":\"\"}", "Value  for field email was rejected. Error: size must be between 1 and 100.");
489   }
490
491   @Test
492   public void updateUser_whenNameIsTooLong_shouldReturnBadRequest() throws Exception {
493     String tooLong = "toolong".repeat(30);
494     String payload = "{\"name\":\"" + tooLong + "\"}";
495     String message = "Value " + tooLong + " for field name was rejected. Error: size must be between 0 and 200.";
496     performPatchCallAndExpectBadRequest(payload, message);
497   }
498
499   @Test
500   public void updateUser_whenLoginIsProvided_shouldUpdateLogin() throws Exception {
501     UpdateUser userUpdate = performPatchCallAndVerifyResponse("{\"login\":\"newLogin\"}");
502     assertThat(userUpdate.login()).isEqualTo("newLogin");
503   }
504
505   @Test
506   public void updateUser_whenExternalProviderIsProvided_shouldUpdate() throws Exception {
507     UpdateUser userUpdate = performPatchCallAndVerifyResponse("{\"externalProvider\":\"newExternalProvider\"}");
508     assertThat(userUpdate.externalIdentityProvider()).isEqualTo("newExternalProvider");
509   }
510   @Test
511   public void updateUser_whenExternalProviderLoginIsProvided_shouldUpdate() throws Exception {
512     UpdateUser userUpdate = performPatchCallAndVerifyResponse("{\"externalLogin\":\"newExternalProviderLogin\"}");
513     assertThat(userUpdate.externalIdentityProviderLogin()).isEqualTo("newExternalProviderLogin");
514   }
515
516   @Test
517   public void updateUser_whenLoginIsEmpty_shouldReturnBadRequest() throws Exception {
518     performPatchCallAndExpectBadRequest("{\"login\":\"\"}", "Value  for field login was rejected. Error: size must be between 2 and 100.");
519   }
520
521   private void performPatchCallAndExpectBadRequest(String payload, String expectedMessage) throws Exception {
522     userSession.logIn().setSystemAdministrator();
523
524     MvcResult mvcResult = mockMvc.perform(patch(USER_ENDPOINT + "/userLogin")
525         .contentType(JSON_MERGE_PATCH_CONTENT_TYPE)
526         .content(payload))
527       .andExpect(
528         status().isBadRequest())
529       .andReturn();
530
531     RestError error = gson.fromJson(mvcResult.getResponse().getContentAsString(), RestError.class);
532     assertThat(error.message()).isEqualTo(expectedMessage);
533   }
534
535   private UpdateUser performPatchCallAndVerifyResponse(String payload) throws Exception {
536     userSession.logIn().setSystemAdministrator();
537     UserInformation mock = mock();
538     when(userService.fetchUser("userUuid")).thenReturn(mock);
539     UserInformation userInformation = generateUserSearchResult("1", true, true, false, 1, 2);
540
541     when(userService.updateUser(eq("userUuid"), any())).thenReturn(userInformation);
542     when(responseGenerator.toRestUser(userInformation)).thenReturn(toRestUser(userInformation));
543
544     MvcResult mvcResult = mockMvc.perform(patch(USER_ENDPOINT + "/userUuid")
545         .contentType(JSON_MERGE_PATCH_CONTENT_TYPE)
546         .content(payload))
547       .andExpect(
548         status().isOk())
549       .andReturn();
550
551     UserRestResponseForAdmins responseUser = gson.fromJson(mvcResult.getResponse().getContentAsString(), UserRestResponseForAdmins.class);
552     assertThat(responseUser).isEqualTo(toRestUser(userInformation));
553
554     ArgumentCaptor<UpdateUser> updateUserCaptor = ArgumentCaptor.forClass(UpdateUser.class);
555     verify(userService).updateUser(eq("userUuid"), updateUserCaptor.capture());
556     return updateUserCaptor.getValue();
557   }
558 }