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 38KB

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