Skip to content

Commit a151fbd

Browse files
Check Workspace permission before showing source code viewer (#3244)
Use the new factory method of the Prism plugin to create the source viewer. This method will make sure that the correct permissions are in place.
1 parent 136accb commit a151fbd

File tree

4 files changed

+107
-36
lines changed

4 files changed

+107
-36
lines changed

plugin/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@
110110
<dependency>
111111
<groupId>io.jenkins.plugins</groupId>
112112
<artifactId>prism-api</artifactId>
113+
<version>1.30.0-701.vf8f8f1f3fd55</version>
113114
</dependency>
114115
<dependency>
115116
<groupId>org.jenkins-ci.plugins</groupId>

plugin/src/main/java/io/jenkins/plugins/analysis/core/model/DetailFactory.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,12 @@ private Object createFilteredView(final String link, final Run<?, ?> owner, fina
119119
else {
120120
var marker = asMarker(issue, labelProvider.getSourceCodeDescription(owner, issue), labelProvider.getSmallIconUrl());
121121
try (var affectedFile = buildFolder.readFile(owner, issue.getFileName(), sourceEncoding)) {
122-
return new SourceCodeViewModel(owner, issue.getBaseName(), affectedFile, marker);
122+
return SourceCodeViewModel.create(owner, issue.getBaseName(), affectedFile, marker);
123123
}
124124
catch (IOException e) {
125125
try (var fallback = new StringReader(
126126
"%s%n%s".formatted(ExceptionUtils.getMessage(e), ExceptionUtils.getStackTrace(e)))) {
127-
return new SourceCodeViewModel(owner, issue.getBaseName(), fallback, marker);
127+
return SourceCodeViewModel.create(owner, issue.getBaseName(), fallback, marker);
128128
}
129129
}
130130
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package io.jenkins.plugins.analysis.core.model;
2+
3+
import org.eclipse.collections.impl.factory.Lists;
4+
import org.junit.jupiter.api.Test;
5+
6+
import edu.hm.hafner.analysis.IssueBuilder;
7+
import edu.hm.hafner.analysis.Report;
8+
9+
import java.io.IOException;
10+
import java.io.StringReader;
11+
import java.nio.charset.Charset;
12+
import java.nio.charset.StandardCharsets;
13+
14+
import hudson.model.Run;
15+
16+
import io.jenkins.plugins.analysis.core.testutil.IntegrationTestWithJenkinsPerSuite;
17+
import io.jenkins.plugins.analysis.core.util.BuildFolderFacade;
18+
import io.jenkins.plugins.prism.SourceCodeViewModel;
19+
import io.jenkins.plugins.util.JenkinsFacade;
20+
21+
import static io.jenkins.plugins.analysis.core.testutil.Assertions.*;
22+
import static org.mockito.ArgumentMatchers.*;
23+
import static org.mockito.Mockito.*;
24+
25+
/**
26+
* Integration tests for {@link DetailFactory} source file reading functionality.
27+
* These tests require a Jenkins instance because SourceCodeViewModel.create() checks permissions.
28+
*
29+
* @author Akash Manna
30+
*/
31+
class DetailFactorySourceFileITest extends IntegrationTestWithJenkinsPerSuite {
32+
private static final Charset ENCODING = StandardCharsets.UTF_8;
33+
private static final String AFFECTED_FILE_CONTENT = "public class Test { }";
34+
private static final Report NEW_ISSUES = new Report();
35+
private static final Report OUTSTANDING_ISSUES = new Report();
36+
private static final Report FIXED_ISSUES = new Report();
37+
38+
/**
39+
* Checks that the error message is shown if an affected file could not be read.
40+
*/
41+
@Test
42+
void shouldShowExceptionMessageIfAffectedFileIsNotReadable() throws IOException {
43+
BuildFolderFacade buildFolder = mock(BuildFolderFacade.class);
44+
when(buildFolder.readFile(any(), anyString(), any())).thenThrow(new IOException("file error"));
45+
46+
var details = createDetails(buildFolder, "a-file");
47+
48+
assertThat(details).isInstanceOfSatisfying(SourceCodeViewModel.class,
49+
s -> assertThat(s.getSourceCode()).contains("IOException: file error"));
50+
}
51+
52+
/**
53+
* Checks that a link to a source returns a SourceDetail-View.
54+
*/
55+
@Test
56+
void shouldReturnSourceDetailWhenCalledWithSourceLinkAndIssueNotInConsoleLog() throws IOException {
57+
BuildFolderFacade buildFolder = mock(BuildFolderFacade.class);
58+
when(buildFolder.readFile(any(), anyString(), any())).thenReturn(new StringReader(AFFECTED_FILE_CONTENT));
59+
60+
var details = createDetails(buildFolder, "a-file");
61+
62+
assertThat(details).isInstanceOfSatisfying(SourceCodeViewModel.class,
63+
s -> assertThat(s.getSourceCode()).contains(AFFECTED_FILE_CONTENT));
64+
}
65+
66+
private Object createDetails(final BuildFolderFacade buildFolder,
67+
final String fileName) {
68+
try (var issueBuilder = new IssueBuilder()) {
69+
var detailFactory = new DetailFactory(new JenkinsFacade(), buildFolder);
70+
71+
issueBuilder.setFileName(fileName);
72+
var issue = issueBuilder.build();
73+
74+
var report = new Report();
75+
report.add(issue);
76+
77+
var project = createFreeStyleProject();
78+
buildSuccessfully(project);
79+
Run<?, ?> run = project.getLastBuild();
80+
81+
return detailFactory.createTrendDetails("source." + issue.getId().toString(),
82+
run, createAnalysisResult(), report, NEW_ISSUES, OUTSTANDING_ISSUES, FIXED_ISSUES, ENCODING,
83+
createParent());
84+
}
85+
}
86+
87+
private AnalysisResult createAnalysisResult() {
88+
AnalysisResult result = mock(AnalysisResult.class);
89+
when(result.getErrorMessages()).thenReturn(Lists.immutable.empty());
90+
when(result.getInfoMessages()).thenReturn(Lists.immutable.empty());
91+
return result;
92+
}
93+
94+
private IssuesDetail createParent() {
95+
IssuesDetail parent = mock(IssuesDetail.class);
96+
StaticAnalysisLabelProvider labelProvider = mock(StaticAnalysisLabelProvider.class);
97+
when(labelProvider.getName()).thenReturn("Test");
98+
when(labelProvider.getSmallIconUrl()).thenReturn("/icon");
99+
when(labelProvider.getLargeIconUrl()).thenReturn("/icon");
100+
when(parent.getLabelProvider()).thenReturn(labelProvider);
101+
return parent;
102+
}
103+
}

plugin/src/test/java/io/jenkins/plugins/analysis/core/model/DetailFactoryTest.java

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88
import edu.hm.hafner.analysis.Report;
99
import edu.hm.hafner.analysis.Severity;
1010

11-
import java.io.IOException;
12-
import java.io.StringReader;
1311
import java.nio.charset.Charset;
1412
import java.nio.charset.StandardCharsets;
1513
import java.util.ArrayList;
@@ -27,7 +25,6 @@
2725
import io.jenkins.plugins.analysis.core.util.BuildFolderFacade;
2826
import io.jenkins.plugins.analysis.core.util.ConsoleLogHandler;
2927
import io.jenkins.plugins.bootstrap5.MessagesViewModel;
30-
import io.jenkins.plugins.prism.SourceCodeViewModel;
3128
import io.jenkins.plugins.util.JenkinsFacade;
3229

3330
import static io.jenkins.plugins.analysis.core.testutil.Assertions.*;
@@ -230,36 +227,6 @@ RUN, createResult(), report, NEW_ISSUES, OUTSTANDING_ISSUES, FIXED_ISSUES, ENCOD
230227
}
231228
}
232229

233-
/**
234-
* Checks that the error message is shown if an affected file could not be read.
235-
*/
236-
@Test
237-
void shouldShowExceptionMessageIfAffectedFileIsNotReadable() throws IOException {
238-
JenkinsFacade jenkins = mock(JenkinsFacade.class);
239-
BuildFolderFacade buildFolder = mock(BuildFolderFacade.class);
240-
when(buildFolder.readFile(any(), anyString(), any())).thenThrow(new IOException("file error"));
241-
242-
var details = createDetails(jenkins, buildFolder, "a-file");
243-
244-
assertThat(details).isInstanceOfSatisfying(SourceCodeViewModel.class,
245-
s -> assertThat(s.getSourceCode()).contains("IOException: file error"));
246-
}
247-
248-
/**
249-
* Checks that a to a source, returns a SourceDetail-View.
250-
*/
251-
@Test
252-
void shouldReturnSourceDetailWhenCalledWithSourceLinkAndIssueNotInConsoleLog() throws IOException {
253-
JenkinsFacade jenkins = mock(JenkinsFacade.class);
254-
BuildFolderFacade buildFolder = mock(BuildFolderFacade.class);
255-
when(buildFolder.readFile(any(), anyString(), any())).thenReturn(new StringReader(AFFECTED_FILE_CONTENT));
256-
257-
var details = createDetails(jenkins, buildFolder, "a-file");
258-
259-
assertThat(details).isInstanceOfSatisfying(SourceCodeViewModel.class,
260-
s -> assertThat(s.getSourceCode()).contains(AFFECTED_FILE_CONTENT));
261-
}
262-
263230
/**
264231
* Checks that a link with a filter, that results to an non empty set, returns an IssueDetail-View that only
265232
* contains filtered issues.
@@ -365,4 +332,4 @@ private static Report createReportWith(final int high, final int normal, final i
365332
return issues;
366333
}
367334
}
368-
}
335+
}

0 commit comments

Comments
 (0)