You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SearchActionTest.java 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2018 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.organization.ws;
  21. import com.google.common.base.Joiner;
  22. import java.util.Arrays;
  23. import java.util.List;
  24. import java.util.Map;
  25. import java.util.Random;
  26. import javax.annotation.Nullable;
  27. import org.junit.Rule;
  28. import org.junit.Test;
  29. import org.junit.rules.ExpectedException;
  30. import org.sonar.api.server.ws.WebService;
  31. import org.sonar.api.utils.System2;
  32. import org.sonar.core.util.Uuids;
  33. import org.sonar.db.DbTester;
  34. import org.sonar.db.alm.AlmAppInstallDto;
  35. import org.sonar.db.alm.OrganizationAlmBindingDto;
  36. import org.sonar.db.organization.OrganizationDto;
  37. import org.sonar.db.user.GroupDto;
  38. import org.sonar.db.user.UserDto;
  39. import org.sonar.server.exceptions.UnauthorizedException;
  40. import org.sonar.server.tester.UserSessionRule;
  41. import org.sonar.server.ws.TestRequest;
  42. import org.sonar.server.ws.WsActionTester;
  43. import org.sonarqube.ws.Common.Paging;
  44. import org.sonarqube.ws.MediaTypes;
  45. import org.sonarqube.ws.Organizations;
  46. import org.sonarqube.ws.Organizations.Organization;
  47. import org.sonarqube.ws.Organizations.SearchWsResponse;
  48. import static java.lang.String.valueOf;
  49. import static java.util.function.Function.identity;
  50. import static java.util.stream.Collectors.toMap;
  51. import static org.assertj.core.api.Assertions.assertThat;
  52. import static org.assertj.core.api.Assertions.tuple;
  53. import static org.mockito.Mockito.mock;
  54. import static org.mockito.Mockito.when;
  55. import static org.sonar.db.organization.OrganizationDto.Subscription.FREE;
  56. import static org.sonar.db.organization.OrganizationDto.Subscription.PAID;
  57. import static org.sonar.db.permission.OrganizationPermission.ADMINISTER;
  58. import static org.sonar.db.permission.OrganizationPermission.PROVISION_PROJECTS;
  59. import static org.sonar.server.organization.ws.SearchAction.PARAM_MEMBER;
  60. import static org.sonar.test.JsonAssert.assertJson;
  61. public class SearchActionTest {
  62. private static final long SOME_DATE = 1_999_999L;
  63. private System2 system2 = mock(System2.class);
  64. @Rule
  65. public UserSessionRule userSession = UserSessionRule.standalone();
  66. @Rule
  67. public DbTester db = DbTester.create(system2).setDisableDefaultOrganization(true);
  68. @Rule
  69. public ExpectedException expectedException = ExpectedException.none();
  70. private SearchAction underTest = new SearchAction(db.getDbClient(), userSession);
  71. private WsActionTester ws = new WsActionTester(underTest);
  72. @Test
  73. public void admin_and_delete_action_available_for_each_organization() {
  74. OrganizationDto userAdminOrganization = db.organizations().insert();
  75. OrganizationDto groupAdminOrganization = db.organizations().insert();
  76. OrganizationDto browseOrganization = db.organizations().insert();
  77. OrganizationDto guardedOrganization = db.organizations().insert(dto -> dto.setGuarded(true));
  78. UserDto user = db.users().insertUser();
  79. GroupDto group = db.users().insertGroup(groupAdminOrganization);
  80. db.users().insertMember(group, user);
  81. userSession.logIn(user).addPermission(ADMINISTER, userAdminOrganization)
  82. .addPermission(ADMINISTER, guardedOrganization);
  83. db.users().insertPermissionOnUser(userAdminOrganization, user, ADMINISTER);
  84. db.users().insertPermissionOnUser(guardedOrganization, user, ADMINISTER);
  85. db.users().insertPermissionOnGroup(group, ADMINISTER);
  86. SearchWsResponse result = call(ws.newRequest());
  87. assertThat(result.getOrganizationsList())
  88. .extracting(Organization::getKey, o -> o.getActions().getAdmin(), o -> o.getActions().getDelete())
  89. .containsExactlyInAnyOrder(
  90. tuple(userAdminOrganization.getKey(), true, true),
  91. tuple(browseOrganization.getKey(), false, false),
  92. tuple(groupAdminOrganization.getKey(), true, true),
  93. tuple(guardedOrganization.getKey(), true, false));
  94. }
  95. @Test
  96. public void root_can_do_everything() {
  97. OrganizationDto organization = db.organizations().insert();
  98. OrganizationDto guardedOrganization = db.organizations().insert(dto -> dto.setGuarded(true));
  99. UserDto user = db.users().insertUser();
  100. userSession.logIn(user).setRoot();
  101. SearchWsResponse result = call(ws.newRequest());
  102. assertThat(result.getOrganizationsList())
  103. .extracting(Organization::getKey, o -> o.getActions().getAdmin(), o -> o.getActions().getDelete(), o -> o.getActions().getProvision())
  104. .containsExactlyInAnyOrder(
  105. tuple(organization.getKey(), true, true, true),
  106. tuple(guardedOrganization.getKey(), true, true, true));
  107. }
  108. @Test
  109. public void provision_action_available_for_each_organization() {
  110. OrganizationDto userProvisionOrganization = db.organizations().insert();
  111. OrganizationDto groupProvisionOrganization = db.organizations().insert();
  112. OrganizationDto browseOrganization = db.organizations().insert();
  113. UserDto user = db.users().insertUser();
  114. GroupDto group = db.users().insertGroup(groupProvisionOrganization);
  115. db.users().insertMember(group, user);
  116. userSession.logIn(user).addPermission(PROVISION_PROJECTS, userProvisionOrganization);
  117. db.users().insertPermissionOnUser(userProvisionOrganization, user, PROVISION_PROJECTS);
  118. db.users().insertPermissionOnGroup(group, PROVISION_PROJECTS);
  119. SearchWsResponse result = call(ws.newRequest());
  120. assertThat(result.getOrganizationsList()).extracting(Organization::getKey, o -> o.getActions().getProvision()).containsExactlyInAnyOrder(
  121. tuple(userProvisionOrganization.getKey(), true),
  122. tuple(browseOrganization.getKey(), false),
  123. tuple(groupProvisionOrganization.getKey(), true));
  124. }
  125. @Test
  126. public void request_returns_empty_on_table_with_single_row_when_not_requesting_the_first_page() {
  127. when(system2.now()).thenReturn(SOME_DATE);
  128. db.organizations().insert();
  129. assertThat(executeRequestAndReturnList(2, null)).isEmpty();
  130. assertThat(executeRequestAndReturnList(2, 1)).isEmpty();
  131. int somePage = Math.abs(new Random().nextInt(10)) + 2;
  132. assertThat(executeRequestAndReturnList(somePage, null)).isEmpty();
  133. assertThat(executeRequestAndReturnList(somePage, 1)).isEmpty();
  134. }
  135. @Test
  136. public void request_returns_rows_ordered_by_createdAt_descending_applying_requested_paging() {
  137. when(system2.now()).thenReturn(SOME_DATE, SOME_DATE + 1_000, SOME_DATE + 2_000, SOME_DATE + 3_000, SOME_DATE + 5_000);
  138. db.organizations().insert(organization -> organization.setKey("key-3"));
  139. db.organizations().insert(organization -> organization.setKey("key-1"));
  140. db.organizations().insert(organization -> organization.setKey("key-2"));
  141. db.organizations().insert(organization -> organization.setKey("key-5"));
  142. db.organizations().insert(organization -> organization.setKey("key-4"));
  143. assertThat(executeRequestAndReturnList(1, 1))
  144. .extracting(Organization::getKey)
  145. .containsExactly("key-4");
  146. assertThat(executeRequestAndReturnList(2, 1))
  147. .extracting(Organization::getKey)
  148. .containsExactly("key-5");
  149. assertThat(executeRequestAndReturnList(3, 1))
  150. .extracting(Organization::getKey)
  151. .containsExactly("key-2");
  152. assertThat(executeRequestAndReturnList(4, 1))
  153. .extracting(Organization::getKey)
  154. .containsExactly("key-1");
  155. assertThat(executeRequestAndReturnList(5, 1))
  156. .extracting(Organization::getKey)
  157. .containsExactly("key-3");
  158. assertThat(executeRequestAndReturnList(6, 1))
  159. .isEmpty();
  160. assertThat(executeRequestAndReturnList(1, 5))
  161. .extracting(Organization::getKey)
  162. .containsExactly("key-4", "key-5", "key-2", "key-1", "key-3");
  163. assertThat(executeRequestAndReturnList(2, 5))
  164. .isEmpty();
  165. assertThat(executeRequestAndReturnList(1, 3))
  166. .extracting(Organization::getKey)
  167. .containsExactly("key-4", "key-5", "key-2");
  168. assertThat(executeRequestAndReturnList(2, 3))
  169. .extracting(Organization::getKey)
  170. .containsExactly("key-1", "key-3");
  171. }
  172. @Test
  173. public void request_returns_only_specified_keys_ordered_by_createdAt_when_filtering_keys() {
  174. when(system2.now()).thenReturn(SOME_DATE, SOME_DATE + 1_000, SOME_DATE + 2_000, SOME_DATE + 3_000, SOME_DATE + 5_000);
  175. db.organizations().insert(organization -> organization.setKey("key-3"));
  176. db.organizations().insert(organization -> organization.setKey("key-1"));
  177. db.organizations().insert(organization -> organization.setKey("key-2"));
  178. db.organizations().insert(organization -> organization.setKey("key-5"));
  179. db.organizations().insert(organization -> organization.setKey("key-4"));
  180. assertThat(executeRequestAndReturnList(1, 10, "key-3", "key-1", "key-5"))
  181. .extracting(Organization::getKey)
  182. .containsExactly("key-5", "key-1", "key-3");
  183. // ensure order of arguments doesn't change order of result
  184. assertThat(executeRequestAndReturnList(1, 10, "key-1", "key-3", "key-5"))
  185. .extracting(Organization::getKey)
  186. .containsExactly("key-5", "key-1", "key-3");
  187. }
  188. @Test
  189. public void result_is_paginated() {
  190. when(system2.now()).thenReturn(SOME_DATE, SOME_DATE + 1_000, SOME_DATE + 2_000, SOME_DATE + 3_000, SOME_DATE + 5_000);
  191. db.organizations().insert(organization -> organization.setKey("key-3"));
  192. db.organizations().insert(organization -> organization.setKey("key-1"));
  193. db.organizations().insert(organization -> organization.setKey("key-2"));
  194. db.organizations().insert(organization -> organization.setKey("key-5"));
  195. db.organizations().insert(organizationo -> organizationo.setKey("key-4"));
  196. SearchWsResponse response = call(1, 1, "key-1", "key-3", "key-5");
  197. assertThat(response.getOrganizationsList()).extracting(Organization::getKey).containsOnly("key-5");
  198. assertThat(response.getPaging()).extracting(Paging::getPageIndex, Paging::getPageSize, Paging::getTotal).containsOnly(1, 1, 3);
  199. response = call(1, 2, "key-1", "key-3", "key-5");
  200. assertThat(response.getOrganizationsList()).extracting(Organization::getKey).containsOnly("key-5", "key-1");
  201. assertThat(response.getPaging()).extracting(Paging::getPageIndex, Paging::getPageSize, Paging::getTotal).containsOnly(1, 2, 3);
  202. response = call(2, 2, "key-1", "key-3", "key-5");
  203. assertThat(response.getOrganizationsList()).extracting(Organization::getKey).containsOnly("key-3");
  204. assertThat(response.getPaging()).extracting(Paging::getPageIndex, Paging::getPageSize, Paging::getTotal).containsOnly(2, 2, 3);
  205. response = call(null, null);
  206. assertThat(response.getOrganizationsList()).extracting(Organization::getKey).hasSize(5);
  207. assertThat(response.getPaging()).extracting(Paging::getPageIndex, Paging::getPageSize, Paging::getTotal).containsOnly(1, 100, 5);
  208. }
  209. @Test
  210. public void request_returns_empty_when_filtering_on_non_existing_key() {
  211. when(system2.now()).thenReturn(SOME_DATE);
  212. OrganizationDto organization = db.organizations().insert();
  213. assertThat(executeRequestAndReturnList(1, 10, organization.getKey()))
  214. .extracting(Organization::getKey)
  215. .containsExactly(organization.getKey());
  216. }
  217. @Test
  218. public void filter_organization_user_is_member_of() {
  219. UserDto user = db.users().insertUser();
  220. userSession.logIn(user);
  221. OrganizationDto organization = db.organizations().insert();
  222. OrganizationDto organizationWithoutMember = db.organizations().insert();
  223. db.organizations().addMember(organization, user);
  224. SearchWsResponse result = call(ws.newRequest().setParam(PARAM_MEMBER, String.valueOf(true)));
  225. assertThat(result.getOrganizationsList()).extracting(Organization::getKey)
  226. .containsExactlyInAnyOrder(organization.getKey())
  227. .doesNotContain(organizationWithoutMember.getKey());
  228. }
  229. @Test
  230. public void return_alm_info() {
  231. OrganizationDto organization = db.organizations().insert();
  232. AlmAppInstallDto almAppInstall = db.alm().insertAlmAppInstall();
  233. OrganizationAlmBindingDto organizationAlmBinding = db.alm().insertOrganizationAlmBinding(organization, almAppInstall);
  234. OrganizationDto organizationNotBoundToAlm = db.organizations().insert();
  235. SearchWsResponse result = call(ws.newRequest());
  236. Map<String, Organization> orgByKey = result.getOrganizationsList().stream().collect(toMap(Organization::getKey, identity()));
  237. assertThat(orgByKey.get(organization.getKey()).getAlm().getKey()).isEqualTo(organizationAlmBinding.getAlm().getId());
  238. assertThat(orgByKey.get(organization.getKey()).getAlm().getUrl()).isEqualTo(organizationAlmBinding.getUrl());
  239. assertThat(orgByKey.get(organizationNotBoundToAlm.getKey()).hasAlm()).isEqualTo(false);
  240. }
  241. @Test
  242. public void return_subscription_info_when_member_parameter_is_set_to_true() {
  243. UserDto user = db.users().insertUser();
  244. OrganizationDto organization1 = db.organizations().insert(o -> o.setSubscription(FREE));
  245. OrganizationDto organization2 = db.organizations().insert(o -> o.setSubscription(PAID));
  246. OrganizationDto organization3 = db.organizations().insert(o -> o.setSubscription(PAID));
  247. db.organizations().addMember(organization1, user);
  248. db.organizations().addMember(organization2, user);
  249. userSession.logIn(user);
  250. SearchWsResponse result = call(ws.newRequest().setParam("member", "true"));
  251. assertThat(result.getOrganizationsList())
  252. .extracting(Organization::getKey, Organization::getSubscription)
  253. .containsExactlyInAnyOrder(
  254. tuple(organization1.getKey(), Organizations.Subscription.FREE),
  255. tuple(organization2.getKey(), Organizations.Subscription.PAID));
  256. }
  257. @Test
  258. public void subscription_info_is_not_returned_when_no_member_parameter() {
  259. UserDto user = db.users().insertUser();
  260. OrganizationDto organization1 = db.organizations().insert(o -> o.setSubscription(FREE));
  261. OrganizationDto organization2 = db.organizations().insert(o -> o.setSubscription(PAID));
  262. OrganizationDto organization3 = db.organizations().insert(o -> o.setSubscription(PAID));
  263. db.organizations().addMember(organization1, user);
  264. db.organizations().addMember(organization2, user);
  265. userSession.logIn(user);
  266. SearchWsResponse result = call(ws.newRequest());
  267. assertThat(result.getOrganizationsList())
  268. .extracting(Organization::getKey, Organization::hasSubscription)
  269. .containsExactlyInAnyOrder(
  270. tuple(organization1.getKey(), false),
  271. tuple(organization2.getKey(), false),
  272. tuple(organization3.getKey(), false));
  273. }
  274. @Test
  275. public void request_on_empty_db_returns_an_empty_organization_list() {
  276. assertThat(executeRequestAndReturnList(null, null)).isEmpty();
  277. assertThat(executeRequestAndReturnList(null, 1)).isEmpty();
  278. assertThat(executeRequestAndReturnList(1, null)).isEmpty();
  279. assertThat(executeRequestAndReturnList(1, 10)).isEmpty();
  280. assertThat(executeRequestAndReturnList(2, null)).isEmpty();
  281. assertThat(executeRequestAndReturnList(2, 1)).isEmpty();
  282. }
  283. @Test
  284. public void fail_if_member_is_set_to_true_but_user_is_not_authenticated() {
  285. UserDto user = db.users().insertUser();
  286. OrganizationDto organization = db.organizations().insert();
  287. db.organizations().addMember(organization, user);
  288. userSession.anonymous();
  289. expectedException.expect(UnauthorizedException.class);
  290. expectedException.expectMessage("Authentication is required");
  291. call(ws.newRequest().setParam(PARAM_MEMBER, String.valueOf(true)));
  292. }
  293. @Test
  294. public void definition() {
  295. WebService.Action action = ws.getDef();
  296. assertThat(action.key()).isEqualTo("search");
  297. assertThat(action.isPost()).isFalse();
  298. assertThat(action.description()).isEqualTo("Search for organizations");
  299. assertThat(action.isInternal()).isTrue();
  300. assertThat(action.since()).isEqualTo("6.2");
  301. assertThat(action.handler()).isEqualTo(underTest);
  302. assertThat(action.params()).hasSize(4);
  303. assertThat(action.responseExample()).isEqualTo(getClass().getResource("search-example.json"));
  304. WebService.Param organizations = action.param("organizations");
  305. assertThat(organizations.isRequired()).isFalse();
  306. assertThat(organizations.defaultValue()).isNull();
  307. assertThat(organizations.description()).isEqualTo("Comma-separated list of organization keys");
  308. assertThat(organizations.exampleValue()).isEqualTo("my-org-1,foocorp");
  309. assertThat(organizations.since()).isEqualTo("6.3");
  310. assertThat(organizations.maxValuesAllowed()).isEqualTo(500);
  311. WebService.Param page = action.param("p");
  312. assertThat(page.isRequired()).isFalse();
  313. assertThat(page.defaultValue()).isEqualTo("1");
  314. assertThat(page.description()).isEqualTo("1-based page number");
  315. WebService.Param pageSize = action.param("ps");
  316. assertThat(pageSize.isRequired()).isFalse();
  317. assertThat(pageSize.defaultValue()).isEqualTo("100");
  318. assertThat(pageSize.maximumValue()).isEqualTo(500);
  319. assertThat(pageSize.description()).isEqualTo("Page size. Must be greater than 0 and less or equal than 500");
  320. WebService.Param member = action.param("member");
  321. assertThat(member.since()).isEqualTo("7.0");
  322. assertThat(member.defaultValue()).isEqualTo(String.valueOf(false));
  323. assertThat(member.isRequired()).isFalse();
  324. }
  325. @Test
  326. public void json_example() {
  327. when(system2.now()).thenReturn(SOME_DATE, SOME_DATE + 1000);
  328. OrganizationDto barOrganization = db.organizations().insert(organization -> organization
  329. .setUuid(Uuids.UUID_EXAMPLE_02)
  330. .setKey("bar-company")
  331. .setName("Bar Company")
  332. .setDescription("The Bar company produces quality software too.")
  333. .setUrl("https://www.bar.com")
  334. .setAvatarUrl("https://www.bar.com/logo.png")
  335. .setSubscription(PAID)
  336. .setGuarded(false));
  337. OrganizationDto fooOrganization = db.organizations().insert(organization -> organization
  338. .setUuid(Uuids.UUID_EXAMPLE_01)
  339. .setKey("foo-company")
  340. .setName("Foo Company")
  341. .setSubscription(FREE)
  342. .setDescription(null)
  343. .setUrl(null)
  344. .setAvatarUrl(null)
  345. .setGuarded(true));
  346. UserDto user = db.users().insertUser();
  347. db.organizations().addMember(barOrganization, user);
  348. db.organizations().addMember(fooOrganization, user);
  349. db.users().insertPermissionOnUser(barOrganization, user, ADMINISTER);
  350. userSession.logIn(user).addPermission(ADMINISTER, barOrganization);
  351. TestRequest request = ws.newRequest()
  352. .setMediaType(MediaTypes.JSON);
  353. populateRequest(request, null, 25);
  354. String result = request.execute().getInput();
  355. assertJson(ws.getDef().responseExampleAsString()).isSimilarTo(result);
  356. assertJson(result).isSimilarTo(ws.getDef().responseExampleAsString());
  357. }
  358. private List<Organization> executeRequestAndReturnList(@Nullable Integer page, @Nullable Integer pageSize, String... keys) {
  359. return call(page, pageSize, keys).getOrganizationsList();
  360. }
  361. private SearchWsResponse call(TestRequest request) {
  362. return request.executeProtobuf(SearchWsResponse.class);
  363. }
  364. private SearchWsResponse call(@Nullable Integer page, @Nullable Integer pageSize, String... keys) {
  365. TestRequest request = ws.newRequest();
  366. populateRequest(request, page, pageSize, keys);
  367. return call(request);
  368. }
  369. private void populateRequest(TestRequest request, @Nullable Integer page, @Nullable Integer pageSize, String... keys) {
  370. if (keys.length > 0) {
  371. request.setParam("organizations", Joiner.on(',').join(Arrays.asList(keys)));
  372. }
  373. if (page != null) {
  374. request.setParam("p", valueOf(page));
  375. }
  376. if (pageSize != null) {
  377. request.setParam("ps", valueOf(pageSize));
  378. }
  379. }
  380. }