private IssuesChangesNotificationBuilder.Rule getRuleByRuleKey(RuleKey ruleKey) { | private IssuesChangesNotificationBuilder.Rule getRuleByRuleKey(RuleKey ruleKey) { | ||||
return ruleRepository.findByKey(ruleKey) | return ruleRepository.findByKey(ruleKey) | ||||
.map(t -> new IssuesChangesNotificationBuilder.Rule(ruleKey, t.getName())) | |||||
.map(t -> new IssuesChangesNotificationBuilder.Rule(ruleKey, t.getType(), t.getName())) | |||||
.orElseThrow(() -> new IllegalStateException("Can not find rule " + ruleKey + " in RuleRepository")); | .orElseThrow(() -> new IllegalStateException("Can not find rule " + ruleKey + " in RuleRepository")); | ||||
} | } | ||||
import static org.mockito.Mockito.verifyNoMoreInteractions; | import static org.mockito.Mockito.verifyNoMoreInteractions; | ||||
import static org.mockito.Mockito.verifyZeroInteractions; | import static org.mockito.Mockito.verifyZeroInteractions; | ||||
import static org.mockito.Mockito.when; | import static org.mockito.Mockito.when; | ||||
import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT; | |||||
import static org.sonar.ce.task.projectanalysis.component.Component.Type; | import static org.sonar.ce.task.projectanalysis.component.Component.Type; | ||||
import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder; | import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder; | ||||
import static org.sonar.ce.task.projectanalysis.step.SendIssueNotificationsStep.NOTIF_TYPES; | import static org.sonar.ce.task.projectanalysis.step.SendIssueNotificationsStep.NOTIF_TYPES; | ||||
public DbTester db = DbTester.create(System2.INSTANCE); | public DbTester db = DbTester.create(System2.INSTANCE); | ||||
private final Random random = new Random(); | private final Random random = new Random(); | ||||
private final RuleType[] RULE_TYPES_EXCEPT_HOTSPOTS = Stream.of(RuleType.values()).filter(r -> r != RuleType.SECURITY_HOTSPOT).toArray(RuleType[]::new); | |||||
private final RuleType[] RULE_TYPES_EXCEPT_HOTSPOTS = Stream.of(RuleType.values()).filter(r -> r != SECURITY_HOTSPOT).toArray(RuleType[]::new); | |||||
private final RuleType randomRuleType = RULE_TYPES_EXCEPT_HOTSPOTS[random.nextInt(RULE_TYPES_EXCEPT_HOTSPOTS.length)]; | private final RuleType randomRuleType = RULE_TYPES_EXCEPT_HOTSPOTS[random.nextInt(RULE_TYPES_EXCEPT_HOTSPOTS.length)]; | ||||
@SuppressWarnings("unchecked") | @SuppressWarnings("unchecked") | ||||
private Class<Map<String, UserDto>> assigneeCacheType = (Class<Map<String, UserDto>>) (Object) Map.class; | private Class<Map<String, UserDto>> assigneeCacheType = (Class<Map<String, UserDto>>) (Object) Map.class; | ||||
.isEmpty(); | .isEmpty(); | ||||
Tuple[] expected = stream(users).map(user -> tuple(user.getUuid(), user.getUuid(), user.getId(), user.getLogin())).toArray(Tuple[]::new); | Tuple[] expected = stream(users).map(user -> tuple(user.getUuid(), user.getUuid(), user.getId(), user.getLogin())).toArray(Tuple[]::new); | ||||
assertThat(cache.entrySet()) | assertThat(cache.entrySet()) | ||||
.extracting(t -> t.getKey(), t -> t.getValue().getUuid(), t -> t.getValue().getId(), t -> t.getValue().getLogin()) | |||||
.extracting(Map.Entry::getKey, t -> t.getValue().getUuid(), t -> t.getValue().getId(), t -> t.getValue().getLogin()) | |||||
.containsOnly(expected); | .containsOnly(expected); | ||||
} | } | ||||
} | } | ||||
@Test | @Test | ||||
public void dont_send_issues_change_notification_for_hotspot() { | |||||
public void do_not_send_new_issues_notifications_for_hotspot() { | |||||
UserDto user = db.users().insertUser(); | UserDto user = db.users().insertUser(); | ||||
ComponentDto project = newPrivateProjectDto(newOrganizationDto()).setDbKey(PROJECT.getDbKey()).setLongName(PROJECT.getName()); | ComponentDto project = newPrivateProjectDto(newOrganizationDto()).setDbKey(PROJECT.getDbKey()).setLongName(PROJECT.getName()); | ||||
ComponentDto file = newFileDto(project).setDbKey(FILE.getDbKey()).setLongName(FILE.getName()); | ComponentDto file = newFileDto(project).setDbKey(FILE.getDbKey()).setLongName(FILE.getName()); |
import com.google.common.collect.ListMultimap; | import com.google.common.collect.ListMultimap; | ||||
import com.google.common.collect.SetMultimap; | import com.google.common.collect.SetMultimap; | ||||
import java.util.Collection; | |||||
import java.util.List; | import java.util.List; | ||||
import javax.annotation.CheckForNull; | import javax.annotation.CheckForNull; | ||||
import org.sonar.api.config.EmailSettings; | import org.sonar.api.config.EmailSettings; | ||||
import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.UserChange; | import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.UserChange; | ||||
import static com.google.common.base.Preconditions.checkState; | import static com.google.common.base.Preconditions.checkState; | ||||
import static java.util.function.Function.identity; | |||||
import static org.sonar.api.issue.Issue.STATUS_CLOSED; | import static org.sonar.api.issue.Issue.STATUS_CLOSED; | ||||
import static org.sonar.api.issue.Issue.STATUS_OPEN; | import static org.sonar.api.issue.Issue.STATUS_OPEN; | ||||
import static org.sonar.core.util.stream.MoreCollectors.index; | import static org.sonar.core.util.stream.MoreCollectors.index; | ||||
import static org.sonar.server.issue.notification.RuleGroup.ISSUES; | |||||
import static org.sonar.server.issue.notification.RuleGroup.SECURITY_HOTSPOTS; | |||||
import static org.sonar.server.issue.notification.RuleGroup.resolveGroup; | |||||
/** | /** | ||||
* Creates email message for notification "Changes on my issues". | * Creates email message for notification "Changes on my issues". | ||||
return new EmailMessage() | return new EmailMessage() | ||||
.setFrom(user.getName().orElse(user.getLogin())) | .setFrom(user.getName().orElse(user.getLogin())) | ||||
.setMessageId("changes-on-my-issues") | .setMessageId("changes-on-my-issues") | ||||
.setSubject("A manual update has changed some of your issues") | |||||
.setSubject("A manual update has changed some of your issues/hotspots") | |||||
.setHtmlMessage(buildMultiProjectMessage(notification)); | .setHtmlMessage(buildMultiProjectMessage(notification)); | ||||
} | } | ||||
private String buildMultiProjectMessage(ChangesOnMyIssuesNotification notification) { | private String buildMultiProjectMessage(ChangesOnMyIssuesNotification notification) { | ||||
ListMultimap<RuleGroup, ChangedIssue> issuesAndHotspots = notification.getChangedIssues().values().stream() | |||||
.collect(index(changedIssue -> resolveGroup(changedIssue.getRule().getRuleType()), identity())); | |||||
List<ChangedIssue> issues = issuesAndHotspots.get(ISSUES); | |||||
List<ChangedIssue> hotspots = issuesAndHotspots.get(SECURITY_HOTSPOTS); | |||||
StringBuilder sb = new StringBuilder(); | StringBuilder sb = new StringBuilder(); | ||||
paragraph(sb, s -> s.append("Hi,")); | paragraph(sb, s -> s.append("Hi,")); | ||||
paragraph(sb, s -> { | |||||
SetMultimap<Project, ChangedIssue> changedIssues = notification.getChangedIssues(); | |||||
s.append("A manual change has updated ").append(issuesOrAnIssue(changedIssues)) | |||||
.append(" assigned to you:"); | |||||
}); | |||||
paragraph(sb, s -> s.append("A manual change has updated ").append(RuleGroup.formatIssuesOrHotspots(issues, hotspots)).append(" assigned to you:")); | |||||
addIssuesByProjectThenRule(sb, notification.getChangedIssues()); | |||||
addIssuesAndHotspotsByProjectThenRule(sb, notification.getChangedIssues()); | |||||
addFooter(sb, NOTIFICATION_NAME_I18N_KEY); | addFooter(sb, NOTIFICATION_NAME_I18N_KEY); | ||||
return sb.toString(); | return sb.toString(); | ||||
} | } | ||||
private static String issueOrIssues(Collection<?> collection) { | |||||
if (collection.size() > 1) { | |||||
return "issues"; | |||||
} | |||||
return "issue"; | |||||
} | |||||
private static String issuesOrAnIssue(SetMultimap<Project, ChangedIssue> changedIssues) { | private static String issuesOrAnIssue(SetMultimap<Project, ChangedIssue> changedIssues) { | ||||
if (changedIssues.size() > 1) { | if (changedIssues.size() > 1) { | ||||
return "issues"; | return "issues"; |
import java.util.List; | import java.util.List; | ||||
import java.util.Locale; | import java.util.Locale; | ||||
import java.util.Optional; | import java.util.Optional; | ||||
import java.util.Set; | |||||
import java.util.SortedSet; | import java.util.SortedSet; | ||||
import java.util.function.BiConsumer; | import java.util.function.BiConsumer; | ||||
import java.util.function.Consumer; | import java.util.function.Consumer; | ||||
import javax.annotation.Nullable; | |||||
import org.sonar.api.config.EmailSettings; | import org.sonar.api.config.EmailSettings; | ||||
import org.sonar.api.rules.RuleType; | |||||
import org.sonar.core.i18n.I18n; | import org.sonar.core.i18n.I18n; | ||||
import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.ChangedIssue; | import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.ChangedIssue; | ||||
import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.Project; | import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.Project; | ||||
import static java.net.URLEncoder.encode; | import static java.net.URLEncoder.encode; | ||||
import static java.nio.charset.StandardCharsets.UTF_8; | import static java.nio.charset.StandardCharsets.UTF_8; | ||||
import static java.util.function.Function.identity; | |||||
import static org.sonar.core.util.stream.MoreCollectors.index; | import static org.sonar.core.util.stream.MoreCollectors.index; | ||||
import static org.sonar.server.issue.notification.RuleGroup.ISSUES; | |||||
import static org.sonar.server.issue.notification.RuleGroup.SECURITY_HOTSPOTS; | |||||
import static org.sonar.server.issue.notification.RuleGroup.formatIssueOrHotspot; | |||||
import static org.sonar.server.issue.notification.RuleGroup.formatIssuesOrHotspots; | |||||
import static org.sonar.server.issue.notification.RuleGroup.resolveGroup; | |||||
public abstract class IssueChangesEmailTemplate implements EmailTemplate { | public abstract class IssueChangesEmailTemplate implements EmailTemplate { | ||||
private static final Comparator<Project> PROJECT_COMPARATOR = Comparator.comparing(Project::getProjectName) | private static final Comparator<Project> PROJECT_COMPARATOR = Comparator.comparing(Project::getProjectName) | ||||
.thenComparing(t -> t.getBranchName().orElse("")); | .thenComparing(t -> t.getBranchName().orElse("")); | ||||
private static final Comparator<ChangedIssue> CHANGED_ISSUE_KEY_COMPARATOR = Comparator.comparing(ChangedIssue::getKey, Comparator.naturalOrder()); | private static final Comparator<ChangedIssue> CHANGED_ISSUE_KEY_COMPARATOR = Comparator.comparing(ChangedIssue::getKey, Comparator.naturalOrder()); | ||||
/** | /** | ||||
* Assuming: | * Assuming: | ||||
* <ul> | * <ul> | ||||
}); | }); | ||||
} | } | ||||
void addIssuesAndHotspotsByProjectThenRule(StringBuilder sb, SetMultimap<Project, ChangedIssue> issuesByProject) { | |||||
issuesByProject.keySet().stream() | |||||
.sorted(PROJECT_COMPARATOR) | |||||
.forEach(project -> { | |||||
String encodedProjectParams = toUrlParams(project); | |||||
paragraph(sb, s -> toString(s, project)); | |||||
Set<ChangedIssue> changedIssues = issuesByProject.get(project); | |||||
ListMultimap<RuleGroup, ChangedIssue> issuesAndHotspots = changedIssues.stream() | |||||
.collect(index(changedIssue -> resolveGroup(changedIssue.getRule().getRuleType()), identity())); | |||||
List<ChangedIssue> issues = issuesAndHotspots.get(ISSUES); | |||||
List<ChangedIssue> hotspots = issuesAndHotspots.get(SECURITY_HOTSPOTS); | |||||
boolean hasSecurityHotspots = !hotspots.isEmpty(); | |||||
boolean hasOtherIssues = !issues.isEmpty(); | |||||
if (hasOtherIssues) { | |||||
addIssuesByRule(sb, issues, projectIssuePageHref(encodedProjectParams)); | |||||
} | |||||
if (hasSecurityHotspots && hasOtherIssues) { | |||||
paragraph(sb, stringBuilder -> { | |||||
}); | |||||
} | |||||
if (hasSecurityHotspots) { | |||||
addIssuesByRule(sb, hotspots, securityHotspotPageHref(encodedProjectParams)); | |||||
} | |||||
}); | |||||
} | |||||
void addIssuesByRule(StringBuilder sb, Collection<ChangedIssue> changedIssues, BiConsumer<StringBuilder, Collection<ChangedIssue>> issuePageHref) { | void addIssuesByRule(StringBuilder sb, Collection<ChangedIssue> changedIssues, BiConsumer<StringBuilder, Collection<ChangedIssue>> issuePageHref) { | ||||
ListMultimap<Rule, ChangedIssue> issuesByRule = changedIssues.stream() | ListMultimap<Rule, ChangedIssue> issuesByRule = changedIssues.stream() | ||||
.collect(index(ChangedIssue::getRule, t -> t)); | .collect(index(ChangedIssue::getRule, t -> t)); | ||||
Collection<ChangedIssue> issues = issuesByRule.get(rule); | Collection<ChangedIssue> issues = issuesByRule.get(rule); | ||||
sb.append("<li>").append("Rule ").append(" <em>").append(rule.getName()).append("</em> - "); | sb.append("<li>").append("Rule ").append(" <em>").append(rule.getName()).append("</em> - "); | ||||
appendIssueLinks(sb, issuePageHref, issues); | |||||
appendIssueLinks(sb, issuePageHref, issues, rule.getRuleType()); | |||||
sb.append("</li>"); | sb.append("</li>"); | ||||
} | } | ||||
sb.append("</ul>"); | sb.append("</ul>"); | ||||
} | } | ||||
private static void appendIssueLinks(StringBuilder sb, BiConsumer<StringBuilder, Collection<ChangedIssue>> issuePageHref, Collection<ChangedIssue> issues) { | |||||
private static void appendIssueLinks(StringBuilder sb, BiConsumer<StringBuilder, Collection<ChangedIssue>> issuePageHref, Collection<ChangedIssue> issues, | |||||
@Nullable RuleType ruleType) { | |||||
SortedSet<ChangedIssue> sortedIssues = ImmutableSortedSet.copyOf(CHANGED_ISSUE_KEY_COMPARATOR, issues); | SortedSet<ChangedIssue> sortedIssues = ImmutableSortedSet.copyOf(CHANGED_ISSUE_KEY_COMPARATOR, issues); | ||||
int issueCount = issues.size(); | int issueCount = issues.size(); | ||||
if (issueCount == 1) { | if (issueCount == 1) { | ||||
link(sb, s -> issuePageHref.accept(s, sortedIssues), s -> s.append("See the single issue")); | |||||
link(sb, s -> issuePageHref.accept(s, sortedIssues), s -> s.append("See the single ").append(formatIssueOrHotspot(ruleType))); | |||||
} else if (issueCount <= MAX_ISSUES_BY_LINK) { | } else if (issueCount <= MAX_ISSUES_BY_LINK) { | ||||
link(sb, s -> issuePageHref.accept(s, sortedIssues), s -> s.append("See all ").append(issueCount).append(" issues")); | |||||
link(sb, s -> issuePageHref.accept(s, sortedIssues), s -> s.append("See all ").append(issueCount).append(" ").append(formatIssuesOrHotspots(ruleType))); | |||||
} else { | } else { | ||||
sb.append("See issues"); | |||||
sb.append("See ").append(formatIssuesOrHotspots(ruleType)); | |||||
List<List<ChangedIssue>> issueGroups = Lists.partition(ImmutableList.copyOf(sortedIssues), MAX_ISSUES_BY_LINK); | List<List<ChangedIssue>> issueGroups = Lists.partition(ImmutableList.copyOf(sortedIssues), MAX_ISSUES_BY_LINK); | ||||
Iterator<List<ChangedIssue>> issueGroupsIterator = issueGroups.iterator(); | Iterator<List<ChangedIssue>> issueGroupsIterator = issueGroups.iterator(); | ||||
int[] groupIndex = new int[] {0}; | int[] groupIndex = new int[] {0}; | ||||
}; | }; | ||||
} | } | ||||
BiConsumer<StringBuilder, Collection<ChangedIssue>> securityHotspotPageHref(String projectParams) { | |||||
return (s, issues) -> { | |||||
s.append(settings.getServerBaseURL()).append("/security_hotspots?").append(projectParams) | |||||
.append("&hotspots="); | |||||
Iterator<ChangedIssue> issueIterator = issues.iterator(); | |||||
while (issueIterator.hasNext()) { | |||||
s.append(urlEncode(issueIterator.next().getKey())); | |||||
if (issueIterator.hasNext()) { | |||||
s.append(URL_ENCODED_COMMA); | |||||
} | |||||
} | |||||
}; | |||||
} | |||||
private static Consumer<StringBuilder> issueGroupLabel(StringBuilder sb, int[] groupIndex, List<ChangedIssue> issueGroup) { | private static Consumer<StringBuilder> issueGroupLabel(StringBuilder sb, int[] groupIndex, List<ChangedIssue> issueGroup) { | ||||
return s -> { | return s -> { | ||||
int firstIssueNumber = (groupIndex[0] * MAX_ISSUES_BY_LINK) + 1; | int firstIssueNumber = (groupIndex[0] * MAX_ISSUES_BY_LINK) + 1; | ||||
} | } | ||||
} | } | ||||
protected static String issueOrIssues(Collection<?> collection) { | |||||
if (collection.size() > 1) { | |||||
return "issues"; | |||||
} | |||||
return "issue"; | |||||
} | |||||
} | } |
import javax.annotation.Nullable; | import javax.annotation.Nullable; | ||||
import javax.annotation.concurrent.Immutable; | import javax.annotation.concurrent.Immutable; | ||||
import org.sonar.api.rule.RuleKey; | import org.sonar.api.rule.RuleKey; | ||||
import org.sonar.api.rules.RuleType; | |||||
import static com.google.common.base.Preconditions.checkArgument; | import static com.google.common.base.Preconditions.checkArgument; | ||||
import static java.util.Objects.requireNonNull; | import static java.util.Objects.requireNonNull; | ||||
@Immutable | @Immutable | ||||
public static final class Rule { | public static final class Rule { | ||||
private final RuleKey key; | private final RuleKey key; | ||||
private final RuleType ruleType; | |||||
private final String name; | private final String name; | ||||
public Rule(RuleKey key, String name) { | |||||
public Rule(RuleKey key, @Nullable String ruleType, String name) { | |||||
this(key, ruleType != null ? RuleType.valueOf(ruleType) : null, name); | |||||
} | |||||
public Rule(RuleKey key, @Nullable RuleType ruleType, String name) { | |||||
this.key = requireNonNull(key, KEY_CANT_BE_NULL_MESSAGE); | this.key = requireNonNull(key, KEY_CANT_BE_NULL_MESSAGE); | ||||
this.ruleType = ruleType; | |||||
this.name = requireNonNull(name, "name can't be null"); | this.name = requireNonNull(name, "name can't be null"); | ||||
} | } | ||||
return key; | return key; | ||||
} | } | ||||
@CheckForNull | |||||
public RuleType getRuleType() { | |||||
return ruleType; | |||||
} | |||||
public String getName() { | public String getName() { | ||||
return name; | return name; | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
Rule that = (Rule) o; | Rule that = (Rule) o; | ||||
return key.equals(that.key) && name.equals(that.name); | |||||
return key.equals(that.key) && ruleType.equals(that.ruleType) && name.equals(that.name); | |||||
} | } | ||||
@Override | @Override | ||||
public int hashCode() { | public int hashCode() { | ||||
return Objects.hash(key, name); | |||||
return Objects.hash(key, ruleType, name); | |||||
} | } | ||||
@Override | @Override | ||||
public String toString() { | public String toString() { | ||||
return "Rule{" + | return "Rule{" + | ||||
"key=" + key + | "key=" + key + | ||||
", type=" + ruleType + | |||||
", name='" + name + '\'' + | ", name='" + name + '\'' + | ||||
'}'; | '}'; | ||||
} | } |
import static com.google.common.base.Preconditions.checkArgument; | import static com.google.common.base.Preconditions.checkArgument; | ||||
import static com.google.common.base.Preconditions.checkState; | import static com.google.common.base.Preconditions.checkState; | ||||
import static java.util.Optional.ofNullable; | |||||
import static org.sonar.core.util.stream.MoreCollectors.toSet; | import static org.sonar.core.util.stream.MoreCollectors.toSet; | ||||
import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex; | import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex; | ||||
private static final String FIELD_CHANGE_AUTHOR_UUID = "change.author.uuid"; | private static final String FIELD_CHANGE_AUTHOR_UUID = "change.author.uuid"; | ||||
private static final String FIELD_CHANGE_AUTHOR_LOGIN = "change.author.login"; | private static final String FIELD_CHANGE_AUTHOR_LOGIN = "change.author.login"; | ||||
private static final String FIELD_CHANGE_AUTHOR_NAME = "change.author.name"; | private static final String FIELD_CHANGE_AUTHOR_NAME = "change.author.name"; | ||||
private static final String FIELD_PREFIX_RULES = "rules."; | |||||
public IssuesChangesNotification serialize(IssuesChangesNotificationBuilder builder) { | public IssuesChangesNotification serialize(IssuesChangesNotificationBuilder builder) { | ||||
IssuesChangesNotification res = new IssuesChangesNotification(); | IssuesChangesNotification res = new IssuesChangesNotification(); | ||||
issues.stream() | issues.stream() | ||||
.map(ChangedIssue::getRule) | .map(ChangedIssue::getRule) | ||||
.collect(Collectors.toSet()) | .collect(Collectors.toSet()) | ||||
.forEach(rule -> res.setFieldValue("rules." + rule.getKey(), rule.getName())); | |||||
.forEach(rule -> { | |||||
res.setFieldValue(FIELD_PREFIX_RULES + rule.getKey(), rule.getName()); | |||||
ofNullable(rule.getRuleType()).ifPresent(ruleType -> res.setFieldValue(FIELD_PREFIX_RULES + rule.getKey() + ".type", rule.getRuleType().name())); | |||||
}); | |||||
} | } | ||||
private static Map<RuleKey, Rule> readRules(IssuesChangesNotification notification, List<Issue> issues) { | private static Map<RuleKey, Rule> readRules(IssuesChangesNotification notification, List<Issue> issues) { | ||||
} | } | ||||
private static Rule readRule(IssuesChangesNotification notification, RuleKey ruleKey) { | private static Rule readRule(IssuesChangesNotification notification, RuleKey ruleKey) { | ||||
String fieldName = "rules." + ruleKey; | |||||
String fieldName = FIELD_PREFIX_RULES + ruleKey; | |||||
String ruleName = notification.getFieldValue(fieldName); | String ruleName = notification.getFieldValue(fieldName); | ||||
String ruleType = notification.getFieldValue(fieldName + ".type"); | |||||
checkState(ruleName != null, "can not find field %s", ruleKey); | checkState(ruleName != null, "can not find field %s", ruleKey); | ||||
return new Rule(ruleKey, ruleName); | |||||
return new Rule(ruleKey, ruleType, ruleName); | |||||
} | } | ||||
private static void serializeProjects(IssuesChangesNotification res, Set<ChangedIssue> issues) { | private static void serializeProjects(IssuesChangesNotification res, Set<ChangedIssue> issues) { |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2020 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.issue.notification; | |||||
import java.util.Collection; | |||||
import javax.annotation.Nullable; | |||||
import org.sonar.api.rules.RuleType; | |||||
import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT; | |||||
enum RuleGroup { | |||||
SECURITY_HOTSPOTS, | |||||
ISSUES; | |||||
static RuleGroup resolveGroup(@Nullable RuleType ruleType) { | |||||
return SECURITY_HOTSPOT.equals(ruleType) ? SECURITY_HOTSPOTS : ISSUES; | |||||
} | |||||
static String formatIssuesOrHotspots(Collection<?> issues, Collection<?> hotspots) { | |||||
if (!issues.isEmpty() && !hotspots.isEmpty()) { | |||||
return "issues/hotspots"; | |||||
} | |||||
if (issues.size() == 1) { | |||||
return "an issue"; | |||||
} | |||||
if (issues.size() > 1) { | |||||
return "issues"; | |||||
} | |||||
if (hotspots.size() == 1) { | |||||
return "a hotspot"; | |||||
} | |||||
return "hotspots"; | |||||
} | |||||
static String formatIssueOrHotspot(@Nullable RuleType ruleType) { | |||||
if (SECURITY_HOTSPOT.equals(ruleType)) { | |||||
return "hotspot"; | |||||
} | |||||
return "issue"; | |||||
} | |||||
static String formatIssuesOrHotspots(@Nullable RuleType ruleType) { | |||||
if (SECURITY_HOTSPOT.equals(ruleType)) { | |||||
return "hotspots"; | |||||
} | |||||
return "issues"; | |||||
} | |||||
} |
import org.junit.runner.RunWith; | import org.junit.runner.RunWith; | ||||
import org.mockito.ArgumentCaptor; | import org.mockito.ArgumentCaptor; | ||||
import org.mockito.Mockito; | import org.mockito.Mockito; | ||||
import org.sonar.api.rule.RuleKey; | |||||
import org.sonar.core.util.stream.MoreCollectors; | import org.sonar.core.util.stream.MoreCollectors; | ||||
import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.AnalysisChange; | import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.AnalysisChange; | ||||
import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.Change; | import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.Change; | ||||
import static org.mockito.Mockito.when; | import static org.mockito.Mockito.when; | ||||
import static org.sonar.core.util.stream.MoreCollectors.index; | import static org.sonar.core.util.stream.MoreCollectors.index; | ||||
import static org.sonar.core.util.stream.MoreCollectors.unorderedIndex; | import static org.sonar.core.util.stream.MoreCollectors.unorderedIndex; | ||||
import static org.sonar.server.issue.notification.IssuesChangesNotificationBuilderTesting.newRandomNotAHotspotRule; | |||||
import static org.sonar.server.notification.NotificationDispatcherMetadata.GLOBAL_NOTIFICATION; | import static org.sonar.server.notification.NotificationDispatcherMetadata.GLOBAL_NOTIFICATION; | ||||
import static org.sonar.server.notification.NotificationDispatcherMetadata.PER_PROJECT_NOTIFICATION; | import static org.sonar.server.notification.NotificationDispatcherMetadata.PER_PROJECT_NOTIFICATION; | ||||
import static org.sonar.server.notification.NotificationManager.SubscriberPermissionsOnProject.ALL_MUST_HAVE_ROLE_USER; | import static org.sonar.server.notification.NotificationManager.SubscriberPermissionsOnProject.ALL_MUST_HAVE_ROLE_USER; | ||||
.collect(toSet()); | .collect(toSet()); | ||||
when(notificationManager.findSubscribedEmailRecipients( | when(notificationManager.findSubscribedEmailRecipients( | ||||
CHANGE_ON_MY_ISSUES_DISPATCHER_KEY, project1.getKey(), ImmutableSet.of(assignee1.getLogin()), ALL_MUST_HAVE_ROLE_USER)) | CHANGE_ON_MY_ISSUES_DISPATCHER_KEY, project1.getKey(), ImmutableSet.of(assignee1.getLogin()), ALL_MUST_HAVE_ROLE_USER)) | ||||
.thenReturn(ImmutableSet.of(emailRecipientOf(assignee1.getLogin()))); | |||||
.thenReturn(ImmutableSet.of(emailRecipientOf(assignee1.getLogin()))); | |||||
when(notificationManager.findSubscribedEmailRecipients( | when(notificationManager.findSubscribedEmailRecipients( | ||||
CHANGE_ON_MY_ISSUES_DISPATCHER_KEY, project2.getKey(), ImmutableSet.of(assignee2.getLogin()), ALL_MUST_HAVE_ROLE_USER)) | CHANGE_ON_MY_ISSUES_DISPATCHER_KEY, project2.getKey(), ImmutableSet.of(assignee2.getLogin()), ALL_MUST_HAVE_ROLE_USER)) | ||||
.thenReturn(ImmutableSet.of(emailRecipientOf(assignee2.getLogin()))); | |||||
.thenReturn(ImmutableSet.of(emailRecipientOf(assignee2.getLogin()))); | |||||
int deliveredCount = new Random().nextInt(100); | int deliveredCount = new Random().nextInt(100); | ||||
when(emailNotificationChannel.deliverAll(anySet())).thenReturn(deliveredCount); | when(emailNotificationChannel.deliverAll(anySet())).thenReturn(deliveredCount); | ||||
} | } | ||||
private static Rule newRule() { | private static Rule newRule() { | ||||
return new Rule(RuleKey.of(randomAlphabetic(3), randomAlphabetic(4)), randomAlphabetic(5)); | |||||
return newRandomNotAHotspotRule(randomAlphabetic(5)); | |||||
} | } | ||||
private static Set<IssuesChangesNotification> randomSetOfNotifications(@Nullable String projectKey, @Nullable String assignee, @Nullable String changeAuthor) { | private static Set<IssuesChangesNotification> randomSetOfNotifications(@Nullable String projectKey, @Nullable String assignee, @Nullable String changeAuthor) { |
import java.util.function.Function; | import java.util.function.Function; | ||||
import java.util.stream.IntStream; | import java.util.stream.IntStream; | ||||
import java.util.stream.Stream; | import java.util.stream.Stream; | ||||
import org.elasticsearch.common.util.set.Sets; | |||||
import org.junit.Test; | import org.junit.Test; | ||||
import org.junit.rules.ExpectedException; | import org.junit.rules.ExpectedException; | ||||
import org.junit.runner.RunWith; | import org.junit.runner.RunWith; | ||||
import org.sonar.api.config.EmailSettings; | import org.sonar.api.config.EmailSettings; | ||||
import org.sonar.api.notifications.Notification; | import org.sonar.api.notifications.Notification; | ||||
import org.sonar.api.rules.RuleType; | |||||
import org.sonar.core.i18n.I18n; | import org.sonar.core.i18n.I18n; | ||||
import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.AnalysisChange; | import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.AnalysisChange; | ||||
import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.Change; | import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.Change; | ||||
import static org.sonar.api.issue.Issue.STATUS_OPEN; | import static org.sonar.api.issue.Issue.STATUS_OPEN; | ||||
import static org.sonar.api.issue.Issue.STATUS_REOPENED; | import static org.sonar.api.issue.Issue.STATUS_REOPENED; | ||||
import static org.sonar.api.issue.Issue.STATUS_RESOLVED; | import static org.sonar.api.issue.Issue.STATUS_RESOLVED; | ||||
import static org.sonar.api.issue.Issue.STATUS_REVIEWED; | |||||
import static org.sonar.api.issue.Issue.STATUS_TO_REVIEW; | |||||
import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT; | |||||
import static org.sonar.server.issue.notification.IssuesChangesNotificationBuilderTesting.newAnalysisChange; | |||||
import static org.sonar.server.issue.notification.IssuesChangesNotificationBuilderTesting.newBranch; | import static org.sonar.server.issue.notification.IssuesChangesNotificationBuilderTesting.newBranch; | ||||
import static org.sonar.server.issue.notification.IssuesChangesNotificationBuilderTesting.newChangedIssue; | import static org.sonar.server.issue.notification.IssuesChangesNotificationBuilderTesting.newChangedIssue; | ||||
import static org.sonar.server.issue.notification.IssuesChangesNotificationBuilderTesting.newProject; | import static org.sonar.server.issue.notification.IssuesChangesNotificationBuilderTesting.newProject; | ||||
import static org.sonar.server.issue.notification.IssuesChangesNotificationBuilderTesting.newRandomNotAHotspotRule; | |||||
import static org.sonar.server.issue.notification.IssuesChangesNotificationBuilderTesting.newRule; | import static org.sonar.server.issue.notification.IssuesChangesNotificationBuilderTesting.newRule; | ||||
import static org.sonar.server.issue.notification.IssuesChangesNotificationBuilderTesting.newSecurityHotspotRule; | |||||
import static org.sonar.server.issue.notification.IssuesChangesNotificationBuilderTesting.newUserChange; | |||||
import static org.sonar.server.issue.notification.IssuesChangesNotificationBuilderTesting.randomRuleTypeHotspotExcluded; | |||||
@RunWith(DataProviderRunner.class) | @RunWith(DataProviderRunner.class) | ||||
public class ChangesOnMyIssuesEmailTemplateTest { | public class ChangesOnMyIssuesEmailTemplateTest { | ||||
private static final String[] ISSUE_STATUSES = {STATUS_OPEN, STATUS_RESOLVED, STATUS_CONFIRMED, STATUS_REOPENED, STATUS_CLOSED}; | private static final String[] ISSUE_STATUSES = {STATUS_OPEN, STATUS_RESOLVED, STATUS_CONFIRMED, STATUS_REOPENED, STATUS_CLOSED}; | ||||
private static final String[] SECURITY_HOTSPOTS_STATUSES = {STATUS_TO_REVIEW, STATUS_REVIEWED}; | |||||
@org.junit.Rule | @org.junit.Rule | ||||
public ExpectedException expectedException = ExpectedException.none(); | public ExpectedException expectedException = ExpectedException.none(); | ||||
@Test | @Test | ||||
public void formats_fails_with_ISE_if_change_from_Analysis_and_no_issue() { | public void formats_fails_with_ISE_if_change_from_Analysis_and_no_issue() { | ||||
AnalysisChange analysisChange = IssuesChangesNotificationBuilderTesting.newAnalysisChange(); | |||||
AnalysisChange analysisChange = newAnalysisChange(); | |||||
expectedException.expect(IllegalStateException.class); | expectedException.expect(IllegalStateException.class); | ||||
expectedException.expectMessage("changedIssues can't be empty"); | expectedException.expectMessage("changedIssues can't be empty"); | ||||
@Test | @Test | ||||
public void format_sets_message_id_with_project_key_of_first_issue_in_set_when_change_from_Analysis() { | public void format_sets_message_id_with_project_key_of_first_issue_in_set_when_change_from_Analysis() { | ||||
Set<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(4)) | Set<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(4)) | ||||
.mapToObj(i -> newChangedIssue(i + "", randomValidStatus(), newProject("prj_" + i), newRule("rule_" + i))) | |||||
.mapToObj(i -> newChangedIssue(i + "", randomValidStatus(), newProject("prj_" + i), newRandomNotAHotspotRule("rule_" + i))) | |||||
.collect(toSet()); | .collect(toSet()); | ||||
AnalysisChange analysisChange = IssuesChangesNotificationBuilderTesting.newAnalysisChange(); | |||||
AnalysisChange analysisChange = newAnalysisChange(); | |||||
EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(analysisChange, changedIssues)); | EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(analysisChange, changedIssues)); | ||||
@Test | @Test | ||||
public void format_sets_subject_with_project_name_of_first_issue_in_set_when_change_from_Analysis() { | public void format_sets_subject_with_project_name_of_first_issue_in_set_when_change_from_Analysis() { | ||||
Set<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(4)) | Set<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(4)) | ||||
.mapToObj(i -> newChangedIssue(i + "", randomValidStatus(), newProject("prj_" + i), newRule("rule_" + i))) | |||||
.mapToObj(i -> newChangedIssue(i + "", randomValidStatus(), newProject("prj_" + i), newRandomNotAHotspotRule("rule_" + i))) | |||||
.collect(toSet()); | .collect(toSet()); | ||||
AnalysisChange analysisChange = IssuesChangesNotificationBuilderTesting.newAnalysisChange(); | AnalysisChange analysisChange = IssuesChangesNotificationBuilderTesting.newAnalysisChange(); | ||||
@Test | @Test | ||||
public void format_sets_subject_with_project_name_and_branch_name_of_first_issue_in_set_when_change_from_Analysis() { | public void format_sets_subject_with_project_name_and_branch_name_of_first_issue_in_set_when_change_from_Analysis() { | ||||
Set<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(4)) | Set<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(4)) | ||||
.mapToObj(i -> newChangedIssue(i + "", randomValidStatus(), newBranch("prj_" + i, "br_" + i), newRule("rule_" + i))) | |||||
.mapToObj(i -> newChangedIssue(i + "", randomValidStatus(), newBranch("prj_" + i, "br_" + i), newRandomNotAHotspotRule("rule_" + i))) | |||||
.collect(toSet()); | .collect(toSet()); | ||||
AnalysisChange analysisChange = IssuesChangesNotificationBuilderTesting.newAnalysisChange(); | |||||
AnalysisChange analysisChange = newAnalysisChange(); | |||||
EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(analysisChange, changedIssues)); | EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(analysisChange, changedIssues)); | ||||
@Test | @Test | ||||
public void format_set_html_message_with_header_dealing_with_plural_when_change_from_Analysis() { | public void format_set_html_message_with_header_dealing_with_plural_when_change_from_Analysis() { | ||||
Set<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(4)) | Set<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(4)) | ||||
.mapToObj(i -> newChangedIssue(i + "", randomValidStatus(), newProject("prj_" + i), newRule("rule_" + i))) | |||||
.mapToObj(i -> newChangedIssue(i + "", randomValidStatus(), newProject("prj_" + i), newRandomNotAHotspotRule("rule_" + i))) | |||||
.collect(toSet()); | .collect(toSet()); | ||||
AnalysisChange analysisChange = IssuesChangesNotificationBuilderTesting.newAnalysisChange(); | |||||
AnalysisChange analysisChange = newAnalysisChange(); | |||||
EmailMessage singleIssueMessage = underTest.format(new ChangesOnMyIssuesNotification(analysisChange, changedIssues.stream().limit(1).collect(toSet()))); | EmailMessage singleIssueMessage = underTest.format(new ChangesOnMyIssuesNotification(analysisChange, changedIssues.stream().limit(1).collect(toSet()))); | ||||
EmailMessage multiIssueMessage = underTest.format(new ChangesOnMyIssuesNotification(analysisChange, changedIssues)); | EmailMessage multiIssueMessage = underTest.format(new ChangesOnMyIssuesNotification(analysisChange, changedIssues)); | ||||
@Test | @Test | ||||
public void format_sets_static_message_id_when_change_from_User() { | public void format_sets_static_message_id_when_change_from_User() { | ||||
Set<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(4)) | Set<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(4)) | ||||
.mapToObj(i -> newChangedIssue(i + "", randomValidStatus(), newProject("prj_" + i), newRule("rule_" + i))) | |||||
.mapToObj(i -> newChangedIssue(i + "", randomValidStatus(), newProject("prj_" + i), newRandomNotAHotspotRule("rule_" + i))) | |||||
.collect(toSet()); | .collect(toSet()); | ||||
UserChange userChange = IssuesChangesNotificationBuilderTesting.newUserChange(); | |||||
UserChange userChange = newUserChange(); | |||||
EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(userChange, changedIssues)); | EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(userChange, changedIssues)); | ||||
@Test | @Test | ||||
public void format_sets_static_subject_when_change_from_User() { | public void format_sets_static_subject_when_change_from_User() { | ||||
Set<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(4)) | Set<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(4)) | ||||
.mapToObj(i -> newChangedIssue(i + "", randomValidStatus(), newProject("prj_" + i), newRule("rule_" + i))) | |||||
.mapToObj(i -> newChangedIssue(i + "", randomValidStatus(), newProject("prj_" + i), newRandomNotAHotspotRule("rule_" + i))) | |||||
.collect(toSet()); | .collect(toSet()); | ||||
UserChange userChange = IssuesChangesNotificationBuilderTesting.newUserChange(); | |||||
UserChange userChange = newUserChange(); | |||||
EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(userChange, changedIssues)); | EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(userChange, changedIssues)); | ||||
assertThat(emailMessage.getSubject()).isEqualTo("A manual update has changed some of your issues"); | |||||
assertThat(emailMessage.getSubject()).isEqualTo("A manual update has changed some of your issues/hotspots"); | |||||
} | } | ||||
@Test | @Test | ||||
public void format_set_html_message_with_header_dealing_with_plural_when_change_from_User() { | |||||
public void format_set_html_message_with_header_dealing_with_plural_issues_when_change_from_User() { | |||||
Set<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(4)) | Set<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(4)) | ||||
.mapToObj(i -> newChangedIssue(i + "", randomValidStatus(), newProject("prj_" + i), newRule("rule_" + i))) | |||||
.mapToObj(i -> newChangedIssue(i + "", randomValidStatus(), newProject("prj_" + i), newRandomNotAHotspotRule("rule_" + i))) | |||||
.collect(toSet()); | .collect(toSet()); | ||||
UserChange userChange = IssuesChangesNotificationBuilderTesting.newUserChange(); | |||||
UserChange userChange = newUserChange(); | |||||
EmailMessage singleIssueMessage = underTest.format(new ChangesOnMyIssuesNotification( | EmailMessage singleIssueMessage = underTest.format(new ChangesOnMyIssuesNotification( | ||||
userChange, changedIssues.stream().limit(1).collect(toSet()))); | userChange, changedIssues.stream().limit(1).collect(toSet()))); | ||||
.withoutLink(); | .withoutLink(); | ||||
} | } | ||||
@Test | |||||
public void format_set_html_message_with_header_dealing_with_plural_security_hotspots_when_change_from_User() { | |||||
Set<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(4)) | |||||
.mapToObj(i -> newChangedIssue(i + "", randomValidStatus(), newProject("prj_" + i), newSecurityHotspotRule("rule_" + i))) | |||||
.collect(toSet()); | |||||
UserChange userChange = newUserChange(); | |||||
EmailMessage singleIssueMessage = underTest.format(new ChangesOnMyIssuesNotification( | |||||
userChange, changedIssues.stream().limit(1).collect(toSet()))); | |||||
EmailMessage multiIssueMessage = underTest.format(new ChangesOnMyIssuesNotification(userChange, changedIssues)); | |||||
HtmlFragmentAssert.assertThat(singleIssueMessage.getMessage()) | |||||
.hasParagraph("Hi,") | |||||
.withoutLink() | |||||
.hasParagraph("A manual change has updated a hotspot assigned to you:") | |||||
.withoutLink(); | |||||
HtmlFragmentAssert.assertThat(multiIssueMessage.getMessage()) | |||||
.hasParagraph("Hi,") | |||||
.withoutLink() | |||||
.hasParagraph("A manual change has updated hotspots assigned to you:") | |||||
.withoutLink(); | |||||
} | |||||
@Test | |||||
public void format_set_html_message_with_header_dealing_with_plural_security_hotspots_and_issues_when_change_from_User() { | |||||
Set<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(4)) | |||||
.mapToObj(i -> newChangedIssue(i + "", randomValidStatus(), newProject("prj_" + i), newRandomNotAHotspotRule("rule_" + i))) | |||||
.collect(toSet()); | |||||
Set<ChangedIssue> changedHotspots = IntStream.range(0, 2 + new Random().nextInt(4)) | |||||
.mapToObj(i -> newChangedIssue(i + "", randomValidStatus(), newProject("prj_" + i), newSecurityHotspotRule("rule_" + i))) | |||||
.collect(toSet()); | |||||
Set<ChangedIssue> issuesAndHotspots = Sets.union(changedIssues, changedHotspots); | |||||
UserChange userChange = newUserChange(); | |||||
EmailMessage multiIssueMessage = underTest.format(new ChangesOnMyIssuesNotification(userChange, issuesAndHotspots)); | |||||
HtmlFragmentAssert.assertThat(multiIssueMessage.getMessage()) | |||||
.hasParagraph("Hi,") | |||||
.withoutLink() | |||||
.hasParagraph("A manual change has updated issues/hotspots assigned to you:") | |||||
.withoutLink(); | |||||
} | |||||
@Test | @Test | ||||
@UseDataProvider("issueStatuses") | @UseDataProvider("issueStatuses") | ||||
public void format_set_html_message_with_footer_when_change_from_user(String issueStatus) { | |||||
UserChange userChange = IssuesChangesNotificationBuilderTesting.newUserChange(); | |||||
public void format_set_html_message_with_footer_when_issue_change_from_user(String issueStatus) { | |||||
UserChange userChange = newUserChange(); | |||||
format_set_html_message_with_footer(userChange, issueStatus, c -> c | format_set_html_message_with_footer(userChange, issueStatus, c -> c | ||||
// skip content | // skip content | ||||
.hasParagraph() // open/closed issue | |||||
.hasList() // rule list | |||||
); | |||||
.hasParagraph() // skip project header | |||||
.hasList(), // rule list, | |||||
randomRuleTypeHotspotExcluded()); | |||||
} | } | ||||
@Test | @Test | ||||
@UseDataProvider("issueStatuses") | @UseDataProvider("issueStatuses") | ||||
public void format_set_html_message_with_footer_when_change_from_analysis(String issueStatus) { | |||||
AnalysisChange analysisChange = IssuesChangesNotificationBuilderTesting.newAnalysisChange(); | |||||
public void format_set_html_message_with_footer_when_issue_change_from_analysis(String issueStatus) { | |||||
AnalysisChange analysisChange = newAnalysisChange(); | |||||
format_set_html_message_with_footer(analysisChange, issueStatus, c -> c | format_set_html_message_with_footer(analysisChange, issueStatus, c -> c | ||||
// skip content | |||||
.hasParagraph() // status | .hasParagraph() // status | ||||
.hasList() // rule list | |||||
); | |||||
.hasList(), // rule list, | |||||
randomRuleTypeHotspotExcluded()); | |||||
} | |||||
@Test | |||||
@UseDataProvider("securityHotspotsStatuses") | |||||
public void format_set_html_message_with_footer_when_security_hotspot_change_from_analysis(String securityHotspotStatus) { | |||||
AnalysisChange analysisChange = newAnalysisChange(); | |||||
format_set_html_message_with_footer(analysisChange, securityHotspotStatus, c -> c | |||||
.hasParagraph() | |||||
.hasList(), // rule list | |||||
SECURITY_HOTSPOT); | |||||
} | |||||
@Test | |||||
@UseDataProvider("securityHotspotsStatuses") | |||||
public void format_set_html_message_with_footer_when_security_hotspot_change_from_user(String securityHotspotStatus) { | |||||
UserChange userChange = newUserChange(); | |||||
format_set_html_message_with_footer(userChange, securityHotspotStatus, c -> c | |||||
.hasParagraph() | |||||
.hasList(), // rule list | |||||
SECURITY_HOTSPOT); | |||||
} | } | ||||
@DataProvider | @DataProvider | ||||
.toArray(Object[][]::new); | .toArray(Object[][]::new); | ||||
} | } | ||||
private void format_set_html_message_with_footer(Change change, String issueStatus, Function<HtmlParagraphAssert, HtmlListAssert> skipContent) { | |||||
@DataProvider | |||||
public static Object[][] securityHotspotsStatuses() { | |||||
return Arrays.stream(SECURITY_HOTSPOTS_STATUSES) | |||||
.map(t -> new Object[] {t}) | |||||
.toArray(Object[][]::new); | |||||
} | |||||
private void format_set_html_message_with_footer(Change change, String issueStatus, Function<HtmlParagraphAssert, HtmlListAssert> skipContent, RuleType ruleType) { | |||||
String wordingNotification = randomAlphabetic(20); | String wordingNotification = randomAlphabetic(20); | ||||
String host = randomAlphabetic(15); | String host = randomAlphabetic(15); | ||||
when(i18n.message(Locale.ENGLISH, "notification.dispatcher.ChangesOnMyIssue", "notification.dispatcher.ChangesOnMyIssue")) | when(i18n.message(Locale.ENGLISH, "notification.dispatcher.ChangesOnMyIssue", "notification.dispatcher.ChangesOnMyIssue")) | ||||
.thenReturn(wordingNotification); | .thenReturn(wordingNotification); | ||||
when(emailSettings.getServerBaseURL()).thenReturn(host); | when(emailSettings.getServerBaseURL()).thenReturn(host); | ||||
Project project = newProject("foo"); | Project project = newProject("foo"); | ||||
Rule rule = newRule("bar"); | |||||
Rule rule = newRule("bar", ruleType); | |||||
Set<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(4)) | Set<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(4)) | ||||
.mapToObj(i -> newChangedIssue(i + "", issueStatus, project, rule)) | .mapToObj(i -> newChangedIssue(i + "", issueStatus, project, rule)) | ||||
.collect(toSet()); | .collect(toSet()); | ||||
@Test | @Test | ||||
public void format_set_html_message_with_issues_grouped_by_status_closed_or_any_other_when_change_from_analysis() { | public void format_set_html_message_with_issues_grouped_by_status_closed_or_any_other_when_change_from_analysis() { | ||||
Project project = newProject("foo"); | Project project = newProject("foo"); | ||||
Rule rule = newRule("bar"); | |||||
Rule rule = newRandomNotAHotspotRule("bar"); | |||||
Set<ChangedIssue> changedIssues = Arrays.stream(ISSUE_STATUSES) | Set<ChangedIssue> changedIssues = Arrays.stream(ISSUE_STATUSES) | ||||
.map(status -> newChangedIssue(status + "", status, project, rule)) | .map(status -> newChangedIssue(status + "", status, project, rule)) | ||||
.collect(toSet()); | .collect(toSet()); | ||||
AnalysisChange analysisChange = IssuesChangesNotificationBuilderTesting.newAnalysisChange(); | |||||
AnalysisChange analysisChange = newAnalysisChange(); | |||||
EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(analysisChange, changedIssues)); | EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(analysisChange, changedIssues)); | ||||
} | } | ||||
@Test | @Test | ||||
public void format_set_html_message_with_status_title_handles_plural_when_change_from_analysis() { | |||||
public void format_set_html_message_with_issue_status_title_handles_plural_when_change_from_analysis() { | |||||
Project project = newProject("foo"); | Project project = newProject("foo"); | ||||
Rule rule = newRule("bar"); | |||||
Rule rule = newRandomNotAHotspotRule("bar"); | |||||
Set<ChangedIssue> closedIssues = IntStream.range(0, 2 + new Random().nextInt(5)) | Set<ChangedIssue> closedIssues = IntStream.range(0, 2 + new Random().nextInt(5)) | ||||
.mapToObj(status -> newChangedIssue(status + "", STATUS_CLOSED, project, rule)) | .mapToObj(status -> newChangedIssue(status + "", STATUS_CLOSED, project, rule)) | ||||
.collect(toSet()); | .collect(toSet()); | ||||
Set<ChangedIssue> openIssues = IntStream.range(0, 2 + new Random().nextInt(5)) | Set<ChangedIssue> openIssues = IntStream.range(0, 2 + new Random().nextInt(5)) | ||||
.mapToObj(status -> newChangedIssue(status + "", STATUS_OPEN, project, rule)) | .mapToObj(status -> newChangedIssue(status + "", STATUS_OPEN, project, rule)) | ||||
.collect(toSet()); | .collect(toSet()); | ||||
AnalysisChange analysisChange = IssuesChangesNotificationBuilderTesting.newAnalysisChange(); | |||||
AnalysisChange analysisChange = newAnalysisChange(); | |||||
EmailMessage closedIssuesMessage = underTest.format(new ChangesOnMyIssuesNotification(analysisChange, closedIssues)); | EmailMessage closedIssuesMessage = underTest.format(new ChangesOnMyIssuesNotification(analysisChange, closedIssues)); | ||||
EmailMessage openIssuesMessage = underTest.format(new ChangesOnMyIssuesNotification(analysisChange, openIssues)); | EmailMessage openIssuesMessage = underTest.format(new ChangesOnMyIssuesNotification(analysisChange, openIssues)); | ||||
Project project = newProject("1"); | Project project = newProject("1"); | ||||
String ruleName = randomAlphabetic(8); | String ruleName = randomAlphabetic(8); | ||||
String host = randomAlphabetic(15); | String host = randomAlphabetic(15); | ||||
ChangedIssue changedIssue = newChangedIssue("key", randomValidStatus(), project, ruleName); | |||||
AnalysisChange analysisChange = IssuesChangesNotificationBuilderTesting.newAnalysisChange(); | |||||
ChangedIssue changedIssue = newChangedIssue("key", randomValidStatus(), project, ruleName, randomRuleTypeHotspotExcluded()); | |||||
AnalysisChange analysisChange = newAnalysisChange(); | |||||
when(emailSettings.getServerBaseURL()).thenReturn(host); | when(emailSettings.getServerBaseURL()).thenReturn(host); | ||||
EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(analysisChange, ImmutableSet.of(changedIssue))); | EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(analysisChange, ImmutableSet.of(changedIssue))); | ||||
Project project = newProject("1"); | Project project = newProject("1"); | ||||
String ruleName = randomAlphabetic(8); | String ruleName = randomAlphabetic(8); | ||||
String host = randomAlphabetic(15); | String host = randomAlphabetic(15); | ||||
ChangedIssue changedIssue = newChangedIssue("key", randomValidStatus(), project, ruleName); | |||||
UserChange userChange = IssuesChangesNotificationBuilderTesting.newUserChange(); | |||||
ChangedIssue changedIssue = newChangedIssue("key", randomValidStatus(), project, ruleName, randomRuleTypeHotspotExcluded()); | |||||
UserChange userChange = newUserChange(); | |||||
when(emailSettings.getServerBaseURL()).thenReturn(host); | when(emailSettings.getServerBaseURL()).thenReturn(host); | ||||
EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(userChange, ImmutableSet.of(changedIssue))); | EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(userChange, ImmutableSet.of(changedIssue))); | ||||
String ruleName = randomAlphabetic(8); | String ruleName = randomAlphabetic(8); | ||||
String host = randomAlphabetic(15); | String host = randomAlphabetic(15); | ||||
String key = "key"; | String key = "key"; | ||||
ChangedIssue changedIssue = newChangedIssue(key, randomValidStatus(), project, ruleName); | |||||
AnalysisChange analysisChange = IssuesChangesNotificationBuilderTesting.newAnalysisChange(); | |||||
ChangedIssue changedIssue = newChangedIssue(key, randomValidStatus(), project, ruleName, randomRuleTypeHotspotExcluded()); | |||||
AnalysisChange analysisChange = newAnalysisChange(); | |||||
when(emailSettings.getServerBaseURL()).thenReturn(host); | when(emailSettings.getServerBaseURL()).thenReturn(host); | ||||
EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(analysisChange, ImmutableSet.of(changedIssue))); | EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(analysisChange, ImmutableSet.of(changedIssue))); | ||||
String ruleName = randomAlphabetic(8); | String ruleName = randomAlphabetic(8); | ||||
String host = randomAlphabetic(15); | String host = randomAlphabetic(15); | ||||
String key = "key"; | String key = "key"; | ||||
ChangedIssue changedIssue = newChangedIssue(key, randomValidStatus(), project, ruleName); | |||||
UserChange userChange = IssuesChangesNotificationBuilderTesting.newUserChange(); | |||||
ChangedIssue changedIssue = newChangedIssue(key, randomValidStatus(), project, ruleName, randomRuleTypeHotspotExcluded()); | |||||
UserChange userChange = newUserChange(); | |||||
when(emailSettings.getServerBaseURL()).thenReturn(host); | when(emailSettings.getServerBaseURL()).thenReturn(host); | ||||
EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(userChange, ImmutableSet.of(changedIssue))); | EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(userChange, ImmutableSet.of(changedIssue))); | ||||
Project project = newProject("1"); | Project project = newProject("1"); | ||||
String ruleName = randomAlphabetic(8); | String ruleName = randomAlphabetic(8); | ||||
String host = randomAlphabetic(15); | String host = randomAlphabetic(15); | ||||
Rule rule = newRule(ruleName); | |||||
Rule rule = newRule(ruleName, randomRuleTypeHotspotExcluded()); | |||||
String issueStatus = randomValidStatus(); | String issueStatus = randomValidStatus(); | ||||
List<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(5)) | List<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(5)) | ||||
.mapToObj(i -> newChangedIssue("issue_" + i, issueStatus, project, rule)) | .mapToObj(i -> newChangedIssue("issue_" + i, issueStatus, project, rule)) | ||||
.collect(toList()); | .collect(toList()); | ||||
AnalysisChange analysisChange = IssuesChangesNotificationBuilderTesting.newAnalysisChange(); | |||||
AnalysisChange analysisChange = newAnalysisChange(); | |||||
when(emailSettings.getServerBaseURL()).thenReturn(host); | when(emailSettings.getServerBaseURL()).thenReturn(host); | ||||
EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(analysisChange, ImmutableSet.copyOf(changedIssues))); | EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(analysisChange, ImmutableSet.copyOf(changedIssues))); | ||||
Project project = newProject("1"); | Project project = newProject("1"); | ||||
String ruleName = randomAlphabetic(8); | String ruleName = randomAlphabetic(8); | ||||
String host = randomAlphabetic(15); | String host = randomAlphabetic(15); | ||||
Rule rule = newRule(ruleName); | |||||
Rule rule = newRule(ruleName, randomRuleTypeHotspotExcluded()); | |||||
List<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(5)) | List<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(5)) | ||||
.mapToObj(i -> newChangedIssue("issue_" + i, randomValidStatus(), project, rule)) | .mapToObj(i -> newChangedIssue("issue_" + i, randomValidStatus(), project, rule)) | ||||
.collect(toList()); | .collect(toList()); | ||||
UserChange userChange = IssuesChangesNotificationBuilderTesting.newUserChange(); | |||||
UserChange userChange = newUserChange(); | |||||
when(emailSettings.getServerBaseURL()).thenReturn(host); | when(emailSettings.getServerBaseURL()).thenReturn(host); | ||||
EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(userChange, ImmutableSet.copyOf(changedIssues))); | EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(userChange, ImmutableSet.copyOf(changedIssues))); | ||||
Project project = newBranch("1", branchName); | Project project = newBranch("1", branchName); | ||||
String ruleName = randomAlphabetic(8); | String ruleName = randomAlphabetic(8); | ||||
String host = randomAlphabetic(15); | String host = randomAlphabetic(15); | ||||
Rule rule = newRule(ruleName); | |||||
Rule rule = newRule(ruleName, randomRuleTypeHotspotExcluded()); | |||||
String status = randomValidStatus(); | String status = randomValidStatus(); | ||||
List<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(5)) | List<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(5)) | ||||
.mapToObj(i -> newChangedIssue("issue_" + i, status, project, rule)) | .mapToObj(i -> newChangedIssue("issue_" + i, status, project, rule)) | ||||
.collect(toList()); | .collect(toList()); | ||||
AnalysisChange analysisChange = IssuesChangesNotificationBuilderTesting.newAnalysisChange(); | |||||
AnalysisChange analysisChange = newAnalysisChange(); | |||||
when(emailSettings.getServerBaseURL()).thenReturn(host); | when(emailSettings.getServerBaseURL()).thenReturn(host); | ||||
EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(analysisChange, ImmutableSet.copyOf(changedIssues))); | EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(analysisChange, ImmutableSet.copyOf(changedIssues))); | ||||
Project project = newBranch("1", branchName); | Project project = newBranch("1", branchName); | ||||
String ruleName = randomAlphabetic(8); | String ruleName = randomAlphabetic(8); | ||||
String host = randomAlphabetic(15); | String host = randomAlphabetic(15); | ||||
Rule rule = newRule(ruleName); | |||||
Rule rule = newRandomNotAHotspotRule(ruleName); | |||||
List<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(5)) | List<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(5)) | ||||
.mapToObj(i -> newChangedIssue("issue_" + i, randomValidStatus(), project, rule)) | .mapToObj(i -> newChangedIssue("issue_" + i, randomValidStatus(), project, rule)) | ||||
.collect(toList()); | .collect(toList()); | ||||
UserChange userChange = IssuesChangesNotificationBuilderTesting.newUserChange(); | |||||
UserChange userChange = newUserChange(); | |||||
when(emailSettings.getServerBaseURL()).thenReturn(host); | when(emailSettings.getServerBaseURL()).thenReturn(host); | ||||
EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(userChange, ImmutableSet.copyOf(changedIssues))); | EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(userChange, ImmutableSet.copyOf(changedIssues))); | ||||
Project project3 = newProject("C"); | Project project3 = newProject("C"); | ||||
String host = randomAlphabetic(15); | String host = randomAlphabetic(15); | ||||
List<ChangedIssue> changedIssues = Stream.of(project1, project1Branch1, project1Branch2, project2, project2Branch1, project3) | List<ChangedIssue> changedIssues = Stream.of(project1, project1Branch1, project1Branch2, project2, project2Branch1, project3) | ||||
.map(project -> newChangedIssue("issue_" + project.getUuid(), randomValidStatus(), project, newRule(randomAlphabetic(2)))) | |||||
.map(project -> newChangedIssue("issue_" + project.getUuid(), randomValidStatus(), project, newRule(randomAlphabetic(2), randomRuleTypeHotspotExcluded()))) | |||||
.collect(toList()); | .collect(toList()); | ||||
Collections.shuffle(changedIssues); | Collections.shuffle(changedIssues); | ||||
UserChange userChange = IssuesChangesNotificationBuilderTesting.newUserChange(); | |||||
UserChange userChange = newUserChange(); | |||||
when(emailSettings.getServerBaseURL()).thenReturn(host); | when(emailSettings.getServerBaseURL()).thenReturn(host); | ||||
EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(userChange, ImmutableSet.copyOf(changedIssues))); | EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(userChange, ImmutableSet.copyOf(changedIssues))); | ||||
@Test | @Test | ||||
public void formats_returns_html_message_with_rules_ordered_by_name_when_analysis_change() { | public void formats_returns_html_message_with_rules_ordered_by_name_when_analysis_change() { | ||||
Project project = newProject("1"); | Project project = newProject("1"); | ||||
Rule rule1 = newRule("1"); | |||||
Rule rule2 = newRule("a"); | |||||
Rule rule3 = newRule("b"); | |||||
Rule rule4 = newRule("X"); | |||||
Rule rule1 = newRandomNotAHotspotRule("1"); | |||||
Rule rule2 = newRandomNotAHotspotRule("a"); | |||||
Rule rule3 = newRandomNotAHotspotRule("b"); | |||||
Rule rule4 = newRandomNotAHotspotRule("X"); | |||||
String host = randomAlphabetic(15); | String host = randomAlphabetic(15); | ||||
String issueStatus = randomValidStatus(); | String issueStatus = randomValidStatus(); | ||||
List<ChangedIssue> changedIssues = Stream.of(rule1, rule2, rule3, rule4) | List<ChangedIssue> changedIssues = Stream.of(rule1, rule2, rule3, rule4) | ||||
.map(rule -> newChangedIssue("issue_" + rule.getName(), issueStatus, project, rule)) | .map(rule -> newChangedIssue("issue_" + rule.getName(), issueStatus, project, rule)) | ||||
.collect(toList()); | .collect(toList()); | ||||
Collections.shuffle(changedIssues); | Collections.shuffle(changedIssues); | ||||
AnalysisChange analysisChange = IssuesChangesNotificationBuilderTesting.newAnalysisChange(); | |||||
AnalysisChange analysisChange = newAnalysisChange(); | |||||
when(emailSettings.getServerBaseURL()).thenReturn(host); | when(emailSettings.getServerBaseURL()).thenReturn(host); | ||||
EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(analysisChange, ImmutableSet.copyOf(changedIssues))); | EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(analysisChange, ImmutableSet.copyOf(changedIssues))); | ||||
} | } | ||||
@Test | @Test | ||||
public void formats_returns_html_message_with_rules_ordered_by_name_when_analysis_change_when_user_analysis() { | |||||
public void formats_returns_html_message_with_rules_ordered_by_name_user_change() { | |||||
Project project = newProject("1"); | Project project = newProject("1"); | ||||
Rule rule1 = newRule("1"); | |||||
Rule rule2 = newRule("a"); | |||||
Rule rule3 = newRule("b"); | |||||
Rule rule4 = newRule("X"); | |||||
Rule rule1 = newRandomNotAHotspotRule("1"); | |||||
Rule rule2 = newRandomNotAHotspotRule("a"); | |||||
Rule rule3 = newRandomNotAHotspotRule("b"); | |||||
Rule rule4 = newRandomNotAHotspotRule("X"); | |||||
Rule hotspot1 = newSecurityHotspotRule("S"); | |||||
Rule hotspot2 = newSecurityHotspotRule("Z"); | |||||
Rule hotspot3 = newSecurityHotspotRule("N"); | |||||
Rule hotspot4 = newSecurityHotspotRule("M"); | |||||
String host = randomAlphabetic(15); | String host = randomAlphabetic(15); | ||||
List<ChangedIssue> changedIssues = Stream.of(rule1, rule2, rule3, rule4) | |||||
List<ChangedIssue> changedIssues = Stream.of(rule1, rule2, rule3, rule4, hotspot1, hotspot2, hotspot3, hotspot4) | |||||
.map(rule -> newChangedIssue("issue_" + rule.getName(), randomValidStatus(), project, rule)) | .map(rule -> newChangedIssue("issue_" + rule.getName(), randomValidStatus(), project, rule)) | ||||
.collect(toList()); | .collect(toList()); | ||||
Collections.shuffle(changedIssues); | Collections.shuffle(changedIssues); | ||||
UserChange userChange = IssuesChangesNotificationBuilderTesting.newUserChange(); | |||||
UserChange userChange = newUserChange(); | |||||
when(emailSettings.getServerBaseURL()).thenReturn(host); | when(emailSettings.getServerBaseURL()).thenReturn(host); | ||||
EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(userChange, ImmutableSet.copyOf(changedIssues))); | EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(userChange, ImmutableSet.copyOf(changedIssues))); | ||||
HtmlFragmentAssert.assertThat(emailMessage.getMessage()) | HtmlFragmentAssert.assertThat(emailMessage.getMessage()) | ||||
.hasParagraph().hasParagraph() // skip header | |||||
.hasParagraph(project.getProjectName()) | |||||
.hasParagraph() | |||||
.hasParagraph() | |||||
.hasParagraph() // skip project name | |||||
.hasList( | .hasList( | ||||
"Rule " + rule1.getName() + " - See the single issue", | "Rule " + rule1.getName() + " - See the single issue", | ||||
"Rule " + rule2.getName() + " - See the single issue", | "Rule " + rule2.getName() + " - See the single issue", | ||||
"Rule " + rule3.getName() + " - See the single issue", | "Rule " + rule3.getName() + " - See the single issue", | ||||
"Rule " + rule4.getName() + " - See the single issue") | "Rule " + rule4.getName() + " - See the single issue") | ||||
.hasEmptyParagraph() | |||||
.hasList( | |||||
"Rule " + hotspot1.getName() + " - See the single hotspot", | |||||
"Rule " + hotspot2.getName() + " - See the single hotspot", | |||||
"Rule " + hotspot3.getName() + " - See the single hotspot", | |||||
"Rule " + hotspot4.getName() + " - See the single hotspot") | |||||
.hasParagraph().hasParagraph() // skip footer | .hasParagraph().hasParagraph() // skip footer | ||||
.noMoreBlock(); | .noMoreBlock(); | ||||
} | } | ||||
@Test | @Test | ||||
public void formats_returns_html_message_with_multiple_links_by_rule_of_groups_of_up_to_40_issues_when_analysis_change() { | public void formats_returns_html_message_with_multiple_links_by_rule_of_groups_of_up_to_40_issues_when_analysis_change() { | ||||
Project project1 = newProject("1"); | Project project1 = newProject("1"); | ||||
Rule rule1 = newRule("1"); | |||||
Rule rule2 = newRule("a"); | |||||
Rule rule1 = newRandomNotAHotspotRule("1"); | |||||
Rule rule2 = newRandomNotAHotspotRule("a"); | |||||
String host = randomAlphabetic(15); | String host = randomAlphabetic(15); | ||||
String issueStatusClosed = STATUS_CLOSED; | String issueStatusClosed = STATUS_CLOSED; | ||||
String otherIssueStatus = STATUS_RESOLVED; | String otherIssueStatus = STATUS_RESOLVED; | ||||
List<ChangedIssue> changedIssues = Stream.of( | List<ChangedIssue> changedIssues = Stream.of( | ||||
IntStream.range(0, 39).mapToObj(i -> newChangedIssue("39_" + i, issueStatusClosed, project1, rule1)), | IntStream.range(0, 39).mapToObj(i -> newChangedIssue("39_" + i, issueStatusClosed, project1, rule1)), | ||||
IntStream.range(0, 40).mapToObj(i -> newChangedIssue("40_" + i, issueStatusClosed, project1, rule2)), | IntStream.range(0, 40).mapToObj(i -> newChangedIssue("40_" + i, issueStatusClosed, project1, rule2)), | ||||
IntStream.range(0, 6).mapToObj(i -> newChangedIssue("6_" + i, otherIssueStatus, project1, rule1))) | IntStream.range(0, 6).mapToObj(i -> newChangedIssue("6_" + i, otherIssueStatus, project1, rule1))) | ||||
.flatMap(t -> t) | .flatMap(t -> t) | ||||
.collect(toList()); | .collect(toList()); | ||||
Collections.shuffle(changedIssues); | Collections.shuffle(changedIssues); | ||||
AnalysisChange analysisChange = IssuesChangesNotificationBuilderTesting.newAnalysisChange(); | |||||
AnalysisChange analysisChange = newAnalysisChange(); | |||||
when(emailSettings.getServerBaseURL()).thenReturn(host); | when(emailSettings.getServerBaseURL()).thenReturn(host); | ||||
EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(analysisChange, ImmutableSet.copyOf(changedIssues))); | EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(analysisChange, ImmutableSet.copyOf(changedIssues))); | ||||
} | } | ||||
@Test | @Test | ||||
public void formats_returns_html_message_with_multiple_links_by_rule_of_groups_of_up_to_40_issues_when_user_change() { | |||||
public void formats_returns_html_message_with_multiple_links_by_rule_of_groups_of_up_to_40_issues_and_hotspots_when_user_change() { | |||||
Project project1 = newProject("1"); | Project project1 = newProject("1"); | ||||
Project project2 = newProject("V"); | Project project2 = newProject("V"); | ||||
Project project2Branch = newBranch("V", "AB"); | Project project2Branch = newBranch("V", "AB"); | ||||
Rule rule1 = newRule("1"); | |||||
Rule rule2 = newRule("a"); | |||||
Rule rule1 = newRule("1", randomRuleTypeHotspotExcluded()); | |||||
Rule rule2 = newRule("a", randomRuleTypeHotspotExcluded()); | |||||
Rule hotspot1 = newSecurityHotspotRule("h1"); | |||||
Rule hotspot2 = newSecurityHotspotRule("h2"); | |||||
String status = randomValidStatus(); | String status = randomValidStatus(); | ||||
String host = randomAlphabetic(15); | String host = randomAlphabetic(15); | ||||
List<ChangedIssue> changedIssues = Stream.of( | List<ChangedIssue> changedIssues = Stream.of( | ||||
IntStream.range(0, 39).mapToObj(i -> newChangedIssue("39_" + i, status, project1, rule1)), | IntStream.range(0, 39).mapToObj(i -> newChangedIssue("39_" + i, status, project1, rule1)), | ||||
IntStream.range(0, 40).mapToObj(i -> newChangedIssue("40_" + i, status, project1, rule2)), | IntStream.range(0, 40).mapToObj(i -> newChangedIssue("40_" + i, status, project1, rule2)), | ||||
IntStream.range(0, 81).mapToObj(i -> newChangedIssue("1-40_41-80_1_" + i, status, project2, rule2)), | IntStream.range(0, 81).mapToObj(i -> newChangedIssue("1-40_41-80_1_" + i, status, project2, rule2)), | ||||
IntStream.range(0, 6).mapToObj(i -> newChangedIssue("6_" + i, status, project2Branch, rule1))) | |||||
IntStream.range(0, 6).mapToObj(i -> newChangedIssue("6_" + i, status, project2Branch, rule1)), | |||||
IntStream.range(0, 39).mapToObj(i -> newChangedIssue("39_" + i, STATUS_REVIEWED, project1, hotspot1)), | |||||
IntStream.range(0, 40).mapToObj(i -> newChangedIssue("40_" + i, STATUS_REVIEWED, project1, hotspot2)), | |||||
IntStream.range(0, 81).mapToObj(i -> newChangedIssue("1-40_41-80_1_" + i, STATUS_TO_REVIEW, project2, hotspot2)), | |||||
IntStream.range(0, 6).mapToObj(i -> newChangedIssue("6_" + i, STATUS_TO_REVIEW, project2Branch, hotspot1))) | |||||
.flatMap(t -> t) | .flatMap(t -> t) | ||||
.collect(toList()); | .collect(toList()); | ||||
Collections.shuffle(changedIssues); | Collections.shuffle(changedIssues); | ||||
UserChange userChange = IssuesChangesNotificationBuilderTesting.newUserChange(); | |||||
UserChange userChange = newUserChange(); | |||||
when(emailSettings.getServerBaseURL()).thenReturn(host); | when(emailSettings.getServerBaseURL()).thenReturn(host); | ||||
EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(userChange, ImmutableSet.copyOf(changedIssues))); | EmailMessage emailMessage = underTest.format(new ChangesOnMyIssuesNotification(userChange, ImmutableSet.copyOf(changedIssues))); | ||||
.withLink("See all 40 issues", | .withLink("See all 40 issues", | ||||
host + "/project/issues?id=" + project1.getKey() | host + "/project/issues?id=" + project1.getKey() | ||||
+ "&issues=" + IntStream.range(0, 40).mapToObj(i -> "40_" + i).sorted().collect(joining("%2C"))) | + "&issues=" + IntStream.range(0, 40).mapToObj(i -> "40_" + i).sorted().collect(joining("%2C"))) | ||||
.hasEmptyParagraph() | |||||
.hasList() | |||||
.withItemTexts( | |||||
"Rule " + hotspot1.getName() + " - See all 39 hotspots", | |||||
"Rule " + hotspot2.getName() + " - See all 40 hotspots") | |||||
.withLink("See all 39 hotspots", | |||||
host + "/security_hotspots?id=" + project1.getKey() | |||||
+ "&hotspots=" + IntStream.range(0, 39).mapToObj(i -> "39_" + i).sorted().collect(joining("%2C"))) | |||||
.withLink("See all 40 hotspots", | |||||
host + "/security_hotspots?id=" + project1.getKey() | |||||
+ "&hotspots=" + IntStream.range(0, 40).mapToObj(i -> "40_" + i).sorted().collect(joining("%2C"))) | |||||
.hasParagraph(project2.getProjectName()) | .hasParagraph(project2.getProjectName()) | ||||
.hasList("Rule " + rule2.getName() + " - See issues 1-40 41-80 81") | |||||
.hasList( | |||||
"Rule " + rule2.getName() + " - See issues 1-40 41-80 81") | |||||
.withLink("1-40", | .withLink("1-40", | ||||
host + "/project/issues?id=" + project2.getKey() | host + "/project/issues?id=" + project2.getKey() | ||||
+ "&issues=" + IntStream.range(0, 81).mapToObj(i -> "1-40_41-80_1_" + i).sorted().limit(40).collect(joining("%2C"))) | + "&issues=" + IntStream.range(0, 81).mapToObj(i -> "1-40_41-80_1_" + i).sorted().limit(40).collect(joining("%2C"))) | ||||
.withLink("81", | .withLink("81", | ||||
host + "/project/issues?id=" + project2.getKey() | host + "/project/issues?id=" + project2.getKey() | ||||
+ "&issues=" + "1-40_41-80_1_9" + "&open=" + "1-40_41-80_1_9") | + "&issues=" + "1-40_41-80_1_9" + "&open=" + "1-40_41-80_1_9") | ||||
.hasEmptyParagraph() | |||||
.hasList("Rule " + hotspot2.getName() + " - See hotspots 1-40 41-80 81") | |||||
.withLink("1-40", | |||||
host + "/security_hotspots?id=" + project2.getKey() | |||||
+ "&hotspots=" + IntStream.range(0, 81).mapToObj(i -> "1-40_41-80_1_" + i).sorted().limit(40).collect(joining("%2C"))) | |||||
.withLink("41-80", | |||||
host + "/security_hotspots?id=" + project2.getKey() | |||||
+ "&hotspots=" + IntStream.range(0, 81).mapToObj(i -> "1-40_41-80_1_" + i).sorted().skip(40).limit(40).collect(joining("%2C"))) | |||||
.withLink("81", | |||||
host + "/security_hotspots?id=" + project2.getKey() | |||||
+ "&hotspots=" + "1-40_41-80_1_9") | |||||
.hasParagraph(project2Branch.getProjectName() + ", " + project2Branch.getBranchName().get()) | .hasParagraph(project2Branch.getProjectName() + ", " + project2Branch.getBranchName().get()) | ||||
.hasList("Rule " + rule1.getName() + " - See all 6 issues") | |||||
.hasList( | |||||
"Rule " + rule1.getName() + " - See all 6 issues") | |||||
.withLink("See all 6 issues", | .withLink("See all 6 issues", | ||||
host + "/project/issues?id=" + project2Branch.getKey() + "&branch=" + project2Branch.getBranchName().get() | host + "/project/issues?id=" + project2Branch.getKey() + "&branch=" + project2Branch.getBranchName().get() | ||||
+ "&issues=" + IntStream.range(0, 6).mapToObj(i -> "6_" + i).sorted().collect(joining("%2C"))) | + "&issues=" + IntStream.range(0, 6).mapToObj(i -> "6_" + i).sorted().collect(joining("%2C"))) | ||||
.hasEmptyParagraph() | |||||
.hasList("Rule " + hotspot1.getName() + " - See all 6 hotspots") | |||||
.withLink("See all 6 hotspots", | |||||
host + "/security_hotspots?id=" + project2Branch.getKey() + "&branch=" + project2Branch.getBranchName().get() | |||||
+ "&hotspots=" + IntStream.range(0, 6).mapToObj(i -> "6_" + i).sorted().collect(joining("%2C"))) | |||||
.hasParagraph().hasParagraph() // skip footer | .hasParagraph().hasParagraph() // skip footer | ||||
.noMoreBlock(); | .noMoreBlock(); | ||||
} | } |
import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic; | import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic; | ||||
import static org.assertj.core.api.Assertions.assertThat; | import static org.assertj.core.api.Assertions.assertThat; | ||||
import static org.mockito.Mockito.mock; | import static org.mockito.Mockito.mock; | ||||
import static org.sonar.server.issue.notification.IssuesChangesNotificationBuilderTesting.newRandomNotAHotspotRule; | |||||
public class ChangesOnMyIssuesNotificationTest { | public class ChangesOnMyIssuesNotificationTest { | ||||
@Test | @Test | ||||
@Test | @Test | ||||
public void equals_is_based_on_change_and_issues() { | public void equals_is_based_on_change_and_issues() { | ||||
AnalysisChange analysisChange = new AnalysisChange(new Random().nextLong()); | AnalysisChange analysisChange = new AnalysisChange(new Random().nextLong()); | ||||
ChangedIssue changedIssue = IssuesChangesNotificationBuilderTesting.newChangedIssue("doo", IssuesChangesNotificationBuilderTesting.newProject("prj"), IssuesChangesNotificationBuilderTesting.newRule("rul")); | |||||
ChangedIssue changedIssue = IssuesChangesNotificationBuilderTesting.newChangedIssue("doo", IssuesChangesNotificationBuilderTesting.newProject("prj"), | |||||
newRandomNotAHotspotRule("rul")); | |||||
ChangesOnMyIssuesNotification underTest = new ChangesOnMyIssuesNotification(analysisChange, ImmutableSet.of(changedIssue)); | ChangesOnMyIssuesNotification underTest = new ChangesOnMyIssuesNotification(analysisChange, ImmutableSet.of(changedIssue)); | ||||
assertThat(underTest) | assertThat(underTest) | ||||
@Test | @Test | ||||
public void hashcode_is_based_on_change_and_issues() { | public void hashcode_is_based_on_change_and_issues() { | ||||
AnalysisChange analysisChange = new AnalysisChange(new Random().nextLong()); | AnalysisChange analysisChange = new AnalysisChange(new Random().nextLong()); | ||||
ChangedIssue changedIssue = IssuesChangesNotificationBuilderTesting.newChangedIssue("doo", IssuesChangesNotificationBuilderTesting.newProject("prj"), IssuesChangesNotificationBuilderTesting.newRule("rul")); | |||||
ChangedIssue changedIssue = IssuesChangesNotificationBuilderTesting.newChangedIssue("doo", IssuesChangesNotificationBuilderTesting.newProject("prj"), | |||||
newRandomNotAHotspotRule("rul")); | |||||
ChangesOnMyIssuesNotification underTest = new ChangesOnMyIssuesNotification(analysisChange, ImmutableSet.of(changedIssue)); | ChangesOnMyIssuesNotification underTest = new ChangesOnMyIssuesNotification(analysisChange, ImmutableSet.of(changedIssue)); | ||||
assertThat(underTest.hashCode()) | assertThat(underTest.hashCode()) |
import org.mockito.ArgumentCaptor; | import org.mockito.ArgumentCaptor; | ||||
import org.mockito.Mockito; | import org.mockito.Mockito; | ||||
import org.sonar.api.issue.Issue; | import org.sonar.api.issue.Issue; | ||||
import org.sonar.api.rule.RuleKey; | |||||
import org.sonar.server.issue.notification.FPOrWontFixNotification.FpOrWontFix; | import org.sonar.server.issue.notification.FPOrWontFixNotification.FpOrWontFix; | ||||
import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.Change; | import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.Change; | ||||
import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.ChangedIssue; | import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.ChangedIssue; | ||||
import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.Project; | import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.Project; | ||||
import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.Rule; | |||||
import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.User; | import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.User; | ||||
import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.UserChange; | import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.UserChange; | ||||
import org.sonar.server.notification.NotificationDispatcherMetadata; | import org.sonar.server.notification.NotificationDispatcherMetadata; | ||||
import static org.sonar.api.issue.Issue.RESOLUTION_WONT_FIX; | import static org.sonar.api.issue.Issue.RESOLUTION_WONT_FIX; | ||||
import static org.sonar.core.util.stream.MoreCollectors.index; | import static org.sonar.core.util.stream.MoreCollectors.index; | ||||
import static org.sonar.server.issue.notification.IssuesChangesNotificationBuilderTesting.newProject; | import static org.sonar.server.issue.notification.IssuesChangesNotificationBuilderTesting.newProject; | ||||
import static org.sonar.server.issue.notification.IssuesChangesNotificationBuilderTesting.newRandomNotAHotspotRule; | |||||
import static org.sonar.server.notification.NotificationDispatcherMetadata.GLOBAL_NOTIFICATION; | import static org.sonar.server.notification.NotificationDispatcherMetadata.GLOBAL_NOTIFICATION; | ||||
import static org.sonar.server.notification.NotificationDispatcherMetadata.PER_PROJECT_NOTIFICATION; | import static org.sonar.server.notification.NotificationDispatcherMetadata.PER_PROJECT_NOTIFICATION; | ||||
import static org.sonar.server.notification.NotificationManager.SubscriberPermissionsOnProject.ALL_MUST_HAVE_ROLE_USER; | import static org.sonar.server.notification.NotificationManager.SubscriberPermissionsOnProject.ALL_MUST_HAVE_ROLE_USER; | ||||
.setAssignee(new User(randomAlphabetic(3), randomAlphabetic(4), randomAlphabetic(5))) | .setAssignee(new User(randomAlphabetic(3), randomAlphabetic(4), randomAlphabetic(5))) | ||||
.setNewStatus(randomAlphabetic(12)) | .setNewStatus(randomAlphabetic(12)) | ||||
.setNewResolution(randomAlphabetic(13)) | .setNewResolution(randomAlphabetic(13)) | ||||
.setRule(new Rule(RuleKey.of(randomAlphabetic(6), randomAlphabetic(7)), randomAlphabetic(8))) | |||||
.setRule(newRandomNotAHotspotRule(randomAlphabetic(8))) | |||||
.setProject(new Project.Builder(randomAlphabetic(9)) | .setProject(new Project.Builder(randomAlphabetic(9)) | ||||
.setKey(randomAlphabetic(10)) | .setKey(randomAlphabetic(10)) | ||||
.setProjectName(randomAlphabetic(11)) | .setProjectName(randomAlphabetic(11)) |
import java.util.stream.Collectors; | import java.util.stream.Collectors; | ||||
import java.util.stream.IntStream; | import java.util.stream.IntStream; | ||||
import org.junit.Test; | import org.junit.Test; | ||||
import org.sonar.api.rule.RuleKey; | |||||
import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.AnalysisChange; | import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.AnalysisChange; | ||||
import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.ChangedIssue; | import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.ChangedIssue; | ||||
import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.Project; | import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.Project; | ||||
import static org.assertj.core.api.Assertions.assertThat; | import static org.assertj.core.api.Assertions.assertThat; | ||||
import static org.sonar.server.issue.notification.FPOrWontFixNotification.FpOrWontFix.FP; | import static org.sonar.server.issue.notification.FPOrWontFixNotification.FpOrWontFix.FP; | ||||
import static org.sonar.server.issue.notification.FPOrWontFixNotification.FpOrWontFix.WONT_FIX; | import static org.sonar.server.issue.notification.FPOrWontFixNotification.FpOrWontFix.WONT_FIX; | ||||
import static org.sonar.server.issue.notification.IssuesChangesNotificationBuilderTesting.newRandomNotAHotspotRule; | |||||
public class FPOrWontFixNotificationTest { | public class FPOrWontFixNotificationTest { | ||||
@Test | @Test | ||||
public void equals_is_based_on_issues_change_and_resolution() { | public void equals_is_based_on_issues_change_and_resolution() { | ||||
Rule rule = new Rule(RuleKey.of("repo", "rule_key"), "rule_name"); | |||||
Rule rule = newRandomNotAHotspotRule("rule_name"); | |||||
Project project = new Project.Builder("prj_uuid").setKey("prj_key").setProjectName("prj_name").build(); | Project project = new Project.Builder("prj_uuid").setKey("prj_key").setProjectName("prj_name").build(); | ||||
Set<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(5)) | Set<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(5)) | ||||
.mapToObj(i -> new ChangedIssue.Builder("key_" + i) | .mapToObj(i -> new ChangedIssue.Builder("key_" + i) | ||||
.setNewStatus("status") | .setNewStatus("status") | ||||
.setRule(rule) | .setRule(rule) | ||||
.setProject(project) | .setProject(project) | ||||
.build()) | |||||
.build()) | |||||
.collect(Collectors.toSet()); | .collect(Collectors.toSet()); | ||||
AnalysisChange change = new AnalysisChange(12); | AnalysisChange change = new AnalysisChange(12); | ||||
User user = new User("uuid", "login", null); | User user = new User("uuid", "login", null); | ||||
.isNotEqualTo(new FPOrWontFixNotification(new IssuesChangesNotificationBuilder.UserChange(12, user), changedIssues, WONT_FIX)) | .isNotEqualTo(new FPOrWontFixNotification(new IssuesChangesNotificationBuilder.UserChange(12, user), changedIssues, WONT_FIX)) | ||||
.isNotEqualTo(new FPOrWontFixNotification(change, changedIssues, FP)); | .isNotEqualTo(new FPOrWontFixNotification(change, changedIssues, FP)); | ||||
} | } | ||||
@Test | @Test | ||||
public void hashcode_is_based_on_issues_change_and_resolution() { | public void hashcode_is_based_on_issues_change_and_resolution() { | ||||
Rule rule = new Rule(RuleKey.of("repo", "rule_key"), "rule_name"); | |||||
Rule rule = newRandomNotAHotspotRule("rule_name"); | |||||
Project project = new Project.Builder("prj_uuid").setKey("prj_key").setProjectName("prj_name").build(); | Project project = new Project.Builder("prj_uuid").setKey("prj_key").setProjectName("prj_name").build(); | ||||
Set<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(5)) | Set<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(5)) | ||||
.mapToObj(i -> new ChangedIssue.Builder("key_" + i) | .mapToObj(i -> new ChangedIssue.Builder("key_" + i) | ||||
.setNewStatus("status") | .setNewStatus("status") | ||||
.setRule(rule) | .setRule(rule) | ||||
.setProject(project) | .setProject(project) | ||||
.build()) | |||||
.build()) | |||||
.collect(Collectors.toSet()); | .collect(Collectors.toSet()); | ||||
AnalysisChange change = new AnalysisChange(12); | AnalysisChange change = new AnalysisChange(12); | ||||
User user = new User("uuid", "login", null); | User user = new User("uuid", "login", null); |
import org.sonar.api.config.EmailSettings; | import org.sonar.api.config.EmailSettings; | ||||
import org.sonar.api.notifications.Notification; | import org.sonar.api.notifications.Notification; | ||||
import org.sonar.api.rule.RuleKey; | import org.sonar.api.rule.RuleKey; | ||||
import org.sonar.api.rules.RuleType; | |||||
import org.sonar.core.i18n.I18n; | import org.sonar.core.i18n.I18n; | ||||
import org.sonar.server.issue.notification.FPOrWontFixNotification.FpOrWontFix; | import org.sonar.server.issue.notification.FPOrWontFixNotification.FpOrWontFix; | ||||
import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.AnalysisChange; | import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.AnalysisChange; | ||||
import static org.assertj.core.api.Assertions.assertThat; | import static org.assertj.core.api.Assertions.assertThat; | ||||
import static org.mockito.Mockito.mock; | import static org.mockito.Mockito.mock; | ||||
import static org.mockito.Mockito.when; | import static org.mockito.Mockito.when; | ||||
import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT; | |||||
import static org.sonar.server.issue.notification.FPOrWontFixNotification.FpOrWontFix.FP; | import static org.sonar.server.issue.notification.FPOrWontFixNotification.FpOrWontFix.FP; | ||||
import static org.sonar.server.issue.notification.FPOrWontFixNotification.FpOrWontFix.WONT_FIX; | import static org.sonar.server.issue.notification.FPOrWontFixNotification.FpOrWontFix.WONT_FIX; | ||||
import static org.sonar.server.issue.notification.IssuesChangesNotificationBuilderTesting.newRandomNotAHotspotRule; | |||||
import static org.sonar.server.issue.notification.IssuesChangesNotificationBuilderTesting.newSecurityHotspotRule; | |||||
import static org.sonar.server.issue.notification.IssuesChangesNotificationBuilderTesting.randomRuleTypeHotspotExcluded; | |||||
@RunWith(DataProviderRunner.class) | @RunWith(DataProviderRunner.class) | ||||
public class FpOrWontFixEmailTemplateTest { | public class FpOrWontFixEmailTemplateTest { | ||||
Project project = newProject("1"); | Project project = newProject("1"); | ||||
String ruleName = randomAlphabetic(8); | String ruleName = randomAlphabetic(8); | ||||
String host = randomAlphabetic(15); | String host = randomAlphabetic(15); | ||||
ChangedIssue changedIssue = newChangedIssue("key", project, ruleName); | |||||
ChangedIssue changedIssue = newChangedIssue("key", project, ruleName, randomRuleTypeHotspotExcluded()); | |||||
when(emailSettings.getServerBaseURL()).thenReturn(host); | when(emailSettings.getServerBaseURL()).thenReturn(host); | ||||
EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, ImmutableSet.of(changedIssue), fpOrWontFix)); | EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, ImmutableSet.of(changedIssue), fpOrWontFix)); | ||||
.noMoreBlock(); | .noMoreBlock(); | ||||
} | } | ||||
@Test | |||||
@UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange") | |||||
public void formats_returns_html_message_for_single_hotspot_on_master(Change change, FpOrWontFix fpOrWontFix) { | |||||
Project project = newProject("1"); | |||||
String ruleName = randomAlphabetic(8); | |||||
String host = randomAlphabetic(15); | |||||
ChangedIssue changedIssue = newChangedIssue("key", project, ruleName, SECURITY_HOTSPOT); | |||||
when(emailSettings.getServerBaseURL()).thenReturn(host); | |||||
EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, ImmutableSet.of(changedIssue), fpOrWontFix)); | |||||
HtmlFragmentAssert.assertThat(emailMessage.getMessage()) | |||||
.hasParagraph().hasParagraph() // skip header | |||||
.hasParagraph(project.getProjectName()) | |||||
.hasList("Rule " + ruleName + " - See the single hotspot") | |||||
.withLink("See the single hotspot", host + "/project/issues?id=" + project.getKey() + "&issues=" + changedIssue.getKey() + "&open=" + changedIssue.getKey()) | |||||
.hasParagraph().hasParagraph() // skip footer | |||||
.noMoreBlock(); | |||||
} | |||||
@Test | @Test | ||||
@UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange") | @UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange") | ||||
public void formats_returns_html_message_for_single_issue_on_branch(Change change, FpOrWontFix fpOrWontFix) { | public void formats_returns_html_message_for_single_issue_on_branch(Change change, FpOrWontFix fpOrWontFix) { | ||||
String ruleName = randomAlphabetic(8); | String ruleName = randomAlphabetic(8); | ||||
String host = randomAlphabetic(15); | String host = randomAlphabetic(15); | ||||
String key = "key"; | String key = "key"; | ||||
ChangedIssue changedIssue = newChangedIssue(key, project, ruleName); | |||||
ChangedIssue changedIssue = newChangedIssue(key, project, ruleName, randomRuleTypeHotspotExcluded()); | |||||
when(emailSettings.getServerBaseURL()).thenReturn(host); | when(emailSettings.getServerBaseURL()).thenReturn(host); | ||||
EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, ImmutableSet.of(changedIssue), fpOrWontFix)); | EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, ImmutableSet.of(changedIssue), fpOrWontFix)); | ||||
.noMoreBlock(); | .noMoreBlock(); | ||||
} | } | ||||
@Test | |||||
@UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange") | |||||
public void formats_returns_html_message_for_single_hotspot_on_branch(Change change, FpOrWontFix fpOrWontFix) { | |||||
String branchName = randomAlphabetic(6); | |||||
Project project = newBranch("1", branchName); | |||||
String ruleName = randomAlphabetic(8); | |||||
String host = randomAlphabetic(15); | |||||
String key = "key"; | |||||
ChangedIssue changedIssue = newChangedIssue(key, project, ruleName, SECURITY_HOTSPOT); | |||||
when(emailSettings.getServerBaseURL()).thenReturn(host); | |||||
EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, ImmutableSet.of(changedIssue), fpOrWontFix)); | |||||
HtmlFragmentAssert.assertThat(emailMessage.getMessage()) | |||||
.hasParagraph().hasParagraph() // skip header | |||||
.hasParagraph(project.getProjectName() + ", " + branchName) | |||||
.hasList("Rule " + ruleName + " - See the single hotspot") | |||||
.withLink("See the single hotspot", | |||||
host + "/project/issues?id=" + project.getKey() + "&branch=" + branchName + "&issues=" + changedIssue.getKey() + "&open=" + changedIssue.getKey()) | |||||
.hasParagraph().hasParagraph() // skip footer | |||||
.noMoreBlock(); | |||||
} | |||||
@Test | @Test | ||||
@UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange") | @UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange") | ||||
public void formats_returns_html_message_for_multiple_issues_of_same_rule_on_same_project_on_master(Change change, FpOrWontFix fpOrWontFix) { | public void formats_returns_html_message_for_multiple_issues_of_same_rule_on_same_project_on_master(Change change, FpOrWontFix fpOrWontFix) { | ||||
Project project = newProject("1"); | Project project = newProject("1"); | ||||
String ruleName = randomAlphabetic(8); | String ruleName = randomAlphabetic(8); | ||||
String host = randomAlphabetic(15); | String host = randomAlphabetic(15); | ||||
Rule rule = newRule(ruleName); | |||||
Rule rule = newRandomNotAHotspotRule(ruleName); | |||||
List<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(5)) | List<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(5)) | ||||
.mapToObj(i -> newChangedIssue("issue_" + i, project, rule)) | .mapToObj(i -> newChangedIssue("issue_" + i, project, rule)) | ||||
.collect(toList()); | .collect(toList()); | ||||
.noMoreBlock(); | .noMoreBlock(); | ||||
} | } | ||||
@Test | |||||
@UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange") | |||||
public void formats_returns_html_message_for_multiple_hotspots_of_same_rule_on_same_project_on_master(Change change, FpOrWontFix fpOrWontFix) { | |||||
Project project = newProject("1"); | |||||
String ruleName = randomAlphabetic(8); | |||||
String host = randomAlphabetic(15); | |||||
Rule rule = newSecurityHotspotRule(ruleName); | |||||
List<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(5)) | |||||
.mapToObj(i -> newChangedIssue("issue_" + i, project, rule)) | |||||
.collect(toList()); | |||||
when(emailSettings.getServerBaseURL()).thenReturn(host); | |||||
EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, ImmutableSet.copyOf(changedIssues), fpOrWontFix)); | |||||
String expectedHref = host + "/project/issues?id=" + project.getKey() | |||||
+ "&issues=" + changedIssues.stream().map(ChangedIssue::getKey).collect(joining("%2C")); | |||||
String expectedLinkText = "See all " + changedIssues.size() + " hotspots"; | |||||
HtmlFragmentAssert.assertThat(emailMessage.getMessage()) | |||||
.hasParagraph().hasParagraph() // skip header | |||||
.hasParagraph(project.getProjectName()) | |||||
.hasList("Rule " + ruleName + " - " + expectedLinkText) | |||||
.withLink(expectedLinkText, expectedHref) | |||||
.hasParagraph().hasParagraph() // skip footer | |||||
.noMoreBlock(); | |||||
} | |||||
@Test | @Test | ||||
@UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange") | @UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange") | ||||
public void formats_returns_html_message_for_multiple_issues_of_same_rule_on_same_project_on_branch(Change change, FpOrWontFix fpOrWontFix) { | public void formats_returns_html_message_for_multiple_issues_of_same_rule_on_same_project_on_branch(Change change, FpOrWontFix fpOrWontFix) { | ||||
Project project = newBranch("1", branchName); | Project project = newBranch("1", branchName); | ||||
String ruleName = randomAlphabetic(8); | String ruleName = randomAlphabetic(8); | ||||
String host = randomAlphabetic(15); | String host = randomAlphabetic(15); | ||||
Rule rule = newRule(ruleName); | |||||
Rule rule = newRandomNotAHotspotRule(ruleName); | |||||
List<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(5)) | List<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(5)) | ||||
.mapToObj(i -> newChangedIssue("issue_" + i, project, rule)) | .mapToObj(i -> newChangedIssue("issue_" + i, project, rule)) | ||||
.collect(toList()); | .collect(toList()); | ||||
.noMoreBlock(); | .noMoreBlock(); | ||||
} | } | ||||
@Test | |||||
@UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange") | |||||
public void formats_returns_html_message_for_multiple_hotspots_of_same_rule_on_same_project_on_branch(Change change, FpOrWontFix fpOrWontFix) { | |||||
String branchName = randomAlphabetic(19); | |||||
Project project = newBranch("1", branchName); | |||||
String ruleName = randomAlphabetic(8); | |||||
String host = randomAlphabetic(15); | |||||
Rule rule = newSecurityHotspotRule(ruleName); | |||||
List<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(5)) | |||||
.mapToObj(i -> newChangedIssue("issue_" + i, project, rule)) | |||||
.collect(toList()); | |||||
when(emailSettings.getServerBaseURL()).thenReturn(host); | |||||
EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, ImmutableSet.copyOf(changedIssues), fpOrWontFix)); | |||||
String expectedHref = host + "/project/issues?id=" + project.getKey() + "&branch=" + branchName | |||||
+ "&issues=" + changedIssues.stream().map(ChangedIssue::getKey).collect(joining("%2C")); | |||||
String expectedLinkText = "See all " + changedIssues.size() + " hotspots"; | |||||
HtmlFragmentAssert.assertThat(emailMessage.getMessage()) | |||||
.hasParagraph().hasParagraph() // skip header | |||||
.hasParagraph(project.getProjectName() + ", " + branchName) | |||||
.hasList("Rule " + ruleName + " - " + expectedLinkText) | |||||
.withLink(expectedLinkText, expectedHref) | |||||
.hasParagraph().hasParagraph() // skip footer | |||||
.noMoreBlock(); | |||||
} | |||||
@Test | @Test | ||||
@UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange") | @UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange") | ||||
public void formats_returns_html_message_with_projects_ordered_by_name(Change change, FpOrWontFix fpOrWontFix) { | public void formats_returns_html_message_with_projects_ordered_by_name(Change change, FpOrWontFix fpOrWontFix) { | ||||
Project project3 = newProject("C"); | Project project3 = newProject("C"); | ||||
String host = randomAlphabetic(15); | String host = randomAlphabetic(15); | ||||
List<ChangedIssue> changedIssues = Stream.of(project1, project1Branch1, project1Branch2, project2, project2Branch1, project3) | List<ChangedIssue> changedIssues = Stream.of(project1, project1Branch1, project1Branch2, project2, project2Branch1, project3) | ||||
.map(project -> newChangedIssue("issue_" + project.getUuid(), project, newRule(randomAlphabetic(2)))) | |||||
.map(project -> newChangedIssue("issue_" + project.getUuid(), project, newRandomNotAHotspotRule(randomAlphabetic(2)))) | |||||
.collect(toList()); | .collect(toList()); | ||||
Collections.shuffle(changedIssues); | Collections.shuffle(changedIssues); | ||||
when(emailSettings.getServerBaseURL()).thenReturn(host); | when(emailSettings.getServerBaseURL()).thenReturn(host); | ||||
@UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange") | @UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange") | ||||
public void formats_returns_html_message_with_rules_ordered_by_name(Change change, FpOrWontFix fpOrWontFix) { | public void formats_returns_html_message_with_rules_ordered_by_name(Change change, FpOrWontFix fpOrWontFix) { | ||||
Project project = newProject("1"); | Project project = newProject("1"); | ||||
Rule rule1 = newRule("1"); | |||||
Rule rule2 = newRule("a"); | |||||
Rule rule3 = newRule("b"); | |||||
Rule rule4 = newRule("X"); | |||||
Rule rule1 = newRandomNotAHotspotRule("1"); | |||||
Rule rule2 = newRandomNotAHotspotRule("a"); | |||||
Rule rule3 = newRandomNotAHotspotRule("b"); | |||||
Rule rule4 = newRandomNotAHotspotRule("X"); | |||||
String host = randomAlphabetic(15); | String host = randomAlphabetic(15); | ||||
List<ChangedIssue> changedIssues = Stream.of(rule1, rule2, rule3, rule4) | List<ChangedIssue> changedIssues = Stream.of(rule1, rule2, rule3, rule4) | ||||
.map(rule -> newChangedIssue("issue_" + rule.getName(), project, rule)) | .map(rule -> newChangedIssue("issue_" + rule.getName(), project, rule)) | ||||
Project project1 = newProject("1"); | Project project1 = newProject("1"); | ||||
Project project2 = newProject("V"); | Project project2 = newProject("V"); | ||||
Project project2Branch = newBranch("V", "AB"); | Project project2Branch = newBranch("V", "AB"); | ||||
Rule rule1 = newRule("1"); | |||||
Rule rule2 = newRule("a"); | |||||
Rule rule1 = newRandomNotAHotspotRule("1"); | |||||
Rule rule2 = newRandomNotAHotspotRule("a"); | |||||
String host = randomAlphabetic(15); | String host = randomAlphabetic(15); | ||||
List<ChangedIssue> changedIssues = Stream.of( | List<ChangedIssue> changedIssues = Stream.of( | ||||
IntStream.range(0, 39).mapToObj(i -> newChangedIssue("39_" + i, project1, rule1)), | IntStream.range(0, 39).mapToObj(i -> newChangedIssue("39_" + i, project1, rule1)), | ||||
}; | }; | ||||
} | } | ||||
private static ChangedIssue newChangedIssue(String key, Project project, String ruleName) { | |||||
return newChangedIssue(key, project, newRule(ruleName)); | |||||
private static ChangedIssue newChangedIssue(String key, Project project, String ruleName, RuleType ruleType) { | |||||
return newChangedIssue(key, project, newRule(ruleName, ruleType)); | |||||
} | } | ||||
private static ChangedIssue newChangedIssue(String key, Project project, Rule rule) { | private static ChangedIssue newChangedIssue(String key, Project project, Rule rule) { | ||||
.build(); | .build(); | ||||
} | } | ||||
private static Rule newRule(String ruleName) { | |||||
return new Rule(RuleKey.of(randomAlphabetic(6), randomAlphabetic(7)), ruleName); | |||||
private static Rule newRule(String ruleName, RuleType ruleType) { | |||||
return new Rule(RuleKey.of(randomAlphabetic(6), randomAlphabetic(7)), ruleType, ruleName); | |||||
} | } | ||||
private static Project newProject(String uuid) { | private static Project newProject(String uuid) { |
import java.util.Random; | import java.util.Random; | ||||
import org.sonar.api.rule.RuleKey; | import org.sonar.api.rule.RuleKey; | ||||
import org.sonar.api.rules.RuleType; | |||||
import org.sonar.db.DbTester; | import org.sonar.db.DbTester; | ||||
import org.sonar.db.component.BranchDto; | import org.sonar.db.component.BranchDto; | ||||
import org.sonar.db.component.ComponentDto; | import org.sonar.db.component.ComponentDto; | ||||
import static com.google.common.base.Preconditions.checkArgument; | import static com.google.common.base.Preconditions.checkArgument; | ||||
import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic; | import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic; | ||||
import static org.sonar.api.rules.RuleType.BUG; | |||||
import static org.sonar.api.rules.RuleType.CODE_SMELL; | |||||
import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT; | |||||
import static org.sonar.api.rules.RuleType.VULNERABILITY; | |||||
public class IssuesChangesNotificationBuilderTesting { | public class IssuesChangesNotificationBuilderTesting { | ||||
private static final RuleType[] RULE_TYPES = {CODE_SMELL, BUG, VULNERABILITY, SECURITY_HOTSPOT}; | |||||
private IssuesChangesNotificationBuilderTesting() { | |||||
} | |||||
public static Rule ruleOf(RuleDto rule) { | public static Rule ruleOf(RuleDto rule) { | ||||
return new Rule(rule.getKey(), rule.getName()); | |||||
return new Rule(rule.getKey(), RuleType.valueOfNullable(rule.getType()), rule.getName()); | |||||
} | } | ||||
public static Rule ruleOf(RuleDefinitionDto rule) { | public static Rule ruleOf(RuleDefinitionDto rule) { | ||||
return new Rule(rule.getKey(), rule.getName()); | |||||
return new Rule(rule.getKey(), RuleType.valueOfNullable(rule.getType()), rule.getName()); | |||||
} | } | ||||
public static User userOf(UserDto changeAuthor) { | public static User userOf(UserDto changeAuthor) { | ||||
.build(); | .build(); | ||||
} | } | ||||
static ChangedIssue newChangedIssue(String key, String status, Project project, String ruleName) { | |||||
return newChangedIssue(key, status, project, newRule(ruleName)); | |||||
static ChangedIssue newChangedIssue(String key, String status, Project project, String ruleName, RuleType ruleType) { | |||||
return newChangedIssue(key, status, project, newRule(ruleName, ruleType)); | |||||
} | } | ||||
static ChangedIssue newChangedIssue(String key, String status, Project project, Rule rule) { | static ChangedIssue newChangedIssue(String key, String status, Project project, Rule rule) { | ||||
.build(); | .build(); | ||||
} | } | ||||
static Rule newRule(String ruleName) { | |||||
return new Rule(RuleKey.of(randomAlphabetic(6), randomAlphabetic(7)), ruleName); | |||||
static Rule newRule(String ruleName, RuleType ruleType) { | |||||
return new Rule(RuleKey.of(randomAlphabetic(6), randomAlphabetic(7)), ruleType, ruleName); | |||||
} | |||||
static Rule newRandomNotAHotspotRule(String ruleName) { | |||||
return newRule(ruleName, randomRuleTypeHotspotExcluded()); | |||||
} | |||||
static Rule newSecurityHotspotRule(String ruleName) { | |||||
return newRule(ruleName, SECURITY_HOTSPOT); | |||||
} | } | ||||
static Project newProject(String uuid) { | static Project newProject(String uuid) { | ||||
static AnalysisChange newAnalysisChange() { | static AnalysisChange newAnalysisChange() { | ||||
return new AnalysisChange(new Random().nextLong()); | return new AnalysisChange(new Random().nextLong()); | ||||
} | } | ||||
static RuleType randomRuleTypeHotspotExcluded() { | |||||
return RULE_TYPES[new Random().nextInt(RULE_TYPES.length - 1)]; | |||||
} | |||||
} | } |
.setNewStatus(issue.status()) | .setNewStatus(issue.status()) | ||||
.setNewResolution(issue.resolution()) | .setNewResolution(issue.resolution()) | ||||
.setAssignee(assignee.map(u -> new User(u.getUuid(), u.getLogin(), u.getName())).orElse(null)) | .setAssignee(assignee.map(u -> new User(u.getUuid(), u.getLogin(), u.getName())).orElse(null)) | ||||
.setRule(new IssuesChangesNotificationBuilder.Rule(ruleDefinitionDto.getKey(), ruleDefinitionDto.getName())) | |||||
.setRule(new IssuesChangesNotificationBuilder.Rule(ruleDefinitionDto.getKey(), RuleType.valueOfNullable(ruleDefinitionDto.getType()), ruleDefinitionDto.getName())) | |||||
.setProject(new Project.Builder(projectDto.uuid()) | .setProject(new Project.Builder(projectDto.uuid()) | ||||
.setKey(projectDto.getKey()) | .setKey(projectDto.getKey()) | ||||
.setProjectName(projectDto.name()) | .setProjectName(projectDto.name()) |
import javax.annotation.Nullable; | import javax.annotation.Nullable; | ||||
import org.sonar.api.rule.RuleKey; | import org.sonar.api.rule.RuleKey; | ||||
import org.sonar.api.rule.RuleStatus; | import org.sonar.api.rule.RuleStatus; | ||||
import org.sonar.api.rules.RuleType; | |||||
import org.sonar.core.issue.DefaultIssue; | import org.sonar.core.issue.DefaultIssue; | ||||
import org.sonar.core.issue.IssueChangeContext; | import org.sonar.core.issue.IssueChangeContext; | ||||
import org.sonar.core.util.stream.MoreCollectors; | import org.sonar.core.util.stream.MoreCollectors; | ||||
.setNewResolution(issue.resolution()) | .setNewResolution(issue.resolution()) | ||||
.setNewStatus(issue.status()) | .setNewStatus(issue.status()) | ||||
.setAssignee(assignee.map(assigneeDto -> new User(assigneeDto.getUuid(), assigneeDto.getLogin(), assigneeDto.getName())).orElse(null)) | .setAssignee(assignee.map(assigneeDto -> new User(assigneeDto.getUuid(), assigneeDto.getLogin(), assigneeDto.getName())).orElse(null)) | ||||
.setRule(rule.map(r -> new Rule(r.getKey(), r.getName())).get()) | |||||
.setRule(rule.map(r -> new Rule(r.getKey(), RuleType.valueOfNullable(r.getType()), r.getName())).get()) | |||||
.setProject(new Project.Builder(project.uuid()) | .setProject(new Project.Builder(project.uuid()) | ||||
.setKey(project.getKey()) | .setKey(project.getKey()) | ||||
.setProjectName(project.name()) | .setProjectName(project.name()) |
#------------------------------------------------------------------------------ | #------------------------------------------------------------------------------ | ||||
notification.channel.EmailNotificationChannel=Email | notification.channel.EmailNotificationChannel=Email | ||||
notification.dispatcher.information=A notification is never sent to the author of the event. | notification.dispatcher.information=A notification is never sent to the author of the event. | ||||
notification.dispatcher.ChangesOnMyIssue=Changes in issues assigned to me | |||||
notification.dispatcher.ChangesOnMyIssue=Changes in issues/hotspots assigned to me | |||||
notification.dispatcher.NewIssues=New issues | notification.dispatcher.NewIssues=New issues | ||||
notification.dispatcher.NewAlerts=New quality gate status | notification.dispatcher.NewAlerts=New quality gate status | ||||
notification.dispatcher.NewFalsePositiveIssue=Issues resolved as false positive or won't fix | notification.dispatcher.NewFalsePositiveIssue=Issues resolved as false positive or won't fix |
} | } | ||||
throw new IllegalArgumentException(format("Unsupported type value : %d", dbConstant)); | throw new IllegalArgumentException(format("Unsupported type value : %d", dbConstant)); | ||||
} | } | ||||
@CheckForNull | @CheckForNull | ||||
public static RuleType valueOfNullable(int dbConstant) { | public static RuleType valueOfNullable(int dbConstant) { | ||||
// iterating the array is fast-enough as size is small. No need for a map. | // iterating the array is fast-enough as size is small. No need for a map. | ||||
} | } | ||||
throw new IllegalArgumentException(format("Unsupported type value : %d", dbConstant)); | throw new IllegalArgumentException(format("Unsupported type value : %d", dbConstant)); | ||||
} | } | ||||
} | } |