3 * Copyright (C) 2009-2023 SonarSource SA
4 * mailto:info AT sonarsource DOT com
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 package org.sonar.server.issue.notification;
22 import com.google.common.collect.ImmutableSet;
23 import com.tngtech.java.junit.dataprovider.DataProvider;
24 import com.tngtech.java.junit.dataprovider.DataProviderRunner;
25 import com.tngtech.java.junit.dataprovider.UseDataProvider;
26 import java.util.Collections;
27 import java.util.List;
28 import java.util.Locale;
29 import java.util.Random;
30 import java.util.stream.IntStream;
31 import java.util.stream.Stream;
32 import org.junit.Test;
33 import org.junit.runner.RunWith;
34 import org.sonar.api.config.EmailSettings;
35 import org.sonar.api.notifications.Notification;
36 import org.sonar.api.rule.RuleKey;
37 import org.sonar.api.rules.RuleType;
38 import org.sonar.core.i18n.I18n;
39 import org.sonar.server.issue.notification.FPOrWontFixNotification.FpOrWontFix;
40 import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.AnalysisChange;
41 import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.Change;
42 import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.ChangedIssue;
43 import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.Project;
44 import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.Rule;
45 import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.User;
46 import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.UserChange;
47 import org.sonar.test.html.HtmlFragmentAssert;
49 import static java.util.stream.Collectors.joining;
50 import static java.util.stream.Collectors.toList;
51 import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
52 import static org.assertj.core.api.Assertions.assertThat;
53 import static org.mockito.Mockito.mock;
54 import static org.mockito.Mockito.when;
55 import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT;
56 import static org.sonar.server.issue.notification.FPOrWontFixNotification.FpOrWontFix.FP;
57 import static org.sonar.server.issue.notification.FPOrWontFixNotification.FpOrWontFix.WONT_FIX;
58 import static org.sonar.server.issue.notification.IssuesChangesNotificationBuilderTesting.newRandomNotAHotspotRule;
59 import static org.sonar.server.issue.notification.IssuesChangesNotificationBuilderTesting.newSecurityHotspotRule;
60 import static org.sonar.server.issue.notification.IssuesChangesNotificationBuilderTesting.randomRuleTypeHotspotExcluded;
62 @RunWith(DataProviderRunner.class)
63 public class FpOrWontFixEmailTemplateTest {
64 private I18n i18n = mock(I18n.class);
65 private EmailSettings emailSettings = mock(EmailSettings.class);
66 private FpOrWontFixEmailTemplate underTest = new FpOrWontFixEmailTemplate(i18n, emailSettings);
69 public void format_returns_null_on_Notification() {
70 EmailMessage emailMessage = underTest.format(mock(Notification.class));
72 assertThat(emailMessage).isNull();
76 public void format_sets_message_id_specific_to_fp() {
77 EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(mock(Change.class), Collections.emptySet(), FP));
79 assertThat(emailMessage.getMessageId()).isEqualTo("fp-issue-changes");
83 public void format_sets_message_id_specific_to_wont_fix() {
84 EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(mock(Change.class), Collections.emptySet(), WONT_FIX));
86 assertThat(emailMessage.getMessageId()).isEqualTo("wontfix-issue-changes");
90 public void format_sets_subject_specific_to_fp() {
91 EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(mock(Change.class), Collections.emptySet(), FP));
93 assertThat(emailMessage.getSubject()).isEqualTo("Issues marked as False Positive");
97 public void format_sets_subject_specific_to_wont_fix() {
98 EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(mock(Change.class), Collections.emptySet(), WONT_FIX));
100 assertThat(emailMessage.getSubject()).isEqualTo("Issues marked as Won't Fix");
104 public void format_sets_from_to_name_of_author_change_when_available() {
105 UserChange change = new UserChange(new Random().nextLong(), new User(randomAlphabetic(5), randomAlphabetic(6), randomAlphabetic(7)));
106 EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, Collections.emptySet(), WONT_FIX));
108 assertThat(emailMessage.getFrom()).isEqualTo(change.getUser().getName().get());
112 public void format_sets_from_to_login_of_author_change_when_name_is_not_available() {
113 UserChange change = new UserChange(new Random().nextLong(), new User(randomAlphabetic(5), randomAlphabetic(6), null));
114 EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, Collections.emptySet(), WONT_FIX));
116 assertThat(emailMessage.getFrom()).isEqualTo(change.getUser().getLogin());
120 public void format_sets_from_to_null_when_analysisChange() {
121 AnalysisChange change = new AnalysisChange(new Random().nextLong());
122 EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, Collections.emptySet(), WONT_FIX));
124 assertThat(emailMessage.getFrom()).isNull();
128 @UseDataProvider("userOrAnalysisChange")
129 public void formats_returns_html_message_with_only_footer_and_header_when_no_issue_for_FPs(Change change) {
130 formats_returns_html_message_with_only_footer_and_header_when_no_issue(change, FP, "False Positive");
134 @UseDataProvider("userOrAnalysisChange")
135 public void formats_returns_html_message_with_only_footer_and_header_when_no_issue_for_Wont_fixs(Change change) {
136 formats_returns_html_message_with_only_footer_and_header_when_no_issue(change, WONT_FIX, "Won't Fix");
139 public void formats_returns_html_message_with_only_footer_and_header_when_no_issue(Change change, FpOrWontFix fpOrWontFix, String fpOrWontFixLabel) {
140 String wordingNotification = randomAlphabetic(20);
141 String host = randomAlphabetic(15);
142 when(i18n.message(Locale.ENGLISH, "notification.dispatcher.NewFalsePositiveIssue", "notification.dispatcher.NewFalsePositiveIssue"))
143 .thenReturn(wordingNotification);
144 when(emailSettings.getServerBaseURL()).thenReturn(host);
146 EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, Collections.emptySet(), fpOrWontFix));
148 String footerText = "You received this email because you are subscribed to \"" + wordingNotification + "\" notifications from SonarQube."
149 + " Click here to edit your email preferences.";
150 HtmlFragmentAssert.assertThat(emailMessage.getMessage())
153 .hasParagraph("A manual change has resolved an issue as " + fpOrWontFixLabel + ":")
156 .hasParagraph(footerText)
157 .withSmallOn(footerText)
158 .withLink("here", host + "/account/notifications")
163 @UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange")
164 public void formats_returns_html_message_for_single_issue_on_master(Change change, FpOrWontFix fpOrWontFix) {
165 Project project = newProject("1");
166 String ruleName = randomAlphabetic(8);
167 String host = randomAlphabetic(15);
168 ChangedIssue changedIssue = newChangedIssue("key", project, ruleName, randomRuleTypeHotspotExcluded());
169 when(emailSettings.getServerBaseURL()).thenReturn(host);
171 EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, ImmutableSet.of(changedIssue), fpOrWontFix));
173 HtmlFragmentAssert.assertThat(emailMessage.getMessage())
174 .hasParagraph().hasParagraph() // skip header
175 .hasParagraph(project.getProjectName())
176 .hasList("Rule " + ruleName + " - See the single issue")
177 .withLink("See the single issue", host + "/project/issues?id=" + project.getKey() + "&issues=" + changedIssue.getKey() + "&open=" + changedIssue.getKey())
178 .hasParagraph().hasParagraph() // skip footer
183 @UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange")
184 public void formats_returns_html_message_for_single_hotspot_on_master(Change change, FpOrWontFix fpOrWontFix) {
185 Project project = newProject("1");
186 String ruleName = randomAlphabetic(8);
187 String host = randomAlphabetic(15);
188 ChangedIssue changedIssue = newChangedIssue("key", project, ruleName, SECURITY_HOTSPOT);
189 when(emailSettings.getServerBaseURL()).thenReturn(host);
191 EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, ImmutableSet.of(changedIssue), fpOrWontFix));
193 HtmlFragmentAssert.assertThat(emailMessage.getMessage())
194 .hasParagraph().hasParagraph() // skip header
195 .hasParagraph(project.getProjectName())
196 .hasList("Rule " + ruleName + " - See the single hotspot")
197 .withLink("See the single hotspot", host + "/project/issues?id=" + project.getKey() + "&issues=" + changedIssue.getKey() + "&open=" + changedIssue.getKey())
198 .hasParagraph().hasParagraph() // skip footer
203 @UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange")
204 public void formats_returns_html_message_for_single_issue_on_branch(Change change, FpOrWontFix fpOrWontFix) {
205 String branchName = randomAlphabetic(6);
206 Project project = newBranch("1", branchName);
207 String ruleName = randomAlphabetic(8);
208 String host = randomAlphabetic(15);
210 ChangedIssue changedIssue = newChangedIssue(key, project, ruleName, randomRuleTypeHotspotExcluded());
211 when(emailSettings.getServerBaseURL()).thenReturn(host);
213 EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, ImmutableSet.of(changedIssue), fpOrWontFix));
215 HtmlFragmentAssert.assertThat(emailMessage.getMessage())
216 .hasParagraph().hasParagraph() // skip header
217 .hasParagraph(project.getProjectName() + ", " + branchName)
218 .hasList("Rule " + ruleName + " - See the single issue")
219 .withLink("See the single issue",
220 host + "/project/issues?id=" + project.getKey() + "&branch=" + branchName + "&issues=" + changedIssue.getKey() + "&open=" + changedIssue.getKey())
221 .hasParagraph().hasParagraph() // skip footer
226 @UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange")
227 public void formats_returns_html_message_for_single_hotspot_on_branch(Change change, FpOrWontFix fpOrWontFix) {
228 String branchName = randomAlphabetic(6);
229 Project project = newBranch("1", branchName);
230 String ruleName = randomAlphabetic(8);
231 String host = randomAlphabetic(15);
233 ChangedIssue changedIssue = newChangedIssue(key, project, ruleName, SECURITY_HOTSPOT);
234 when(emailSettings.getServerBaseURL()).thenReturn(host);
236 EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, ImmutableSet.of(changedIssue), fpOrWontFix));
238 HtmlFragmentAssert.assertThat(emailMessage.getMessage())
239 .hasParagraph().hasParagraph() // skip header
240 .hasParagraph(project.getProjectName() + ", " + branchName)
241 .hasList("Rule " + ruleName + " - See the single hotspot")
242 .withLink("See the single hotspot",
243 host + "/project/issues?id=" + project.getKey() + "&branch=" + branchName + "&issues=" + changedIssue.getKey() + "&open=" + changedIssue.getKey())
244 .hasParagraph().hasParagraph() // skip footer
249 @UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange")
250 public void formats_returns_html_message_for_multiple_issues_of_same_rule_on_same_project_on_master(Change change, FpOrWontFix fpOrWontFix) {
251 Project project = newProject("1");
252 String ruleName = randomAlphabetic(8);
253 String host = randomAlphabetic(15);
254 Rule rule = newRandomNotAHotspotRule(ruleName);
255 List<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(5))
256 .mapToObj(i -> newChangedIssue("issue_" + i, project, rule))
258 when(emailSettings.getServerBaseURL()).thenReturn(host);
260 EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, ImmutableSet.copyOf(changedIssues), fpOrWontFix));
262 String expectedHref = host + "/project/issues?id=" + project.getKey()
263 + "&issues=" + changedIssues.stream().map(ChangedIssue::getKey).collect(joining("%2C"));
264 String expectedLinkText = "See all " + changedIssues.size() + " issues";
265 HtmlFragmentAssert.assertThat(emailMessage.getMessage())
266 .hasParagraph().hasParagraph() // skip header
267 .hasParagraph(project.getProjectName())
268 .hasList("Rule " + ruleName + " - " + expectedLinkText)
269 .withLink(expectedLinkText, expectedHref)
270 .hasParagraph().hasParagraph() // skip footer
275 @UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange")
276 public void formats_returns_html_message_for_multiple_hotspots_of_same_rule_on_same_project_on_master(Change change, FpOrWontFix fpOrWontFix) {
277 Project project = newProject("1");
278 String ruleName = randomAlphabetic(8);
279 String host = randomAlphabetic(15);
280 Rule rule = newSecurityHotspotRule(ruleName);
281 List<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(5))
282 .mapToObj(i -> newChangedIssue("issue_" + i, project, rule))
284 when(emailSettings.getServerBaseURL()).thenReturn(host);
286 EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, ImmutableSet.copyOf(changedIssues), fpOrWontFix));
288 String expectedHref = host + "/project/issues?id=" + project.getKey()
289 + "&issues=" + changedIssues.stream().map(ChangedIssue::getKey).collect(joining("%2C"));
290 String expectedLinkText = "See all " + changedIssues.size() + " hotspots";
291 HtmlFragmentAssert.assertThat(emailMessage.getMessage())
292 .hasParagraph().hasParagraph() // skip header
293 .hasParagraph(project.getProjectName())
294 .hasList("Rule " + ruleName + " - " + expectedLinkText)
295 .withLink(expectedLinkText, expectedHref)
296 .hasParagraph().hasParagraph() // skip footer
301 @UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange")
302 public void formats_returns_html_message_for_multiple_issues_of_same_rule_on_same_project_on_branch(Change change, FpOrWontFix fpOrWontFix) {
303 String branchName = randomAlphabetic(19);
304 Project project = newBranch("1", branchName);
305 String ruleName = randomAlphabetic(8);
306 String host = randomAlphabetic(15);
307 Rule rule = newRandomNotAHotspotRule(ruleName);
308 List<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(5))
309 .mapToObj(i -> newChangedIssue("issue_" + i, project, rule))
311 when(emailSettings.getServerBaseURL()).thenReturn(host);
313 EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, ImmutableSet.copyOf(changedIssues), fpOrWontFix));
315 String expectedHref = host + "/project/issues?id=" + project.getKey() + "&branch=" + branchName
316 + "&issues=" + changedIssues.stream().map(ChangedIssue::getKey).collect(joining("%2C"));
317 String expectedLinkText = "See all " + changedIssues.size() + " issues";
318 HtmlFragmentAssert.assertThat(emailMessage.getMessage())
319 .hasParagraph().hasParagraph() // skip header
320 .hasParagraph(project.getProjectName() + ", " + branchName)
321 .hasList("Rule " + ruleName + " - " + expectedLinkText)
322 .withLink(expectedLinkText, expectedHref)
323 .hasParagraph().hasParagraph() // skip footer
328 @UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange")
329 public void formats_returns_html_message_for_multiple_hotspots_of_same_rule_on_same_project_on_branch(Change change, FpOrWontFix fpOrWontFix) {
330 String branchName = randomAlphabetic(19);
331 Project project = newBranch("1", branchName);
332 String ruleName = randomAlphabetic(8);
333 String host = randomAlphabetic(15);
334 Rule rule = newSecurityHotspotRule(ruleName);
335 List<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(5))
336 .mapToObj(i -> newChangedIssue("issue_" + i, project, rule))
338 when(emailSettings.getServerBaseURL()).thenReturn(host);
340 EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, ImmutableSet.copyOf(changedIssues), fpOrWontFix));
342 String expectedHref = host + "/project/issues?id=" + project.getKey() + "&branch=" + branchName
343 + "&issues=" + changedIssues.stream().map(ChangedIssue::getKey).collect(joining("%2C"));
344 String expectedLinkText = "See all " + changedIssues.size() + " hotspots";
345 HtmlFragmentAssert.assertThat(emailMessage.getMessage())
346 .hasParagraph().hasParagraph() // skip header
347 .hasParagraph(project.getProjectName() + ", " + branchName)
348 .hasList("Rule " + ruleName + " - " + expectedLinkText)
349 .withLink(expectedLinkText, expectedHref)
350 .hasParagraph().hasParagraph() // skip footer
355 @UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange")
356 public void formats_returns_html_message_with_projects_ordered_by_name(Change change, FpOrWontFix fpOrWontFix) {
357 Project project1 = newProject("1");
358 Project project1Branch1 = newBranch("1", "a");
359 Project project1Branch2 = newBranch("1", "b");
360 Project project2 = newProject("B");
361 Project project2Branch1 = newBranch("B", "a");
362 Project project3 = newProject("C");
363 String host = randomAlphabetic(15);
364 List<ChangedIssue> changedIssues = Stream.of(project1, project1Branch1, project1Branch2, project2, project2Branch1, project3)
365 .map(project -> newChangedIssue("issue_" + project.getUuid(), project, newRandomNotAHotspotRule(randomAlphabetic(2))))
367 Collections.shuffle(changedIssues);
368 when(emailSettings.getServerBaseURL()).thenReturn(host);
370 EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, ImmutableSet.copyOf(changedIssues), fpOrWontFix));
372 HtmlFragmentAssert.assertThat(emailMessage.getMessage())
373 .hasParagraph().hasParagraph() // skip header
374 .hasParagraph(project1.getProjectName())
376 .hasParagraph(project1Branch1.getProjectName() + ", " + project1Branch1.getBranchName().get())
378 .hasParagraph(project1Branch2.getProjectName() + ", " + project1Branch2.getBranchName().get())
380 .hasParagraph(project2.getProjectName())
382 .hasParagraph(project2Branch1.getProjectName() + ", " + project2Branch1.getBranchName().get())
384 .hasParagraph(project3.getProjectName())
386 .hasParagraph().hasParagraph() // skip footer
391 @UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange")
392 public void formats_returns_html_message_with_rules_ordered_by_name(Change change, FpOrWontFix fpOrWontFix) {
393 Project project = newProject("1");
394 Rule rule1 = newRandomNotAHotspotRule("1");
395 Rule rule2 = newRandomNotAHotspotRule("a");
396 Rule rule3 = newRandomNotAHotspotRule("b");
397 Rule rule4 = newRandomNotAHotspotRule("X");
398 String host = randomAlphabetic(15);
399 List<ChangedIssue> changedIssues = Stream.of(rule1, rule2, rule3, rule4)
400 .map(rule -> newChangedIssue("issue_" + rule.getName(), project, rule))
402 Collections.shuffle(changedIssues);
403 when(emailSettings.getServerBaseURL()).thenReturn(host);
405 EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, ImmutableSet.copyOf(changedIssues), fpOrWontFix));
407 HtmlFragmentAssert.assertThat(emailMessage.getMessage())
408 .hasParagraph().hasParagraph() // skip header
409 .hasParagraph(project.getProjectName())
411 "Rule " + rule1.getName() + " - See the single issue",
412 "Rule " + rule2.getName() + " - See the single issue",
413 "Rule " + rule3.getName() + " - See the single issue",
414 "Rule " + rule4.getName() + " - See the single issue")
415 .hasParagraph().hasParagraph() // skip footer
420 @UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange")
421 public void formats_returns_html_message_with_multiple_links_by_rule_of_groups_of_up_to_40_issues(Change change, FpOrWontFix fpOrWontFix) {
422 Project project1 = newProject("1");
423 Project project2 = newProject("V");
424 Project project2Branch = newBranch("V", "AB");
425 Rule rule1 = newRandomNotAHotspotRule("1");
426 Rule rule2 = newRandomNotAHotspotRule("a");
427 String host = randomAlphabetic(15);
428 List<ChangedIssue> changedIssues = Stream.of(
429 IntStream.range(0, 39).mapToObj(i -> newChangedIssue("39_" + i, project1, rule1)),
430 IntStream.range(0, 40).mapToObj(i -> newChangedIssue("40_" + i, project1, rule2)),
431 IntStream.range(0, 81).mapToObj(i -> newChangedIssue("1-40_41-80_1_" + i, project2, rule2)),
432 IntStream.range(0, 6).mapToObj(i -> newChangedIssue("6_" + i, project2Branch, rule1)))
435 Collections.shuffle(changedIssues);
436 when(emailSettings.getServerBaseURL()).thenReturn(host);
438 EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, ImmutableSet.copyOf(changedIssues), fpOrWontFix));
440 HtmlFragmentAssert.assertThat(emailMessage.getMessage())
441 .hasParagraph().hasParagraph() // skip header
442 .hasParagraph(project1.getProjectName())
445 "Rule " + rule1.getName() + " - See all 39 issues",
446 "Rule " + rule2.getName() + " - See all 40 issues")
447 .withLink("See all 39 issues",
448 host + "/project/issues?id=" + project1.getKey()
449 + "&issues=" + IntStream.range(0, 39).mapToObj(i -> "39_" + i).sorted().collect(joining("%2C")))
450 .withLink("See all 40 issues",
451 host + "/project/issues?id=" + project1.getKey()
452 + "&issues=" + IntStream.range(0, 40).mapToObj(i -> "40_" + i).sorted().collect(joining("%2C")))
453 .hasParagraph(project2.getProjectName())
454 .hasList("Rule " + rule2.getName() + " - See issues 1-40 41-80 81")
456 host + "/project/issues?id=" + project2.getKey()
457 + "&issues=" + IntStream.range(0, 81).mapToObj(i -> "1-40_41-80_1_" + i).sorted().limit(40).collect(joining("%2C")))
459 host + "/project/issues?id=" + project2.getKey()
460 + "&issues=" + IntStream.range(0, 81).mapToObj(i -> "1-40_41-80_1_" + i).sorted().skip(40).limit(40).collect(joining("%2C")))
462 host + "/project/issues?id=" + project2.getKey()
463 + "&issues=" + "1-40_41-80_1_9" + "&open=" + "1-40_41-80_1_9")
464 .hasParagraph(project2Branch.getProjectName() + ", " + project2Branch.getBranchName().get())
465 .hasList("Rule " + rule1.getName() + " - See all 6 issues")
466 .withLink("See all 6 issues",
467 host + "/project/issues?id=" + project2Branch.getKey() + "&branch=" + project2Branch.getBranchName().get()
468 + "&issues=" + IntStream.range(0, 6).mapToObj(i -> "6_" + i).sorted().collect(joining("%2C")))
469 .hasParagraph().hasParagraph() // skip footer
474 public static Object[][] userOrAnalysisChange() {
475 AnalysisChange analysisChange = new AnalysisChange(new Random().nextLong());
476 UserChange userChange = new UserChange(new Random().nextLong(), new User(randomAlphabetic(5), randomAlphabetic(6),
477 new Random().nextBoolean() ? null : randomAlphabetic(7)));
478 return new Object[][] {
485 public static Object[][] fpOrWontFixValuesByUserOrAnalysisChange() {
486 AnalysisChange analysisChange = new AnalysisChange(new Random().nextLong());
487 UserChange userChange = new UserChange(new Random().nextLong(), new User(randomAlphabetic(5), randomAlphabetic(6),
488 new Random().nextBoolean() ? null : randomAlphabetic(7)));
489 return new Object[][] {
490 {analysisChange, FP},
491 {analysisChange, WONT_FIX},
493 {userChange, WONT_FIX}
497 private static ChangedIssue newChangedIssue(String key, Project project, String ruleName, RuleType ruleType) {
498 return newChangedIssue(key, project, newRule(ruleName, ruleType));
501 private static ChangedIssue newChangedIssue(String key, Project project, Rule rule) {
502 return new ChangedIssue.Builder(key)
503 .setNewStatus(randomAlphabetic(19))
509 private static Rule newRule(String ruleName, RuleType ruleType) {
510 return new Rule(RuleKey.of(randomAlphabetic(6), randomAlphabetic(7)), ruleType, ruleName);
513 private static Project newProject(String uuid) {
514 return new Project.Builder(uuid).setProjectName(uuid + "_name").setKey(uuid + "_key").build();
517 private static Project newBranch(String uuid, String branchName) {
518 return new Project.Builder(uuid).setProjectName(uuid + "_name").setKey(uuid + "_key").setBranchName(branchName).build();