/* * SonarQube * Copyright (C) 2009-2017 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.qualityprofile.ws; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.sonar.api.resources.Language; import org.sonar.api.resources.Languages; import org.sonar.api.rule.RuleStatus; import org.sonar.api.server.ws.WebService; import org.sonar.api.utils.DateUtils; import org.sonar.api.utils.System2; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.DbTester; import org.sonar.db.component.ComponentDbTester; import org.sonar.db.component.ComponentDto; import org.sonar.db.organization.OrganizationDto; import org.sonar.db.organization.OrganizationTesting; import org.sonar.db.qualityprofile.QualityProfileDao; import org.sonar.db.qualityprofile.QualityProfileDbTester; import org.sonar.db.qualityprofile.QualityProfileDto; import org.sonar.db.qualityprofile.QualityProfileTesting; import org.sonar.db.rule.RuleDefinitionDto; import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.language.LanguageTesting; import org.sonar.server.organization.DefaultOrganizationProvider; import org.sonar.server.organization.TestDefaultOrganizationProvider; import org.sonar.server.qualityprofile.QProfileLookup; import org.sonar.server.tester.UserSessionRule; import org.sonar.server.ws.TestRequest; import org.sonar.server.ws.WsActionTester; import org.sonarqube.ws.QualityProfiles; import org.sonarqube.ws.QualityProfiles.SearchWsResponse; import org.sonarqube.ws.QualityProfiles.SearchWsResponse.QualityProfile; import org.sonarqube.ws.client.qualityprofile.SearchWsRequest; import static org.assertj.core.api.Assertions.assertThat; import static org.sonar.api.utils.DateUtils.parseDateTime; import static org.sonar.db.qualityprofile.QualityProfileTesting.newQualityProfileDto; import static org.sonar.test.JsonAssert.assertJson; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_DEFAULTS; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_ORGANIZATION; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PROFILE_NAME; import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_PROJECT_KEY; public class SearchActionTest { @Rule public DbTester db = DbTester.create(System2.INSTANCE); @Rule public UserSessionRule userSession = UserSessionRule.standalone(); @Rule public ExpectedException thrown = ExpectedException.none(); private QualityProfileDbTester qualityProfileDb = db.qualityProfiles(); private ComponentDbTester componentDb = new ComponentDbTester(db); private DbClient dbClient = db.getDbClient(); private DbSession dbSession = db.getSession(); private QualityProfileDao qualityProfileDao = dbClient.qualityProfileDao(); private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db); private QProfileWsSupport qProfileWsSupport = new QProfileWsSupport(dbClient, userSession, defaultOrganizationProvider); private Language xoo1; private Language xoo2; private WsActionTester ws; private SearchAction underTest; @Before public void setUp() { xoo1 = LanguageTesting.newLanguage("xoo1"); xoo2 = LanguageTesting.newLanguage("xoo2"); Languages languages = new Languages(xoo1, xoo2); underTest = new SearchAction( new SearchDataLoader( languages, new QProfileLookup(dbClient), dbClient ), languages, dbClient, qProfileWsSupport); ws = new WsActionTester(underTest); } @Test public void define_search() { WebService.Action search = ws.getDef(); assertThat(search).isNotNull(); assertThat(search.isPost()).isFalse(); assertThat(search.param("language").possibleValues()).containsExactly("xoo1", "xoo2"); assertThat(search.param("language").deprecatedSince()).isEqualTo("6.4"); assertThat(search.param("profileName").deprecatedSince()).isEqualTo("6.4"); assertThat(search.param("projectKey")).isNotNull(); assertThat(search.param("defaults")).isNotNull(); assertThat(search.param("organization")).isNotNull(); assertThat(search.param("organization").isRequired()).isFalse(); assertThat(search.param("organization").isInternal()).isTrue(); assertThat(search.param("organization").description()).isNotEmpty(); assertThat(search.param("organization").since()).isEqualTo("6.4"); } @Test public void ws_returns_the_profiles_of_default_organization() throws Exception { OrganizationDto organization = getDefaultOrganization(); QualityProfileDto defaultProfile = QualityProfileDto.createFor("sonar-way-xoo1-12345") .setOrganizationUuid(organization.getUuid()) .setLanguage(xoo1.getKey()) .setName("Sonar way") .setDefault(true); QualityProfileDto parentProfile = QualityProfileDto .createFor("sonar-way-xoo2-23456") .setOrganizationUuid(organization.getUuid()) .setLanguage(xoo2.getKey()) .setName("Sonar way"); QualityProfileDto childProfile = QualityProfileDto .createFor("my-sonar-way-xoo2-34567") .setOrganizationUuid(organization.getUuid()) .setLanguage(xoo2.getKey()) .setName("My Sonar way") .setParentKee(parentProfile.getKey()); QualityProfileDto profileOnUnknownLang = QualityProfileDto.createFor("sonar-way-other-666") .setOrganizationUuid(organization.getUuid()) .setLanguage("other").setName("Sonar way") .setDefault(true); qualityProfileDao.insert(dbSession, defaultProfile, parentProfile, childProfile, profileOnUnknownLang); ComponentDto project1 = db.components().insertPrivateProject(organization); ComponentDto project2 = db.components().insertPrivateProject(organization); db.qualityProfiles().associateProjectWithQualityProfile(project1, parentProfile); db.qualityProfiles().associateProjectWithQualityProfile(project2, parentProfile); String result = ws.newRequest().execute().getInput(); assertJson(result).isSimilarTo("{" + " \"profiles\": [" + " {" + " \"key\": \"sonar-way-xoo1-12345\"," + " \"name\": \"Sonar way\"," + " \"language\": \"xoo1\"," + " \"languageName\": \"Xoo1\"," + " \"isInherited\": false," + " \"isDefault\": true," + " \"activeRuleCount\": 0," + " \"activeDeprecatedRuleCount\": 0," + " \"organization\": \"" + organization.getKey() + "\"" + " }," + " {" + " \"key\": \"my-sonar-way-xoo2-34567\"," + " \"name\": \"My Sonar way\"," + " \"language\": \"xoo2\"," + " \"languageName\": \"Xoo2\"," + " \"isInherited\": true," + " \"isDefault\": false," + " \"parentKey\": \"sonar-way-xoo2-23456\"," + " \"parentName\": \"Sonar way\"," + " \"activeRuleCount\": 0," + " \"activeDeprecatedRuleCount\": 0," + " \"projectCount\": 0," + " \"organization\": \"" + organization.getKey() + "\"" + " }," + " {" + " \"key\": \"sonar-way-xoo2-23456\"," + " \"name\": \"Sonar way\"," + " \"language\": \"xoo2\"," + " \"languageName\": \"Xoo2\"," + " \"isInherited\": false," + " \"isDefault\": false," + " \"activeRuleCount\": 0," + " \"activeDeprecatedRuleCount\": 0," + " \"projectCount\": 2," + " \"organization\": \"" + organization.getKey() + "\"" + " }" + " ]" + "}"); } @Test public void response_contains_statistics_on_active_rules() { QualityProfileDto profile = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage(xoo1.getKey())); RuleDefinitionDto rule = db.rules().insertRule(r -> r.setLanguage(xoo1.getKey())).getDefinition(); RuleDefinitionDto deprecatedRule1 = db.rules().insertRule(r -> r.setStatus(RuleStatus.DEPRECATED)).getDefinition(); RuleDefinitionDto deprecatedRule2 = db.rules().insertRule(r -> r.setStatus(RuleStatus.DEPRECATED)).getDefinition(); db.qualityProfiles().activateRule(profile, rule); db.qualityProfiles().activateRule(profile, deprecatedRule1); db.qualityProfiles().activateRule(profile, deprecatedRule2); String result = ws.newRequest().execute().getInput(); assertJson(result).isSimilarTo("{\"profiles\":[" + "{" + " \"key\":\"" + profile.getKey() + "\"," + " \"activeRuleCount\":3," + " \"activeDeprecatedRuleCount\":2" + "}]}"); } @Test public void search_map_dates() { long time = DateUtils.parseDateTime("2016-12-22T19:10:03+0100").getTime(); qualityProfileDb.insertQualityProfiles(newQualityProfileDto() .setOrganizationUuid(defaultOrganizationProvider.get().getUuid()) .setLanguage(xoo1.getKey()) .setRulesUpdatedAt("2016-12-21T19:10:03+0100") .setLastUsed(time) .setUserUpdatedAt(time)); SearchWsResponse result = ws.newRequest() .executeProtobuf(SearchWsResponse.class); assertThat(result.getProfilesCount()).isEqualTo(1); assertThat(result.getProfiles(0).getRulesUpdatedAt()).isEqualTo("2016-12-21T19:10:03+0100"); assertThat(parseDateTime(result.getProfiles(0).getLastUsed()).getTime()).isEqualTo(time); assertThat(parseDateTime(result.getProfiles(0).getUserUpdatedAt()).getTime()).isEqualTo(time); } @Test public void search_for_language() throws Exception { qualityProfileDb.insertQualityProfiles( QualityProfileDto.createFor("sonar-way-xoo1-12345") .setOrganizationUuid(defaultOrganizationProvider.get().getUuid()) .setLanguage(xoo1.getKey()) .setName("Sonar way")); String result = ws.newRequest().setParam("language", xoo1.getKey()).execute().getInput(); assertJson(result).isSimilarTo(getClass().getResource("SearchActionTest/search_xoo1.json")); } @Test public void search_for_project_qp() { OrganizationDto org = db.organizations().insert(); ComponentDto project = db.components().insertPrivateProject(org); QualityProfileDto qualityProfileOnXoo1 = db.qualityProfiles().insert(org, q -> q.setLanguage(xoo1.getKey())); db.qualityProfiles().associateProjectWithQualityProfile(project, qualityProfileOnXoo1); QualityProfileDto qualityProfileOnXoo2 = db.qualityProfiles().insert(org, q -> q.setLanguage(xoo2.getKey()).setDefault(true)); db.qualityProfiles().associateProjectWithQualityProfile(project, qualityProfileOnXoo2); db.qualityProfiles().insert(org, q -> q.setLanguage(xoo1.getKey()).setDefault(true)); SearchWsResponse result = ws.newRequest() .setParam(PARAM_PROJECT_KEY, project.key()) .executeProtobuf(SearchWsResponse.class); assertThat(result.getProfilesList()) .extracting(QualityProfile::getKey) .containsExactlyInAnyOrder(qualityProfileOnXoo1.getKey(), qualityProfileOnXoo2.getKey()); } @Test public void search_for_project_qp_with_wrong_organization() { OrganizationDto org1 = db.organizations().insert(); OrganizationDto org2 = db.organizations().insert(); ComponentDto project = db.components().insertPrivateProject(org1); TestRequest request = ws.newRequest() .setParam(PARAM_PROJECT_KEY, project.key()) .setParam(PARAM_ORGANIZATION, org2.getKey()); thrown.expect(IllegalArgumentException.class); thrown.expectMessage( "The provided organization key '" + org2.getKey() + "' does not match the organization key '" + org1.getKey() + "' of the component '" + project.getKey() + "'"); request.execute(); } @Test public void search_for_default_qp_with_profile_name() { String orgUuid = defaultOrganizationProvider.get().getUuid(); QualityProfileDto qualityProfileOnXoo1 = QualityProfileDto.createFor("sonar-way-xoo1-12345") .setOrganizationUuid(orgUuid) .setLanguage(xoo1.getKey()) .setName("Sonar way") .setDefault(false); QualityProfileDto qualityProfileOnXoo2 = QualityProfileDto.createFor("sonar-way-xoo2-12345") .setOrganizationUuid(orgUuid) .setLanguage(xoo2.getKey()) .setName("Sonar way") .setDefault(true); QualityProfileDto anotherQualityProfileOnXoo1 = QualityProfileDto.createFor("sonar-way-xoo1-45678") .setOrganizationUuid(orgUuid) .setLanguage(xoo1.getKey()) .setName("Another way") .setDefault(true); qualityProfileDb.insertQualityProfiles(qualityProfileOnXoo1, qualityProfileOnXoo2, anotherQualityProfileOnXoo1); String result = ws.newRequest() .setParam(PARAM_DEFAULTS, Boolean.TRUE.toString()) .setParam(PARAM_PROFILE_NAME, "Sonar way") .execute().getInput(); assertThat(result) .contains("sonar-way-xoo1-12345", "sonar-way-xoo2-12345") .doesNotContain("sonar-way-xoo1-45678"); } @Test public void search_by_profile_name() { OrganizationDto org = db.organizations().insert(); QualityProfileDto qualityProfileOnXoo1 = db.qualityProfiles().insert(org, q -> q.setLanguage(xoo1.getKey()).setName("Sonar way")); QualityProfileDto qualityProfileOnXoo2 = db.qualityProfiles().insert(org, q -> q.setLanguage(xoo2.getKey()).setName("Sonar way").setDefault(true)); QualityProfileDto anotherQualityProfileOnXoo1 = db.qualityProfiles().insert(org, q -> q.setLanguage(xoo1.getKey()).setName("Another way").setDefault(true)); ComponentDto project = componentDb.insertPrivateProject(org); SearchWsResponse result = ws.newRequest() .setParam(PARAM_ORGANIZATION, org.getKey()) .setParam(PARAM_PROJECT_KEY, project.key()) .setParam(PARAM_PROFILE_NAME, "Sonar way") .executeProtobuf(SearchWsResponse.class); assertThat(result.getProfilesList()) .extracting(QualityProfile::getKey) .contains(qualityProfileOnXoo1.getKey(), qualityProfileOnXoo2.getKey()) .doesNotContain(anotherQualityProfileOnXoo1.getKey()); } @Test public void search_default_profile_by_profile_name_and_org() { List<QualityProfileDto> profiles = new ArrayList<>(); for (String orgKey : Arrays.asList("ORG1", "ORG2")) { OrganizationDto org = db.organizations().insert(OrganizationTesting.newOrganizationDto().setKey(orgKey)); profiles.add(createProfile("A", xoo1, org, "MATCH", false)); profiles.add(createProfile("B", xoo2, org, "NOMATCH", false)); profiles.add(createProfile("C", xoo1, org, "NOMATCH", true)); profiles.add(createProfile("D", xoo2, org, "NOMATCH", true)); } profiles.forEach(db.qualityProfiles()::insertQualityProfile); SearchWsRequest request = new SearchWsRequest() .setDefaults(true) .setProfileName("MATCH") .setOrganizationKey("ORG1"); QualityProfiles.SearchWsResponse response = underTest.doHandle(request); assertThat(response.getProfilesList()) .extracting(QualityProfiles.SearchWsResponse.QualityProfile::getKey) .containsExactlyInAnyOrder( // name match for xoo1 "ORG1-A", // default for xoo2 "ORG1-D"); } @Test public void name_and_default_query_is_valid() throws Exception { minimalValidSetup(); SearchWsRequest request = new SearchWsRequest() .setProfileName("bla") .setDefaults(true); assertThat(findProfiles(request).getProfilesList()).isNotNull(); } @Test public void name_and_component_query_is_valid() throws Exception { minimalValidSetup(); ComponentDto project = db.components().insertPrivateProject(); SearchWsRequest request = new SearchWsRequest() .setProfileName("bla") .setProjectKey(project.key()); assertThat(findProfiles(request).getProfilesList()).isNotNull(); } @Test public void name_requires_either_component_or_defaults() throws Exception { SearchWsRequest request = new SearchWsRequest() .setProfileName("bla"); thrown.expect(BadRequestException.class); thrown.expectMessage("The name parameter requires either projectKey or defaults to be set."); findProfiles(request); } @Test public void default_and_component_cannot_be_set_at_same_time() throws Exception { SearchWsRequest request = new SearchWsRequest() .setDefaults(true) .setProjectKey("blubb"); thrown.expect(BadRequestException.class); thrown.expectMessage("The default parameter cannot be provided at the same time than the component key."); findProfiles(request); } @Test public void language_and_component_cannot_be_set_at_same_time() throws Exception { SearchWsRequest request = new SearchWsRequest() .setLanguage("xoo") .setProjectKey("bla"); thrown.expect(BadRequestException.class); thrown.expectMessage("The language parameter cannot be provided at the same time than the component key or profile name."); findProfiles(request); } @Test public void language_and_name_cannot_be_set_at_same_time() throws Exception { SearchWsRequest request = new SearchWsRequest() .setLanguage("xoo") .setProfileName("bla"); thrown.expect(BadRequestException.class); thrown.expectMessage("The language parameter cannot be provided at the same time than the component key or profile name."); findProfiles(request); } private void minimalValidSetup() { for (Language language : Arrays.asList(xoo1, xoo2)) { db.qualityProfiles().insertQualityProfile( QualityProfileTesting.newQualityProfileDto() .setOrganizationUuid(getDefaultOrganization().getUuid()) .setLanguage(language.getKey()) .setDefault(true)); } } private QualityProfileDto createProfile(String keySuffix, Language language, OrganizationDto org, String name, boolean isDefault) { return QualityProfileDto.createFor(org.getKey() + "-" + keySuffix) .setOrganizationUuid(org.getUuid()) .setLanguage(language.getKey()) .setName(name) .setDefault(isDefault); } private SearchWsResponse findProfiles(SearchWsRequest request) { return underTest.doHandle(request); } private OrganizationDto getDefaultOrganization() { return db.getDefaultOrganization(); } }