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.

NotificationFactoryTest.java 36KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2024 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.ce.task.projectanalysis.notification;
  21. import com.google.common.collect.ImmutableMap;
  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.lang.reflect.Field;
  27. import java.util.Collections;
  28. import java.util.Map;
  29. import java.util.Random;
  30. import java.util.Set;
  31. import java.util.function.Function;
  32. import java.util.stream.Collectors;
  33. import java.util.stream.IntStream;
  34. import java.util.stream.Stream;
  35. import org.junit.Rule;
  36. import org.junit.Test;
  37. import org.junit.runner.RunWith;
  38. import org.mockito.ArgumentCaptor;
  39. import org.sonar.api.rule.RuleKey;
  40. import org.sonar.api.utils.Durations;
  41. import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
  42. import org.sonar.ce.task.projectanalysis.analysis.Branch;
  43. import org.sonar.ce.task.projectanalysis.component.ReportComponent;
  44. import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
  45. import org.sonar.ce.task.projectanalysis.issue.DumbRule;
  46. import org.sonar.ce.task.projectanalysis.issue.RuleRepositoryRule;
  47. import org.sonar.core.issue.DefaultIssue;
  48. import org.sonar.db.component.BranchType;
  49. import org.sonar.db.user.UserDto;
  50. import org.sonar.db.user.UserTesting;
  51. import org.sonar.server.issue.notification.IssuesChangesNotification;
  52. import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder;
  53. import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.AnalysisChange;
  54. import org.sonar.server.issue.notification.IssuesChangesNotificationBuilder.ChangedIssue;
  55. import org.sonar.server.issue.notification.IssuesChangesNotificationSerializer;
  56. import org.sonar.server.issue.notification.MyNewIssuesNotification;
  57. import org.sonar.server.issue.notification.NewIssuesNotification;
  58. import org.sonar.server.issue.notification.NewIssuesNotification.DetailsSupplier;
  59. import org.sonar.server.issue.notification.NewIssuesNotification.RuleDefinition;
  60. import static java.util.Collections.emptyMap;
  61. import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
  62. import static org.assertj.core.api.Assertions.assertThat;
  63. import static org.assertj.core.api.Assertions.assertThatThrownBy;
  64. import static org.mockito.ArgumentMatchers.any;
  65. import static org.mockito.Mockito.mock;
  66. import static org.mockito.Mockito.verify;
  67. import static org.mockito.Mockito.verifyNoMoreInteractions;
  68. import static org.mockito.Mockito.when;
  69. import static org.sonar.api.issue.Issue.STATUS_OPEN;
  70. import static org.sonar.ce.task.projectanalysis.component.Component.Type.DIRECTORY;
  71. import static org.sonar.ce.task.projectanalysis.component.Component.Type.FILE;
  72. import static org.sonar.ce.task.projectanalysis.component.Component.Type.PROJECT;
  73. @RunWith(DataProviderRunner.class)
  74. public class NotificationFactoryTest {
  75. @Rule
  76. public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
  77. @Rule
  78. public RuleRepositoryRule ruleRepository = new RuleRepositoryRule();
  79. @Rule
  80. public AnalysisMetadataHolderRule analysisMetadata = new AnalysisMetadataHolderRule();
  81. private IssuesChangesNotificationSerializer issuesChangesSerializer = mock(IssuesChangesNotificationSerializer.class);
  82. private NotificationFactory underTest = new NotificationFactory(treeRootHolder, analysisMetadata, ruleRepository, issuesChangesSerializer);
  83. @Test
  84. public void newMyNewIssuesNotification_throws_NPE_if_assigneesByUuid_is_null() {
  85. assertThatThrownBy(() -> underTest.newMyNewIssuesNotification(null))
  86. .isInstanceOf(NullPointerException.class)
  87. .hasMessage("assigneesByUuid can't be null");
  88. }
  89. @Test
  90. public void newNewIssuesNotification_throws_NPE_if_assigneesByUuid_is_null() {
  91. assertThatThrownBy(() -> underTest.newNewIssuesNotification(null))
  92. .isInstanceOf(NullPointerException.class)
  93. .hasMessage("assigneesByUuid can't be null");
  94. }
  95. @Test
  96. public void newMyNewIssuesNotification_DetailsSupplier_getUserNameByUuid_fails_with_NPE_if_uuid_is_null() {
  97. MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
  98. DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
  99. assertThatThrownBy(() -> detailsSupplier.getUserNameByUuid(null))
  100. .isInstanceOf(NullPointerException.class)
  101. .hasMessage("uuid can't be null");
  102. }
  103. @Test
  104. public void newMyNewIssuesNotification_DetailsSupplier_getUserNameByUuid_always_returns_empty_if_map_argument_is_empty() {
  105. MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
  106. DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
  107. assertThat(detailsSupplier.getUserNameByUuid("foo")).isEmpty();
  108. }
  109. @Test
  110. public void newMyNewIssuesNotification_DetailsSupplier_getUserNameByUuid_returns_name_of_user_from_map_argument() {
  111. Set<UserDto> users = IntStream.range(0, 1 + new Random().nextInt(10))
  112. .mapToObj(i -> UserTesting.newUserDto().setLogin("user" + i))
  113. .collect(Collectors.toSet());
  114. MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(
  115. users.stream().collect(Collectors.toMap(UserDto::getUuid, Function.identity())));
  116. DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
  117. assertThat(detailsSupplier.getUserNameByUuid("foo")).isEmpty();
  118. users
  119. .forEach(user -> assertThat(detailsSupplier.getUserNameByUuid(user.getUuid())).contains(user.getName()));
  120. }
  121. @Test
  122. public void newMyNewIssuesNotification_DetailsSupplier_getUserNameByUuid_returns_empty_if_user_has_null_name() {
  123. UserDto user = UserTesting.newUserDto().setLogin("user_noname").setName(null);
  124. MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(ImmutableMap.of(user.getUuid(), user));
  125. DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
  126. assertThat(detailsSupplier.getUserNameByUuid(user.getUuid())).isEmpty();
  127. }
  128. @Test
  129. public void newNewIssuesNotification_DetailsSupplier_getUserNameByUuid_fails_with_NPE_if_uuid_is_null() {
  130. NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(emptyMap());
  131. DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
  132. assertThatThrownBy(() -> detailsSupplier.getUserNameByUuid(null))
  133. .isInstanceOf(NullPointerException.class)
  134. .hasMessage("uuid can't be null");
  135. }
  136. @Test
  137. public void newNewIssuesNotification_DetailsSupplier_getUserNameByUuid_always_returns_empty_if_map_argument_is_empty() {
  138. NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(emptyMap());
  139. DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
  140. assertThat(detailsSupplier.getUserNameByUuid("foo")).isEmpty();
  141. }
  142. @Test
  143. public void newNewIssuesNotification_DetailsSupplier_getUserNameByUuid_returns_name_of_user_from_map_argument() {
  144. Set<UserDto> users = IntStream.range(0, 1 + new Random().nextInt(10))
  145. .mapToObj(i -> UserTesting.newUserDto().setLogin("user" + i))
  146. .collect(Collectors.toSet());
  147. NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(
  148. users.stream().collect(Collectors.toMap(UserDto::getUuid, Function.identity())));
  149. DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
  150. assertThat(detailsSupplier.getUserNameByUuid("foo")).isEmpty();
  151. users
  152. .forEach(user -> assertThat(detailsSupplier.getUserNameByUuid(user.getUuid())).contains(user.getName()));
  153. }
  154. @Test
  155. public void newNewIssuesNotification_DetailsSupplier_getUserNameByUuid_returns_empty_if_user_has_null_name() {
  156. UserDto user = UserTesting.newUserDto().setLogin("user_noname").setName(null);
  157. NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(ImmutableMap.of(user.getUuid(), user));
  158. DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
  159. assertThat(detailsSupplier.getUserNameByUuid(user.getUuid())).isEmpty();
  160. }
  161. @Test
  162. public void newMyNewIssuesNotification_DetailsSupplier_getComponentNameByUuid_fails_with_ISE_if_TreeRootHolder_is_not_initialized() {
  163. MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
  164. DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
  165. assertThatThrownBy(() -> detailsSupplier.getComponentNameByUuid("foo"))
  166. .isInstanceOf(IllegalStateException.class)
  167. .hasMessage("Holder has not been initialized yet");
  168. }
  169. @Test
  170. public void newMyNewIssuesNotification_DetailsSupplier_getComponentNameByUuid_fails_with_NPE_if_uuid_is_null() {
  171. treeRootHolder.setRoot(ReportComponent.builder(PROJECT, 1).setUuid("rootUuid").setName("root").build());
  172. MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
  173. DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
  174. assertThatThrownBy(() -> detailsSupplier.getComponentNameByUuid(null))
  175. .isInstanceOf(NullPointerException.class)
  176. .hasMessage("uuid can't be null");
  177. }
  178. @Test
  179. public void newMyNewIssuesNotification_DetailsSupplier_getComponentNameByUuid_returns_name_of_project_in_TreeRootHolder() {
  180. treeRootHolder.setRoot(ReportComponent.builder(PROJECT, 1).setUuid("rootUuid").setName("root").build());
  181. MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
  182. DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
  183. assertThat(detailsSupplier.getComponentNameByUuid("rootUuid")).contains("root");
  184. assertThat(detailsSupplier.getComponentNameByUuid("foo")).isEmpty();
  185. }
  186. @Test
  187. public void newMyNewIssuesNotification_DetailsSupplier_getComponentNameByUuid_returns_shortName_of_dir_and_file_in_TreeRootHolder() {
  188. treeRootHolder.setRoot(ReportComponent.builder(PROJECT, 1).setUuid("rootUuid").setName("root")
  189. .addChildren(ReportComponent.builder(DIRECTORY, 2).setUuid("dir1Uuid").setName("dir1").setShortName("dir1_short")
  190. .addChildren(ReportComponent.builder(FILE, 21).setUuid("file21Uuid").setName("file21").setShortName("file21_short").build())
  191. .build())
  192. .addChildren(ReportComponent.builder(DIRECTORY, 3).setUuid("dir2Uuid").setName("dir2").setShortName("dir2_short")
  193. .addChildren(ReportComponent.builder(FILE, 31).setUuid("file31Uuid").setName("file31").setShortName("file31_short").build())
  194. .addChildren(ReportComponent.builder(FILE, 32).setUuid("file32Uuid").setName("file32").setShortName("file32_short").build())
  195. .build())
  196. .addChildren(ReportComponent.builder(FILE, 11).setUuid("file11Uuid").setName("file11").setShortName("file11_short").build())
  197. .build());
  198. MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
  199. DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
  200. Stream.of("dir1", "dir2", "file11", "file21", "file31", "file32")
  201. .forEach(name -> {
  202. assertThat(detailsSupplier.getComponentNameByUuid(name + "Uuid")).contains(name + "_short");
  203. assertThat(detailsSupplier.getComponentNameByUuid(name)).isEmpty();
  204. });
  205. }
  206. @Test
  207. public void newNewIssuesNotification_DetailsSupplier_getComponentNameByUuid_fails_with_ISE_if_TreeRootHolder_is_not_initialized() {
  208. NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(emptyMap());
  209. DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
  210. assertThatThrownBy(() -> detailsSupplier.getComponentNameByUuid("foo"))
  211. .isInstanceOf(IllegalStateException.class)
  212. .hasMessage("Holder has not been initialized yet");
  213. }
  214. @Test
  215. public void newNewIssuesNotification_DetailsSupplier_getComponentNameByUuid_fails_with_NPE_if_uuid_is_null() {
  216. treeRootHolder.setRoot(ReportComponent.builder(PROJECT, 1).setUuid("rootUuid").setName("root").build());
  217. NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(emptyMap());
  218. DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
  219. assertThatThrownBy(() -> detailsSupplier.getComponentNameByUuid(null))
  220. .isInstanceOf(NullPointerException.class)
  221. .hasMessage("uuid can't be null");
  222. }
  223. @Test
  224. public void newNewIssuesNotification_DetailsSupplier_getComponentNameByUuid_returns_name_of_project_in_TreeRootHolder() {
  225. treeRootHolder.setRoot(ReportComponent.builder(PROJECT, 1).setUuid("rootUuid").setName("root").build());
  226. NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(emptyMap());
  227. DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
  228. assertThat(detailsSupplier.getComponentNameByUuid("rootUuid")).contains("root");
  229. assertThat(detailsSupplier.getComponentNameByUuid("foo")).isEmpty();
  230. }
  231. @Test
  232. public void newNewIssuesNotification_DetailsSupplier_getComponentNameByUuid_returns_shortName_of_dir_and_file_in_TreeRootHolder() {
  233. treeRootHolder.setRoot(ReportComponent.builder(PROJECT, 1).setUuid("rootUuid").setName("root")
  234. .addChildren(ReportComponent.builder(DIRECTORY, 2).setUuid("dir1Uuid").setName("dir1").setShortName("dir1_short")
  235. .addChildren(ReportComponent.builder(FILE, 21).setUuid("file21Uuid").setName("file21").setShortName("file21_short").build())
  236. .build())
  237. .addChildren(ReportComponent.builder(DIRECTORY, 3).setUuid("dir2Uuid").setName("dir2").setShortName("dir2_short")
  238. .addChildren(ReportComponent.builder(FILE, 31).setUuid("file31Uuid").setName("file31").setShortName("file31_short").build())
  239. .addChildren(ReportComponent.builder(FILE, 32).setUuid("file32Uuid").setName("file32").setShortName("file32_short").build())
  240. .build())
  241. .addChildren(ReportComponent.builder(FILE, 11).setUuid("file11Uuid").setName("file11").setShortName("file11_short").build())
  242. .build());
  243. NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(emptyMap());
  244. DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
  245. Stream.of("dir1", "dir2", "file11", "file21", "file31", "file32")
  246. .forEach(name -> {
  247. assertThat(detailsSupplier.getComponentNameByUuid(name + "Uuid")).contains(name + "_short");
  248. assertThat(detailsSupplier.getComponentNameByUuid(name)).isEmpty();
  249. });
  250. }
  251. @Test
  252. public void newMyNewIssuesNotification_DetailsSupplier_getRuleDefinitionByRuleKey_fails_with_NPE_if_ruleKey_is_null() {
  253. MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
  254. DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
  255. assertThatThrownBy(() -> detailsSupplier.getRuleDefinitionByRuleKey(null))
  256. .isInstanceOf(NullPointerException.class)
  257. .hasMessage("ruleKey can't be null");
  258. }
  259. @Test
  260. public void newMyNewIssuesNotification_DetailsSupplier_getRuleDefinitionByRuleKey_always_returns_empty_if_RuleRepository_is_empty() {
  261. MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
  262. DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
  263. assertThat(detailsSupplier.getRuleDefinitionByRuleKey(RuleKey.of("foo", "bar"))).isEmpty();
  264. assertThat(detailsSupplier.getRuleDefinitionByRuleKey(RuleKey.of("bar", "foo"))).isEmpty();
  265. }
  266. @Test
  267. public void newMyNewIssuesNotification_DetailsSupplier_getRuleDefinitionByRuleKey_returns_name_and_language_from_RuleRepository() {
  268. RuleKey rulekey1 = RuleKey.of("foo", "bar");
  269. RuleKey rulekey2 = RuleKey.of("foo", "donut");
  270. RuleKey rulekey3 = RuleKey.of("no", "language");
  271. DumbRule rule1 = ruleRepository.add(rulekey1).setName("rule1").setLanguage("lang1");
  272. DumbRule rule2 = ruleRepository.add(rulekey2).setName("rule2").setLanguage("lang2");
  273. DumbRule rule3 = ruleRepository.add(rulekey3).setName("rule3");
  274. MyNewIssuesNotification underTest = this.underTest.newMyNewIssuesNotification(emptyMap());
  275. DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
  276. assertThat(detailsSupplier.getRuleDefinitionByRuleKey(rulekey1))
  277. .contains(new RuleDefinition(rule1.getName(), rule1.getLanguage()));
  278. assertThat(detailsSupplier.getRuleDefinitionByRuleKey(rulekey2))
  279. .contains(new RuleDefinition(rule2.getName(), rule2.getLanguage()));
  280. assertThat(detailsSupplier.getRuleDefinitionByRuleKey(rulekey3))
  281. .contains(new RuleDefinition(rule3.getName(), null));
  282. assertThat(detailsSupplier.getRuleDefinitionByRuleKey(RuleKey.of("donut", "foo")))
  283. .isEmpty();
  284. }
  285. @Test
  286. public void newNewIssuesNotification_DetailsSupplier_getRuleDefinitionByRuleKey_fails_with_NPE_if_ruleKey_is_null() {
  287. NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(emptyMap());
  288. DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
  289. assertThatThrownBy(() -> detailsSupplier.getRuleDefinitionByRuleKey(null))
  290. .isInstanceOf(NullPointerException.class)
  291. .hasMessage("ruleKey can't be null");
  292. }
  293. @Test
  294. public void newNewIssuesNotification_DetailsSupplier_getRuleDefinitionByRuleKey_always_returns_empty_if_RuleRepository_is_empty() {
  295. NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(emptyMap());
  296. DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
  297. assertThat(detailsSupplier.getRuleDefinitionByRuleKey(RuleKey.of("foo", "bar"))).isEmpty();
  298. assertThat(detailsSupplier.getRuleDefinitionByRuleKey(RuleKey.of("bar", "foo"))).isEmpty();
  299. }
  300. @Test
  301. public void newNewIssuesNotification_DetailsSupplier_getRuleDefinitionByRuleKey_returns_name_and_language_from_RuleRepository() {
  302. RuleKey rulekey1 = RuleKey.of("foo", "bar");
  303. RuleKey rulekey2 = RuleKey.of("foo", "donut");
  304. RuleKey rulekey3 = RuleKey.of("no", "language");
  305. DumbRule rule1 = ruleRepository.add(rulekey1).setName("rule1").setLanguage("lang1");
  306. DumbRule rule2 = ruleRepository.add(rulekey2).setName("rule2").setLanguage("lang2");
  307. DumbRule rule3 = ruleRepository.add(rulekey3).setName("rule3");
  308. NewIssuesNotification underTest = this.underTest.newNewIssuesNotification(emptyMap());
  309. DetailsSupplier detailsSupplier = readDetailsSupplier(underTest);
  310. assertThat(detailsSupplier.getRuleDefinitionByRuleKey(rulekey1))
  311. .contains(new RuleDefinition(rule1.getName(), rule1.getLanguage()));
  312. assertThat(detailsSupplier.getRuleDefinitionByRuleKey(rulekey2))
  313. .contains(new RuleDefinition(rule2.getName(), rule2.getLanguage()));
  314. assertThat(detailsSupplier.getRuleDefinitionByRuleKey(rulekey3))
  315. .contains(new RuleDefinition(rule3.getName(), null));
  316. assertThat(detailsSupplier.getRuleDefinitionByRuleKey(RuleKey.of("donut", "foo")))
  317. .isEmpty();
  318. }
  319. @Test
  320. public void newIssuesChangesNotification_fails_with_ISE_if_analysis_date_has_not_been_set() {
  321. Set<DefaultIssue> issues = IntStream.range(0, 1 + new Random().nextInt(2))
  322. .mapToObj(i -> new DefaultIssue())
  323. .collect(Collectors.toSet());
  324. Map<String, UserDto> assigneesByUuid = nonEmptyAssigneesByUuid();
  325. assertThatThrownBy(() -> underTest.newIssuesChangesNotification(issues, assigneesByUuid))
  326. .isInstanceOf(IllegalStateException.class)
  327. .hasMessage("Analysis date has not been set");
  328. }
  329. @Test
  330. public void newIssuesChangesNotification_fails_with_IAE_if_issues_is_empty() {
  331. analysisMetadata.setAnalysisDate(new Random().nextLong());
  332. Map<String, UserDto> assigneesByUuid = nonEmptyAssigneesByUuid();
  333. assertThatThrownBy(() -> underTest.newIssuesChangesNotification(Collections.emptySet(), assigneesByUuid))
  334. .isInstanceOf(IllegalArgumentException.class)
  335. .hasMessage("issues can't be empty");
  336. }
  337. @Test
  338. public void newIssuesChangesNotification_fails_with_NPE_if_issue_has_no_rule() {
  339. DefaultIssue issue = new DefaultIssue();
  340. Map<String, UserDto> assigneesByUuid = nonEmptyAssigneesByUuid();
  341. analysisMetadata.setAnalysisDate(new Random().nextLong());
  342. assertThatThrownBy(() -> underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid))
  343. .isInstanceOf(NullPointerException.class);
  344. }
  345. @Test
  346. public void newIssuesChangesNotification_fails_with_ISE_if_rule_of_issue_does_not_exist_in_repository() {
  347. RuleKey ruleKey = RuleKey.of("foo", "bar");
  348. DefaultIssue issue = new DefaultIssue()
  349. .setRuleKey(ruleKey);
  350. Map<String, UserDto> assigneesByUuid = nonEmptyAssigneesByUuid();
  351. analysisMetadata.setAnalysisDate(new Random().nextLong());
  352. assertThatThrownBy(() -> underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid))
  353. .isInstanceOf(IllegalStateException.class)
  354. .hasMessage("Can not find rule " + ruleKey + " in RuleRepository");
  355. }
  356. @Test
  357. public void newIssuesChangesNotification_fails_with_ISE_if_treeRootHolder_is_empty() {
  358. RuleKey ruleKey = RuleKey.of("foo", "bar");
  359. DefaultIssue issue = new DefaultIssue()
  360. .setRuleKey(ruleKey);
  361. Map<String, UserDto> assigneesByUuid = nonEmptyAssigneesByUuid();
  362. ruleRepository.add(ruleKey);
  363. analysisMetadata.setAnalysisDate(new Random().nextLong());
  364. assertThatThrownBy(() -> underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid))
  365. .isInstanceOf(IllegalStateException.class)
  366. .hasMessage("Holder has not been initialized yet");
  367. }
  368. @Test
  369. public void newIssuesChangesNotification_fails_with_ISE_if_branch_has_not_been_set() {
  370. RuleKey ruleKey = RuleKey.of("foo", "bar");
  371. DefaultIssue issue = new DefaultIssue()
  372. .setRuleKey(ruleKey);
  373. Map<String, UserDto> assigneesByUuid = nonEmptyAssigneesByUuid();
  374. ruleRepository.add(ruleKey);
  375. analysisMetadata.setAnalysisDate(new Random().nextLong());
  376. treeRootHolder.setRoot(ReportComponent.builder(PROJECT, 1).build());
  377. assertThatThrownBy(() -> underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid))
  378. .isInstanceOf(IllegalStateException.class)
  379. .hasMessage("Branch has not been set");
  380. }
  381. @Test
  382. public void newIssuesChangesNotification_fails_with_NPE_if_issue_has_no_key() {
  383. RuleKey ruleKey = RuleKey.of("foo", "bar");
  384. DefaultIssue issue = new DefaultIssue()
  385. .setRuleKey(ruleKey);
  386. Map<String, UserDto> assigneesByUuid = nonEmptyAssigneesByUuid();
  387. ruleRepository.add(ruleKey);
  388. treeRootHolder.setRoot(ReportComponent.builder(PROJECT, 1).build());
  389. analysisMetadata.setAnalysisDate(new Random().nextLong());
  390. analysisMetadata.setBranch(mock(Branch.class));
  391. assertThatThrownBy(() -> underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid))
  392. .isInstanceOf(NullPointerException.class)
  393. .hasMessage("key can't be null");
  394. }
  395. @Test
  396. public void newIssuesChangesNotification_fails_with_NPE_if_issue_has_no_status() {
  397. RuleKey ruleKey = RuleKey.of("foo", "bar");
  398. DefaultIssue issue = new DefaultIssue()
  399. .setRuleKey(ruleKey)
  400. .setKey("issueKey");
  401. Map<String, UserDto> assigneesByUuid = nonEmptyAssigneesByUuid();
  402. ruleRepository.add(ruleKey);
  403. treeRootHolder.setRoot(ReportComponent.builder(PROJECT, 1).build());
  404. analysisMetadata.setAnalysisDate(new Random().nextLong());
  405. analysisMetadata.setBranch(mock(Branch.class));
  406. assertThatThrownBy(() -> underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid))
  407. .isInstanceOf(NullPointerException.class)
  408. .hasMessage("newStatus can't be null");
  409. }
  410. @Test
  411. @UseDataProvider("noBranchNameBranches")
  412. public void newIssuesChangesNotification_creates_project_from_TreeRootHolder_and_branch_name_only_on_non_main_branches(Branch branch) {
  413. RuleKey ruleKey = RuleKey.of("foo", "bar");
  414. DefaultIssue issue = new DefaultIssue()
  415. .setRuleKey(ruleKey)
  416. .setKey("issueKey")
  417. .setStatus(STATUS_OPEN);
  418. Map<String, UserDto> assigneesByUuid = nonEmptyAssigneesByUuid();
  419. ReportComponent project = ReportComponent.builder(PROJECT, 1).build();
  420. ruleRepository.add(ruleKey);
  421. treeRootHolder.setRoot(project);
  422. analysisMetadata.setAnalysisDate(new Random().nextLong());
  423. analysisMetadata.setBranch(branch);
  424. IssuesChangesNotification expected = mock(IssuesChangesNotification.class);
  425. when(issuesChangesSerializer.serialize(any(IssuesChangesNotificationBuilder.class))).thenReturn(expected);
  426. IssuesChangesNotification notification = underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid);
  427. assertThat(notification).isSameAs(expected);
  428. IssuesChangesNotificationBuilder builder = verifyAndCaptureIssueChangeNotificationBuilder();
  429. assertThat(builder.getIssues()).hasSize(1);
  430. ChangedIssue changeIssue = builder.getIssues().iterator().next();
  431. assertThat(changeIssue.getProject().getUuid()).isEqualTo(project.getUuid());
  432. assertThat(changeIssue.getProject().getKey()).isEqualTo(project.getKey());
  433. assertThat(changeIssue.getProject().getProjectName()).isEqualTo(project.getName());
  434. assertThat(changeIssue.getProject().getBranchName()).isEmpty();
  435. }
  436. @DataProvider
  437. public static Object[][] noBranchNameBranches() {
  438. Branch mainBranch = mock(Branch.class);
  439. when(mainBranch.isMain()).thenReturn(true);
  440. when(mainBranch.getType()).thenReturn(BranchType.BRANCH);
  441. Branch pr = mock(Branch.class);
  442. when(pr.isMain()).thenReturn(false);
  443. when(pr.getType()).thenReturn(BranchType.PULL_REQUEST);
  444. return new Object[][] {
  445. {mainBranch},
  446. {pr}
  447. };
  448. }
  449. @Test
  450. public void newIssuesChangesNotification_creates_project_from_TreeRootHolder_and_branch_name_from_branch() {
  451. RuleKey ruleKey = RuleKey.of("foo", "bar");
  452. DefaultIssue issue = new DefaultIssue()
  453. .setRuleKey(ruleKey)
  454. .setKey("issueKey")
  455. .setStatus(STATUS_OPEN);
  456. Map<String, UserDto> assigneesByUuid = nonEmptyAssigneesByUuid();
  457. ReportComponent project = ReportComponent.builder(PROJECT, 1).build();
  458. String branchName = randomAlphabetic(12);
  459. ruleRepository.add(ruleKey);
  460. treeRootHolder.setRoot(project);
  461. analysisMetadata.setAnalysisDate(new Random().nextLong());
  462. analysisMetadata.setBranch(newNonMainBranch(BranchType.BRANCH, branchName));
  463. IssuesChangesNotification expected = mock(IssuesChangesNotification.class);
  464. when(issuesChangesSerializer.serialize(any(IssuesChangesNotificationBuilder.class))).thenReturn(expected);
  465. IssuesChangesNotification notification = underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid);
  466. assertThat(notification).isSameAs(expected);
  467. IssuesChangesNotificationBuilder builder = verifyAndCaptureIssueChangeNotificationBuilder();
  468. assertThat(builder.getIssues()).hasSize(1);
  469. ChangedIssue changeIssue = builder.getIssues().iterator().next();
  470. assertThat(changeIssue.getProject().getUuid()).isEqualTo(project.getUuid());
  471. assertThat(changeIssue.getProject().getKey()).isEqualTo(project.getKey());
  472. assertThat(changeIssue.getProject().getProjectName()).isEqualTo(project.getName());
  473. assertThat(changeIssue.getProject().getBranchName()).contains(branchName);
  474. }
  475. @Test
  476. public void newIssuesChangesNotification_creates_rule_from_RuleRepository() {
  477. RuleKey ruleKey = RuleKey.of("foo", "bar");
  478. DefaultIssue issue = new DefaultIssue()
  479. .setRuleKey(ruleKey)
  480. .setKey("issueKey")
  481. .setStatus(STATUS_OPEN);
  482. Map<String, UserDto> assigneesByUuid = nonEmptyAssigneesByUuid();
  483. ReportComponent project = ReportComponent.builder(PROJECT, 1).build();
  484. String branchName = randomAlphabetic(12);
  485. ruleRepository.add(ruleKey);
  486. treeRootHolder.setRoot(project);
  487. analysisMetadata.setAnalysisDate(new Random().nextLong());
  488. analysisMetadata.setBranch(newNonMainBranch(BranchType.BRANCH, branchName));
  489. IssuesChangesNotification expected = mock(IssuesChangesNotification.class);
  490. when(issuesChangesSerializer.serialize(any(IssuesChangesNotificationBuilder.class))).thenReturn(expected);
  491. IssuesChangesNotification notification = underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid);
  492. assertThat(notification).isSameAs(expected);
  493. IssuesChangesNotificationBuilder builder = verifyAndCaptureIssueChangeNotificationBuilder();
  494. assertThat(builder.getIssues()).hasSize(1);
  495. ChangedIssue changeIssue = builder.getIssues().iterator().next();
  496. assertThat(changeIssue.getRule().getKey()).isEqualTo(ruleKey);
  497. assertThat(changeIssue.getRule().getName()).isEqualTo(ruleRepository.getByKey(ruleKey).getName());
  498. }
  499. @Test
  500. public void newIssuesChangesNotification_fails_with_ISE_if_issue_has_assignee_not_in_assigneesByUuid() {
  501. RuleKey ruleKey = RuleKey.of("foo", "bar");
  502. String assigneeUuid = randomAlphabetic(40);
  503. DefaultIssue issue = new DefaultIssue()
  504. .setRuleKey(ruleKey)
  505. .setKey("issueKey")
  506. .setStatus(STATUS_OPEN)
  507. .setAssigneeUuid(assigneeUuid);
  508. Map<String, UserDto> assigneesByUuid = Collections.emptyMap();
  509. ReportComponent project = ReportComponent.builder(PROJECT, 1).build();
  510. ruleRepository.add(ruleKey);
  511. treeRootHolder.setRoot(project);
  512. analysisMetadata.setAnalysisDate(new Random().nextLong());
  513. analysisMetadata.setBranch(newNonMainBranch(BranchType.BRANCH, randomAlphabetic(12)));
  514. assertThatThrownBy(() -> underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid))
  515. .isInstanceOf(IllegalStateException.class)
  516. .hasMessage("Can not find DTO for assignee uuid " + assigneeUuid);
  517. }
  518. @Test
  519. public void newIssuesChangesNotification_creates_assignee_from_UserDto() {
  520. RuleKey ruleKey = RuleKey.of("foo", "bar");
  521. String assigneeUuid = randomAlphabetic(40);
  522. DefaultIssue issue = new DefaultIssue()
  523. .setRuleKey(ruleKey)
  524. .setKey("issueKey")
  525. .setStatus(STATUS_OPEN)
  526. .setAssigneeUuid(assigneeUuid);
  527. UserDto userDto = UserTesting.newUserDto();
  528. Map<String, UserDto> assigneesByUuid = ImmutableMap.of(assigneeUuid, userDto);
  529. ReportComponent project = ReportComponent.builder(PROJECT, 1).build();
  530. ruleRepository.add(ruleKey);
  531. treeRootHolder.setRoot(project);
  532. analysisMetadata.setAnalysisDate(new Random().nextLong());
  533. analysisMetadata.setBranch(newNonMainBranch(BranchType.BRANCH, randomAlphabetic(12)));
  534. IssuesChangesNotification expected = mock(IssuesChangesNotification.class);
  535. when(issuesChangesSerializer.serialize(any(IssuesChangesNotificationBuilder.class))).thenReturn(expected);
  536. IssuesChangesNotification notification = underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid);
  537. assertThat(notification).isSameAs(expected);
  538. IssuesChangesNotificationBuilder builder = verifyAndCaptureIssueChangeNotificationBuilder();
  539. assertThat(builder.getIssues()).hasSize(1);
  540. ChangedIssue changeIssue = builder.getIssues().iterator().next();
  541. assertThat(changeIssue.getAssignee()).isPresent();
  542. IssuesChangesNotificationBuilder.User assignee = changeIssue.getAssignee().get();
  543. assertThat(assignee.getUuid()).isEqualTo(userDto.getUuid());
  544. assertThat(assignee.getName()).contains(userDto.getName());
  545. assertThat(assignee.getLogin()).isEqualTo(userDto.getLogin());
  546. }
  547. @Test
  548. public void newIssuesChangesNotification_creates_AnalysisChange_with_analysis_date() {
  549. RuleKey ruleKey = RuleKey.of("foo", "bar");
  550. DefaultIssue issue = new DefaultIssue()
  551. .setRuleKey(ruleKey)
  552. .setKey("issueKey")
  553. .setStatus(STATUS_OPEN);
  554. Map<String, UserDto> assigneesByUuid = nonEmptyAssigneesByUuid();
  555. ReportComponent project = ReportComponent.builder(PROJECT, 1).build();
  556. long analysisDate = new Random().nextLong();
  557. ruleRepository.add(ruleKey);
  558. treeRootHolder.setRoot(project);
  559. analysisMetadata.setAnalysisDate(analysisDate);
  560. analysisMetadata.setBranch(newNonMainBranch(BranchType.BRANCH, randomAlphabetic(12)));
  561. IssuesChangesNotification expected = mock(IssuesChangesNotification.class);
  562. when(issuesChangesSerializer.serialize(any(IssuesChangesNotificationBuilder.class))).thenReturn(expected);
  563. IssuesChangesNotification notification = underTest.newIssuesChangesNotification(ImmutableSet.of(issue), assigneesByUuid);
  564. assertThat(notification).isSameAs(expected);
  565. IssuesChangesNotificationBuilder builder = verifyAndCaptureIssueChangeNotificationBuilder();
  566. assertThat(builder.getIssues()).hasSize(1);
  567. assertThat(builder.getChange())
  568. .isInstanceOf(AnalysisChange.class)
  569. .extracting(IssuesChangesNotificationBuilder.Change::getDate)
  570. .isEqualTo(analysisDate);
  571. }
  572. @Test
  573. public void newIssuesChangesNotification_maps_all_issues() {
  574. Set<DefaultIssue> issues = IntStream.range(0, 3 + new Random().nextInt(5))
  575. .mapToObj(i -> new DefaultIssue()
  576. .setRuleKey(RuleKey.of("repo_" + i, "rule_" + i))
  577. .setKey("issue_key_" + i)
  578. .setStatus("status_" + i))
  579. .collect(Collectors.toSet());
  580. ReportComponent project = ReportComponent.builder(PROJECT, 1).build();
  581. long analysisDate = new Random().nextLong();
  582. issues.stream()
  583. .map(DefaultIssue::ruleKey)
  584. .forEach(ruleKey -> ruleRepository.add(ruleKey));
  585. treeRootHolder.setRoot(project);
  586. analysisMetadata.setAnalysisDate(analysisDate);
  587. analysisMetadata.setBranch(newNonMainBranch(BranchType.BRANCH, randomAlphabetic(12)));
  588. IssuesChangesNotification expected = mock(IssuesChangesNotification.class);
  589. when(issuesChangesSerializer.serialize(any(IssuesChangesNotificationBuilder.class))).thenReturn(expected);
  590. IssuesChangesNotification notification = underTest.newIssuesChangesNotification(issues, emptyMap());
  591. assertThat(notification).isSameAs(expected);
  592. IssuesChangesNotificationBuilder builder = verifyAndCaptureIssueChangeNotificationBuilder();
  593. assertThat(builder.getIssues()).hasSize(issues.size());
  594. Map<String, ChangedIssue> changedIssuesByKey = builder.getIssues().stream()
  595. .collect(Collectors.toMap(ChangedIssue::getKey, Function.identity()));
  596. issues.forEach(
  597. issue -> {
  598. ChangedIssue changedIssue = changedIssuesByKey.get(issue.key());
  599. assertThat(changedIssue.getNewStatus()).isEqualTo(issue.status());
  600. assertThat(changedIssue.getAssignee()).isEmpty();
  601. assertThat(changedIssue.getRule().getKey()).isEqualTo(issue.ruleKey());
  602. assertThat(changedIssue.getRule().getName()).isEqualTo(ruleRepository.getByKey(issue.ruleKey()).getName());
  603. });
  604. }
  605. private static Map<String, UserDto> nonEmptyAssigneesByUuid() {
  606. return IntStream.range(0, 1 + new Random().nextInt(3))
  607. .boxed()
  608. .collect(Collectors.toMap(i -> "uuid_" + i, i1 -> new UserDto()));
  609. }
  610. private IssuesChangesNotificationBuilder verifyAndCaptureIssueChangeNotificationBuilder() {
  611. ArgumentCaptor<IssuesChangesNotificationBuilder> builderCaptor = ArgumentCaptor.forClass(IssuesChangesNotificationBuilder.class);
  612. verify(issuesChangesSerializer).serialize(builderCaptor.capture());
  613. verifyNoMoreInteractions(issuesChangesSerializer);
  614. return builderCaptor.getValue();
  615. }
  616. private static Branch newNonMainBranch(BranchType branchType, String branchName) {
  617. Branch nonMainBranch = mock(Branch.class);
  618. when(nonMainBranch.isMain()).thenReturn(false);
  619. when(nonMainBranch.getType()).thenReturn(branchType);
  620. when(nonMainBranch.getName()).thenReturn(branchName);
  621. return nonMainBranch;
  622. }
  623. private static Durations readDurationsField(NewIssuesNotification notification) {
  624. return readField(notification, "durations");
  625. }
  626. private static Durations readField(NewIssuesNotification notification, String fieldName) {
  627. try {
  628. Field durationsField = NewIssuesNotification.class.getDeclaredField(fieldName);
  629. durationsField.setAccessible(true);
  630. Object o = durationsField.get(notification);
  631. return (Durations) o;
  632. } catch (IllegalAccessException | NoSuchFieldException e) {
  633. throw new RuntimeException(e);
  634. }
  635. }
  636. private static DetailsSupplier readDetailsSupplier(NewIssuesNotification notification) {
  637. try {
  638. Field durationsField = NewIssuesNotification.class.getDeclaredField("detailsSupplier");
  639. durationsField.setAccessible(true);
  640. return (DetailsSupplier) durationsField.get(notification);
  641. } catch (IllegalAccessException | NoSuchFieldException e) {
  642. throw new RuntimeException(e);
  643. }
  644. }
  645. }