3 * Copyright (C) 2009-2017 SonarSource SA
4 * mailto:info AT sonarsource DOT com
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 package org.sonar.server.qualityprofile;
22 import com.google.common.collect.Multimap;
23 import java.util.Arrays;
24 import java.util.Random;
25 import org.junit.Rule;
26 import org.junit.Test;
27 import org.junit.rules.ExpectedException;
28 import org.mockito.ArgumentCaptor;
29 import org.sonar.api.profiles.RulesProfile;
30 import org.sonar.api.rules.ActiveRule;
31 import org.sonar.api.rules.RulePriority;
32 import org.sonar.api.utils.System2;
33 import org.sonar.api.utils.log.LogTester;
34 import org.sonar.core.util.UuidFactoryFast;
35 import org.sonar.db.DbClient;
36 import org.sonar.db.DbTester;
37 import org.sonar.db.organization.OrganizationDto;
38 import org.sonar.db.qualityprofile.ActiveRuleDto;
39 import org.sonar.db.qualityprofile.QProfileDto;
40 import org.sonar.db.qualityprofile.RulesProfileDto;
41 import org.sonar.db.rule.RuleDefinitionDto;
42 import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
43 import org.sonar.server.rule.index.RuleIndex;
44 import org.sonar.server.tester.UserSessionRule;
45 import org.sonar.server.util.TypeValidations;
47 import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
48 import static org.apache.commons.lang.math.RandomUtils.nextLong;
49 import static org.assertj.core.api.Assertions.assertThat;
50 import static org.assertj.core.api.Assertions.tuple;
51 import static org.mockito.Matchers.any;
52 import static org.mockito.Matchers.anyLong;
53 import static org.mockito.Matchers.eq;
54 import static org.mockito.Mockito.mock;
55 import static org.mockito.Mockito.verify;
56 import static org.mockito.Mockito.verifyZeroInteractions;
57 import static org.mockito.Mockito.when;
58 import static org.sonar.api.rules.Rule.create;
59 import static org.sonar.api.rules.RulePriority.MAJOR;
60 import static org.sonar.db.qualityprofile.QualityProfileTesting.newRuleProfileDto;
61 import static org.sonar.server.language.LanguageTesting.newLanguage;
62 import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.ACTIVATED;
63 import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.DEACTIVATED;
65 public class RegisterQualityProfilesNotificationTest {
67 private static final Random RANDOM = new Random();
69 private System2 system2 = mock(System2.class);
71 public DbTester db = DbTester.create(system2);
73 public UserSessionRule userSessionRule = UserSessionRule.standalone();
75 public ExpectedException expectedException = ExpectedException.none();
77 public BuiltInQProfileRepositoryRule builtInQProfileRepositoryRule = new BuiltInQProfileRepositoryRule();
80 public LogTester logTester = new LogTester();
81 private DbClient dbClient = db.getDbClient();
82 private TypeValidations typeValidations = mock(TypeValidations.class);
83 private ActiveRuleIndexer activeRuleIndexer = mock(ActiveRuleIndexer.class);
84 private BuiltInQProfileInsert builtInQProfileInsert = new BuiltInQProfileInsertImpl(dbClient, system2, UuidFactoryFast.getInstance(), typeValidations, activeRuleIndexer);
85 private RuleActivator ruleActivator = new RuleActivator(system2, dbClient, mock(RuleIndex.class), new RuleActivatorContextFactory(dbClient), typeValidations, activeRuleIndexer,
87 private BuiltInQProfileUpdate builtInQProfileUpdate = new BuiltInQProfileUpdateImpl(dbClient, ruleActivator, activeRuleIndexer);
88 private BuiltInQualityProfilesUpdateListener builtInQualityProfilesNotification = mock(BuiltInQualityProfilesUpdateListener.class);
89 private RegisterQualityProfiles underTest = new RegisterQualityProfiles(builtInQProfileRepositoryRule, dbClient,
90 builtInQProfileInsert, builtInQProfileUpdate, builtInQualityProfilesNotification, system2);
93 public void does_not_send_notification_on_new_profile() {
94 String language = newLanguageKey();
95 builtInQProfileRepositoryRule.add(newLanguage(language), "Sonar way");
96 builtInQProfileRepositoryRule.initialize();
100 verifyZeroInteractions(builtInQualityProfilesNotification);
104 public void does_not_send_notification_when_built_in_profile_is_not_updated() {
105 String language = newLanguageKey();
106 RuleDefinitionDto dbRule = db.rules().insert(r -> r.setLanguage(language));
107 RulesProfileDto dbProfile = insertBuiltInProfile(language);
108 activateRuleInDb(dbProfile, dbRule, MAJOR);
109 addPluginProfile(dbProfile, dbRule);
110 builtInQProfileRepositoryRule.initialize();
114 verifyZeroInteractions(builtInQualityProfilesNotification);
118 public void send_notification_when_built_in_profile_contains_new_rule() {
119 String language = newLanguageKey();
120 RuleDefinitionDto existingRule = db.rules().insert(r -> r.setLanguage(language));
121 RulesProfileDto dbProfile = insertBuiltInProfile(language);
122 activateRuleInDb(dbProfile, existingRule, MAJOR);
123 RuleDefinitionDto newRule = db.rules().insert(r -> r.setLanguage(language));
124 addPluginProfile(dbProfile, existingRule, newRule);
125 builtInQProfileRepositoryRule.initialize();
129 ArgumentCaptor<Multimap> captor = ArgumentCaptor.forClass(Multimap.class);
130 verify(builtInQualityProfilesNotification).onChange(captor.capture(), anyLong(), anyLong());
131 Multimap<QProfileName, ActiveRuleChange> updatedProfiles = captor.<Multimap<QProfileName, ActiveRuleChange>>getValue();
132 assertThat(updatedProfiles.keySet())
133 .extracting(QProfileName::getName, QProfileName::getLanguage)
134 .containsExactlyInAnyOrder(tuple(dbProfile.getName(), dbProfile.getLanguage()));
135 assertThat(updatedProfiles.values())
136 .extracting(value -> value.getActiveRule().getRuleId(), ActiveRuleChange::getType)
137 .containsExactlyInAnyOrder(tuple(newRule.getId(), ACTIVATED));
141 public void send_notification_when_built_in_profile_contains_deactivated_rule() {
142 String language = newLanguageKey();
143 RuleDefinitionDto existingRule = db.rules().insert(r -> r.setLanguage(language));
144 RulesProfileDto dbProfile = insertBuiltInProfile(language);
145 activateRuleInDb(dbProfile, existingRule, MAJOR);
146 addPluginProfile(dbProfile);
147 builtInQProfileRepositoryRule.initialize();
151 ArgumentCaptor<Multimap> captor = ArgumentCaptor.forClass(Multimap.class);
152 verify(builtInQualityProfilesNotification).onChange(captor.capture(), anyLong(), anyLong());
153 Multimap<QProfileName, ActiveRuleChange> updatedProfiles = captor.<Multimap<QProfileName, ActiveRuleChange>>getValue();
154 assertThat(updatedProfiles.keySet())
155 .extracting(QProfileName::getName, QProfileName::getLanguage)
156 .containsExactlyInAnyOrder(tuple(dbProfile.getName(), dbProfile.getLanguage()));
157 assertThat(updatedProfiles.values())
158 .extracting(value -> value.getActiveRule().getRuleId(), ActiveRuleChange::getType)
159 .containsExactlyInAnyOrder(tuple(existingRule.getId(), DEACTIVATED));
163 public void only_send_one_notification_when_several_built_in_profiles_contain_new_rules() {
164 String language = newLanguageKey();
166 RuleDefinitionDto existingRule1 = db.rules().insert(r -> r.setLanguage(language));
167 RuleDefinitionDto newRule1 = db.rules().insert(r -> r.setLanguage(language));
168 RulesProfileDto dbProfile1 = insertBuiltInProfile(language);
169 activateRuleInDb(dbProfile1, existingRule1, MAJOR);
170 addPluginProfile(dbProfile1, existingRule1, newRule1);
172 RuleDefinitionDto existingRule2 = db.rules().insert(r -> r.setLanguage(language));
173 RuleDefinitionDto newRule2 = db.rules().insert(r -> r.setLanguage(language));
174 RulesProfileDto dbProfile2 = insertBuiltInProfile(language);
175 activateRuleInDb(dbProfile2, existingRule2, MAJOR);
176 addPluginProfile(dbProfile2, existingRule2, newRule2);
177 builtInQProfileRepositoryRule.initialize();
181 ArgumentCaptor<Multimap> captor = ArgumentCaptor.forClass(Multimap.class);
182 verify(builtInQualityProfilesNotification).onChange(captor.capture(), anyLong(), anyLong());
183 Multimap<QProfileName, ActiveRuleChange> updatedProfiles = captor.<Multimap<QProfileName, ActiveRuleChange>>getValue();
184 assertThat(updatedProfiles.keySet())
185 .extracting(QProfileName::getName, QProfileName::getLanguage)
186 .containsExactlyInAnyOrder(
187 tuple(dbProfile1.getName(), dbProfile1.getLanguage()),
188 tuple(dbProfile2.getName(), dbProfile2.getLanguage())
190 assertThat(updatedProfiles.values())
191 .extracting(value -> value.getActiveRule().getRuleId(), ActiveRuleChange::getType)
192 .containsExactlyInAnyOrder(
193 tuple(newRule1.getId(), ACTIVATED),
194 tuple(newRule2.getId(), ACTIVATED)
199 public void do_not_include_inherited_quality_profile_changes() {
200 String language = newLanguageKey();
202 OrganizationDto organization = db.organizations().insert();
203 QProfileDto builtInQProfileDto = db.qualityProfiles().insert(organization, orgQProfile -> orgQProfile.setIsBuiltIn(true).setLanguage(language));
204 QProfileDto customQProfileDto = db.qualityProfiles().insert(organization, orgQProfile -> orgQProfile.setIsBuiltIn(false).setLanguage(language).setParentKee(builtInQProfileDto.getKee()));
206 RuleDefinitionDto newRule = db.rules().insert(r -> r.setLanguage(language));
208 RulesProfile pluginProfile = RulesProfile.create(builtInQProfileDto.getName(), builtInQProfileDto.getLanguage());
209 pluginProfile.activateRule(create(newRule.getRepositoryKey(), newRule.getRuleKey()), MAJOR);
210 builtInQProfileRepositoryRule.add(newLanguage(builtInQProfileDto.getLanguage()), builtInQProfileDto.getName(), false, pluginProfile.getActiveRules().toArray(new ActiveRule[0]));
211 builtInQProfileRepositoryRule.initialize();
215 ArgumentCaptor<Multimap> captor = ArgumentCaptor.forClass(Multimap.class);
216 verify(builtInQualityProfilesNotification).onChange(captor.capture(), anyLong(), anyLong());
217 Multimap<QProfileName, ActiveRuleChange> updatedProfiles = captor.<Multimap<QProfileName, ActiveRuleChange>>getValue();
218 assertThat(updatedProfiles.keySet())
219 .extracting(QProfileName::getName, QProfileName::getLanguage)
220 .containsExactlyInAnyOrder(tuple(builtInQProfileDto.getName(), builtInQProfileDto.getLanguage()));
221 assertThat(updatedProfiles.values())
222 .extracting(value -> value.getActiveRule().getRuleId(), ActiveRuleChange::getType)
223 .containsExactlyInAnyOrder(tuple(newRule.getId(), ACTIVATED));
227 public void send_start_and_end_date() {
228 String language = newLanguageKey();
229 RuleDefinitionDto existingRule = db.rules().insert(r -> r.setLanguage(language));
230 RulesProfileDto dbProfile = insertBuiltInProfile(language);
231 activateRuleInDb(dbProfile, existingRule, MAJOR);
232 RuleDefinitionDto newRule = db.rules().insert(r -> r.setLanguage(language));
233 addPluginProfile(dbProfile, existingRule, newRule);
234 builtInQProfileRepositoryRule.initialize();
235 long startDate = RANDOM.nextInt(5000);
236 long endDate = startDate + RANDOM.nextInt(5000);
237 when(system2.now()).thenReturn(startDate, endDate);
241 verify(builtInQualityProfilesNotification).onChange(any(), eq(startDate), eq(endDate));
244 private void addPluginProfile(RulesProfileDto dbProfile, RuleDefinitionDto... dbRules) {
245 RulesProfile pluginProfile = RulesProfile.create(dbProfile.getName(), dbProfile.getLanguage());
246 Arrays.stream(dbRules).forEach(dbRule -> pluginProfile.activateRule(create(dbRule.getRepositoryKey(), dbRule.getRuleKey()), MAJOR));
247 builtInQProfileRepositoryRule.add(newLanguage(dbProfile.getLanguage()), dbProfile.getName(), false, pluginProfile.getActiveRules().toArray(new ActiveRule[0]));
250 private RulesProfileDto insertBuiltInProfile(String language) {
251 RulesProfileDto ruleProfileDto = newRuleProfileDto(rp -> rp.setIsBuiltIn(true).setLanguage(language));
252 db.getDbClient().qualityProfileDao().insert(db.getSession(), ruleProfileDto);
254 return ruleProfileDto;
257 private void activateRuleInDb(RulesProfileDto profile, RuleDefinitionDto rule, RulePriority severity) {
258 ActiveRuleDto dto = new ActiveRuleDto()
259 .setProfileId(profile.getId())
260 .setSeverity(severity.name())
261 .setRuleId(rule.getId())
262 .setCreatedAt(nextLong())
263 .setUpdatedAt(nextLong());
264 db.getDbClient().activeRuleDao().insert(db.getSession(), dto);
268 private static String newLanguageKey() {
269 return randomAlphanumeric(20).toLowerCase();