diff options
author | Simon Brandhof <simon.brandhof@sonarsource.com> | 2014-06-06 20:09:16 +0200 |
---|---|---|
committer | Simon Brandhof <simon.brandhof@sonarsource.com> | 2014-06-06 20:09:16 +0200 |
commit | 37351e9f46baa1bfe2dd701db4d7764b942d60dc (patch) | |
tree | 74f73ff09fb708af755fb5877eaa30183f663e4f /sonar-server | |
parent | 5df41898c6e29d3db47915777d7fb579f158cab2 (diff) | |
download | sonarqube-37351e9f46baa1bfe2dd701db4d7764b942d60dc.tar.gz sonarqube-37351e9f46baa1bfe2dd701db4d7764b942d60dc.zip |
SONAR-5007 fix deactivation of removed rules
Diffstat (limited to 'sonar-server')
10 files changed, 581 insertions, 316 deletions
diff --git a/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java b/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java index 35a2803784f..a9755d354ca 100644 --- a/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java +++ b/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java @@ -52,7 +52,13 @@ import org.sonar.core.measure.db.MeasureFilterDao; import org.sonar.core.metric.DefaultMetricFinder; import org.sonar.core.notification.DefaultNotificationManager; import org.sonar.core.permission.PermissionFacade; -import org.sonar.core.persistence.*; +import org.sonar.core.persistence.DaoUtils; +import org.sonar.core.persistence.DatabaseVersion; +import org.sonar.core.persistence.DefaultDatabase; +import org.sonar.core.persistence.MyBatis; +import org.sonar.core.persistence.PreviewDatabaseFactory; +import org.sonar.core.persistence.SemaphoreUpdater; +import org.sonar.core.persistence.SemaphoresImpl; import org.sonar.core.preview.PreviewCache; import org.sonar.core.profiling.Profiling; import org.sonar.core.purge.PurgeProfiler; @@ -86,11 +92,33 @@ import org.sonar.server.db.DbClient; import org.sonar.server.db.EmbeddedDatabaseFactory; import org.sonar.server.db.migrations.DatabaseMigrations; import org.sonar.server.db.migrations.DatabaseMigrator; -import org.sonar.server.debt.*; +import org.sonar.server.debt.DebtCharacteristicsXMLImporter; +import org.sonar.server.debt.DebtModelBackup; +import org.sonar.server.debt.DebtModelLookup; +import org.sonar.server.debt.DebtModelOperations; +import org.sonar.server.debt.DebtModelPluginRepository; +import org.sonar.server.debt.DebtModelService; +import org.sonar.server.debt.DebtModelXMLExporter; +import org.sonar.server.debt.DebtRulesXMLImporter; import org.sonar.server.duplication.ws.DuplicationsParser; import org.sonar.server.duplication.ws.DuplicationsWriter; import org.sonar.server.duplication.ws.DuplicationsWs; -import org.sonar.server.issue.*; +import org.sonar.server.issue.ActionService; +import org.sonar.server.issue.AssignAction; +import org.sonar.server.issue.CommentAction; +import org.sonar.server.issue.DefaultIssueFinder; +import org.sonar.server.issue.InternalRubyIssueService; +import org.sonar.server.issue.IssueBulkChangeService; +import org.sonar.server.issue.IssueChangelogFormatter; +import org.sonar.server.issue.IssueChangelogService; +import org.sonar.server.issue.IssueCommentService; +import org.sonar.server.issue.IssueService; +import org.sonar.server.issue.IssueStatsFinder; +import org.sonar.server.issue.PlanAction; +import org.sonar.server.issue.PublicRubyIssueService; +import org.sonar.server.issue.ServerIssueStorage; +import org.sonar.server.issue.SetSeverityAction; +import org.sonar.server.issue.TransitionAction; import org.sonar.server.issue.actionplan.ActionPlanService; import org.sonar.server.issue.actionplan.ActionPlanWs; import org.sonar.server.issue.filter.IssueFilterService; @@ -119,22 +147,83 @@ import org.sonar.server.platform.ws.L10nWs; import org.sonar.server.platform.ws.RestartHandler; import org.sonar.server.platform.ws.ServerWs; import org.sonar.server.platform.ws.SystemWs; -import org.sonar.server.plugins.*; +import org.sonar.server.plugins.BatchWs; +import org.sonar.server.plugins.InstalledPluginReferentialFactory; +import org.sonar.server.plugins.PluginDownloader; +import org.sonar.server.plugins.ServerExtensionInstaller; +import org.sonar.server.plugins.ServerPluginJarInstaller; +import org.sonar.server.plugins.ServerPluginJarsInstaller; +import org.sonar.server.plugins.ServerPluginRepository; +import org.sonar.server.plugins.UpdateCenterClient; +import org.sonar.server.plugins.UpdateCenterMatrixFactory; import org.sonar.server.qualitygate.QgateProjectFinder; import org.sonar.server.qualitygate.QualityGates; import org.sonar.server.qualitygate.RegisterQualityGates; -import org.sonar.server.qualitygate.ws.*; -import org.sonar.server.qualityprofile.*; +import org.sonar.server.qualitygate.ws.QGatesAppAction; +import org.sonar.server.qualitygate.ws.QGatesCopyAction; +import org.sonar.server.qualitygate.ws.QGatesCreateAction; +import org.sonar.server.qualitygate.ws.QGatesCreateConditionAction; +import org.sonar.server.qualitygate.ws.QGatesDeleteConditionAction; +import org.sonar.server.qualitygate.ws.QGatesDeselectAction; +import org.sonar.server.qualitygate.ws.QGatesDestroyAction; +import org.sonar.server.qualitygate.ws.QGatesListAction; +import org.sonar.server.qualitygate.ws.QGatesRenameAction; +import org.sonar.server.qualitygate.ws.QGatesSearchAction; +import org.sonar.server.qualitygate.ws.QGatesSelectAction; +import org.sonar.server.qualitygate.ws.QGatesSetAsDefaultAction; +import org.sonar.server.qualitygate.ws.QGatesShowAction; +import org.sonar.server.qualitygate.ws.QGatesUnsetDefaultAction; +import org.sonar.server.qualitygate.ws.QGatesUpdateConditionAction; +import org.sonar.server.qualitygate.ws.QGatesWs; +import org.sonar.server.qualityprofile.BuiltInProfiles; +import org.sonar.server.qualityprofile.ProfilesManager; +import org.sonar.server.qualityprofile.QProfileBackuper; +import org.sonar.server.qualityprofile.QProfileCopier; +import org.sonar.server.qualityprofile.QProfileLookup; +import org.sonar.server.qualityprofile.QProfileOperations; +import org.sonar.server.qualityprofile.QProfileProjectLookup; +import org.sonar.server.qualityprofile.QProfileProjectOperations; +import org.sonar.server.qualityprofile.QProfileRepositoryExporter; +import org.sonar.server.qualityprofile.QProfileReset; +import org.sonar.server.qualityprofile.QProfileService; +import org.sonar.server.qualityprofile.QProfiles; +import org.sonar.server.qualityprofile.RegisterQualityProfiles; +import org.sonar.server.qualityprofile.RuleActivator; +import org.sonar.server.qualityprofile.RuleActivatorContextFactory; import org.sonar.server.qualityprofile.db.ActiveRuleDao; import org.sonar.server.qualityprofile.index.ActiveRuleIndex; import org.sonar.server.qualityprofile.index.ActiveRuleNormalizer; -import org.sonar.server.qualityprofile.ws.*; -import org.sonar.server.rule.*; +import org.sonar.server.qualityprofile.ws.BulkRuleActivationActions; +import org.sonar.server.qualityprofile.ws.ProfilesWs; +import org.sonar.server.qualityprofile.ws.QProfileRecreateBuiltInAction; +import org.sonar.server.qualityprofile.ws.QProfilesWs; +import org.sonar.server.qualityprofile.ws.RuleActivationActions; +import org.sonar.server.rule.DeprecatedRulesDefinition; +import org.sonar.server.rule.RegisterRules; +import org.sonar.server.rule.RubyRuleService; +import org.sonar.server.rule.RuleCreator; +import org.sonar.server.rule.RuleDefinitionsLoader; +import org.sonar.server.rule.RuleDeleter; +import org.sonar.server.rule.RuleOperations; +import org.sonar.server.rule.RuleRepositories; +import org.sonar.server.rule.RuleService; +import org.sonar.server.rule.RuleUpdater; import org.sonar.server.rule.db.RuleDao; import org.sonar.server.rule.index.RuleIndex; import org.sonar.server.rule.index.RuleNormalizer; -import org.sonar.server.rule.ws.*; -import org.sonar.server.search.*; +import org.sonar.server.rule.ws.ActiveRuleCompleter; +import org.sonar.server.rule.ws.AppAction; +import org.sonar.server.rule.ws.DeleteAction; +import org.sonar.server.rule.ws.RuleMapping; +import org.sonar.server.rule.ws.RulesWebService; +import org.sonar.server.rule.ws.SearchAction; +import org.sonar.server.rule.ws.TagsAction; +import org.sonar.server.rule.ws.UpdateAction; +import org.sonar.server.search.ESNode; +import org.sonar.server.search.IndexClient; +import org.sonar.server.search.IndexQueue; +import org.sonar.server.search.IndexQueueWorker; +import org.sonar.server.search.IndexSynchronizer; import org.sonar.server.source.CodeColorizers; import org.sonar.server.source.DeprecatedSourceDecorator; import org.sonar.server.source.HtmlSourceDecorator; @@ -143,9 +232,27 @@ import org.sonar.server.source.ws.ScmAction; import org.sonar.server.source.ws.ScmWriter; import org.sonar.server.source.ws.ShowAction; import org.sonar.server.source.ws.SourcesWs; -import org.sonar.server.startup.*; +import org.sonar.server.startup.CleanPreviewAnalysisCache; +import org.sonar.server.startup.CopyRequirementsFromCharacteristicsToRules; +import org.sonar.server.startup.GeneratePluginIndex; +import org.sonar.server.startup.GwtPublisher; +import org.sonar.server.startup.JdbcDriverDeployer; +import org.sonar.server.startup.LogServerId; +import org.sonar.server.startup.RegisterDashboards; +import org.sonar.server.startup.RegisterDebtModel; +import org.sonar.server.startup.RegisterMetrics; +import org.sonar.server.startup.RegisterNewMeasureFilters; +import org.sonar.server.startup.RegisterPermissionTemplates; +import org.sonar.server.startup.RegisterServletFilters; +import org.sonar.server.startup.RenameDeprecatedPropertyKeys; +import org.sonar.server.startup.ServerMetadataPersister; import org.sonar.server.test.CoverageService; -import org.sonar.server.test.ws.*; +import org.sonar.server.test.ws.CoverageShowAction; +import org.sonar.server.test.ws.CoverageWs; +import org.sonar.server.test.ws.TestsCoveredFilesAction; +import org.sonar.server.test.ws.TestsShowAction; +import org.sonar.server.test.ws.TestsTestCasesAction; +import org.sonar.server.test.ws.TestsWs; import org.sonar.server.text.MacroInterpreter; import org.sonar.server.text.RubyTextService; import org.sonar.server.ui.JRubyI18n; @@ -153,9 +260,20 @@ import org.sonar.server.ui.JRubyProfiling; import org.sonar.server.ui.PageDecorations; import org.sonar.server.ui.Views; import org.sonar.server.updatecenter.ws.UpdateCenterWs; -import org.sonar.server.user.*; +import org.sonar.server.user.DefaultUserService; +import org.sonar.server.user.DoPrivileged; +import org.sonar.server.user.GroupMembershipFinder; +import org.sonar.server.user.GroupMembershipService; +import org.sonar.server.user.NewUserNotifier; +import org.sonar.server.user.SecurityRealmFactory; import org.sonar.server.user.ws.UsersWs; -import org.sonar.server.util.*; +import org.sonar.server.util.BooleanTypeValidation; +import org.sonar.server.util.FloatTypeValidation; +import org.sonar.server.util.IntegerTypeValidation; +import org.sonar.server.util.StringListTypeValidation; +import org.sonar.server.util.StringTypeValidation; +import org.sonar.server.util.TextTypeValidation; +import org.sonar.server.util.TypeValidations; import org.sonar.server.ws.ListingWs; import org.sonar.server.ws.WebServiceEngine; @@ -319,7 +437,7 @@ class ServerComponents { pico.addSingleton(BulkRuleActivationActions.class); pico.addSingleton(RuleActivator.class); pico.addSingleton(QProfileService.class); - pico.addSingleton(RuleActivationContextFactory.class); + pico.addSingleton(RuleActivatorContextFactory.class); pico.addSingleton(QProfileCopier.class); pico.addSingleton(QProfileBackuper.class); pico.addSingleton(QProfileReset.class); diff --git a/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileService.java b/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileService.java index 20b358c7da2..0d74a2caa81 100644 --- a/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileService.java +++ b/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileService.java @@ -106,7 +106,6 @@ public class QProfileService implements ServerComponent { return ruleActivator.deactivate(key); } - public Multimap<String, String> bulkActivate(RuleQuery ruleQuery, QualityProfileKey profile, @Nullable String severity) { verifyAdminPermission(); return ruleActivator.bulkActivate(ruleQuery, profile, severity); diff --git a/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivator.java b/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivator.java index 394e8fa9fc1..afbebafd5b2 100644 --- a/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivator.java +++ b/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivator.java @@ -34,6 +34,7 @@ import org.sonar.core.qualityprofile.db.ActiveRuleKey; import org.sonar.core.qualityprofile.db.ActiveRuleParamDto; import org.sonar.core.qualityprofile.db.QualityProfileDto; import org.sonar.core.qualityprofile.db.QualityProfileKey; +import org.sonar.core.rule.RuleDto; import org.sonar.core.rule.RuleParamDto; import org.sonar.server.db.DbClient; import org.sonar.server.exceptions.BadRequestException; @@ -66,13 +67,13 @@ public class RuleActivator implements ServerComponent { private final DbClient db; private final TypeValidations typeValidations; - private final RuleActivationContextFactory contextFactory; + private final RuleActivatorContextFactory contextFactory; private final PreviewCache previewCache; private final IndexClient index; private final LogService log; public RuleActivator(DbClient db, IndexClient index, - RuleActivationContextFactory contextFactory, TypeValidations typeValidations, + RuleActivatorContextFactory contextFactory, TypeValidations typeValidations, PreviewCache previewCache, LogService log) { this.db = db; this.index = index; @@ -100,7 +101,8 @@ public class RuleActivator implements ServerComponent { } List<ActiveRuleChange> activate(DbSession dbSession, RuleActivation activation) { - RuleActivationContext context = contextFactory.create(activation.getKey(), dbSession); + RuleActivatorContext context = contextFactory.create(activation.getKey(), dbSession); + context.verifyForActivation(); List<ActiveRuleChange> changes = Lists.newArrayList(); ActiveRuleChange change; if (context.activeRule() == null) { @@ -169,7 +171,7 @@ public class RuleActivator implements ServerComponent { return changes; } - private ActiveRuleDto persist(ActiveRuleChange change, RuleActivationContext context, DbSession dbSession) { + private ActiveRuleDto persist(ActiveRuleChange change, RuleActivatorContext context, DbSession dbSession) { ActiveRuleDao dao = db.activeRuleDao(); ActiveRuleDto activeRule = null; if (change.getType() == ActiveRuleChange.Type.ACTIVATED) { @@ -244,32 +246,55 @@ public class RuleActivator implements ServerComponent { * Deactivate a rule on a Quality profile WITHOUT committing db session and WITHOUT checking permissions */ List<ActiveRuleChange> deactivate(DbSession dbSession, ActiveRuleKey key) { - return cascadeDeactivation(key, dbSession, false); + return deactivate(dbSession, key, false); } - private List<ActiveRuleChange> cascadeDeactivation(ActiveRuleKey key, DbSession dbSession, boolean isCascade) { + public List<ActiveRuleChange> deactivate(RuleDto ruleDto) { + DbSession dbSession = db.openSession(false); + try { + return deactivate(dbSession, ruleDto); + } finally { + dbSession.close(); + } + } + + private List<ActiveRuleChange> deactivate(DbSession dbSession, RuleDto ruleDto) { + List<ActiveRuleChange> changes = Lists.newArrayList(); + List<ActiveRuleDto> activeRules = db.activeRuleDao().findByRule(dbSession, ruleDto); + for (ActiveRuleDto activeRule : activeRules) { + changes.addAll(deactivate(dbSession, activeRule.getKey(), true)); + } + dbSession.commit(); + return changes; + } + + /** + * @param force if true then inherited rules are deactivated + */ + private List<ActiveRuleChange> deactivate(DbSession dbSession, ActiveRuleKey key, boolean force) { + return cascadeDeactivation(key, dbSession, false, force); + } + + private List<ActiveRuleChange> cascadeDeactivation(ActiveRuleKey key, DbSession dbSession, boolean isCascade, boolean force) { List<ActiveRuleChange> changes = Lists.newArrayList(); - RuleActivationContext context = contextFactory.create(key, dbSession); + RuleActivatorContext context = contextFactory.create(key, dbSession); ActiveRuleChange change; if (context.activeRule() == null) { return changes; } - if (!isCascade && (context.activeRule().isInherited() || - context.activeRule().doesOverride())) { + if (!force && !isCascade && context.activeRule().getInheritance() != null) { throw new IllegalStateException("Cannot deactivate inherited rule '" + key.ruleKey() + "'"); } change = new ActiveRuleChange(ActiveRuleChange.Type.DEACTIVATED, key); changes.add(change); persist(change, context, dbSession); - // get all inherited profiles - List<QualityProfileDto> profiles = - db.qualityProfileDao().findByParentKey(dbSession, key.qProfile()); + List<QualityProfileDto> profiles = db.qualityProfileDao().findByParentKey(dbSession, key.qProfile()); for (QualityProfileDto profile : profiles) { ActiveRuleKey activeRuleKey = ActiveRuleKey.of(profile.getKey(), key.ruleKey()); - changes.addAll(cascadeDeactivation(activeRuleKey, dbSession, true)); + changes.addAll(cascadeDeactivation(activeRuleKey, dbSession, true, force)); } if (!changes.isEmpty()) { @@ -370,8 +395,8 @@ public class RuleActivator implements ServerComponent { RuleActivation activation = new RuleActivation(ActiveRuleKey.of(key, parentActiveRule.getKey().ruleKey())); activate(dbSession, activation); } - dbSession.commit(); } + dbSession.commit(); } finally { dbSession.close(); @@ -386,7 +411,12 @@ public class RuleActivator implements ServerComponent { profileDto.setParent(null); db.qualityProfileDao().update(dbSession, profileDto); for (ActiveRuleDto activeRule : db.activeRuleDao().findByProfileKey(dbSession, profileDto.getKey())) { - deactivate(dbSession, activeRule.getKey()); + if (ActiveRuleDto.INHERITED.equals(activeRule.getInheritance())) { + deactivate(dbSession, activeRule.getKey(), true); + } else if (ActiveRuleDto.OVERRIDES.equals(activeRule.getInheritance())) { + activeRule.setInheritance(null); + db.activeRuleDao().update(dbSession, activeRule); + } } } } diff --git a/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivationContext.java b/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivatorContext.java index 25ba2e92a45..f4f279fb1b4 100644 --- a/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivationContext.java +++ b/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivatorContext.java @@ -21,18 +21,22 @@ package org.sonar.server.qualityprofile; import com.google.common.collect.Maps; import org.apache.commons.lang.StringUtils; +import org.sonar.api.rule.RuleStatus; +import org.sonar.check.Cardinality; import org.sonar.core.qualityprofile.db.ActiveRuleDto; import org.sonar.core.qualityprofile.db.ActiveRuleParamDto; import org.sonar.core.qualityprofile.db.QualityProfileDto; import org.sonar.core.rule.RuleDto; import org.sonar.core.rule.RuleParamDto; +import org.sonar.server.exceptions.BadRequestException; +import org.sonar.server.rule.Rule; import javax.annotation.CheckForNull; import javax.annotation.Nullable; import java.util.Collection; import java.util.Map; -class RuleActivationContext { +class RuleActivatorContext { private RuleDto rule; private final Map<String, RuleParamDto> ruleParams = Maps.newHashMap(); @@ -44,7 +48,7 @@ class RuleActivationContext { return rule; } - RuleActivationContext setRule(RuleDto rule) { + RuleActivatorContext setRule(RuleDto rule) { this.rule = rule; return this; } @@ -57,7 +61,7 @@ class RuleActivationContext { return ruleParams.values(); } - RuleActivationContext setRuleParams(Collection<RuleParamDto> ruleParams) { + RuleActivatorContext setRuleParams(Collection<RuleParamDto> ruleParams) { this.ruleParams.clear(); for (RuleParamDto ruleParam : ruleParams) { this.ruleParams.put(ruleParam.getName(), ruleParam); @@ -69,7 +73,7 @@ class RuleActivationContext { return profile; } - RuleActivationContext setProfile(QualityProfileDto profile) { + RuleActivatorContext setProfile(QualityProfileDto profile) { this.profile = profile; return this; } @@ -79,7 +83,7 @@ class RuleActivationContext { return parentProfile; } - RuleActivationContext setParentProfile(@Nullable QualityProfileDto p) { + RuleActivatorContext setParentProfile(@Nullable QualityProfileDto p) { this.parentProfile = p; return this; } @@ -89,12 +93,12 @@ class RuleActivationContext { return activeRule; } - RuleActivationContext setActiveRule(@Nullable ActiveRuleDto a) { + RuleActivatorContext setActiveRule(@Nullable ActiveRuleDto a) { this.activeRule = a; return this; } - RuleActivationContext setParentActiveRule(@Nullable ActiveRuleDto a) { + RuleActivatorContext setParentActiveRule(@Nullable ActiveRuleDto a) { this.parentActiveRule = a; return this; } @@ -117,7 +121,7 @@ class RuleActivationContext { return activeRuleParams.values(); } - RuleActivationContext setActiveRuleParams(@Nullable Collection<ActiveRuleParamDto> a) { + RuleActivatorContext setActiveRuleParams(@Nullable Collection<ActiveRuleParamDto> a) { activeRuleParams.clear(); if (a != null) { for (ActiveRuleParamDto ar : a) { @@ -127,7 +131,7 @@ class RuleActivationContext { return this; } - RuleActivationContext setParentActiveRuleParams(@Nullable Collection<ActiveRuleParamDto> a) { + RuleActivatorContext setParentActiveRuleParams(@Nullable Collection<ActiveRuleParamDto> a) { parentActiveRuleParams.clear(); if (a != null) { for (ActiveRuleParamDto ar : a) { @@ -169,4 +173,20 @@ class RuleActivationContext { } return false; } + + void verifyForActivation() { + if (RuleStatus.REMOVED == rule.getStatus()) { + throw new BadRequestException("Rule was removed: " + rule.getKey()); + } + if (Cardinality.MULTIPLE.equals(rule.getCardinality())) { + throw new BadRequestException("Rule template can't be activated on a Quality profile: " + rule.getKey()); + } + if (Rule.MANUAL_REPOSITORY_KEY.equals(rule.getRepositoryKey())) { + throw new BadRequestException("Manual rule can't be activated on a Quality profile: " + rule.getKey()); + } + if (!profile.getLanguage().equals(rule.getLanguage())) { + throw new BadRequestException(String.format("Rule %s and profile %s have different languages", rule.getKey(), profile.getKey())); + } + + } } diff --git a/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivationContextFactory.java b/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivatorContextFactory.java index 02a32f3e891..57e35e1327c 100644 --- a/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivationContextFactory.java +++ b/sonar-server/src/main/java/org/sonar/server/qualityprofile/RuleActivatorContextFactory.java @@ -21,8 +21,6 @@ package org.sonar.server.qualityprofile; import org.sonar.api.ServerComponent; import org.sonar.api.rule.RuleKey; -import org.sonar.api.rule.RuleStatus; -import org.sonar.check.Cardinality; import org.sonar.core.persistence.DbSession; import org.sonar.core.qualityprofile.db.ActiveRuleDto; import org.sonar.core.qualityprofile.db.ActiveRuleKey; @@ -32,28 +30,24 @@ import org.sonar.core.qualityprofile.db.QualityProfileKey; import org.sonar.core.rule.RuleDto; import org.sonar.server.db.DbClient; import org.sonar.server.exceptions.BadRequestException; -import org.sonar.server.rule.Rule; import java.util.Collection; -public class RuleActivationContextFactory implements ServerComponent { +public class RuleActivatorContextFactory implements ServerComponent { private final DbClient db; - public RuleActivationContextFactory(DbClient db) { + public RuleActivatorContextFactory(DbClient db) { this.db = db; } - public RuleActivationContext create(ActiveRuleKey key, DbSession session) { - RuleActivationContext context = new RuleActivationContext(); + public RuleActivatorContext create(ActiveRuleKey key, DbSession session) { + RuleActivatorContext context = new RuleActivatorContext(); RuleDto rule = initRule(key.ruleKey(), context, session); QualityProfileDto profile = initProfile(key, context, session, false); initActiveRules(key, context, session, false); - if (!profile.getLanguage().equals(rule.getLanguage())) { - throw new BadRequestException(String.format("Rule %s and profile %s have different languages", rule.getKey(), profile.getKey())); - } if (profile.getParent() != null) { ActiveRuleKey parentKey = ActiveRuleKey.of( @@ -64,26 +58,17 @@ public class RuleActivationContextFactory implements ServerComponent { return context; } - private RuleDto initRule(RuleKey ruleKey, RuleActivationContext context, DbSession dbSession) { + private RuleDto initRule(RuleKey ruleKey, RuleActivatorContext context, DbSession dbSession) { RuleDto rule = db.ruleDao().getNullableByKey(dbSession, ruleKey); if (rule == null) { throw new BadRequestException("Rule not found: " + ruleKey); } - if (RuleStatus.REMOVED == rule.getStatus()) { - throw new BadRequestException("Rule was removed: " + ruleKey); - } - if (Cardinality.MULTIPLE.equals(rule.getCardinality())) { - throw new BadRequestException("Rule template can't be activated on a Quality profile: " + ruleKey); - } - if (Rule.MANUAL_REPOSITORY_KEY.equals(rule.getRepositoryKey())) { - throw new BadRequestException("Manual rule can't be activated on a Quality profile: " + ruleKey); - } context.setRule(rule); context.setRuleParams(db.ruleDao().findRuleParamsByRuleKey(dbSession, rule.getKey())); return rule; } - private QualityProfileDto initProfile(ActiveRuleKey key, RuleActivationContext context, DbSession session, boolean parent) { + private QualityProfileDto initProfile(ActiveRuleKey key, RuleActivatorContext context, DbSession session, boolean parent) { QualityProfileDto profile = db.qualityProfileDao().getByKey(session, key.qProfile()); if (profile == null) { throw new BadRequestException("Quality profile not found: " + key.qProfile()); @@ -96,7 +81,7 @@ public class RuleActivationContextFactory implements ServerComponent { return profile; } - private void initActiveRules(ActiveRuleKey key, RuleActivationContext context, DbSession session, boolean parent) { + private void initActiveRules(ActiveRuleKey key, RuleActivatorContext context, DbSession session, boolean parent) { ActiveRuleDto activeRule = db.activeRuleDao().getNullableByKey(session, key); Collection<ActiveRuleParamDto> activeRuleParams = null; if (activeRule != null) { diff --git a/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java b/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java index 1cf13c34b69..d0d69ea2349 100644 --- a/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java +++ b/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java @@ -41,16 +41,19 @@ import org.sonar.core.rule.RuleParamDto; import org.sonar.core.technicaldebt.db.CharacteristicDao; import org.sonar.core.technicaldebt.db.CharacteristicDto; import org.sonar.server.db.DbClient; -import org.sonar.server.qualityprofile.ActiveRule; -import org.sonar.server.qualityprofile.QProfileService; +import org.sonar.server.qualityprofile.RuleActivator; import org.sonar.server.search.IndexDefinition; import org.sonar.server.search.action.EmbeddedIndexAction; import org.sonar.server.search.action.IndexAction; import org.sonar.server.search.action.KeyIndexAction; import javax.annotation.Nullable; - -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import static com.google.common.collect.Lists.newArrayList; @@ -62,22 +65,22 @@ public class RegisterRules implements Startable { private static final Logger LOG = LoggerFactory.getLogger(RegisterRules.class); private final RuleDefinitionsLoader defLoader; - private final QProfileService profileService; + private final RuleActivator ruleActivator; private final DbClient dbClient; private final CharacteristicDao characteristicDao; - public RegisterRules(RuleDefinitionsLoader defLoader, QProfileService profileService, + public RegisterRules(RuleDefinitionsLoader defLoader, RuleActivator ruleActivator, DbClient dbClient) { - this(defLoader, profileService, dbClient, System2.INSTANCE); + this(defLoader, ruleActivator, dbClient, System2.INSTANCE); } @VisibleForTesting - RegisterRules(RuleDefinitionsLoader defLoader, QProfileService profileService, + RegisterRules(RuleDefinitionsLoader defLoader, RuleActivator ruleActivator, DbClient dbClient, System2 system) { this.defLoader = defLoader; - this.profileService = profileService; + this.ruleActivator = ruleActivator; this.dbClient = dbClient; this.characteristicDao = dbClient.debtCharacteristicDao(); } @@ -392,10 +395,7 @@ public class RegisterRules implements Startable { for (RuleDto rule : removedRules) { // SONAR-4642 Remove active rules only when repository still exists if (repositoryKeys.contains(rule.getRepositoryKey())) { - List<ActiveRule> activeRules = profileService.findActiveRulesByRule(rule.getKey()); - for (ActiveRule activeRule : activeRules) { - profileService.deactivate(activeRule.key()); - } + ruleActivator.deactivate(rule); } } } diff --git a/sonar-server/src/test/java/org/sonar/server/qualityprofile/RuleActivatorMediumTest.java b/sonar-server/src/test/java/org/sonar/server/qualityprofile/RuleActivatorMediumTest.java index 2c03d7d7a65..f894d5b42da 100644 --- a/sonar-server/src/test/java/org/sonar/server/qualityprofile/RuleActivatorMediumTest.java +++ b/sonar-server/src/test/java/org/sonar/server/qualityprofile/RuleActivatorMediumTest.java @@ -20,7 +20,6 @@ package org.sonar.server.qualityprofile; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Multimap; import org.junit.After; import org.junit.Before; import org.junit.ClassRule; @@ -49,6 +48,8 @@ import org.sonar.server.search.QueryOptions; import org.sonar.server.tester.ServerTester; import javax.annotation.Nullable; +import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -62,6 +63,7 @@ public class RuleActivatorMediumTest { static final QualityProfileKey XOO_GRAND_CHILD_PROFILE_KEY = QualityProfileKey.of("P3", "xoo"); static final RuleKey MANUAL_RULE_KEY = RuleKey.of(Rule.MANUAL_REPOSITORY_KEY, "m1"); static final RuleKey XOO_RULE_1 = RuleKey.of("xoo", "x1"); + static final RuleKey XOO_RULE_2 = RuleKey.of("xoo", "x2"); @ClassRule public static ServerTester tester = new ServerTester(); @@ -84,10 +86,11 @@ public class RuleActivatorMediumTest { .setSeverity("MAJOR").setLanguage("java"); RuleDto xooRule1 = RuleTesting.newDto(XOO_RULE_1) .setSeverity("MINOR").setLanguage("xoo"); + RuleDto xooRule2 = RuleTesting.newDto(XOO_RULE_2).setLanguage("xoo"); RuleDto xooTemplateRule1 = RuleTesting.newDto(RuleKey.of("xoo", "template1")) .setSeverity("MINOR").setLanguage("xoo").setCardinality(Cardinality.MULTIPLE); RuleDto manualRule = RuleTesting.newDto(MANUAL_RULE_KEY); - db.ruleDao().insert(dbSession, javaRule, xooRule1, xooTemplateRule1, manualRule); + db.ruleDao().insert(dbSession, javaRule, xooRule1, xooRule2, xooTemplateRule1, manualRule); db.ruleDao().addRuleParam(dbSession, xooRule1, RuleParamDto.createFor(xooRule1) .setName("max").setDefaultValue("10").setType(RuleParamType.INTEGER.type())); db.ruleDao().addRuleParam(dbSession, xooRule1, RuleParamDto.createFor(xooRule1) @@ -106,45 +109,53 @@ public class RuleActivatorMediumTest { @Test public void activate() throws Exception { - RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1)); + ActiveRuleKey activeRuleKey = ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1); + RuleActivation activation = new RuleActivation(activeRuleKey); activation.setSeverity(Severity.BLOCKER); activation.setParameter("max", "7"); ruleActivator.activate(activation); - verifyOneActiveRule(XOO_PROFILE_KEY, Severity.BLOCKER, null, ImmutableMap.of("max", "7")); + assertThat(countActiveRules(XOO_PROFILE_KEY)).isEqualTo(1); + verifyHasActiveRule(activeRuleKey, Severity.BLOCKER, null, ImmutableMap.of("max", "7")); } @Test public void activate_with_default_severity_and_parameter() throws Exception { - RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1)); + ActiveRuleKey activeRuleKey = ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1); + RuleActivation activation = new RuleActivation(activeRuleKey); ruleActivator.activate(activation); - verifyOneActiveRule(XOO_PROFILE_KEY, Severity.MINOR, null, ImmutableMap.of("max", "10")); + assertThat(countActiveRules(XOO_PROFILE_KEY)).isEqualTo(1); + verifyHasActiveRule(activeRuleKey, Severity.MINOR, null, ImmutableMap.of("max", "10")); } @Test public void activation_ignores_unsupported_parameters() throws Exception { - RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1)); + ActiveRuleKey activeRuleKey = ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1); + RuleActivation activation = new RuleActivation(activeRuleKey); activation.setParameter("xxx", "yyy"); ruleActivator.activate(activation); - verifyOneActiveRule(XOO_PROFILE_KEY, Severity.MINOR, null, ImmutableMap.of("max", "10")); + assertThat(countActiveRules(XOO_PROFILE_KEY)).isEqualTo(1); + verifyHasActiveRule(activeRuleKey, Severity.MINOR, null, ImmutableMap.of("max", "10")); } @Test public void update_activation_severity_and_parameters() throws Exception { // initial activation - RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1)); + ActiveRuleKey activeRuleKey = ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1); + RuleActivation activation = new RuleActivation(activeRuleKey); activation.setSeverity(Severity.BLOCKER); ruleActivator.activate(activation); // update - RuleActivation update = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1)); + RuleActivation update = new RuleActivation(activeRuleKey); update.setSeverity(Severity.CRITICAL); update.setParameter("max", "42"); ruleActivator.activate(update); - verifyOneActiveRule(XOO_PROFILE_KEY, Severity.CRITICAL, null, ImmutableMap.of("max", "42")); + assertThat(countActiveRules(XOO_PROFILE_KEY)).isEqualTo(1); + verifyHasActiveRule(activeRuleKey, Severity.CRITICAL, null, ImmutableMap.of("max", "42")); } @Test @@ -169,22 +180,25 @@ public class RuleActivatorMediumTest { // contrary to activerule, the param 'max' is supposed to be inserted but not updated ruleActivator.activate(update); - verifyOneActiveRule(XOO_PROFILE_KEY, Severity.CRITICAL, null, ImmutableMap.of("max", "42")); + assertThat(countActiveRules(XOO_PROFILE_KEY)).isEqualTo(1); + verifyHasActiveRule(activeRuleKey, Severity.CRITICAL, null, ImmutableMap.of("max", "42")); } @Test public void revert_activation_to_default_severity_and_parameters() throws Exception { // initial activation - RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1)); + ActiveRuleKey activeRuleKey = ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1); + RuleActivation activation = new RuleActivation(activeRuleKey); activation.setSeverity(Severity.BLOCKER); activation.setParameter("max", "7"); ruleActivator.activate(activation); // update - RuleActivation update = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1)); + RuleActivation update = new RuleActivation(activeRuleKey); ruleActivator.activate(update); - verifyOneActiveRule(XOO_PROFILE_KEY, Severity.MINOR, null, ImmutableMap.of("max", "10")); + assertThat(countActiveRules(XOO_PROFILE_KEY)).isEqualTo(1); + verifyHasActiveRule(activeRuleKey, Severity.MINOR, null, ImmutableMap.of("max", "10")); } @Test @@ -334,6 +348,26 @@ public class RuleActivatorMediumTest { } } + @Test + public void allow_to_deactivate_removed_rule() throws Exception { + // activation + ActiveRuleKey key = ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1); + RuleActivation activation = new RuleActivation(key); + ruleActivator.activate(activation); + + // set rule as removed + RuleDto rule = db.ruleDao().getByKey(dbSession, XOO_RULE_1); + rule.setStatus(RuleStatus.REMOVED); + db.ruleDao().update(dbSession, rule); + dbSession.commit(); + dbSession.clearCache(); + + // deactivation + ruleActivator.deactivate(key); + + verifyZeroActiveRules(XOO_PROFILE_KEY); + } + // INHERITANCE OF PROFILES @Test public void activate_on_child_profile() throws Exception { @@ -346,8 +380,8 @@ public class RuleActivatorMediumTest { ruleActivator.activate(activation); verifyZeroActiveRules(XOO_PROFILE_KEY); - verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, Severity.BLOCKER, null, ImmutableMap.of("max", "7")); - verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.BLOCKER, null, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); } @Test @@ -360,9 +394,9 @@ public class RuleActivatorMediumTest { activation.setParameter("max", "7"); ruleActivator.activate(activation); - verifyOneActiveRule(XOO_PROFILE_KEY, Severity.BLOCKER, null, ImmutableMap.of("max", "7")); - verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); - verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_PROFILE_KEY, XOO_RULE_1, Severity.BLOCKER, null, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); } @Test @@ -374,9 +408,9 @@ public class RuleActivatorMediumTest { activation.setSeverity(Severity.BLOCKER); activation.setParameter("max", "7"); ruleActivator.activate(activation); - verifyOneActiveRule(XOO_PROFILE_KEY, Severity.BLOCKER, null, ImmutableMap.of("max", "7")); - verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); - verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_PROFILE_KEY, XOO_RULE_1, Severity.BLOCKER, null, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); // update on parent activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1)); @@ -384,9 +418,9 @@ public class RuleActivatorMediumTest { activation.setParameter("max", "8"); ruleActivator.activate(activation); dbSession.clearCache(); - verifyOneActiveRule(XOO_PROFILE_KEY, Severity.INFO, null, ImmutableMap.of("max", "8")); - verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, Severity.INFO, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "8")); - verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, Severity.INFO, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "8")); + verifyOneActiveRule(XOO_PROFILE_KEY, XOO_RULE_1, Severity.INFO, null, ImmutableMap.of("max", "8")); + verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.INFO, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "8")); + verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.INFO, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "8")); // update on child -> propagate on grand child only activation = new RuleActivation(ActiveRuleKey.of(XOO_CHILD_PROFILE_KEY, XOO_RULE_1)); @@ -394,9 +428,9 @@ public class RuleActivatorMediumTest { activation.setParameter("max", "9"); ruleActivator.activate(activation); dbSession.clearCache(); - verifyOneActiveRule(XOO_PROFILE_KEY, Severity.INFO, null, ImmutableMap.of("max", "8")); - verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, Severity.MINOR, ActiveRuleDto.OVERRIDES, ImmutableMap.of("max", "9")); - verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, Severity.MINOR, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "9")); + verifyOneActiveRule(XOO_PROFILE_KEY, XOO_RULE_1, Severity.INFO, null, ImmutableMap.of("max", "8")); + verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.MINOR, ActiveRuleDto.OVERRIDES, ImmutableMap.of("max", "9")); + verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.MINOR, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "9")); // update on grand child activation = new RuleActivation(ActiveRuleKey.of(XOO_GRAND_CHILD_PROFILE_KEY, XOO_RULE_1)); @@ -404,9 +438,9 @@ public class RuleActivatorMediumTest { activation.setParameter("max", "10"); ruleActivator.activate(activation); dbSession.clearCache(); - verifyOneActiveRule(XOO_PROFILE_KEY, Severity.INFO, null, ImmutableMap.of("max", "8")); - verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, Severity.MINOR, ActiveRuleDto.OVERRIDES, ImmutableMap.of("max", "9")); - verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, Severity.BLOCKER, ActiveRuleDto.OVERRIDES, ImmutableMap.of("max", "10")); + verifyOneActiveRule(XOO_PROFILE_KEY, XOO_RULE_1, Severity.INFO, null, ImmutableMap.of("max", "8")); + verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.MINOR, ActiveRuleDto.OVERRIDES, ImmutableMap.of("max", "9")); + verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.BLOCKER, ActiveRuleDto.OVERRIDES, ImmutableMap.of("max", "10")); } @Test @@ -418,9 +452,9 @@ public class RuleActivatorMediumTest { activation.setSeverity(Severity.INFO); activation.setParameter("max", "7"); ruleActivator.activate(activation); - verifyOneActiveRule(XOO_PROFILE_KEY, Severity.INFO, null, ImmutableMap.of("max", "7")); - verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, Severity.INFO, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); - verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, Severity.INFO, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_PROFILE_KEY, XOO_RULE_1, Severity.INFO, null, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.INFO, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.INFO, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); // override on child activation = new RuleActivation(ActiveRuleKey.of(XOO_CHILD_PROFILE_KEY, XOO_RULE_1)); @@ -428,9 +462,9 @@ public class RuleActivatorMediumTest { activation.setParameter("max", "8"); ruleActivator.activate(activation); dbSession.clearCache(); - verifyOneActiveRule(XOO_PROFILE_KEY, Severity.INFO, null, ImmutableMap.of("max", "7")); - verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, Severity.BLOCKER, ActiveRuleDto.OVERRIDES, ImmutableMap.of("max", "8")); - verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "8")); + verifyOneActiveRule(XOO_PROFILE_KEY, XOO_RULE_1, Severity.INFO, null, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.BLOCKER, ActiveRuleDto.OVERRIDES, ImmutableMap.of("max", "8")); + verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "8")); // change on parent -> do not propagate on children because they're overriding values activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1)); @@ -438,9 +472,9 @@ public class RuleActivatorMediumTest { activation.setParameter("max", "10"); ruleActivator.activate(activation); dbSession.clearCache(); - verifyOneActiveRule(XOO_PROFILE_KEY, Severity.CRITICAL, null, ImmutableMap.of("max", "10")); - verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, Severity.BLOCKER, ActiveRuleDto.OVERRIDES, ImmutableMap.of("max", "8")); - verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "8")); + verifyOneActiveRule(XOO_PROFILE_KEY, XOO_RULE_1, Severity.CRITICAL, null, ImmutableMap.of("max", "10")); + verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.BLOCKER, ActiveRuleDto.OVERRIDES, ImmutableMap.of("max", "8")); + verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "8")); } @Test @@ -452,8 +486,8 @@ public class RuleActivatorMediumTest { activation.setSeverity(Severity.INFO); activation.setParameter("max", "7"); ruleActivator.activate(activation); - verifyOneActiveRule(XOO_PROFILE_KEY, Severity.INFO, null, ImmutableMap.of("max", "7")); - verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, Severity.INFO, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_PROFILE_KEY, XOO_RULE_1, Severity.INFO, null, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.INFO, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); // override on child with same severity and params -> do nothing (still INHERITED but not OVERRIDDEN) activation = new RuleActivation(ActiveRuleKey.of(XOO_CHILD_PROFILE_KEY, XOO_RULE_1)); @@ -461,8 +495,8 @@ public class RuleActivatorMediumTest { activation.setParameter("max", "7"); ruleActivator.activate(activation); dbSession.clearCache(); - verifyOneActiveRule(XOO_PROFILE_KEY, Severity.INFO, null, ImmutableMap.of("max", "7")); - verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, Severity.INFO, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_PROFILE_KEY, XOO_RULE_1, Severity.INFO, null, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.INFO, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); } @Test @@ -474,9 +508,9 @@ public class RuleActivatorMediumTest { activation.setSeverity(Severity.BLOCKER); activation.setParameter("max", "7"); ruleActivator.activate(activation); - verifyOneActiveRule(XOO_PROFILE_KEY, Severity.BLOCKER, null, ImmutableMap.of("max", "7")); - verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); - verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_PROFILE_KEY, XOO_RULE_1, Severity.BLOCKER, null, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); // deactivate on root ruleActivator.deactivate(activation.getKey()); @@ -495,9 +529,9 @@ public class RuleActivatorMediumTest { activation.setSeverity(Severity.BLOCKER); activation.setParameter("max", "7"); ruleActivator.activate(activation); - verifyOneActiveRule(XOO_PROFILE_KEY, Severity.BLOCKER, null, ImmutableMap.of("max", "7")); - verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); - verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_PROFILE_KEY, XOO_RULE_1, Severity.BLOCKER, null, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); // try to deactivate on child try { @@ -517,9 +551,9 @@ public class RuleActivatorMediumTest { activation.setSeverity(Severity.BLOCKER); activation.setParameter("max", "7"); ruleActivator.activate(activation); - verifyOneActiveRule(XOO_PROFILE_KEY, Severity.BLOCKER, null, ImmutableMap.of("max", "7")); - verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); - verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_PROFILE_KEY, XOO_RULE_1, Severity.BLOCKER, null, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); // override activation = new RuleActivation(ActiveRuleKey.of(XOO_CHILD_PROFILE_KEY, XOO_RULE_1)); @@ -527,17 +561,17 @@ public class RuleActivatorMediumTest { activation.setParameter("max", "10"); ruleActivator.activate(activation); dbSession.clearCache(); - verifyOneActiveRule(XOO_PROFILE_KEY, Severity.BLOCKER, null, ImmutableMap.of("max", "7")); - verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, Severity.INFO, ActiveRuleDto.OVERRIDES, ImmutableMap.of("max", "10")); - verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, Severity.INFO, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "10")); + verifyOneActiveRule(XOO_PROFILE_KEY, XOO_RULE_1, Severity.BLOCKER, null, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.INFO, ActiveRuleDto.OVERRIDES, ImmutableMap.of("max", "10")); + verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.INFO, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "10")); // reset -> remove overridden values activation = new RuleActivation(ActiveRuleKey.of(XOO_CHILD_PROFILE_KEY, XOO_RULE_1)); ruleActivator.activate(activation); dbSession.clearCache(); - verifyOneActiveRule(XOO_PROFILE_KEY, Severity.BLOCKER, null, ImmutableMap.of("max", "7")); - verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); - verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_PROFILE_KEY, XOO_RULE_1, Severity.BLOCKER, null, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); } @Test @@ -549,9 +583,9 @@ public class RuleActivatorMediumTest { activation.setSeverity(Severity.BLOCKER); activation.setParameter("max", "7"); ruleActivator.activate(activation); - verifyOneActiveRule(XOO_PROFILE_KEY, Severity.BLOCKER, null, ImmutableMap.of("max", "7")); - verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); - verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_PROFILE_KEY, XOO_RULE_1, Severity.BLOCKER, null, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); // override on child activation = new RuleActivation(ActiveRuleKey.of(XOO_CHILD_PROFILE_KEY, XOO_RULE_1)); @@ -559,9 +593,9 @@ public class RuleActivatorMediumTest { activation.setParameter("max", "10"); ruleActivator.activate(activation); dbSession.clearCache(); - verifyOneActiveRule(XOO_PROFILE_KEY, Severity.BLOCKER, null, ImmutableMap.of("max", "7")); - verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, Severity.INFO, ActiveRuleDto.OVERRIDES, ImmutableMap.of("max", "10")); - verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, Severity.INFO, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "10")); + verifyOneActiveRule(XOO_PROFILE_KEY, XOO_RULE_1, Severity.BLOCKER, null, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.INFO, ActiveRuleDto.OVERRIDES, ImmutableMap.of("max", "10")); + verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.INFO, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "10")); // override on grand child activation = new RuleActivation(ActiveRuleKey.of(XOO_GRAND_CHILD_PROFILE_KEY, XOO_RULE_1)); @@ -569,43 +603,37 @@ public class RuleActivatorMediumTest { activation.setParameter("max", "20"); ruleActivator.activate(activation); dbSession.clearCache(); - verifyOneActiveRule(XOO_PROFILE_KEY, Severity.BLOCKER, null, ImmutableMap.of("max", "7")); - verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, Severity.INFO, ActiveRuleDto.OVERRIDES, ImmutableMap.of("max", "10")); - verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, Severity.MINOR, ActiveRuleDto.OVERRIDES, ImmutableMap.of("max", "20")); + verifyOneActiveRule(XOO_PROFILE_KEY, XOO_RULE_1, Severity.BLOCKER, null, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.INFO, ActiveRuleDto.OVERRIDES, ImmutableMap.of("max", "10")); + verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.MINOR, ActiveRuleDto.OVERRIDES, ImmutableMap.of("max", "20")); // reset child -> keep the overridden grand-child activation = new RuleActivation(ActiveRuleKey.of(XOO_CHILD_PROFILE_KEY, XOO_RULE_1)); ruleActivator.activate(activation); dbSession.clearCache(); - verifyOneActiveRule(XOO_PROFILE_KEY, Severity.BLOCKER, null, ImmutableMap.of("max", "7")); - verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); - verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, Severity.MINOR, ActiveRuleDto.OVERRIDES, ImmutableMap.of("max", "20")); + verifyOneActiveRule(XOO_PROFILE_KEY, XOO_RULE_1, Severity.BLOCKER, null, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.BLOCKER, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "7")); + verifyOneActiveRule(XOO_GRAND_CHILD_PROFILE_KEY, XOO_RULE_1, Severity.MINOR, ActiveRuleDto.OVERRIDES, ImmutableMap.of("max", "20")); } @Test - public void bulk_activate() { - + public void bulk_activate_lot_of_rules() { // Generate more rules than the search's max limit int bulkSize = QueryOptions.MAX_LIMIT + 10; for (int i = 0; i < bulkSize; i++) { - db.ruleDao().insert(dbSession, RuleDto.createFor( - RuleKey.of("bulk", "r_" + i)) - .setLanguage("xoo") - .setSeverity("BLOCKER") - .setStatus(RuleStatus.READY)); + db.ruleDao().insert(dbSession, RuleTesting.newDto(RuleKey.of("bulk", "r_" + i)).setLanguage("xoo")); } dbSession.commit(); // 0. No active rules so far (base case) and plenty rules available verifyZeroActiveRules(XOO_PROFILE_KEY); assertThat(tester.get(RuleIndex.class) - .search(new RuleQuery().setQueryText("bulk"), new QueryOptions()).getTotal()) + .search(new RuleQuery().setRepositories(Arrays.asList("bulk")), new QueryOptions()).getTotal()) .isEqualTo(bulkSize); - // 1. assert that bulk activate generates all Activation - Multimap<String, String> result = ruleActivator.bulkActivate( - new RuleQuery().setQueryText("bulk"), XOO_PROFILE_KEY, "MINOR"); -// assertThat(result.get(RuleActivator.ACTIVATED)).hasSize(bulkSize); + // 1. bulk activate all the rules + ruleActivator.bulkActivate( + new RuleQuery().setRepositories(Arrays.asList("bulk")), XOO_PROFILE_KEY, "MINOR"); // 2. assert that all activation has been commited to DB and ES dbSession.clearCache(); @@ -614,38 +642,126 @@ public class RuleActivatorMediumTest { } - private void verifyOneActiveRule(QualityProfileKey profileKey, String expectedSeverity, + @Test + public void set_and_unset_parent_profile() { + // x1 is activated on the "future parent" + RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1)); + activation.setSeverity("MAJOR"); + ruleActivator.activate(activation); + verifyOneActiveRule(XOO_PROFILE_KEY, XOO_RULE_1, Severity.MAJOR, null, ImmutableMap.of("max", "10")); + + // create profile with x2 + QualityProfileKey childKey = QualityProfileKey.of("newChild", "xoo"); + db.qualityProfileDao().insert(dbSession, QualityProfileDto.createFor(childKey)); + activation = new RuleActivation(ActiveRuleKey.of(childKey, XOO_RULE_2)); + activation.setSeverity("MAJOR"); + ruleActivator.activate(dbSession, activation); + dbSession.commit(); + dbSession.clearCache(); + + // set parent -> child profile inherits rule x1 and still has x2 + ruleActivator.setParent(childKey, XOO_PROFILE_KEY); + assertThat(db.qualityProfileDao().getByKey(dbSession, childKey).getParentKey()).isEqualTo(XOO_PROFILE_KEY); + verifyHasActiveRule(ActiveRuleKey.of(childKey, XOO_RULE_1), Severity.MAJOR, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "10")); + verifyHasActiveRule(ActiveRuleKey.of(childKey, XOO_RULE_2), Severity.MAJOR, null, Collections.<String, String>emptyMap()); + + // unset parent + dbSession.clearCache(); + ruleActivator.setParent(childKey, null); + assertThat(countActiveRules(childKey)).isEqualTo(1); + assertThat(db.qualityProfileDao().getByKey(dbSession, childKey).getParentKey()).isNull(); + verifyHasActiveRule(ActiveRuleKey.of(childKey, XOO_RULE_2), Severity.MAJOR, null, Collections.<String, String>emptyMap()); + } + + @Test + public void keep_overridden_rules_when_unsetting_parent() { + // x1 is activated on the "future parent" + RuleActivation activation = new RuleActivation(ActiveRuleKey.of(XOO_PROFILE_KEY, XOO_RULE_1)); + activation.setSeverity("MAJOR"); + ruleActivator.activate(activation); + verifyOneActiveRule(XOO_PROFILE_KEY, XOO_RULE_1, Severity.MAJOR, null, ImmutableMap.of("max", "10")); + + // create empty profile + QualityProfileKey childKey = QualityProfileKey.of("newChild", "xoo"); + db.qualityProfileDao().insert(dbSession, QualityProfileDto.createFor(childKey)); + dbSession.commit(); + dbSession.clearCache(); + + // set parent -> child profile inherits rule x1 + ruleActivator.setParent(childKey, XOO_PROFILE_KEY); + verifyOneActiveRule(childKey, XOO_RULE_1, Severity.MAJOR, ActiveRuleDto.INHERITED, ImmutableMap.of("max", "10")); + + // override x1 + activation = new RuleActivation(ActiveRuleKey.of(childKey, XOO_RULE_1)); + activation.setSeverity("BLOCKER").setParameter("max", "333"); + ruleActivator.activate(activation); + dbSession.clearCache(); + verifyOneActiveRule(childKey, XOO_RULE_1, Severity.BLOCKER, ActiveRuleDto.OVERRIDES, ImmutableMap.of("max", "333")); + + // unset parent -> keep x1 + ruleActivator.setParent(childKey, null); + dbSession.clearCache(); + assertThat(db.qualityProfileDao().getByKey(dbSession, childKey).getParentKey()).isNull(); + verifyOneActiveRule(childKey, XOO_RULE_1, Severity.BLOCKER, null, ImmutableMap.of("max", "333")); + } + + private int countActiveRules(QualityProfileKey profileKey) { + List<ActiveRuleDto> activeRuleDtos = db.activeRuleDao().findByProfileKey(dbSession, profileKey); + List<ActiveRule> activeRules = index.findByProfile(profileKey); + assertThat(activeRuleDtos.size()).as("Not same active rules between db and index").isEqualTo(activeRules.size()); + return activeRuleDtos.size(); + } + + private void verifyOneActiveRule(QualityProfileKey profileKey, RuleKey ruleKey, String expectedSeverity, + @Nullable String expectedInheritance, Map<String, String> expectedParams) { + assertThat(countActiveRules(profileKey)).isEqualTo(1); + verifyHasActiveRule(profileKey, ruleKey, expectedSeverity, expectedInheritance, expectedParams); + } + + private void verifyHasActiveRule(QualityProfileKey profileKey, RuleKey ruleKey, String expectedSeverity, @Nullable String expectedInheritance, Map<String, String> expectedParams) { + verifyHasActiveRule(ActiveRuleKey.of(profileKey, ruleKey), expectedSeverity, expectedInheritance, expectedParams); + } + private void verifyHasActiveRule(ActiveRuleKey activeRuleKey, String expectedSeverity, + @Nullable String expectedInheritance, Map<String, String> expectedParams) { // verify db - List<ActiveRuleDto> activeRuleDtos = db.activeRuleDao().findByProfileKey(dbSession, profileKey); - assertThat(activeRuleDtos).hasSize(1); - ActiveRuleDto activeRuleDto = activeRuleDtos.get(0); - assertThat(activeRuleDto.getSeverityString()).isEqualTo(expectedSeverity); - assertThat(activeRuleDto.getInheritance()).isEqualTo(expectedInheritance); - - // verify parameters in db - List<ActiveRuleParamDto> paramDtos = db.activeRuleDao().findParamsByActiveRuleKey(dbSession, activeRuleDto.getKey()); - assertThat(paramDtos).hasSize(expectedParams.size()); - for (Map.Entry<String, String> entry : expectedParams.entrySet()) { - ActiveRuleParamDto paramDto = db.activeRuleDao().getParamByKeyAndName(activeRuleDto.getKey(), entry.getKey(), dbSession); - assertThat(paramDto).isNotNull(); - assertThat(paramDto.getValue()).isEqualTo(entry.getValue()); + boolean found = false; + List<ActiveRuleDto> activeRuleDtos = db.activeRuleDao().findByProfileKey(dbSession, activeRuleKey.qProfile()); + for (ActiveRuleDto activeRuleDto : activeRuleDtos) { + if (activeRuleDto.getKey().equals(activeRuleKey)) { + found = true; + assertThat(activeRuleDto.getSeverityString()).isEqualTo(expectedSeverity); + assertThat(activeRuleDto.getInheritance()).isEqualTo(expectedInheritance); + List<ActiveRuleParamDto> paramDtos = db.activeRuleDao().findParamsByActiveRuleKey(dbSession, activeRuleDto.getKey()); + assertThat(paramDtos).hasSize(expectedParams.size()); + for (Map.Entry<String, String> entry : expectedParams.entrySet()) { + ActiveRuleParamDto paramDto = db.activeRuleDao().getParamByKeyAndName(activeRuleDto.getKey(), entry.getKey(), dbSession); + assertThat(paramDto).isNotNull(); + assertThat(paramDto.getValue()).isEqualTo(entry.getValue()); + } + } } + assertThat(found).as("Rule is not activated in db").isTrue(); // verify es - List<ActiveRule> activeRules = index.findByProfile(profileKey); - assertThat(activeRules).hasSize(1); - ActiveRule activeRule = activeRules.get(0); - assertThat(activeRule.severity()).isEqualTo(expectedSeverity); - assertThat(activeRule.inheritance()).isEqualTo(expectedInheritance == null ? ActiveRule.Inheritance.NONE : ActiveRule.Inheritance.valueOf(expectedInheritance)); - - // verify parameters in es - assertThat(activeRule.params()).hasSize(expectedParams.size()); - for (Map.Entry<String, String> entry : expectedParams.entrySet()) { - String value = activeRule.params().get(entry.getKey()); - assertThat(value).isEqualTo(entry.getValue()); + List<ActiveRule> activeRules = index.findByProfile(activeRuleKey.qProfile()); + found = false; + for (ActiveRule activeRule : activeRules) { + if (activeRule.key().equals(activeRuleKey)) { + found = true; + assertThat(activeRule.severity()).isEqualTo(expectedSeverity); + assertThat(activeRule.inheritance()).isEqualTo(expectedInheritance == null ? ActiveRule.Inheritance.NONE : ActiveRule.Inheritance.valueOf(expectedInheritance)); + + // verify parameters in es + assertThat(activeRule.params()).hasSize(expectedParams.size()); + for (Map.Entry<String, String> entry : expectedParams.entrySet()) { + String value = activeRule.params().get(entry.getKey()); + assertThat(value).isEqualTo(entry.getValue()); + } + } } + assertThat(found).as("Rule is not activated in index").isTrue(); } private void createChildProfiles() { diff --git a/sonar-server/src/test/java/org/sonar/server/qualityprofile/RuleChangeTest.java b/sonar-server/src/test/java/org/sonar/server/qualityprofile/RuleChangeTest.java deleted file mode 100644 index a962470d8c2..00000000000 --- a/sonar-server/src/test/java/org/sonar/server/qualityprofile/RuleChangeTest.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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 org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.sonar.api.rules.ActiveRuleChange; -import org.sonar.api.rules.RulePriority; -import org.sonar.core.preview.PreviewCache; -import org.sonar.jpa.test.AbstractDbUnitTestCase; - -import static org.fest.assertions.Assertions.assertThat; -import static org.mockito.Mockito.mock; - -@Deprecated -/** - * @deprecated Medium tests executed in RegisterRuleMediumTest - */ -@Ignore -public class RuleChangeTest extends AbstractDbUnitTestCase { - private ProfilesManager profilesManager; - - @Before - public void setUp() { - profilesManager = new ProfilesManager(getSession(), mock(PreviewCache.class)); - } - - @Test - public void should_increase_version_if_used() { - setupData("initialData"); - profilesManager.activated(2, 3, "admin"); - checkTables("versionIncreaseIfUsed", "rules_profiles"); - } - - @Test - public void should_track_rule_activation() { - setupData("initialData"); - profilesManager.activated(2, 3, "admin"); - checkTables("ruleActivated", new String[]{"change_date"}, "active_rule_changes"); - } - - @Test - public void should_track_rule_deactivation() { - setupData("initialData"); - profilesManager.deactivated(2, 3, "admin"); - checkTables("ruleDeactivated", new String[]{"change_date"}, "active_rule_changes"); - } - - @Test - public void should_track_rule_param_change() { - setupData("initialData"); - profilesManager.ruleParamChanged(2, 3, "param1", "20", "30", "admin"); - checkTables("ruleParamChanged", new String[]{"change_date"}, "active_rule_changes", "active_rule_param_changes"); - } - - @Test - public void should_not_track_rule_param_change_if_no_change() { - setupData("initialData"); - profilesManager.ruleParamChanged(2, 3, "param1", "20", "20", "admin"); - assertThat(getHQLCount(ActiveRuleChange.class)).isEqualTo(0); - } - - @Test - public void should_track_rule_severity_change() { - setupData("initialData"); - profilesManager.ruleSeverityChanged(2, 3, RulePriority.BLOCKER, RulePriority.CRITICAL, "admin"); - checkTables("ruleSeverityChanged", new String[]{"change_date"}, "active_rule_changes"); - } - - @Test - public void should_not_track_rule_severity_change_if_no_change() { - setupData("initialData"); - profilesManager.ruleSeverityChanged(2, 3, RulePriority.BLOCKER, RulePriority.BLOCKER, "admin"); - assertThat(getHQLCount(ActiveRuleChange.class)).isEqualTo(0); - } - - @Test - public void should_track_change_parent_profile() { - setupData("changeParentProfile"); - profilesManager.profileParentChanged(2, "parent", "admin"); - checkTables("changeParentProfile", new String[]{"change_date"}, "active_rule_changes"); - } - -} diff --git a/sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesMediumTest.java b/sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesMediumTest.java index b5d3347d938..34797df5b82 100644 --- a/sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesMediumTest.java +++ b/sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesMediumTest.java @@ -20,21 +20,31 @@ package org.sonar.server.rule; +import org.junit.After; +import org.junit.Before; import org.junit.Test; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rule.RuleStatus; import org.sonar.api.rule.Severity; import org.sonar.api.server.rule.RuleParamType; import org.sonar.api.server.rule.RulesDefinition; +import org.sonar.core.permission.GlobalPermissions; import org.sonar.core.persistence.DbSession; +import org.sonar.core.qualityprofile.db.ActiveRuleKey; +import org.sonar.core.qualityprofile.db.QualityProfileDto; +import org.sonar.core.qualityprofile.db.QualityProfileKey; import org.sonar.core.rule.RuleDto; import org.sonar.core.rule.RuleParamDto; import org.sonar.server.db.DbClient; import org.sonar.server.platform.Platform; -import org.sonar.server.rule.db.RuleDao; +import org.sonar.server.qualityprofile.QProfileService; +import org.sonar.server.qualityprofile.RuleActivation; import org.sonar.server.rule.index.RuleIndex; import org.sonar.server.rule.index.RuleQuery; import org.sonar.server.rule.index.RuleResult; import org.sonar.server.search.QueryOptions; import org.sonar.server.tester.ServerTester; +import org.sonar.server.user.MockUserSession; import java.util.List; @@ -42,30 +52,55 @@ import static org.fest.assertions.Assertions.assertThat; public class RegisterRulesMediumTest { + // Hack to restart server without x2 + static boolean includeX1 = true, includeX2 = true; + @org.junit.Rule public ServerTester tester = new ServerTester().addComponents(XooRulesDefinition.class); + DbClient db; + DbSession dbSession; + + @Before + public void before() { + db = tester.get(DbClient.class); + dbSession = tester.get(DbClient.class).openSession(false); + } + + @After + public void after() { + dbSession.close(); + includeX1 = true; + includeX2 = true; + } + @Test public void register_rules_at_startup() throws Exception { - verifyRulesInDb(); + verifyTwoRulesInDb(); RuleIndex index = tester.get(RuleIndex.class); - RuleResult searchResult = index.search(new RuleQuery(), new QueryOptions()); assertThat(searchResult.getTotal()).isEqualTo(2); assertThat(searchResult.getHits()).hasSize(2); } + /** + * support the use-case: + * 1. start server + * 2. stop server + * 3. drop elasticsearch index: rm -rf data/es + * 4. start server -> db is up-to-date (no changes) but rules must be re-indexed + */ @Test public void index_even_if_no_changes() throws Exception { RuleIndex index = tester.get(RuleIndex.class); - verifyRulesInDb(); + verifyTwoRulesInDb(); // clear ES but keep db tester.clearIndexes(); - verifyRulesInDb(); + verifyTwoRulesInDb(); RuleResult searchResult = index.search(new RuleQuery(), new QueryOptions()); assertThat(searchResult.getTotal()).isEqualTo(0); assertThat(searchResult.getHits()).hasSize(0); @@ -75,41 +110,105 @@ public class RegisterRulesMediumTest { index = tester.get(RuleIndex.class); - verifyRulesInDb(); + verifyTwoRulesInDb(); searchResult = index.search(new RuleQuery().setKey("xoo:x1"), new QueryOptions()); assertThat(searchResult.getTotal()).isEqualTo(1); assertThat(searchResult.getHits()).hasSize(1); assertThat(searchResult.getHits().get(0).params()).hasSize(1); } - private void verifyRulesInDb() { - RuleDao dao = tester.get(RuleDao.class); - DbClient dbClient = tester.get(DbClient.class); - DbSession session = dbClient.openSession(false); - List<RuleDto> rules = dao.findAll(session); + @Test + public void mark_rule_as_removed() throws Exception { + verifyTwoRulesInDb(); + + includeX2 = false; + tester.get(Platform.class).restart(); + + verifyTwoRulesInDb(); + RuleDto rule = db.ruleDao().getByKey(dbSession, RuleKey.of("xoo", "x2")); + assertThat(rule.getStatus()).isEqualTo(RuleStatus.REMOVED); + } + + @Test + public void deactivate_removed_rules_only_if_repository_still_exists() throws Exception { + MockUserSession.set().setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN).setLogin("me"); + + // create a profile and activate rule + QualityProfileKey profileKey = QualityProfileKey.of("P1", "xoo"); + db.qualityProfileDao().insert(dbSession, QualityProfileDto.createFor(profileKey)); + dbSession.commit(); + dbSession.clearCache(); + ActiveRuleKey activeRuleKey = ActiveRuleKey.of(profileKey, RuleKey.of("xoo", "x1")); + RuleActivation activation = new RuleActivation(activeRuleKey); + tester.get(QProfileService.class).activate(activation); + dbSession.clearCache(); + + // restart, x2 still exists -> deactivate x1 + includeX1 = false; + includeX2 = true; + tester.get(Platform.class).restart(); + dbSession.clearCache(); + assertThat(db.ruleDao().getByKey(dbSession, RuleKey.of("xoo", "x1")).getStatus()).isEqualTo(RuleStatus.REMOVED); + assertThat(db.ruleDao().getByKey(dbSession, RuleKey.of("xoo", "x2")).getStatus()).isEqualTo(RuleStatus.READY); + assertThat(db.activeRuleDao().findByProfileKey(dbSession, profileKey)).hasSize(0); + } + + @Test + public void do_not_deactivate_removed_rules_if_repository_accidentaly_uninstalled() throws Exception { + MockUserSession.set().setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN).setLogin("me"); + + // create a profile and activate rule + QualityProfileKey profileKey = QualityProfileKey.of("P1", "xoo"); + db.qualityProfileDao().insert(dbSession, QualityProfileDto.createFor(profileKey)); + dbSession.commit(); + dbSession.clearCache(); + ActiveRuleKey activeRuleKey = ActiveRuleKey.of(profileKey, RuleKey.of("xoo", "x1")); + RuleActivation activation = new RuleActivation(activeRuleKey); + tester.get(QProfileService.class).activate(activation); + dbSession.clearCache(); + + // restart without x1 and x2 -> keep active rule of x1 + includeX1 = false; + includeX2 = false; + tester.get(Platform.class).restart(); + dbSession.clearCache(); + assertThat(db.ruleDao().getByKey(dbSession, RuleKey.of("xoo", "x1")).getStatus()).isEqualTo(RuleStatus.REMOVED); + assertThat(db.ruleDao().getByKey(dbSession, RuleKey.of("xoo", "x2")).getStatus()).isEqualTo(RuleStatus.REMOVED); + assertThat(db.activeRuleDao().findByProfileKey(dbSession, profileKey)).hasSize(1); + } + + private void verifyTwoRulesInDb() { + List<RuleDto> rules = db.ruleDao().findAll(dbSession); assertThat(rules).hasSize(2); - List<RuleParamDto> ruleParams = dao.findAllRuleParams(session); + List<RuleParamDto> ruleParams = db.ruleDao().findAllRuleParams(dbSession); assertThat(ruleParams).hasSize(1); } public static class XooRulesDefinition implements RulesDefinition { + @Override public void define(Context context) { - NewRepository repository = context.createRepository("xoo", "xoo").setName("Xoo Repo"); - repository.createRule("x1") - .setName("x1 name") - .setHtmlDescription("x1 desc") - .setSeverity(Severity.MINOR) - .createParam("acceptWhitespace") - .setDefaultValue("false") - .setType(RuleParamType.BOOLEAN) - .setDescription("Accept whitespaces on the line"); - - repository.createRule("x2") - .setName("x2 name") - .setHtmlDescription("x2 desc") - .setSeverity(Severity.MAJOR); - repository.done(); + if (includeX1 || includeX2) { + NewRepository repository = context.createRepository("xoo", "xoo").setName("Xoo Repo"); + if (includeX1) { + repository.createRule("x1") + .setName("x1 name") + .setHtmlDescription("x1 desc") + .setSeverity(Severity.MINOR) + .createParam("acceptWhitespace") + .setDefaultValue("false") + .setType(RuleParamType.BOOLEAN) + .setDescription("Accept whitespaces on the line"); + } + + if (includeX2) { + repository.createRule("x2") + .setName("x2 name") + .setHtmlDescription("x2 desc") + .setSeverity(Severity.MAJOR); + } + repository.done(); + } } } } diff --git a/sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesTest.java b/sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesTest.java index f4b55bfcbc0..88ab7080b19 100644 --- a/sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesTest.java +++ b/sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesTest.java @@ -36,7 +36,7 @@ import org.sonar.core.rule.RuleDto; import org.sonar.core.rule.RuleParamDto; import org.sonar.core.technicaldebt.db.CharacteristicDao; import org.sonar.server.db.DbClient; -import org.sonar.server.qualityprofile.QProfileService; +import org.sonar.server.qualityprofile.RuleActivator; import org.sonar.server.qualityprofile.db.ActiveRuleDao; import org.sonar.server.rule.db.RuleDao; @@ -53,7 +53,7 @@ public class RegisterRulesTest extends AbstractDaoTestCase { static final Date DATE2 = DateUtils.parseDateTime("2014-02-01T12:10:03+0100"); static final Date DATE3 = DateUtils.parseDateTime("2014-03-01T12:10:03+0100"); - QProfileService profileService = mock(QProfileService.class); + RuleActivator ruleActivator = mock(RuleActivator.class); System2 system; DbClient dbClient; DbSession dbSession; @@ -200,7 +200,7 @@ public class RegisterRulesTest extends AbstractDaoTestCase { private void execute(RulesDefinition... defs) { RuleDefinitionsLoader loader = new RuleDefinitionsLoader(mock(RuleRepositories.class), defs); - RegisterRules task = new RegisterRules(loader, profileService, dbClient, system); + RegisterRules task = new RegisterRules(loader, ruleActivator, dbClient, system); task.start(); } |