Skip to content

Commit 2a95132

Browse files
authored
[apache#377](https://github.com/maximiln/maven-build-cache-extension/issues/377) Fix snapshot artifact resolution in MavenProjectInput (apache#379)
1 parent 1992853 commit 2a95132

File tree

2 files changed

+332
-0
lines changed

2 files changed

+332
-0
lines changed

src/main/java/org/apache/maven/buildcache/checksum/MavenProjectInput.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,7 @@ private DigestItem resolveArtifact(final Dependency dependency)
818818
dependency.getVersion(),
819819
new DefaultArtifactType(dependency.getType()));
820820
ArtifactRequest artifactRequest = new ArtifactRequest().setArtifact(dependencyArtifact);
821+
artifactRequest.setRepositories(project.getRemoteProjectRepositories());
821822

822823
ArtifactResult result = repoSystem.resolveArtifact(session.getRepositorySession(), artifactRequest);
823824

Lines changed: 331 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,331 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.maven.buildcache.checksum;
20+
21+
import java.nio.file.Path;
22+
import java.util.ArrayList;
23+
import java.util.List;
24+
import java.util.Properties;
25+
26+
import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
27+
import org.apache.maven.buildcache.MultiModuleSupport;
28+
import org.apache.maven.buildcache.NormalizedModelProvider;
29+
import org.apache.maven.buildcache.ProjectInputCalculator;
30+
import org.apache.maven.buildcache.RemoteCacheRepository;
31+
import org.apache.maven.buildcache.xml.CacheConfig;
32+
import org.apache.maven.execution.MavenSession;
33+
import org.apache.maven.model.Dependency;
34+
import org.apache.maven.project.MavenProject;
35+
import org.eclipse.aether.RepositorySystem;
36+
import org.eclipse.aether.RepositorySystemSession;
37+
import org.eclipse.aether.artifact.DefaultArtifact;
38+
import org.eclipse.aether.repository.RemoteRepository;
39+
import org.eclipse.aether.resolution.ArtifactRequest;
40+
import org.eclipse.aether.resolution.ArtifactResult;
41+
import org.junit.jupiter.api.BeforeEach;
42+
import org.junit.jupiter.api.Test;
43+
import org.junit.jupiter.api.extension.ExtendWith;
44+
import org.junit.jupiter.api.io.TempDir;
45+
import org.mockito.ArgumentCaptor;
46+
import org.mockito.Mock;
47+
import org.mockito.junit.jupiter.MockitoExtension;
48+
import org.mockito.junit.jupiter.MockitoSettings;
49+
import org.mockito.quality.Strictness;
50+
51+
import static org.junit.jupiter.api.Assertions.assertEquals;
52+
import static org.junit.jupiter.api.Assertions.assertFalse;
53+
import static org.junit.jupiter.api.Assertions.assertTrue;
54+
import static org.mockito.ArgumentMatchers.any;
55+
import static org.mockito.ArgumentMatchers.eq;
56+
import static org.mockito.Mockito.verify;
57+
import static org.mockito.Mockito.when;
58+
59+
/**
60+
* Test for snapshot artifact resolution bug fix in MavenProjectInput.
61+
* This test verifies that when MavenProjectInput resolves snapshot dependencies,
62+
* the remote repositories are properly set on the ArtifactRequest, enabling
63+
* resolution from remote repositories.
64+
*/
65+
@ExtendWith(MockitoExtension.class)
66+
@MockitoSettings(strictness = Strictness.LENIENT)
67+
class MavenProjectInputSnapshotResolutionTest {
68+
69+
@Mock
70+
private MavenProject project;
71+
72+
@Mock
73+
private MavenSession session;
74+
75+
@Mock
76+
private RepositorySystem repoSystem;
77+
78+
@Mock
79+
private RepositorySystemSession repositorySystemSession;
80+
81+
@Mock
82+
private NormalizedModelProvider normalizedModelProvider;
83+
84+
@Mock
85+
private MultiModuleSupport multiModuleSupport;
86+
87+
@Mock
88+
private ProjectInputCalculator projectInputCalculator;
89+
90+
@Mock
91+
private CacheConfig config;
92+
93+
@Mock
94+
private RemoteCacheRepository remoteCache;
95+
96+
@Mock
97+
private ArtifactHandlerManager artifactHandlerManager;
98+
99+
@TempDir
100+
Path tempDir;
101+
102+
private MavenProjectInput mavenProjectInput;
103+
104+
@BeforeEach
105+
void setUp() {
106+
// Setup basic mocks that MavenProjectInput constructor needs
107+
when(session.getRepositorySession()).thenReturn(repositorySystemSession);
108+
when(project.getBasedir()).thenReturn(tempDir.toFile());
109+
when(project.getProperties()).thenReturn(new Properties());
110+
when(config.getDefaultGlob()).thenReturn("*");
111+
when(config.isProcessPlugins()).thenReturn("false");
112+
when(config.getGlobalExcludePaths()).thenReturn(new java.util.ArrayList<>());
113+
when(config.calculateProjectVersionChecksum()).thenReturn(Boolean.FALSE);
114+
115+
// Mock the build object that MavenProjectInput constructor needs
116+
org.apache.maven.model.Build build = new org.apache.maven.model.Build();
117+
build.setDirectory(tempDir.toString());
118+
build.setOutputDirectory(tempDir.resolve("target/classes").toString());
119+
build.setTestOutputDirectory(tempDir.resolve("target/test-classes").toString());
120+
build.setSourceDirectory(tempDir.resolve("src/main/java").toString());
121+
build.setTestSourceDirectory(tempDir.resolve("src/test/java").toString());
122+
build.setResources(new java.util.ArrayList<>());
123+
build.setTestResources(new java.util.ArrayList<>());
124+
when(project.getBuild()).thenReturn(build);
125+
126+
// Mock additional project methods that might be needed
127+
when(project.getDependencies()).thenReturn(new java.util.ArrayList<>());
128+
when(project.getBuildPlugins()).thenReturn(new java.util.ArrayList<>());
129+
when(project.getModules()).thenReturn(new java.util.ArrayList<>());
130+
when(project.getPackaging()).thenReturn("jar");
131+
132+
// Create the actual MavenProjectInput instance
133+
mavenProjectInput = new MavenProjectInput(
134+
project,
135+
normalizedModelProvider,
136+
multiModuleSupport,
137+
projectInputCalculator,
138+
session,
139+
config,
140+
repoSystem,
141+
remoteCache,
142+
artifactHandlerManager);
143+
}
144+
145+
@Test
146+
void testArtifactRequestWithRepositoriesSet() throws Exception {
147+
// Given: A snapshot dependency and configured repositories
148+
Dependency dependency = new Dependency();
149+
dependency.setGroupId("com.example");
150+
dependency.setArtifactId("test-artifact");
151+
dependency.setVersion("1.0-SNAPSHOT");
152+
dependency.setType("jar");
153+
154+
// Mock repository setup
155+
RemoteRepository centralRepo =
156+
new RemoteRepository.Builder("central", "default", "https://repo.maven.apache.org/maven2").build();
157+
List<RemoteRepository> remoteRepositories = new ArrayList<>();
158+
remoteRepositories.add(centralRepo);
159+
when(project.getRemoteProjectRepositories()).thenReturn(remoteRepositories);
160+
161+
// Create a mock ArtifactResult that will be returned by the repository system
162+
DefaultArtifact resolvedArtifact =
163+
new DefaultArtifact("com.example", "test-artifact", "1.0-20231201.123456-1", "jar", null);
164+
165+
ArtifactRequest originalRequest = new ArtifactRequest();
166+
originalRequest.setArtifact(new DefaultArtifact("com.example", "test-artifact", "1.0-SNAPSHOT", "jar", null));
167+
ArtifactResult artifactResult = new ArtifactResult(originalRequest);
168+
artifactResult.setArtifact(resolvedArtifact);
169+
170+
// Mock the repository system to return our result
171+
when(repoSystem.resolveArtifact(eq(repositorySystemSession), any(ArtifactRequest.class)))
172+
.thenReturn(artifactResult);
173+
174+
// When: Call the resolveArtifact method directly using reflection
175+
java.lang.reflect.Method resolveArtifactMethod =
176+
MavenProjectInput.class.getDeclaredMethod("resolveArtifact", Dependency.class);
177+
resolveArtifactMethod.setAccessible(true);
178+
179+
try {
180+
resolveArtifactMethod.invoke(mavenProjectInput, dependency);
181+
} catch (Exception e) {
182+
// We expect this to fail because ArtifactResult.isResolved() returns false
183+
// But we can still verify that the ArtifactRequest was created correctly
184+
}
185+
186+
// Then: Verify that resolveArtifact was called with repositories set
187+
ArgumentCaptor<ArtifactRequest> requestCaptor = ArgumentCaptor.forClass(ArtifactRequest.class);
188+
verify(repoSystem).resolveArtifact(eq(repositorySystemSession), requestCaptor.capture());
189+
190+
ArtifactRequest capturedRequest = requestCaptor.getValue();
191+
192+
// Verify that repositories are set on the request (this is the bug fix!)
193+
assertFalse(
194+
capturedRequest.getRepositories().isEmpty(),
195+
"Repositories should be set on ArtifactRequest for snapshot resolution");
196+
197+
// Verify the repository details
198+
assertEquals(1, capturedRequest.getRepositories().size());
199+
assertEquals("central", capturedRequest.getRepositories().get(0).getId());
200+
assertEquals(
201+
"https://repo.maven.apache.org/maven2",
202+
capturedRequest.getRepositories().get(0).getUrl());
203+
204+
// Verify the artifact being resolved
205+
assertEquals("com.example", capturedRequest.getArtifact().getGroupId());
206+
assertEquals("test-artifact", capturedRequest.getArtifact().getArtifactId());
207+
assertEquals("1.0-SNAPSHOT", capturedRequest.getArtifact().getVersion());
208+
assertEquals("jar", capturedRequest.getArtifact().getExtension());
209+
210+
// Verify that getRemoteProjectRepositories was called
211+
verify(project).getRemoteProjectRepositories();
212+
}
213+
214+
@Test
215+
void testArtifactRequestWithoutRepositories() throws Exception {
216+
// Given: A snapshot dependency with no remote repositories configured
217+
Dependency dependency = new Dependency();
218+
dependency.setGroupId("com.example");
219+
dependency.setArtifactId("test-artifact");
220+
dependency.setVersion("1.0-SNAPSHOT");
221+
dependency.setType("jar");
222+
223+
// Mock empty repository list
224+
when(project.getRemoteProjectRepositories()).thenReturn(new ArrayList<>());
225+
226+
// Create a mock ArtifactResult
227+
DefaultArtifact resolvedArtifact =
228+
new DefaultArtifact("com.example", "test-artifact", "1.0-20231201.123456-1", "jar", null);
229+
230+
ArtifactRequest originalRequest = new ArtifactRequest();
231+
originalRequest.setArtifact(new DefaultArtifact("com.example", "test-artifact", "1.0-SNAPSHOT", "jar", null));
232+
ArtifactResult artifactResult = new ArtifactResult(originalRequest);
233+
artifactResult.setArtifact(resolvedArtifact);
234+
235+
// Mock the repository system
236+
when(repoSystem.resolveArtifact(eq(repositorySystemSession), any(ArtifactRequest.class)))
237+
.thenReturn(artifactResult);
238+
239+
// When: Call the resolveArtifact method directly using reflection
240+
java.lang.reflect.Method resolveArtifactMethod =
241+
MavenProjectInput.class.getDeclaredMethod("resolveArtifact", Dependency.class);
242+
resolveArtifactMethod.setAccessible(true);
243+
244+
try {
245+
resolveArtifactMethod.invoke(mavenProjectInput, dependency);
246+
} catch (Exception e) {
247+
// We expect this to fail because ArtifactResult.isResolved() returns false
248+
// But we can still verify that the ArtifactRequest was created correctly
249+
}
250+
251+
// Then: Verify that resolveArtifact was called
252+
ArgumentCaptor<ArtifactRequest> requestCaptor = ArgumentCaptor.forClass(ArtifactRequest.class);
253+
verify(repoSystem).resolveArtifact(eq(repositorySystemSession), requestCaptor.capture());
254+
255+
ArtifactRequest capturedRequest = requestCaptor.getValue();
256+
257+
// Verify that repositories list is empty (as configured)
258+
assertTrue(
259+
capturedRequest.getRepositories().isEmpty(),
260+
"Repositories list should be empty when no remote repositories are configured");
261+
262+
// Verify the artifact being resolved
263+
assertEquals("com.example", capturedRequest.getArtifact().getGroupId());
264+
assertEquals("test-artifact", capturedRequest.getArtifact().getArtifactId());
265+
assertEquals("1.0-SNAPSHOT", capturedRequest.getArtifact().getVersion());
266+
assertEquals("jar", capturedRequest.getArtifact().getExtension());
267+
268+
// Verify that getRemoteProjectRepositories was called
269+
verify(project).getRemoteProjectRepositories();
270+
}
271+
272+
@Test
273+
void testArtifactRequestWithMultipleRepositories() throws Exception {
274+
// Given: A snapshot dependency with multiple repositories configured
275+
Dependency dependency = new Dependency();
276+
dependency.setGroupId("com.example");
277+
dependency.setArtifactId("test-artifact");
278+
dependency.setVersion("1.0-SNAPSHOT");
279+
dependency.setType("jar");
280+
281+
// Mock multiple repositories
282+
List<RemoteRepository> remoteRepositories = new ArrayList<>();
283+
remoteRepositories.add(
284+
new RemoteRepository.Builder("central", "default", "https://repo.maven.apache.org/maven2").build());
285+
remoteRepositories.add(
286+
new RemoteRepository.Builder("spring-milestones", "default", "https://repo.spring.io/milestone")
287+
.build());
288+
289+
when(project.getRemoteProjectRepositories()).thenReturn(remoteRepositories);
290+
291+
// Create a mock ArtifactResult
292+
DefaultArtifact resolvedArtifact =
293+
new DefaultArtifact("com.example", "test-artifact", "1.0-20231201.123456-1", "jar", null);
294+
295+
ArtifactRequest originalRequest = new ArtifactRequest();
296+
originalRequest.setArtifact(new DefaultArtifact("com.example", "test-artifact", "1.0-SNAPSHOT", "jar", null));
297+
ArtifactResult artifactResult = new ArtifactResult(originalRequest);
298+
artifactResult.setArtifact(resolvedArtifact);
299+
300+
// Mock the repository system
301+
when(repoSystem.resolveArtifact(eq(repositorySystemSession), any(ArtifactRequest.class)))
302+
.thenReturn(artifactResult);
303+
304+
// When: Call the resolveArtifact method directly using reflection
305+
java.lang.reflect.Method resolveArtifactMethod =
306+
MavenProjectInput.class.getDeclaredMethod("resolveArtifact", Dependency.class);
307+
resolveArtifactMethod.setAccessible(true);
308+
309+
try {
310+
resolveArtifactMethod.invoke(mavenProjectInput, dependency);
311+
} catch (Exception e) {
312+
// We expect this to fail because ArtifactResult.isResolved() returns false
313+
// But we can still verify that the ArtifactRequest was created correctly
314+
}
315+
316+
// Then: Verify that resolveArtifact was called with all repositories set
317+
ArgumentCaptor<ArtifactRequest> requestCaptor = ArgumentCaptor.forClass(ArtifactRequest.class);
318+
verify(repoSystem).resolveArtifact(eq(repositorySystemSession), requestCaptor.capture());
319+
320+
ArtifactRequest capturedRequest = requestCaptor.getValue();
321+
322+
// Verify that all repositories are set on the request
323+
assertEquals(2, capturedRequest.getRepositories().size());
324+
assertEquals("central", capturedRequest.getRepositories().get(0).getId());
325+
assertEquals(
326+
"spring-milestones", capturedRequest.getRepositories().get(1).getId());
327+
328+
// Verify that getRemoteProjectRepositories was called
329+
verify(project).getRemoteProjectRepositories();
330+
}
331+
}

0 commit comments

Comments
 (0)