/* * 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; import com.google.common.io.Resources; import java.io.StringReader; import java.io.StringWriter; import java.nio.charset.StandardCharsets; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; import javax.xml.stream.XMLStreamException; import org.junit.After; import org.junit.Before; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.Severity; import org.sonar.api.server.rule.RuleParamType; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.organization.OrganizationDto; import org.sonar.db.organization.OrganizationTesting; import org.sonar.db.qualityprofile.ActiveRuleDao; import org.sonar.db.qualityprofile.ActiveRuleDto; import org.sonar.db.qualityprofile.ActiveRuleParamDto; import org.sonar.db.qualityprofile.QualityProfileDto; import org.sonar.db.rule.RuleDefinitionDto; import org.sonar.db.rule.RuleDto; import org.sonar.db.rule.RuleParamDto; import org.sonar.server.qualityprofile.index.ActiveRuleIndexer; import org.sonar.server.rule.index.RuleIndex; import org.sonar.server.rule.index.RuleIndexer; import org.sonar.server.rule.index.RuleQuery; import org.sonar.server.tester.ServerTester; import org.sonar.server.tester.UserSessionRule; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; import static org.sonar.db.qualityprofile.ActiveRuleDto.INHERITED; import static org.sonar.db.qualityprofile.ActiveRuleDto.OVERRIDES; import static org.sonar.db.rule.RuleTesting.XOO_X1; import static org.sonar.db.rule.RuleTesting.XOO_X2; import static org.sonar.db.rule.RuleTesting.newRule; import static org.sonar.db.rule.RuleTesting.newXooX1; import static org.sonar.db.rule.RuleTesting.newXooX2; import static org.sonar.server.qualityprofile.QProfileTesting.XOO_P1_KEY; import static org.sonar.server.qualityprofile.QProfileTesting.XOO_P1_NAME; import static org.sonar.server.qualityprofile.QProfileTesting.XOO_P2_KEY; import static org.sonar.server.qualityprofile.QProfileTesting.XOO_P2_NAME; import static org.sonar.server.qualityprofile.QProfileTesting.XOO_P3_NAME; import static org.sonar.server.qualityprofile.QProfileTesting.newXooP1; import static org.sonar.server.qualityprofile.QProfileTesting.newXooP2; public class QProfileBackuperMediumTest { @ClassRule public static ServerTester tester = new ServerTester().withEsIndexes(); @Rule public ExpectedException thrown = ExpectedException.none(); @Rule public UserSessionRule userSessionRule = UserSessionRule.forServerTester(tester); private DbClient db; private DbSession dbSession; private RuleIndexer ruleIndexer; private ActiveRuleIndexer activeRuleIndexer; private OrganizationDto organization; @Before public void before() { tester.clearDbAndIndexes(); db = tester.get(DbClient.class); dbSession = db.openSession(false); ruleIndexer = tester.get(RuleIndexer.class); activeRuleIndexer = tester.get(ActiveRuleIndexer.class); // create pre-defined rules RuleDto xooRule1 = newXooX1().setSeverity("MINOR").setLanguage("xoo"); db.ruleDao().insert(dbSession, xooRule1.getDefinition()); db.ruleDao().insertRuleParam(dbSession, xooRule1.getDefinition(), RuleParamDto.createFor(xooRule1.getDefinition()) .setName("max").setDefaultValue("10").setType(RuleParamType.INTEGER.type())); dbSession.commit(); ruleIndexer.indexRuleDefinition(xooRule1.getDefinition().getKey()); RuleDto xooRule2 = newXooX2().setSeverity("MAJOR").setLanguage("xoo"); db.ruleDao().insert(dbSession, xooRule2.getDefinition()); dbSession.commit(); ruleIndexer.indexRuleDefinition(xooRule2.getDefinition().getKey()); this.organization = OrganizationTesting.newOrganizationDto(); db.organizationDao().insert(dbSession, organization, false); } @After public void after() { dbSession.close(); } @Test public void backup() throws Exception { RuleKey blahRuleKey = RuleKey.of("blah", "my-rule"); RuleDefinitionDto blahRule = newRule(blahRuleKey).setSeverity("INFO").setLanguage("xoo"); db.ruleDao().insert(dbSession, blahRule); dbSession.commit(); ruleIndexer.indexRuleDefinition(blahRule.getKey()); // create profile P1 with rules x2 and x1 activated QualityProfileDto profile = newXooP1(organization); db.qualityProfileDao().insert(dbSession, profile); RuleActivation activation1 = new RuleActivation(XOO_X2).setSeverity("MINOR"); RuleActivation activation2 = new RuleActivation(XOO_X1); RuleActivation activation3 = new RuleActivation(blahRuleKey); activation2.setSeverity(Severity.BLOCKER); activation2.setParameter("max", "7"); QualityProfileDto profileDto = get(XOO_P1_NAME); tester.get(RuleActivator.class).activate(dbSession, activation1, profileDto); tester.get(RuleActivator.class).activate(dbSession, activation2, profileDto); tester.get(RuleActivator.class).activate(dbSession, activation3, profileDto); dbSession.commit(); dbSession.clearCache(); activeRuleIndexer.index(); StringWriter output = new StringWriter(); tester.get(QProfileBackuper.class).backup(dbSession, profile, output); String expectedXml = Resources.toString(getClass().getResource("QProfileBackuperMediumTest/expected-backup.xml"), StandardCharsets.UTF_8); assertThat(output.toString()).isXmlEqualTo(expectedXml); } @Test public void restore_and_create_profile() throws Exception { // Backup file declares profile P1 on xoo tester.get(QProfileBackuper.class).restore(dbSession, new StringReader( Resources.toString(getClass().getResource("QProfileBackuperMediumTest/restore.xml"), StandardCharsets.UTF_8)), organization, null); // Check in db QualityProfileDto profile = db.qualityProfileDao().selectByNameAndLanguage(organization, "P1", "xoo", dbSession); assertThat(profile).isNotNull(); List<ActiveRuleDto> activeRules = db.activeRuleDao().selectByProfileKey(dbSession, profile.getKey()); assertThat(activeRules).hasSize(1); ActiveRuleDto activeRuleDoc = activeRules.get(0); assertThat(activeRuleDoc.getSeverityString()).isEqualTo("BLOCKER"); assertThat(activeRuleDoc.getInheritance()).isNull(); ActiveRuleDto activeRuleDto = db.activeRuleDao().selectOrFailByKey(dbSession, activeRuleDoc.getKey()); List<ActiveRuleParamDto> params = tester.get(ActiveRuleDao.class).selectParamsByActiveRuleId(dbSession, activeRuleDto.getId()); assertThat(params).hasSize(1); assertThat(params.get(0).getKey()).isEqualTo("max"); assertThat(params.get(0).getValue()).isEqualTo("7"); // Check in es assertThat(tester.get(RuleIndex.class).searchAll(new RuleQuery().setQProfileKey(profile.getKey()).setActivation(true))).containsOnly(XOO_X1); } @Test public void restore_and_update_profile() throws Exception { // create profile P1 with rules x1 and x2 activated db.qualityProfileDao().insert(dbSession, newXooP1(organization)); RuleActivation activation = new RuleActivation(XOO_X1); activation.setSeverity(Severity.INFO); activation.setParameter("max", "10"); QualityProfileDto profileDto = get(XOO_P1_NAME); tester.get(RuleActivator.class).activate(dbSession, activation, profileDto); activation = new RuleActivation(XOO_X2); activation.setSeverity(Severity.INFO); tester.get(RuleActivator.class).activate(dbSession, activation, profileDto); dbSession.commit(); dbSession.clearCache(); activeRuleIndexer.index(); // restore backup, which activates only x1 // -> update x1 and deactivate x2 tester.get(QProfileBackuper.class).restore(dbSession, new StringReader( Resources.toString(getClass().getResource("QProfileBackuperMediumTest/restore.xml"), StandardCharsets.UTF_8)), organization, null); // Check in db List<ActiveRuleDto> activeRules = db.activeRuleDao().selectByProfileKey(dbSession, XOO_P1_KEY); assertThat(activeRules).hasSize(1); ActiveRuleDto activeRuleDoc = activeRules.get(0); assertThat(activeRuleDoc.getSeverityString()).isEqualTo("BLOCKER"); assertThat(activeRuleDoc.getInheritance()).isNull(); ActiveRuleDto activeRuleDto = db.activeRuleDao().selectOrFailByKey(dbSession, activeRuleDoc.getKey()); List<ActiveRuleParamDto> params = tester.get(ActiveRuleDao.class).selectParamsByActiveRuleId(dbSession, activeRuleDto.getId()); assertThat(params).hasSize(1); assertThat(params.get(0).getKey()).isEqualTo("max"); assertThat(params.get(0).getValue()).isEqualTo("7"); // Check in es assertThat(tester.get(RuleIndex.class).searchAll(new RuleQuery().setQProfileKey(XOO_P1_KEY).setActivation(true))).containsOnly(XOO_X1); } @Test public void restore_child_profile() throws Exception { // define two parent/child profiles db.qualityProfileDao().insert(dbSession, newXooP1(organization), newXooP2(organization).setParentKee(XOO_P1_KEY)); dbSession.commit(); // rule x1 is activated on parent profile (so inherited by child profile) RuleActivation activation = new RuleActivation(XOO_X1); activation.setSeverity(Severity.INFO); activation.setParameter("max", "10"); tester.get(RuleActivator.class).activate(dbSession, activation, XOO_P1_KEY); dbSession.commit(); dbSession.clearCache(); activeRuleIndexer.index(); // restore backup of child profile -> overrides x1 tester.get(QProfileBackuper.class).restore(dbSession, new StringReader( Resources.toString(getClass().getResource("QProfileBackuperMediumTest/restore-child.xml"), StandardCharsets.UTF_8)), organization, null); // parent profile is unchanged List<ActiveRuleDto> activeRules = db.activeRuleDao().selectByProfileKey(dbSession, XOO_P1_KEY); assertThat(activeRules).hasSize(1); ActiveRuleDto activeRuleDoc = activeRules.get(0); assertThat(activeRuleDoc.getSeverityString()).isEqualTo("INFO"); assertThat(activeRuleDoc.getInheritance()).isNull(); ActiveRuleDto activeRuleDto = db.activeRuleDao().selectOrFailByKey(dbSession, activeRuleDoc.getKey()); List<ActiveRuleParamDto> params = tester.get(ActiveRuleDao.class).selectParamsByActiveRuleId(dbSession, activeRuleDto.getId()); assertThat(params).hasSize(1); assertThat(params.get(0).getKey()).isEqualTo("max"); assertThat(params.get(0).getValue()).isEqualTo("10"); // child profile overrides parent activeRules = db.activeRuleDao().selectByProfileKey(dbSession, XOO_P2_KEY); assertThat(activeRules).hasSize(1); activeRuleDoc = activeRules.get(0); assertThat(activeRuleDoc.getSeverityString()).isEqualTo("BLOCKER"); assertThat(activeRuleDoc.getInheritance()).isEqualTo(OVERRIDES); activeRuleDto = db.activeRuleDao().selectOrFailByKey(dbSession, activeRuleDoc.getKey()); params = tester.get(ActiveRuleDao.class).selectParamsByActiveRuleId(dbSession, activeRuleDto.getId()); assertThat(params).hasSize(1); assertThat(params.get(0).getKey()).isEqualTo("max"); assertThat(params.get(0).getValue()).isEqualTo("7"); } @Test public void restore_parent_profile() throws Exception { // define two parent/child profiles db.qualityProfileDao().insert(dbSession, newXooP1(organization), newXooP2(organization).setParentKee(XOO_P1_KEY)); dbSession.commit(); // rule x1 is activated on parent profile (so inherited by child profile) RuleActivation activation = new RuleActivation(XOO_X1); activation.setSeverity(Severity.INFO); activation.setParameter("max", "10"); tester.get(RuleActivator.class).activate(dbSession, activation, XOO_P1_KEY); dbSession.commit(); dbSession.clearCache(); activeRuleIndexer.index(); // restore backup of parent profile -> update x1 and propagates to child tester.get(QProfileBackuper.class).restore(dbSession, new StringReader( Resources.toString(getClass().getResource("QProfileBackuperMediumTest/restore-parent.xml"), StandardCharsets.UTF_8)), organization, null); // parent profile is updated List<ActiveRuleDto> activeRules = db.activeRuleDao().selectByProfileKey(dbSession, XOO_P1_KEY); assertThat(activeRules).hasSize(1); ActiveRuleDto activeRuleDoc = activeRules.get(0); assertThat(activeRuleDoc.getSeverityString()).isEqualTo("BLOCKER"); assertThat(activeRuleDoc.getInheritance()).isNull(); ActiveRuleDto activeRuleDto = db.activeRuleDao().selectOrFailByKey(dbSession, activeRuleDoc.getKey()); List<ActiveRuleParamDto> params = tester.get(ActiveRuleDao.class).selectParamsByActiveRuleId(dbSession, activeRuleDto.getId()); assertThat(params).hasSize(1); assertThat(params.get(0).getKey()).isEqualTo("max"); assertThat(params.get(0).getValue()).isEqualTo("7"); // child profile is inherited activeRules = db.activeRuleDao().selectByProfileKey(dbSession, XOO_P2_KEY); assertThat(activeRules).hasSize(1); activeRuleDoc = activeRules.get(0); assertThat(activeRuleDoc.getSeverityString()).isEqualTo("BLOCKER"); assertThat(activeRuleDoc.getInheritance()).isEqualTo(INHERITED); activeRuleDto = db.activeRuleDao().selectOrFailByKey(dbSession, activeRuleDoc.getKey()); params = tester.get(ActiveRuleDao.class).selectParamsByActiveRuleId(dbSession, activeRuleDto.getId()); assertThat(params).hasSize(1); assertThat(params.get(0).getKey()).isEqualTo("max"); assertThat(params.get(0).getValue()).isEqualTo("7"); } @Test public void keep_other_inherited_rules() throws Exception { // define two parent/child profiles db.qualityProfileDao().insert(dbSession, newXooP1(organization), newXooP2(organization).setParentKee(XOO_P1_KEY)); dbSession.commit(); // rule x1 is activated on parent profile and is inherited by child profile RuleActivation activation = new RuleActivation(XOO_X1); activation.setSeverity(Severity.INFO); activation.setParameter("max", "10"); tester.get(RuleActivator.class).activate(dbSession, activation, XOO_P1_KEY); dbSession.commit(); dbSession.clearCache(); activeRuleIndexer.index(); // backup of child profile contains x2 but not x1 tester.get(QProfileBackuper.class).restore(dbSession, new StringReader( Resources.toString(getClass().getResource("QProfileBackuperMediumTest/keep_other_inherited_rules.xml"), StandardCharsets.UTF_8)), organization, XOO_P2_NAME.getName()); // x1 and x2 assertThat(db.activeRuleDao().selectByProfileKey(dbSession, XOO_P2_KEY)).hasSize(2); } @Test public void fail_to_restore_if_not_xml_backup() throws Exception { try { tester.get(QProfileBackuper.class).restore(dbSession, new StringReader( Resources.toString(getClass().getResource("QProfileBackuperMediumTest/not-xml-backup.txt"), StandardCharsets.UTF_8)), organization, null); fail(); } catch (IllegalStateException e) { assertThat(e).hasMessage("Fail to restore Quality profile backup"); assertThat(e.getCause()).isInstanceOf(XMLStreamException.class); } } @Test public void fail_to_restore_if_bad_xml_format() throws Exception { try { tester.get(QProfileBackuper.class).restore(dbSession, new StringReader( Resources.toString(getClass().getResource("QProfileBackuperMediumTest/bad-xml-backup.xml"), StandardCharsets.UTF_8)), organization, null); fail(); } catch (IllegalArgumentException e) { assertThat(e).hasMessage("Backup XML is not valid. Root element must be <profile>."); } } @Test public void fail_to_restore_if_duplicate_rule() throws Exception { try { tester.get(QProfileBackuper.class).restore(dbSession, new StringReader( Resources.toString(getClass().getResource("QProfileBackuperMediumTest/duplicates-xml-backup.xml"), StandardCharsets.UTF_8)), organization, null); fail(); } catch (IllegalArgumentException e) { assertThat(e).hasMessage("The quality profile cannot be restored as it contains duplicates for the following rules: xoo:x1, xoo:x2"); } } @Test public void restore_and_override_profile_name() throws Exception { tester.get(QProfileBackuper.class).restore(dbSession, new StringReader( Resources.toString(getClass().getResource("QProfileBackuperMediumTest/restore.xml"), StandardCharsets.UTF_8)), organization, XOO_P3_NAME.getName()); List<ActiveRuleDto> activeRules = db.activeRuleDao().selectByProfileKey(dbSession, XOO_P1_KEY); assertThat(activeRules).hasSize(0); QualityProfileDto target = db.qualityProfileDao().selectByNameAndLanguage(organization, "P3", "xoo", dbSession); assertThat(target).isNotNull(); assertThat(db.activeRuleDao().selectByProfileKey(dbSession, target.getKey())).hasSize(1); } @Test public void restore_profile_with_zero_rules() throws Exception { tester.get(QProfileBackuper.class).restore(dbSession, new StringReader( Resources.toString(getClass().getResource("QProfileBackuperMediumTest/empty.xml"), StandardCharsets.UTF_8)), organization, null); dbSession.clearCache(); assertThat(anyActiveRuleExists()).isFalse(); List<QualityProfileDto> profiles = db.qualityProfileDao().selectAll(dbSession, organization); assertThat(profiles).hasSize(1); assertThat(profiles.get(0).getName()).isEqualTo("P1"); } private boolean anyActiveRuleExists() throws SQLException { try (PreparedStatement preparedStatement = db.openSession(false).getConnection().prepareStatement("SELECT * FROM active_rules"); ResultSet resultSet = preparedStatement.executeQuery();) { return resultSet.next(); } } private QualityProfileDto get(QProfileName profileName) { return db.qualityProfileDao().selectByNameAndLanguage(organization, profileName.getName(), profileName.getLanguage(), dbSession); } }