2 * SonarQube, open source software quality management tool.
3 * Copyright (C) 2008-2014 SonarSource
4 * mailto:contact AT sonarsource DOT com
6 * SonarQube is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
11 * SonarQube is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 package org.sonar.plugins.core.issue;
22 import org.junit.Before;
23 import org.junit.Test;
24 import org.mockito.ArgumentCaptor;
25 import org.mockito.ArgumentMatcher;
26 import org.sonar.api.batch.DecoratorContext;
27 import org.sonar.api.batch.SonarIndex;
28 import org.sonar.api.component.ResourcePerspectives;
29 import org.sonar.api.issue.Issue;
30 import org.sonar.api.issue.internal.DefaultIssue;
31 import org.sonar.api.issue.internal.IssueChangeContext;
32 import org.sonar.api.profiles.RulesProfile;
33 import org.sonar.api.resources.File;
34 import org.sonar.api.resources.Project;
35 import org.sonar.api.resources.Resource;
36 import org.sonar.api.rule.RuleKey;
37 import org.sonar.api.rules.Rule;
38 import org.sonar.api.rules.RuleFinder;
39 import org.sonar.api.utils.Duration;
40 import org.sonar.batch.issue.IssueCache;
41 import org.sonar.batch.scan.LastSnapshots;
42 import org.sonar.core.issue.IssueUpdater;
43 import org.sonar.core.issue.db.IssueChangeDto;
44 import org.sonar.core.issue.db.IssueDto;
45 import org.sonar.core.issue.workflow.IssueWorkflow;
46 import org.sonar.core.persistence.AbstractDaoTestCase;
47 import org.sonar.java.api.JavaClass;
49 import java.util.Arrays;
50 import java.util.Collection;
51 import java.util.Collections;
52 import java.util.List;
54 import static com.google.common.collect.Lists.newArrayList;
55 import static org.fest.assertions.Assertions.assertThat;
56 import static org.mockito.Matchers.any;
57 import static org.mockito.Matchers.anyCollection;
58 import static org.mockito.Matchers.argThat;
59 import static org.mockito.Matchers.eq;
60 import static org.mockito.Matchers.isA;
61 import static org.mockito.Mockito.*;
62 import static org.mockito.Mockito.anyString;
64 public class IssueTrackingDecoratorTest extends AbstractDaoTestCase {
66 IssueTrackingDecorator decorator;
67 IssueCache issueCache = mock(IssueCache.class, RETURNS_MOCKS);
68 InitialOpenIssuesStack initialOpenIssues = mock(InitialOpenIssuesStack.class);
69 IssueTracking tracking = mock(IssueTracking.class, RETURNS_MOCKS);
70 LastSnapshots lastSnapshots = mock(LastSnapshots.class);
71 SonarIndex index = mock(SonarIndex.class);
72 IssueHandlers handlers = mock(IssueHandlers.class);
73 IssueWorkflow workflow = mock(IssueWorkflow.class);
74 IssueUpdater updater = mock(IssueUpdater.class);
75 ResourcePerspectives perspectives = mock(ResourcePerspectives.class);
76 RulesProfile profile = mock(RulesProfile.class);
77 RuleFinder ruleFinder = mock(RuleFinder.class);
81 decorator = new IssueTrackingDecorator(
97 public void should_execute_on_project() {
98 Project project = mock(Project.class);
99 assertThat(decorator.shouldExecuteOnProject(project)).isTrue();
103 public void should_not_be_executed_on_classes_not_methods() throws Exception {
104 DecoratorContext context = mock(DecoratorContext.class);
105 decorator.decorate(JavaClass.create("org.foo.Bar"), context);
106 verifyZeroInteractions(context, issueCache, tracking, handlers, workflow);
110 public void should_process_open_issues() throws Exception {
111 Resource file = new File("Action.java").setEffectiveKey("struts:Action.java").setId(123);
112 final DefaultIssue issue = new DefaultIssue();
114 // INPUT : one issue, no open issues during previous scan, no filtering
115 when(issueCache.byComponent("struts:Action.java")).thenReturn(Arrays.asList(issue));
116 List<IssueDto> dbIssues = Collections.emptyList();
117 when(initialOpenIssues.selectAndRemoveIssues("struts:Action.java")).thenReturn(dbIssues);
119 decorator.doDecorate(file);
121 // Apply filters, track, apply transitions, notify extensions then update cache
122 verify(tracking).track(isA(SourceHashHolder.class), eq(dbIssues), argThat(new ArgumentMatcher<Collection<DefaultIssue>>() {
124 public boolean matches(Object o) {
125 List<DefaultIssue> issues = (List<DefaultIssue>) o;
126 return issues.size() == 1 && issues.get(0) == issue;
129 verify(workflow).doAutomaticTransition(eq(issue), any(IssueChangeContext.class));
130 verify(handlers).execute(eq(issue), any(IssueChangeContext.class));
131 verify(issueCache).put(issue);
135 public void should_register_unmatched_issues_as_end_of_life() throws Exception {
136 // "Unmatched" issues existed in previous scan but not in current one -> they have to be closed
137 Resource file = new File("Action.java").setEffectiveKey("struts:Action.java").setId(123);
139 // INPUT : one issue existing during previous scan
140 IssueDto unmatchedIssue = new IssueDto().setKee("ABCDE").setResolution(null).setStatus("OPEN").setRuleKey_unit_test_only("squid", "AvoidCycle");
142 IssueTrackingResult trackingResult = new IssueTrackingResult();
143 trackingResult.addUnmatched(unmatchedIssue);
145 when(tracking.track(isA(SourceHashHolder.class), anyCollection(), anyCollection())).thenReturn(trackingResult);
147 decorator.doDecorate(file);
149 verify(workflow, times(1)).doAutomaticTransition(any(DefaultIssue.class), any(IssueChangeContext.class));
150 verify(handlers, times(1)).execute(any(DefaultIssue.class), any(IssueChangeContext.class));
152 ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class);
153 verify(issueCache).put(argument.capture());
155 DefaultIssue issue = argument.getValue();
156 assertThat(issue.key()).isEqualTo("ABCDE");
157 assertThat(issue.isNew()).isFalse();
158 assertThat(issue.isEndOfLife()).isTrue();
162 public void manual_issues_should_be_moved_if_matching_line_found() throws Exception {
163 Resource file = new File("Action.java").setEffectiveKey("struts:Action.java").setId(123);
165 // INPUT : one issue existing during previous scan
166 IssueDto unmatchedIssue = new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(6).setStatus("OPEN").setRuleKey_unit_test_only("manual", "Performance");
167 when(ruleFinder.findByKey(RuleKey.of("manual", "Performance"))).thenReturn(new Rule("manual", "Performance"));
169 IssueTrackingResult trackingResult = new IssueTrackingResult();
170 trackingResult.addUnmatched(unmatchedIssue);
172 String originalSource = "public interface Action {\n"
173 + " void method1();\n"
174 + " void method2();\n"
175 + " void method3();\n"
176 + " void method4();\n"
177 + " void method5();\n" // Original issue here
179 String newSource = "public interface Action {\n"
180 + " void method5();\n" // New issue here
181 + " void method1();\n"
182 + " void method2();\n"
183 + " void method3();\n"
184 + " void method4();\n"
186 when(index.getSource(file)).thenReturn(newSource);
187 when(lastSnapshots.getSource(file)).thenReturn(originalSource);
189 when(tracking.track(isA(SourceHashHolder.class), anyCollection(), anyCollection())).thenReturn(trackingResult);
191 decorator.doDecorate(file);
193 verify(workflow, times(1)).doAutomaticTransition(any(DefaultIssue.class), any(IssueChangeContext.class));
194 verify(handlers, times(1)).execute(any(DefaultIssue.class), any(IssueChangeContext.class));
196 ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class);
197 verify(issueCache).put(argument.capture());
199 DefaultIssue issue = argument.getValue();
200 assertThat(issue.line()).isEqualTo(2);
201 assertThat(issue.key()).isEqualTo("ABCDE");
202 assertThat(issue.isNew()).isFalse();
203 assertThat(issue.isEndOfLife()).isFalse();
204 assertThat(issue.isOnDisabledRule()).isFalse();
208 public void manual_issues_should_be_untouched_if_already_closed() throws Exception {
209 Resource file = new File("Action.java").setEffectiveKey("struts:Action.java").setId(123);
211 // INPUT : one issue existing during previous scan
212 IssueDto unmatchedIssue = new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(1).setStatus("CLOSED").setRuleKey_unit_test_only("manual", "Performance");
213 when(ruleFinder.findByKey(RuleKey.of("manual", "Performance"))).thenReturn(new Rule("manual", "Performance"));
215 IssueTrackingResult trackingResult = new IssueTrackingResult();
216 trackingResult.addUnmatched(unmatchedIssue);
218 String originalSource = "public interface Action {}";
219 when(index.getSource(file)).thenReturn(originalSource);
220 when(lastSnapshots.getSource(file)).thenReturn(originalSource);
222 when(tracking.track(isA(SourceHashHolder.class), anyCollection(), anyCollection())).thenReturn(trackingResult);
224 decorator.doDecorate(file);
226 verify(workflow, times(1)).doAutomaticTransition(any(DefaultIssue.class), any(IssueChangeContext.class));
227 verify(handlers, times(1)).execute(any(DefaultIssue.class), any(IssueChangeContext.class));
229 ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class);
230 verify(issueCache).put(argument.capture());
232 DefaultIssue issue = argument.getValue();
233 assertThat(issue.line()).isEqualTo(1);
234 assertThat(issue.key()).isEqualTo("ABCDE");
235 assertThat(issue.isNew()).isFalse();
236 assertThat(issue.isEndOfLife()).isFalse();
237 assertThat(issue.isOnDisabledRule()).isFalse();
238 assertThat(issue.status()).isEqualTo("CLOSED");
242 public void manual_issues_should_be_untouched_if_line_is_null() throws Exception {
243 Resource file = new File("Action.java").setEffectiveKey("struts:Action.java").setId(123);
245 // INPUT : one issue existing during previous scan
246 IssueDto unmatchedIssue = new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(null).setStatus("OPEN").setRuleKey_unit_test_only("manual", "Performance");
247 when(ruleFinder.findByKey(RuleKey.of("manual", "Performance"))).thenReturn(new Rule("manual", "Performance"));
249 IssueTrackingResult trackingResult = new IssueTrackingResult();
250 trackingResult.addUnmatched(unmatchedIssue);
252 String originalSource = "public interface Action {}";
253 when(index.getSource(file)).thenReturn(originalSource);
254 when(lastSnapshots.getSource(file)).thenReturn(originalSource);
256 when(tracking.track(isA(SourceHashHolder.class), anyCollection(), anyCollection())).thenReturn(trackingResult);
258 decorator.doDecorate(file);
260 verify(workflow, times(1)).doAutomaticTransition(any(DefaultIssue.class), any(IssueChangeContext.class));
261 verify(handlers, times(1)).execute(any(DefaultIssue.class), any(IssueChangeContext.class));
263 ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class);
264 verify(issueCache).put(argument.capture());
266 DefaultIssue issue = argument.getValue();
267 assertThat(issue.line()).isEqualTo(null);
268 assertThat(issue.key()).isEqualTo("ABCDE");
269 assertThat(issue.isNew()).isFalse();
270 assertThat(issue.isEndOfLife()).isFalse();
271 assertThat(issue.isOnDisabledRule()).isFalse();
272 assertThat(issue.status()).isEqualTo("OPEN");
276 public void manual_issues_should_be_kept_if_matching_line_not_found() throws Exception {
277 // "Unmatched" issues existed in previous scan but not in current one -> they have to be closed
278 Resource file = new File("Action.java").setEffectiveKey("struts:Action.java").setId(123);
280 // INPUT : one issue existing during previous scan
281 final int issueOnLine = 6;
282 IssueDto unmatchedIssue = new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(issueOnLine).setStatus("OPEN").setRuleKey_unit_test_only("manual", "Performance");
283 when(ruleFinder.findByKey(RuleKey.of("manual", "Performance"))).thenReturn(new Rule("manual", "Performance"));
285 IssueTrackingResult trackingResult = new IssueTrackingResult();
286 trackingResult.addUnmatched(unmatchedIssue);
288 String originalSource = "public interface Action {\n"
289 + " void method1();\n"
290 + " void method2();\n"
291 + " void method3();\n"
292 + " void method4();\n"
293 + " void method5();\n" // Original issue here
295 String newSource = "public interface Action {\n"
296 + " void method1();\n"
297 + " void method2();\n"
298 + " void method3();\n"
299 + " void method4();\n"
300 + " void method6();\n" // Poof, no method5 anymore
302 when(index.getSource(file)).thenReturn(newSource);
303 when(lastSnapshots.getSource(file)).thenReturn(originalSource);
305 when(tracking.track(isA(SourceHashHolder.class), anyCollection(), anyCollection())).thenReturn(trackingResult);
307 decorator.doDecorate(file);
309 verify(workflow, times(1)).doAutomaticTransition(any(DefaultIssue.class), any(IssueChangeContext.class));
310 verify(handlers, times(1)).execute(any(DefaultIssue.class), any(IssueChangeContext.class));
312 ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class);
313 verify(issueCache).put(argument.capture());
315 DefaultIssue issue = argument.getValue();
316 assertThat(issue.line()).isEqualTo(issueOnLine);
317 assertThat(issue.key()).isEqualTo("ABCDE");
318 assertThat(issue.isNew()).isFalse();
319 assertThat(issue.isEndOfLife()).isFalse();
320 assertThat(issue.isOnDisabledRule()).isFalse();
324 public void manual_issues_should_be_kept_if_multiple_matching_lines_found() throws Exception {
325 // "Unmatched" issues existed in previous scan but not in current one -> they have to be closed
326 Resource file = new File("Action.java").setEffectiveKey("struts:Action.java").setId(123);
328 // INPUT : one issue existing during previous scan
329 final int issueOnLine = 3;
330 IssueDto unmatchedIssue = new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(issueOnLine).setStatus("OPEN").setRuleKey_unit_test_only("manual", "Performance");
331 when(ruleFinder.findByKey(RuleKey.of("manual", "Performance"))).thenReturn(new Rule("manual", "Performance"));
333 IssueTrackingResult trackingResult = new IssueTrackingResult();
334 trackingResult.addUnmatched(unmatchedIssue);
336 String originalSource = "public class Action {\n"
337 + " void method1() {\n"
338 + " notify();\n" // initial issue
341 String newSource = "public class Action {\n"
343 + " void method1() {\n" // new issue will appear here
346 + " void method2() {\n"
350 when(index.getSource(file)).thenReturn(newSource);
351 when(lastSnapshots.getSource(file)).thenReturn(originalSource);
353 when(tracking.track(isA(SourceHashHolder.class), anyCollection(), anyCollection())).thenReturn(trackingResult);
355 decorator.doDecorate(file);
357 verify(workflow, times(1)).doAutomaticTransition(any(DefaultIssue.class), any(IssueChangeContext.class));
358 verify(handlers, times(1)).execute(any(DefaultIssue.class), any(IssueChangeContext.class));
360 ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class);
361 verify(issueCache).put(argument.capture());
363 DefaultIssue issue = argument.getValue();
364 assertThat(issue.line()).isEqualTo(issueOnLine);
365 assertThat(issue.key()).isEqualTo("ABCDE");
366 assertThat(issue.isNew()).isFalse();
367 assertThat(issue.isEndOfLife()).isFalse();
368 assertThat(issue.isOnDisabledRule()).isFalse();
373 public void manual_issues_should_be_closed_if_manual_rule_is_removed() throws Exception {
374 // "Unmatched" issues existed in previous scan but not in current one -> they have to be closed
375 Resource file = new File("Action.java").setEffectiveKey("struts:Action.java").setId(123);
377 // INPUT : one issue existing during previous scan
378 IssueDto unmatchedIssue = new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(1).setStatus("OPEN").setRuleKey_unit_test_only("manual", "Performance");
379 when(ruleFinder.findByKey(RuleKey.of("manual", "Performance"))).thenReturn(new Rule("manual", "Performance").setStatus(Rule.STATUS_REMOVED));
381 IssueTrackingResult trackingResult = new IssueTrackingResult();
382 trackingResult.addUnmatched(unmatchedIssue);
384 String source = "public interface Action {}";
385 when(index.getSource(file)).thenReturn(source);
386 when(lastSnapshots.getSource(file)).thenReturn(source);
388 when(tracking.track(isA(SourceHashHolder.class), anyCollection(), anyCollection())).thenReturn(trackingResult);
390 decorator.doDecorate(file);
392 verify(workflow, times(1)).doAutomaticTransition(any(DefaultIssue.class), any(IssueChangeContext.class));
393 verify(handlers, times(1)).execute(any(DefaultIssue.class), any(IssueChangeContext.class));
395 ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class);
396 verify(issueCache).put(argument.capture());
398 DefaultIssue issue = argument.getValue();
399 assertThat(issue.key()).isEqualTo("ABCDE");
400 assertThat(issue.isNew()).isFalse();
401 assertThat(issue.isEndOfLife()).isTrue();
402 assertThat(issue.isOnDisabledRule()).isTrue();
406 public void manual_issues_should_be_closed_if_manual_rule_is_not_found() throws Exception {
407 // "Unmatched" issues existed in previous scan but not in current one -> they have to be closed
408 Resource file = new File("Action.java").setEffectiveKey("struts:Action.java").setId(123);
410 // INPUT : one issue existing during previous scan
411 IssueDto unmatchedIssue = new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(1).setStatus("OPEN").setRuleKey_unit_test_only("manual", "Performance");
412 when(ruleFinder.findByKey(RuleKey.of("manual", "Performance"))).thenReturn(null);
414 IssueTrackingResult trackingResult = new IssueTrackingResult();
415 trackingResult.addUnmatched(unmatchedIssue);
417 String source = "public interface Action {}";
418 when(index.getSource(file)).thenReturn(source);
419 when(lastSnapshots.getSource(file)).thenReturn(source);
421 when(tracking.track(isA(SourceHashHolder.class), anyCollection(), anyCollection())).thenReturn(trackingResult);
423 decorator.doDecorate(file);
425 verify(workflow, times(1)).doAutomaticTransition(any(DefaultIssue.class), any(IssueChangeContext.class));
426 verify(handlers, times(1)).execute(any(DefaultIssue.class), any(IssueChangeContext.class));
428 ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class);
429 verify(issueCache).put(argument.capture());
431 DefaultIssue issue = argument.getValue();
432 assertThat(issue.key()).isEqualTo("ABCDE");
433 assertThat(issue.isNew()).isFalse();
434 assertThat(issue.isEndOfLife()).isTrue();
435 assertThat(issue.isOnDisabledRule()).isTrue();
439 public void manual_issues_should_be_closed_if_new_source_is_shorter() throws Exception {
440 // "Unmatched" issues existed in previous scan but not in current one -> they have to be closed
441 Resource file = new File("Action.java").setEffectiveKey("struts:Action.java").setId(123);
443 // INPUT : one issue existing during previous scan
444 IssueDto unmatchedIssue = new IssueDto().setKee("ABCDE").setReporter("freddy").setLine(6).setStatus("OPEN").setRuleKey_unit_test_only("manual", "Performance");
445 when(ruleFinder.findByKey(RuleKey.of("manual", "Performance"))).thenReturn(null);
447 IssueTrackingResult trackingResult = new IssueTrackingResult();
448 trackingResult.addUnmatched(unmatchedIssue);
450 String originalSource = "public interface Action {\n"
451 + " void method1();\n"
452 + " void method2();\n"
453 + " void method3();\n"
454 + " void method4();\n"
455 + " void method5();\n"
457 String newSource = "public interface Action {\n"
458 + " void method1();\n"
459 + " void method2();\n"
461 when(index.getSource(file)).thenReturn(newSource);
462 when(lastSnapshots.getSource(file)).thenReturn(originalSource);
464 when(tracking.track(isA(SourceHashHolder.class), anyCollection(), anyCollection())).thenReturn(trackingResult);
466 decorator.doDecorate(file);
468 verify(workflow, times(1)).doAutomaticTransition(any(DefaultIssue.class), any(IssueChangeContext.class));
469 verify(handlers, times(1)).execute(any(DefaultIssue.class), any(IssueChangeContext.class));
471 ArgumentCaptor<DefaultIssue> argument = ArgumentCaptor.forClass(DefaultIssue.class);
472 verify(issueCache).put(argument.capture());
474 DefaultIssue issue = argument.getValue();
475 verify(updater).setResolution(eq(issue), eq(Issue.RESOLUTION_REMOVED), any(IssueChangeContext.class));
476 verify(updater).setStatus(eq(issue), eq(Issue.STATUS_CLOSED), any(IssueChangeContext.class));
478 assertThat(issue.key()).isEqualTo("ABCDE");
479 assertThat(issue.isNew()).isFalse();
480 assertThat(issue.isEndOfLife()).isTrue();
481 assertThat(issue.isOnDisabledRule()).isTrue();
485 public void should_register_issues_on_deleted_components() throws Exception {
486 Project project = new Project("struts");
487 DefaultIssue openIssue = new DefaultIssue();
488 when(issueCache.byComponent("struts")).thenReturn(Arrays.asList(openIssue));
489 IssueDto deadIssue = new IssueDto().setKee("ABCDE").setResolution(null).setStatus("OPEN").setRuleKey_unit_test_only("squid", "AvoidCycle");
490 when(initialOpenIssues.selectAllIssues()).thenReturn(Arrays.asList(deadIssue));
492 decorator.doDecorate(project);
494 // the dead issue must be closed -> apply automatic transition, notify handlers and add to cache
495 verify(workflow, times(2)).doAutomaticTransition(any(DefaultIssue.class), any(IssueChangeContext.class));
496 verify(handlers, times(2)).execute(any(DefaultIssue.class), any(IssueChangeContext.class));
497 verify(issueCache, times(2)).put(any(DefaultIssue.class));
499 verify(issueCache).put(argThat(new ArgumentMatcher<DefaultIssue>() {
501 public boolean matches(Object o) {
502 DefaultIssue dead = (DefaultIssue) o;
503 return "ABCDE".equals(dead.key()) && !dead.isNew() && dead.isEndOfLife();
509 public void merge_matched_issue() throws Exception {
510 IssueDto previousIssue = new IssueDto().setKee("ABCDE").setResolution(null).setStatus("OPEN").setRuleKey_unit_test_only("squid", "AvoidCycle")
511 .setLine(10).setSeverity("MAJOR").setMessage("Message").setEffortToFix(1.5).setDebt(1L).setRootComponentKey_unit_test_only("sample");
512 DefaultIssue issue = new DefaultIssue();
514 IssueTrackingResult trackingResult = mock(IssueTrackingResult.class);
515 when(trackingResult.matched()).thenReturn(newArrayList(issue));
516 when(trackingResult.matching(eq(issue))).thenReturn(previousIssue);
517 decorator.mergeMatched(trackingResult);
519 verify(updater).setPastSeverity(eq(issue), eq("MAJOR"), any(IssueChangeContext.class));
520 verify(updater).setPastLine(eq(issue), eq(10));
521 verify(updater).setPastMessage(eq(issue), eq("Message"), any(IssueChangeContext.class));
522 verify(updater).setPastEffortToFix(eq(issue), eq(1.5), any(IssueChangeContext.class));
523 verify(updater).setPastTechnicalDebt(eq(issue), eq(Duration.create(1L)), any(IssueChangeContext.class));
524 verify(updater).setPastProject(eq(issue), eq("sample"), any(IssueChangeContext.class));
528 public void merge_matched_issue_on_manual_severity() throws Exception {
529 IssueDto previousIssue = new IssueDto().setKee("ABCDE").setResolution(null).setStatus("OPEN").setRuleKey_unit_test_only("squid", "AvoidCycle")
530 .setLine(10).setManualSeverity(true).setSeverity("MAJOR").setMessage("Message").setEffortToFix(1.5).setDebt(1L);
531 DefaultIssue issue = new DefaultIssue();
533 IssueTrackingResult trackingResult = mock(IssueTrackingResult.class);
534 when(trackingResult.matched()).thenReturn(newArrayList(issue));
535 when(trackingResult.matching(eq(issue))).thenReturn(previousIssue);
536 decorator.mergeMatched(trackingResult);
538 assertThat(issue.manualSeverity()).isTrue();
539 assertThat(issue.severity()).isEqualTo("MAJOR");
540 verify(updater, never()).setPastSeverity(eq(issue), anyString(), any(IssueChangeContext.class));
544 public void merge_issue_changelog_with_previous_changelog() throws Exception {
545 when(initialOpenIssues.selectChangelog("ABCDE")).thenReturn(newArrayList(new IssueChangeDto().setIssueKey("ABCD")));
547 IssueDto previousIssue = new IssueDto().setKee("ABCDE").setResolution(null).setStatus("OPEN").setRuleKey_unit_test_only("squid", "AvoidCycle")
548 .setLine(10).setMessage("Message").setEffortToFix(1.5).setDebt(1L);
549 DefaultIssue issue = new DefaultIssue();
551 IssueTrackingResult trackingResult = mock(IssueTrackingResult.class);
552 when(trackingResult.matched()).thenReturn(newArrayList(issue));
553 when(trackingResult.matching(eq(issue))).thenReturn(previousIssue);
554 decorator.mergeMatched(trackingResult);
556 assertThat(issue.changes()).hasSize(1);