You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

FpOrWontFixEmailTemplateTest.java 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2020 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  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.
  10. *
  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.
  15. *
  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.
  19. */
  20. package org.sonar.server.issue.notification;
  21. import com.google.common.collect.ImmutableSet;
  22. import com.tngtech.java.junit.dataprovider.DataProvider;
  23. import com.tngtech.java.junit.dataprovider.DataProviderRunner;
  24. import com.tngtech.java.junit.dataprovider.UseDataProvider;
  25. import java.util.Collections;
  26. import java.util.List;
  27. import java.util.Locale;
  28. import java.util.Random;
  29. import java.util.stream.IntStream;
  30. import java.util.stream.Stream;
  31. import org.junit.Test;
  32. import org.junit.runner.RunWith;
  33. import org.sonar.api.config.EmailSettings;
  34. import org.sonar.api.notifications.Notification;
  35. import org.sonar.api.rule.RuleKey;
  36. import org.sonar.api.rules.RuleType;
  37. import org.sonar.core.i18n.I18n;
  38. import org.sonar.server.issue.notification.FPOrWontFixNotification.FpOrWontFix;
  39. import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.AnalysisChange;
  40. import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.Change;
  41. import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.ChangedIssue;
  42. import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.Project;
  43. import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.Rule;
  44. import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.User;
  45. import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.UserChange;
  46. import org.sonar.test.html.HtmlFragmentAssert;
  47. import static java.util.stream.Collectors.joining;
  48. import static java.util.stream.Collectors.toList;
  49. import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
  50. import static org.assertj.core.api.Assertions.assertThat;
  51. import static org.mockito.Mockito.mock;
  52. import static org.mockito.Mockito.when;
  53. import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT;
  54. import static org.sonar.server.issue.notification.FPOrWontFixNotification.FpOrWontFix.FP;
  55. import static org.sonar.server.issue.notification.FPOrWontFixNotification.FpOrWontFix.WONT_FIX;
  56. import static org.sonar.server.issue.notification.IssuesChangesNotificationBuilderTesting.newRandomNotAHotspotRule;
  57. import static org.sonar.server.issue.notification.IssuesChangesNotificationBuilderTesting.newSecurityHotspotRule;
  58. import static org.sonar.server.issue.notification.IssuesChangesNotificationBuilderTesting.randomRuleTypeHotspotExcluded;
  59. @RunWith(DataProviderRunner.class)
  60. public class FpOrWontFixEmailTemplateTest {
  61. private I18n i18n = mock(I18n.class);
  62. private EmailSettings emailSettings = mock(EmailSettings.class);
  63. private FpOrWontFixEmailTemplate underTest = new FpOrWontFixEmailTemplate(i18n, emailSettings);
  64. @Test
  65. public void format_returns_null_on_Notification() {
  66. EmailMessage emailMessage = underTest.format(mock(Notification.class));
  67. assertThat(emailMessage).isNull();
  68. }
  69. @Test
  70. public void format_sets_message_id_specific_to_fp() {
  71. EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(mock(Change.class), Collections.emptySet(), FP));
  72. assertThat(emailMessage.getMessageId()).isEqualTo("fp-issue-changes");
  73. }
  74. @Test
  75. public void format_sets_message_id_specific_to_wont_fix() {
  76. EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(mock(Change.class), Collections.emptySet(), WONT_FIX));
  77. assertThat(emailMessage.getMessageId()).isEqualTo("wontfix-issue-changes");
  78. }
  79. @Test
  80. public void format_sets_subject_specific_to_fp() {
  81. EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(mock(Change.class), Collections.emptySet(), FP));
  82. assertThat(emailMessage.getSubject()).isEqualTo("Issues marked as False Positive");
  83. }
  84. @Test
  85. public void format_sets_subject_specific_to_wont_fix() {
  86. EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(mock(Change.class), Collections.emptySet(), WONT_FIX));
  87. assertThat(emailMessage.getSubject()).isEqualTo("Issues marked as Won't Fix");
  88. }
  89. @Test
  90. public void format_sets_from_to_name_of_author_change_when_available() {
  91. UserChange change = new UserChange(new Random().nextLong(), new User(randomAlphabetic(5), randomAlphabetic(6), randomAlphabetic(7)));
  92. EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, Collections.emptySet(), WONT_FIX));
  93. assertThat(emailMessage.getFrom()).isEqualTo(change.getUser().getName().get());
  94. }
  95. @Test
  96. public void format_sets_from_to_login_of_author_change_when_name_is_not_available() {
  97. UserChange change = new UserChange(new Random().nextLong(), new User(randomAlphabetic(5), randomAlphabetic(6), null));
  98. EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, Collections.emptySet(), WONT_FIX));
  99. assertThat(emailMessage.getFrom()).isEqualTo(change.getUser().getLogin());
  100. }
  101. @Test
  102. public void format_sets_from_to_null_when_analysisChange() {
  103. AnalysisChange change = new AnalysisChange(new Random().nextLong());
  104. EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, Collections.emptySet(), WONT_FIX));
  105. assertThat(emailMessage.getFrom()).isNull();
  106. }
  107. @Test
  108. @UseDataProvider("userOrAnalysisChange")
  109. public void formats_returns_html_message_with_only_footer_and_header_when_no_issue_for_FPs(Change change) {
  110. formats_returns_html_message_with_only_footer_and_header_when_no_issue(change, FP, "False Positive");
  111. }
  112. @Test
  113. @UseDataProvider("userOrAnalysisChange")
  114. public void formats_returns_html_message_with_only_footer_and_header_when_no_issue_for_Wont_fixs(Change change) {
  115. formats_returns_html_message_with_only_footer_and_header_when_no_issue(change, WONT_FIX, "Won't Fix");
  116. }
  117. public void formats_returns_html_message_with_only_footer_and_header_when_no_issue(Change change, FpOrWontFix fpOrWontFix, String fpOrWontFixLabel) {
  118. String wordingNotification = randomAlphabetic(20);
  119. String host = randomAlphabetic(15);
  120. when(i18n.message(Locale.ENGLISH, "notification.dispatcher.NewFalsePositiveIssue", "notification.dispatcher.NewFalsePositiveIssue"))
  121. .thenReturn(wordingNotification);
  122. when(emailSettings.getServerBaseURL()).thenReturn(host);
  123. EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, Collections.emptySet(), fpOrWontFix));
  124. String footerText = "You received this email because you are subscribed to \"" + wordingNotification + "\" notifications from SonarQube."
  125. + " Click here to edit your email preferences.";
  126. HtmlFragmentAssert.assertThat(emailMessage.getMessage())
  127. .hasParagraph("Hi,")
  128. .withoutLink()
  129. .hasParagraph("A manual change has resolved an issue as " + fpOrWontFixLabel + ":")
  130. .withoutLink()
  131. .hasEmptyParagraph()
  132. .hasParagraph(footerText)
  133. .withSmallOn(footerText)
  134. .withLink("here", host + "/account/notifications")
  135. .noMoreBlock();
  136. }
  137. @Test
  138. @UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange")
  139. public void formats_returns_html_message_for_single_issue_on_master(Change change, FpOrWontFix fpOrWontFix) {
  140. Project project = newProject("1");
  141. String ruleName = randomAlphabetic(8);
  142. String host = randomAlphabetic(15);
  143. ChangedIssue changedIssue = newChangedIssue("key", project, ruleName, randomRuleTypeHotspotExcluded());
  144. when(emailSettings.getServerBaseURL()).thenReturn(host);
  145. EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, ImmutableSet.of(changedIssue), fpOrWontFix));
  146. HtmlFragmentAssert.assertThat(emailMessage.getMessage())
  147. .hasParagraph().hasParagraph() // skip header
  148. .hasParagraph(project.getProjectName())
  149. .hasList("Rule " + ruleName + " - See the single issue")
  150. .withLink("See the single issue", host + "/project/issues?id=" + project.getKey() + "&issues=" + changedIssue.getKey() + "&open=" + changedIssue.getKey())
  151. .hasParagraph().hasParagraph() // skip footer
  152. .noMoreBlock();
  153. }
  154. @Test
  155. @UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange")
  156. public void formats_returns_html_message_for_single_hotspot_on_master(Change change, FpOrWontFix fpOrWontFix) {
  157. Project project = newProject("1");
  158. String ruleName = randomAlphabetic(8);
  159. String host = randomAlphabetic(15);
  160. ChangedIssue changedIssue = newChangedIssue("key", project, ruleName, SECURITY_HOTSPOT);
  161. when(emailSettings.getServerBaseURL()).thenReturn(host);
  162. EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, ImmutableSet.of(changedIssue), fpOrWontFix));
  163. HtmlFragmentAssert.assertThat(emailMessage.getMessage())
  164. .hasParagraph().hasParagraph() // skip header
  165. .hasParagraph(project.getProjectName())
  166. .hasList("Rule " + ruleName + " - See the single hotspot")
  167. .withLink("See the single hotspot", host + "/project/issues?id=" + project.getKey() + "&issues=" + changedIssue.getKey() + "&open=" + changedIssue.getKey())
  168. .hasParagraph().hasParagraph() // skip footer
  169. .noMoreBlock();
  170. }
  171. @Test
  172. @UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange")
  173. public void formats_returns_html_message_for_single_issue_on_branch(Change change, FpOrWontFix fpOrWontFix) {
  174. String branchName = randomAlphabetic(6);
  175. Project project = newBranch("1", branchName);
  176. String ruleName = randomAlphabetic(8);
  177. String host = randomAlphabetic(15);
  178. String key = "key";
  179. ChangedIssue changedIssue = newChangedIssue(key, project, ruleName, randomRuleTypeHotspotExcluded());
  180. when(emailSettings.getServerBaseURL()).thenReturn(host);
  181. EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, ImmutableSet.of(changedIssue), fpOrWontFix));
  182. HtmlFragmentAssert.assertThat(emailMessage.getMessage())
  183. .hasParagraph().hasParagraph() // skip header
  184. .hasParagraph(project.getProjectName() + ", " + branchName)
  185. .hasList("Rule " + ruleName + " - See the single issue")
  186. .withLink("See the single issue",
  187. host + "/project/issues?id=" + project.getKey() + "&branch=" + branchName + "&issues=" + changedIssue.getKey() + "&open=" + changedIssue.getKey())
  188. .hasParagraph().hasParagraph() // skip footer
  189. .noMoreBlock();
  190. }
  191. @Test
  192. @UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange")
  193. public void formats_returns_html_message_for_single_hotspot_on_branch(Change change, FpOrWontFix fpOrWontFix) {
  194. String branchName = randomAlphabetic(6);
  195. Project project = newBranch("1", branchName);
  196. String ruleName = randomAlphabetic(8);
  197. String host = randomAlphabetic(15);
  198. String key = "key";
  199. ChangedIssue changedIssue = newChangedIssue(key, project, ruleName, SECURITY_HOTSPOT);
  200. when(emailSettings.getServerBaseURL()).thenReturn(host);
  201. EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, ImmutableSet.of(changedIssue), fpOrWontFix));
  202. HtmlFragmentAssert.assertThat(emailMessage.getMessage())
  203. .hasParagraph().hasParagraph() // skip header
  204. .hasParagraph(project.getProjectName() + ", " + branchName)
  205. .hasList("Rule " + ruleName + " - See the single hotspot")
  206. .withLink("See the single hotspot",
  207. host + "/project/issues?id=" + project.getKey() + "&branch=" + branchName + "&issues=" + changedIssue.getKey() + "&open=" + changedIssue.getKey())
  208. .hasParagraph().hasParagraph() // skip footer
  209. .noMoreBlock();
  210. }
  211. @Test
  212. @UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange")
  213. public void formats_returns_html_message_for_multiple_issues_of_same_rule_on_same_project_on_master(Change change, FpOrWontFix fpOrWontFix) {
  214. Project project = newProject("1");
  215. String ruleName = randomAlphabetic(8);
  216. String host = randomAlphabetic(15);
  217. Rule rule = newRandomNotAHotspotRule(ruleName);
  218. List<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(5))
  219. .mapToObj(i -> newChangedIssue("issue_" + i, project, rule))
  220. .collect(toList());
  221. when(emailSettings.getServerBaseURL()).thenReturn(host);
  222. EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, ImmutableSet.copyOf(changedIssues), fpOrWontFix));
  223. String expectedHref = host + "/project/issues?id=" + project.getKey()
  224. + "&issues=" + changedIssues.stream().map(ChangedIssue::getKey).collect(joining("%2C"));
  225. String expectedLinkText = "See all " + changedIssues.size() + " issues";
  226. HtmlFragmentAssert.assertThat(emailMessage.getMessage())
  227. .hasParagraph().hasParagraph() // skip header
  228. .hasParagraph(project.getProjectName())
  229. .hasList("Rule " + ruleName + " - " + expectedLinkText)
  230. .withLink(expectedLinkText, expectedHref)
  231. .hasParagraph().hasParagraph() // skip footer
  232. .noMoreBlock();
  233. }
  234. @Test
  235. @UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange")
  236. public void formats_returns_html_message_for_multiple_hotspots_of_same_rule_on_same_project_on_master(Change change, FpOrWontFix fpOrWontFix) {
  237. Project project = newProject("1");
  238. String ruleName = randomAlphabetic(8);
  239. String host = randomAlphabetic(15);
  240. Rule rule = newSecurityHotspotRule(ruleName);
  241. List<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(5))
  242. .mapToObj(i -> newChangedIssue("issue_" + i, project, rule))
  243. .collect(toList());
  244. when(emailSettings.getServerBaseURL()).thenReturn(host);
  245. EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, ImmutableSet.copyOf(changedIssues), fpOrWontFix));
  246. String expectedHref = host + "/project/issues?id=" + project.getKey()
  247. + "&issues=" + changedIssues.stream().map(ChangedIssue::getKey).collect(joining("%2C"));
  248. String expectedLinkText = "See all " + changedIssues.size() + " hotspots";
  249. HtmlFragmentAssert.assertThat(emailMessage.getMessage())
  250. .hasParagraph().hasParagraph() // skip header
  251. .hasParagraph(project.getProjectName())
  252. .hasList("Rule " + ruleName + " - " + expectedLinkText)
  253. .withLink(expectedLinkText, expectedHref)
  254. .hasParagraph().hasParagraph() // skip footer
  255. .noMoreBlock();
  256. }
  257. @Test
  258. @UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange")
  259. public void formats_returns_html_message_for_multiple_issues_of_same_rule_on_same_project_on_branch(Change change, FpOrWontFix fpOrWontFix) {
  260. String branchName = randomAlphabetic(19);
  261. Project project = newBranch("1", branchName);
  262. String ruleName = randomAlphabetic(8);
  263. String host = randomAlphabetic(15);
  264. Rule rule = newRandomNotAHotspotRule(ruleName);
  265. List<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(5))
  266. .mapToObj(i -> newChangedIssue("issue_" + i, project, rule))
  267. .collect(toList());
  268. when(emailSettings.getServerBaseURL()).thenReturn(host);
  269. EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, ImmutableSet.copyOf(changedIssues), fpOrWontFix));
  270. String expectedHref = host + "/project/issues?id=" + project.getKey() + "&branch=" + branchName
  271. + "&issues=" + changedIssues.stream().map(ChangedIssue::getKey).collect(joining("%2C"));
  272. String expectedLinkText = "See all " + changedIssues.size() + " issues";
  273. HtmlFragmentAssert.assertThat(emailMessage.getMessage())
  274. .hasParagraph().hasParagraph() // skip header
  275. .hasParagraph(project.getProjectName() + ", " + branchName)
  276. .hasList("Rule " + ruleName + " - " + expectedLinkText)
  277. .withLink(expectedLinkText, expectedHref)
  278. .hasParagraph().hasParagraph() // skip footer
  279. .noMoreBlock();
  280. }
  281. @Test
  282. @UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange")
  283. public void formats_returns_html_message_for_multiple_hotspots_of_same_rule_on_same_project_on_branch(Change change, FpOrWontFix fpOrWontFix) {
  284. String branchName = randomAlphabetic(19);
  285. Project project = newBranch("1", branchName);
  286. String ruleName = randomAlphabetic(8);
  287. String host = randomAlphabetic(15);
  288. Rule rule = newSecurityHotspotRule(ruleName);
  289. List<ChangedIssue> changedIssues = IntStream.range(0, 2 + new Random().nextInt(5))
  290. .mapToObj(i -> newChangedIssue("issue_" + i, project, rule))
  291. .collect(toList());
  292. when(emailSettings.getServerBaseURL()).thenReturn(host);
  293. EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, ImmutableSet.copyOf(changedIssues), fpOrWontFix));
  294. String expectedHref = host + "/project/issues?id=" + project.getKey() + "&branch=" + branchName
  295. + "&issues=" + changedIssues.stream().map(ChangedIssue::getKey).collect(joining("%2C"));
  296. String expectedLinkText = "See all " + changedIssues.size() + " hotspots";
  297. HtmlFragmentAssert.assertThat(emailMessage.getMessage())
  298. .hasParagraph().hasParagraph() // skip header
  299. .hasParagraph(project.getProjectName() + ", " + branchName)
  300. .hasList("Rule " + ruleName + " - " + expectedLinkText)
  301. .withLink(expectedLinkText, expectedHref)
  302. .hasParagraph().hasParagraph() // skip footer
  303. .noMoreBlock();
  304. }
  305. @Test
  306. @UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange")
  307. public void formats_returns_html_message_with_projects_ordered_by_name(Change change, FpOrWontFix fpOrWontFix) {
  308. Project project1 = newProject("1");
  309. Project project1Branch1 = newBranch("1", "a");
  310. Project project1Branch2 = newBranch("1", "b");
  311. Project project2 = newProject("B");
  312. Project project2Branch1 = newBranch("B", "a");
  313. Project project3 = newProject("C");
  314. String host = randomAlphabetic(15);
  315. List<ChangedIssue> changedIssues = Stream.of(project1, project1Branch1, project1Branch2, project2, project2Branch1, project3)
  316. .map(project -> newChangedIssue("issue_" + project.getUuid(), project, newRandomNotAHotspotRule(randomAlphabetic(2))))
  317. .collect(toList());
  318. Collections.shuffle(changedIssues);
  319. when(emailSettings.getServerBaseURL()).thenReturn(host);
  320. EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, ImmutableSet.copyOf(changedIssues), fpOrWontFix));
  321. HtmlFragmentAssert.assertThat(emailMessage.getMessage())
  322. .hasParagraph().hasParagraph() // skip header
  323. .hasParagraph(project1.getProjectName())
  324. .hasList()
  325. .hasParagraph(project1Branch1.getProjectName() + ", " + project1Branch1.getBranchName().get())
  326. .hasList()
  327. .hasParagraph(project1Branch2.getProjectName() + ", " + project1Branch2.getBranchName().get())
  328. .hasList()
  329. .hasParagraph(project2.getProjectName())
  330. .hasList()
  331. .hasParagraph(project2Branch1.getProjectName() + ", " + project2Branch1.getBranchName().get())
  332. .hasList()
  333. .hasParagraph(project3.getProjectName())
  334. .hasList()
  335. .hasParagraph().hasParagraph() // skip footer
  336. .noMoreBlock();
  337. }
  338. @Test
  339. @UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange")
  340. public void formats_returns_html_message_with_rules_ordered_by_name(Change change, FpOrWontFix fpOrWontFix) {
  341. Project project = newProject("1");
  342. Rule rule1 = newRandomNotAHotspotRule("1");
  343. Rule rule2 = newRandomNotAHotspotRule("a");
  344. Rule rule3 = newRandomNotAHotspotRule("b");
  345. Rule rule4 = newRandomNotAHotspotRule("X");
  346. String host = randomAlphabetic(15);
  347. List<ChangedIssue> changedIssues = Stream.of(rule1, rule2, rule3, rule4)
  348. .map(rule -> newChangedIssue("issue_" + rule.getName(), project, rule))
  349. .collect(toList());
  350. Collections.shuffle(changedIssues);
  351. when(emailSettings.getServerBaseURL()).thenReturn(host);
  352. EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, ImmutableSet.copyOf(changedIssues), fpOrWontFix));
  353. HtmlFragmentAssert.assertThat(emailMessage.getMessage())
  354. .hasParagraph().hasParagraph() // skip header
  355. .hasParagraph(project.getProjectName())
  356. .hasList(
  357. "Rule " + rule1.getName() + " - See the single issue",
  358. "Rule " + rule2.getName() + " - See the single issue",
  359. "Rule " + rule3.getName() + " - See the single issue",
  360. "Rule " + rule4.getName() + " - See the single issue")
  361. .hasParagraph().hasParagraph() // skip footer
  362. .noMoreBlock();
  363. }
  364. @Test
  365. @UseDataProvider("fpOrWontFixValuesByUserOrAnalysisChange")
  366. public void formats_returns_html_message_with_multiple_links_by_rule_of_groups_of_up_to_40_issues(Change change, FpOrWontFix fpOrWontFix) {
  367. Project project1 = newProject("1");
  368. Project project2 = newProject("V");
  369. Project project2Branch = newBranch("V", "AB");
  370. Rule rule1 = newRandomNotAHotspotRule("1");
  371. Rule rule2 = newRandomNotAHotspotRule("a");
  372. String host = randomAlphabetic(15);
  373. List<ChangedIssue> changedIssues = Stream.of(
  374. IntStream.range(0, 39).mapToObj(i -> newChangedIssue("39_" + i, project1, rule1)),
  375. IntStream.range(0, 40).mapToObj(i -> newChangedIssue("40_" + i, project1, rule2)),
  376. IntStream.range(0, 81).mapToObj(i -> newChangedIssue("1-40_41-80_1_" + i, project2, rule2)),
  377. IntStream.range(0, 6).mapToObj(i -> newChangedIssue("6_" + i, project2Branch, rule1)))
  378. .flatMap(t -> t)
  379. .collect(toList());
  380. Collections.shuffle(changedIssues);
  381. when(emailSettings.getServerBaseURL()).thenReturn(host);
  382. EmailMessage emailMessage = underTest.format(new FPOrWontFixNotification(change, ImmutableSet.copyOf(changedIssues), fpOrWontFix));
  383. HtmlFragmentAssert.assertThat(emailMessage.getMessage())
  384. .hasParagraph().hasParagraph() // skip header
  385. .hasParagraph(project1.getProjectName())
  386. .hasList()
  387. .withItemTexts(
  388. "Rule " + rule1.getName() + " - See all 39 issues",
  389. "Rule " + rule2.getName() + " - See all 40 issues")
  390. .withLink("See all 39 issues",
  391. host + "/project/issues?id=" + project1.getKey()
  392. + "&issues=" + IntStream.range(0, 39).mapToObj(i -> "39_" + i).sorted().collect(joining("%2C")))
  393. .withLink("See all 40 issues",
  394. host + "/project/issues?id=" + project1.getKey()
  395. + "&issues=" + IntStream.range(0, 40).mapToObj(i -> "40_" + i).sorted().collect(joining("%2C")))
  396. .hasParagraph(project2.getProjectName())
  397. .hasList("Rule " + rule2.getName() + " - See issues 1-40 41-80 81")
  398. .withLink("1-40",
  399. host + "/project/issues?id=" + project2.getKey()
  400. + "&issues=" + IntStream.range(0, 81).mapToObj(i -> "1-40_41-80_1_" + i).sorted().limit(40).collect(joining("%2C")))
  401. .withLink("41-80",
  402. host + "/project/issues?id=" + project2.getKey()
  403. + "&issues=" + IntStream.range(0, 81).mapToObj(i -> "1-40_41-80_1_" + i).sorted().skip(40).limit(40).collect(joining("%2C")))
  404. .withLink("81",
  405. host + "/project/issues?id=" + project2.getKey()
  406. + "&issues=" + "1-40_41-80_1_9" + "&open=" + "1-40_41-80_1_9")
  407. .hasParagraph(project2Branch.getProjectName() + ", " + project2Branch.getBranchName().get())
  408. .hasList("Rule " + rule1.getName() + " - See all 6 issues")
  409. .withLink("See all 6 issues",
  410. host + "/project/issues?id=" + project2Branch.getKey() + "&branch=" + project2Branch.getBranchName().get()
  411. + "&issues=" + IntStream.range(0, 6).mapToObj(i -> "6_" + i).sorted().collect(joining("%2C")))
  412. .hasParagraph().hasParagraph() // skip footer
  413. .noMoreBlock();
  414. }
  415. @DataProvider
  416. public static Object[][] userOrAnalysisChange() {
  417. AnalysisChange analysisChange = new AnalysisChange(new Random().nextLong());
  418. UserChange userChange = new UserChange(new Random().nextLong(), new User(randomAlphabetic(5), randomAlphabetic(6),
  419. new Random().nextBoolean() ? null : randomAlphabetic(7)));
  420. return new Object[][] {
  421. {analysisChange},
  422. {userChange}
  423. };
  424. }
  425. @DataProvider
  426. public static Object[][] fpOrWontFixValuesByUserOrAnalysisChange() {
  427. AnalysisChange analysisChange = new AnalysisChange(new Random().nextLong());
  428. UserChange userChange = new UserChange(new Random().nextLong(), new User(randomAlphabetic(5), randomAlphabetic(6),
  429. new Random().nextBoolean() ? null : randomAlphabetic(7)));
  430. return new Object[][] {
  431. {analysisChange, FP},
  432. {analysisChange, WONT_FIX},
  433. {userChange, FP},
  434. {userChange, WONT_FIX}
  435. };
  436. }
  437. private static ChangedIssue newChangedIssue(String key, Project project, String ruleName, RuleType ruleType) {
  438. return newChangedIssue(key, project, newRule(ruleName, ruleType));
  439. }
  440. private static ChangedIssue newChangedIssue(String key, Project project, Rule rule) {
  441. return new ChangedIssue.Builder(key)
  442. .setNewStatus(randomAlphabetic(19))
  443. .setProject(project)
  444. .setRule(rule)
  445. .build();
  446. }
  447. private static Rule newRule(String ruleName, RuleType ruleType) {
  448. return new Rule(RuleKey.of(randomAlphabetic(6), randomAlphabetic(7)), ruleType, ruleName);
  449. }
  450. private static Project newProject(String uuid) {
  451. return new Project.Builder(uuid).setProjectName(uuid + "_name").setKey(uuid + "_key").build();
  452. }
  453. private static Project newBranch(String uuid, String branchName) {
  454. return new Project.Builder(uuid).setProjectName(uuid + "_name").setKey(uuid + "_key").setBranchName(branchName).build();
  455. }
  456. }