From 174124018dae157a01efea114202b31974f51447 Mon Sep 17 00:00:00 2001 From: Carlos Feria <2582866+carlosthe19916@users.noreply.github.com> Date: Thu, 20 Mar 2025 10:32:04 +0100 Subject: [PATCH 1/5] fix: make sure npm is enabled only if package-lock.json is enabled Signed-off-by: Carlos Feria <2582866+carlosthe19916@users.noreply.github.com> --- README.md | 2 +- src/main/java/com/redhat/exhort/Api.java | 5 +- .../com/redhat/exhort/impl/ExhortApi.java | 6 +- .../com/redhat/exhort/tools/Ecosystem.java | 13 ++- .../java/com/redhat/exhort/ExhortTest.java | 78 ++++++++++++++++- .../com/redhat/exhort/impl/ExhortApiIT.java | 85 +++++++++++++------ .../redhat/exhort/impl/Exhort_Api_Test.java | 17 ++-- 7 files changed, 163 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 2e4ebb07..afa398b0 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ public class ExhortExample { // get a AnalysisReport future holding a deserialized Component Analysis report var manifestContent = Files.readAllBytes(Paths.get("/path/to/pom.xml")); - CompletableFuture componentReport = exhortApi.componentAnalysis("pom.xml", manifestContent); + CompletableFuture componentReport = exhortApi.componentAnalysis("pom.xml", manifestContent, Paths.get("/path/to/pom.xml")); } } ``` diff --git a/src/main/java/com/redhat/exhort/Api.java b/src/main/java/com/redhat/exhort/Api.java index d728cffa..309e47a6 100644 --- a/src/main/java/com/redhat/exhort/Api.java +++ b/src/main/java/com/redhat/exhort/Api.java @@ -18,6 +18,7 @@ import com.redhat.exhort.api.AnalysisReport; import com.redhat.exhort.image.ImageRef; import java.io.IOException; +import java.nio.file.Path; import java.util.Arrays; import java.util.Map; import java.util.Objects; @@ -104,8 +105,8 @@ public int hashCode() { * @return the deserialized Json report as an AnalysisReport wrapped in a CompletableFuture * @throws IOException when failed to load the manifest content */ - CompletableFuture componentAnalysis(String manifestType, byte[] manifestContent) - throws IOException; + CompletableFuture componentAnalysis( + String manifestType, byte[] manifestContent, Path manifestPath) throws IOException; CompletableFuture componentAnalysis(String manifestFile) throws IOException; diff --git a/src/main/java/com/redhat/exhort/impl/ExhortApi.java b/src/main/java/com/redhat/exhort/impl/ExhortApi.java index c60448c9..0618e674 100644 --- a/src/main/java/com/redhat/exhort/impl/ExhortApi.java +++ b/src/main/java/com/redhat/exhort/impl/ExhortApi.java @@ -39,6 +39,7 @@ import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; +import java.nio.file.Path; import java.nio.file.Paths; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; @@ -420,9 +421,10 @@ public static boolean debugLoggingIsNeeded() { @Override public CompletableFuture componentAnalysis( - final String manifestType, final byte[] manifestContent) throws IOException { + final String manifestType, final byte[] manifestContent, final Path manifestPath) + throws IOException { String exClientTraceId = commonHookBeginning(false); - var provider = Ecosystem.getProvider(manifestType); + var provider = Ecosystem.getProvider(manifestType, manifestPath); var uri = URI.create(String.format("%s/api/v4/analysis", this.endpoint)); var content = provider.provideComponent(manifestContent); commonHookAfterProviderCreatedSbomAndBeforeExhort(); diff --git a/src/main/java/com/redhat/exhort/tools/Ecosystem.java b/src/main/java/com/redhat/exhort/tools/Ecosystem.java index f84f5c60..554ac12a 100644 --- a/src/main/java/com/redhat/exhort/tools/Ecosystem.java +++ b/src/main/java/com/redhat/exhort/tools/Ecosystem.java @@ -21,6 +21,7 @@ import com.redhat.exhort.providers.JavaMavenProvider; import com.redhat.exhort.providers.JavaScriptNpmProvider; import com.redhat.exhort.providers.PythonPipProvider; +import java.nio.file.Files; import java.nio.file.Path; /** Utility class used for instantiating providers. * */ @@ -55,7 +56,7 @@ private Ecosystem() { * @return a Manifest record */ public static Provider getProvider(final Path manifestPath) { - return Ecosystem.getProvider(manifestPath.getFileName().toString()); + return Ecosystem.getProvider(manifestPath.getFileName().toString(), manifestPath); } /** @@ -64,12 +65,18 @@ public static Provider getProvider(final Path manifestPath) { * @param manifestType the type (filename + type) of the manifest * @return a Manifest record */ - public static Provider getProvider(final String manifestType) { + public static Provider getProvider(final String manifestType, final Path manifestPath) { switch (manifestType) { case "pom.xml": return new JavaMavenProvider(); case "package.json": - return new JavaScriptNpmProvider(); + Path lockFile = manifestPath.getParent().resolve("package-lock.json"); + if (Files.exists(lockFile)) { + return new JavaScriptNpmProvider(); + } else { + throw new IllegalStateException( + String.format("NPM Lock file could not be found for %s", manifestType)); + } case "go.mod": return new GoModulesProvider(); case "requirements.txt": diff --git a/src/test/java/com/redhat/exhort/ExhortTest.java b/src/test/java/com/redhat/exhort/ExhortTest.java index dca86b7c..2f18e494 100644 --- a/src/test/java/com/redhat/exhort/ExhortTest.java +++ b/src/test/java/com/redhat/exhort/ExhortTest.java @@ -15,11 +15,17 @@ */ package com.redhat.exhort; +import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; +import java.util.List; import java.util.Objects; +import java.util.Optional; +import java.util.function.Supplier; +import org.apache.commons.io.FileUtils; public class ExhortTest { @@ -36,8 +42,8 @@ protected String getStringFromFile(String... list) { return new String(bytes); } - public static InputStream getResourceAsStreamDecision( - Class theClass, String[] list) throws IOException { + public static InputStream getResourceAsStreamDecision(Class theClass, String[] list) + throws IOException { InputStream resourceAsStreamFromModule = theClass.getModule().getResourceAsStream(String.join("/", list)); if (Objects.isNull(resourceAsStreamFromModule)) { @@ -69,6 +75,74 @@ protected String getFileFromResource(String fileName, String... pathList) { return tmpFile.toString(); } + public static class TempDirFromResources { + private final Path tmpDir; + + public TempDirFromResources() throws IOException { + tmpDir = Files.createTempDirectory("exhort_test_"); + } + + public class AddPath { + private final String fileName; + + public AddPath(String fileName) { + this.fileName = fileName; + } + + public TempDirFromResources fromResources(String... pathList) { + Path tmpFile; + try { + tmpFile = Files.createFile(tmpDir.resolve(this.fileName)); + try (var is = getResourceAsStreamDecision(super.getClass(), pathList)) { + if (Objects.nonNull(is)) { + Files.write(tmpFile, is.readAllBytes()); + } else { + InputStream resourceIs = + getClass().getClassLoader().getResourceAsStream(String.join("/", pathList)); + Files.write(tmpFile, resourceIs.readAllBytes()); + resourceIs.close(); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + return TempDirFromResources.this; + } + } + + public AddPath addFile(String fileName) { + return new AddPath(fileName); + } + + public TempDirFromResources addDirectory(String dirName, String... pathList) { + File target = this.tmpDir.resolve(dirName).toFile(); + String join = String.join("/", pathList); + URL resource = this.getClass().getClassLoader().getResource(join); + File source = new File(Objects.requireNonNull(resource).getFile()); + try { + FileUtils.copyDirectory(source, target); + } catch (IOException e) { + throw new RuntimeException(e); + } + return this; + } + + public TempDirFromResources addFile( + Optional fileName, Supplier> pathList) { + if (fileName.isEmpty()) { + return this; + } + + return new AddPath(fileName.get()).fromResources(pathList.get().toArray(new String[0])); + } + + public Path getTempDir() { + return this.tmpDir; + } + } + protected String getFileFromString(String fileName, String content) { Path tmpFile; try { diff --git a/src/test/java/com/redhat/exhort/impl/ExhortApiIT.java b/src/test/java/com/redhat/exhort/impl/ExhortApiIT.java index 79346af5..b2d863f0 100644 --- a/src/test/java/com/redhat/exhort/impl/ExhortApiIT.java +++ b/src/test/java/com/redhat/exhort/impl/ExhortApiIT.java @@ -39,6 +39,8 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.util.AbstractMap; +import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -69,7 +71,8 @@ class ExhortApiIT extends ExhortTest { private static Api api; - private static Map ecoSystemsManifestNames; + private static Map>> + ecoSystemsManifestNames; private MockedStatic mockedOperations; @@ -81,17 +84,17 @@ static void beforeAll() { ecoSystemsManifestNames = Map.of( "golang", - "go.mod", + new SimpleEntry<>("go.mod", Optional.empty()), "maven", - "pom.xml", + new SimpleEntry<>("pom.xml", Optional.empty()), "npm", - "package.json", + new SimpleEntry<>("package.json", Optional.of("package-lock.json")), "pypi", - "requirements.txt", + new SimpleEntry<>("requirements.txt", Optional.empty()), "gradle-groovy", - "build.gradle", + new SimpleEntry<>("build.gradle", Optional.empty()), "gradle-kotlin", - "build.gradle.kts"); + new SimpleEntry<>("build.gradle.kts", Optional.empty())); } @Tag("IntegrationTest") @@ -104,19 +107,28 @@ static void afterAll() { private static List scenarios() { return ecoSystemsManifestNames.entrySet().stream() - .map(e -> Arguments.of(e.getKey(), e.getValue())) + .map(e -> Arguments.of(e.getKey(), e.getValue().getKey(), e.getValue().getValue())) .collect(Collectors.toList()); } @Tag("IntegrationTest") @ParameterizedTest(name = "StackAnalysis for: {0} with manifest: {1}") @MethodSource("scenarios") - void Integration_Test_End_To_End_Stack_Analysis(String useCase, String manifestFileName) + void Integration_Test_End_To_End_Stack_Analysis( + String useCase, String manifestFileName, Optional lockFilename) throws IOException, ExecutionException, InterruptedException { - var provider = Ecosystem.getProvider(manifestFileName); + Path manifestDirPath = + new TempDirFromResources() + .addFile(manifestFileName) + .fromResources("tst_manifests", "it", useCase, manifestFileName) + .addFile( + lockFilename, + () -> Arrays.asList("tst_manifests", "it", useCase, lockFilename.get())) + .getTempDir(); + Path manifestFileNamePath = manifestDirPath.resolve(manifestFileName); + var provider = Ecosystem.getProvider(manifestFileName, manifestFileNamePath); var packageManager = provider.ecosystem; - String pathToManifest = - getFileFromResource(manifestFileName, "tst_manifests", "it", useCase, manifestFileName); + String pathToManifest = manifestFileNamePath.toString(); preparePythonEnvironment(packageManager); // Github action runner with all maven and java versions seems to enter infinite loop in // integration tests of @@ -137,12 +149,21 @@ private void releaseStaticMock(Ecosystem.Type packageManager) { @Tag("IntegrationTest") @ParameterizedTest(name = "StackAnalysis Mixed for: {0} with manifest: {1}") @MethodSource("scenarios") - void Integration_Test_End_To_End_Stack_Analysis_Mixed(String useCase, String manifestFileName) + void Integration_Test_End_To_End_Stack_Analysis_Mixed( + String useCase, String manifestFileName, Optional lockFilename) throws IOException, ExecutionException, InterruptedException { - var provider = Ecosystem.getProvider(manifestFileName); + Path manifestDirPath = + new TempDirFromResources() + .addFile(manifestFileName) + .fromResources("tst_manifests", "it", useCase, manifestFileName) + .addFile( + lockFilename, + () -> Arrays.asList("tst_manifests", "it", useCase, lockFilename.get())) + .getTempDir(); + Path manifestFileNamePath = manifestDirPath.resolve(manifestFileName); + String pathToManifest = manifestFileNamePath.toString(); + var provider = Ecosystem.getProvider(manifestFileName, Path.of(pathToManifest)); var packageManager = provider.ecosystem; - String pathToManifest = - getFileFromResource(manifestFileName, "tst_manifests", "it", useCase, manifestFileName); preparePythonEnvironment(packageManager); // Github action runner with all maven and java versions seems to enter infinite loop in // integration tests of @@ -159,12 +180,21 @@ void Integration_Test_End_To_End_Stack_Analysis_Mixed(String useCase, String man @Tag("IntegrationTest") @ParameterizedTest(name = "StackAnalysis HTML for: {0} with manifest: {1}") @MethodSource("scenarios") - void Integration_Test_End_To_End_Stack_Analysis_Html(String useCase, String manifestFileName) + void Integration_Test_End_To_End_Stack_Analysis_Html( + String useCase, String manifestFileName, Optional lockFilename) throws IOException, ExecutionException, InterruptedException { - var provider = Ecosystem.getProvider(manifestFileName); + Path manifestDirPath = + new TempDirFromResources() + .addFile(manifestFileName) + .fromResources("tst_manifests", "it", useCase, manifestFileName) + .addFile( + lockFilename, + () -> Arrays.asList("tst_manifests", "it", useCase, lockFilename.get())) + .getTempDir(); + Path manifestFileNamePath = manifestDirPath.resolve(manifestFileName); + String pathToManifest = manifestFileNamePath.toString(); + var provider = Ecosystem.getProvider(manifestFileName, Path.of(pathToManifest)); var packageManager = provider.ecosystem; - String pathToManifest = - getFileFromResource(manifestFileName, "tst_manifests", "it", useCase, manifestFileName); preparePythonEnvironment(packageManager); // Github action runner with all maven and java versions seems to enter infinite loop in // integration tests of @@ -183,13 +213,18 @@ void Integration_Test_End_To_End_Stack_Analysis_Html(String useCase, String mani names = {"GOLANG", "MAVEN", "NPM", "PYTHON"}) void Integration_Test_End_To_End_Component_Analysis(Ecosystem.Type packageManager) throws IOException, ExecutionException, InterruptedException { - String manifestFileName = ecoSystemsManifestNames.get(packageManager.getType()); - byte[] manifestContent = - getStringFromFile("tst_manifests", "it", packageManager.getType(), manifestFileName) - .getBytes(); + String manifestFileName = ecoSystemsManifestNames.get(packageManager.getType()).getKey(); + + Path tempDir = + new TempDirFromResources() + .addDirectory(packageManager.getType(), "tst_manifests", "it", packageManager.getType()) + .getTempDir(); + Path manifestPath = tempDir.resolve(packageManager.getType()).resolve(manifestFileName); + byte[] manifestContent = Files.readAllBytes(manifestPath); + preparePythonEnvironment(packageManager); AnalysisReport analysisReportResult = - api.componentAnalysis(manifestFileName, manifestContent).get(); + api.componentAnalysis(manifestFileName, manifestContent, manifestPath).get(); handleJsonResponse(analysisReportResult, false); } diff --git a/src/test/java/com/redhat/exhort/impl/Exhort_Api_Test.java b/src/test/java/com/redhat/exhort/impl/Exhort_Api_Test.java index 35bd45e6..d812318e 100644 --- a/src/test/java/com/redhat/exhort/impl/Exhort_Api_Test.java +++ b/src/test/java/com/redhat/exhort/impl/Exhort_Api_Test.java @@ -53,6 +53,7 @@ import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Path; import java.util.HashMap; import java.util.Set; import java.util.concurrent.CompletableFuture; @@ -227,12 +228,12 @@ void stackAnalysis_with_pom_xml_should_return_json_object_from_the_backend() void componentAnalysis_with_pom_xml_should_return_json_object_from_the_backend() throws IOException, ExecutionException, InterruptedException { // load pom.xml - byte[] targetPom; - try (var is = - getResourceAsStreamDecision( - this.getClass(), new String[] {"tst_manifests", "maven", "empty", "pom.xml"})) { - targetPom = is.readAllBytes(); - } + Path tempDir = + new TempDirFromResources() + .addFile("pom.xml") + .fromResources(new String[] {"tst_manifests", "maven", "empty", "pom.xml"}) + .getTempDir(); + byte[] targetPom = Files.readAllBytes(tempDir.resolve("pom.xml")); // stub the mocked provider with a fake content object given(mockProvider.provideComponent(targetPom)) @@ -274,14 +275,14 @@ void componentAnalysis_with_pom_xml_should_return_json_object_from_the_backend() // mock static getProvider utility function try (var ecosystemTool = mockStatic(Ecosystem.class)) { // stub static getProvider utility function to return our mock provider - ecosystemTool.when(() -> Ecosystem.getProvider("pom.xml")).thenReturn(mockProvider); + ecosystemTool.when(() -> Ecosystem.getProvider("pom.xml", tempDir)).thenReturn(mockProvider); // stub the http client to return our mocked response when request matches our arg matcher given(mockHttpClient.sendAsync(argThat(matchesRequest), any())) .willReturn(CompletableFuture.completedFuture(mockHttpResponse)); // when invoking the api for a json stack analysis report - var responseAnalysis = exhortApiSut.componentAnalysis("pom.xml", targetPom); + var responseAnalysis = exhortApiSut.componentAnalysis("pom.xml", targetPom, tempDir); // verify we got the correct analysis report then(responseAnalysis.get()).isEqualTo(expectedReport); } From ef0ba31d4cea9535d5120f32676aa6a4e7a6fc12 Mon Sep 17 00:00:00 2001 From: Ruben Romero Montes Date: Tue, 8 Apr 2025 14:16:26 +0200 Subject: [PATCH 2/5] fix: cleanup and refactor Signed-off-by: Ruben Romero Montes --- README.md | 4 +- pom.xml | 24 +- sbom.json | 0 src/main/java/com/redhat/exhort/Api.java | 9 +- src/main/java/com/redhat/exhort/Provider.java | 18 +- .../com/redhat/exhort/api/PackageRef.java | 2 - .../com/redhat/exhort/image/ImageUtils.java | 4 - .../com/redhat/exhort/impl/ExhortApi.java | 44 +- .../logging/ClientTraceIdSimpleFormatter.java | 13 +- .../exhort/providers/BaseJavaProvider.java | 5 +- .../exhort/providers/GoModulesProvider.java | 177 +++--- .../exhort/providers/GradleProvider.java | 32 +- .../exhort/providers/JavaMavenProvider.java | 75 +-- .../providers/JavaScriptNpmProvider.java | 57 +- .../exhort/providers/PythonPipProvider.java | 78 +-- .../com/redhat/exhort/tools/Ecosystem.java | 40 +- .../com/redhat/exhort/tools/Operations.java | 69 ++- .../exhort/utils/PythonControllerBase.java | 226 +++----- .../exhort/utils/PythonControllerTestEnv.java | 11 +- .../utils/PythonControllerVirtualEnv.java | 7 +- .../vcs/GitVersionControlSystemImpl.java | 13 +- src/main/java/module-info.java | 3 + .../java/com/redhat/exhort/ExhortTest.java | 40 +- .../com/redhat/exhort/image/ImageRefTest.java | 2 +- .../redhat/exhort/image/ImageUtilsTest.java | 26 +- .../com/redhat/exhort/impl/ExhortApiIT.java | 88 ++- .../redhat/exhort/impl/ExhortApiIT.java.orig | 226 -------- .../redhat/exhort/impl/Exhort_Api_Test.java | 68 +-- .../exhort/impl/Exhort_Api_Test.java.orig | 517 ------------------ .../GoModulesMainModuleVersionTest.java | 52 +- .../Golang_Modules_Provider_Test.java | 60 +- .../providers/Gradle_Provider_Test.java | 125 ++--- .../exhort/providers/HelperExtension.java | 28 +- .../exhort/providers/Java_Envs_Test.java | 6 +- .../providers/Java_Maven_Provider_Test.java | 121 ++-- .../providers/Javascript_Envs_Test.java | 8 +- .../Javascript_Npm_Provider_Test.java | 146 +++-- .../providers/Python_Provider_Test.java | 104 ++-- .../redhat/exhort/tools/Ecosystem_Test.java | 6 +- .../utils/PythonControllerRealEnvTest.java | 74 +-- .../utils/PythonControllerVirtualEnvTest.java | 27 +- src/test/java/module-info.test | 12 +- src/test/resources/git_config | 5 + .../golang/go_mod_light_no_ignore/go.mod | 2 - .../build.gradle.kts | 8 +- .../settings.gradle.kts | 2 +- 46 files changed, 754 insertions(+), 1910 deletions(-) delete mode 100644 sbom.json delete mode 100644 src/test/java/com/redhat/exhort/impl/ExhortApiIT.java.orig delete mode 100644 src/test/java/com/redhat/exhort/impl/Exhort_Api_Test.java.orig create mode 100644 src/test/resources/git_config diff --git a/README.md b/README.md index afa398b0..a01f3ba7 100644 --- a/README.md +++ b/README.md @@ -157,8 +157,8 @@ public class ExhortExample { CompletableFuture mixedStackReport = exhortApi.stackAnalysisMixed("/path/to/pom.xml"); // get a AnalysisReport future holding a deserialized Component Analysis report - var manifestContent = Files.readAllBytes(Paths.get("/path/to/pom.xml")); - CompletableFuture componentReport = exhortApi.componentAnalysis("pom.xml", manifestContent, Paths.get("/path/to/pom.xml")); + var manifestContent = Files.readAllBytes(Path.of("/path/to/pom.xml")); + CompletableFuture componentReport = exhortApi.componentAnalysis("/path/to/pom.xml", manifestContent); } } ``` diff --git a/pom.xml b/pom.xml index f996021e..beeb9200 100644 --- a/pom.xml +++ b/pom.xml @@ -23,10 +23,10 @@ 2.0.2 7.3.2 - 3.24.2 - 5.9.1 - 2.0.1 - 5.3.1 + 3.27.3 + 5.10.5 + 2.3.0 + 5.17.0 3.2.0 3.11.0 @@ -42,18 +42,18 @@ 3.3.1 4.0.0-M6 3.2.1 - 3.1.2 + 3.5.3 3.4.0 1.6.2 1.4.1 0.8.10 - 1.1.7 + 1.1.8 4.1 6.6.0 1.13.2 1.1.2 2.15.0 - 3.2.5 + 3.5.3 @@ -215,11 +215,6 @@ junit-jupiter-api test - - - - - org.junit.jupiter junit-jupiter-params @@ -453,7 +448,6 @@ limitations under the License.]]> de.sormuras.junit junit-platform-maven-plugin - Launch JUNit Platform @@ -464,10 +458,6 @@ limitations under the License.]]> - - - - ${skip.junit_platform} .*Exhort_Api_Test diff --git a/sbom.json b/sbom.json deleted file mode 100644 index e69de29b..00000000 diff --git a/src/main/java/com/redhat/exhort/Api.java b/src/main/java/com/redhat/exhort/Api.java index 309e47a6..a96744a5 100644 --- a/src/main/java/com/redhat/exhort/Api.java +++ b/src/main/java/com/redhat/exhort/Api.java @@ -18,7 +18,6 @@ import com.redhat.exhort.api.AnalysisReport; import com.redhat.exhort.image.ImageRef; import java.io.IOException; -import java.nio.file.Path; import java.util.Arrays; import java.util.Map; import java.util.Objects; @@ -100,15 +99,15 @@ public int hashCode() { /** * Use for creating a component analysis deserialized Json report for a given type and content. * - * @param manifestType the type of the manifest, i.e. {@code pom.xml} + * @param manifest the path of the manifest, example {@code /path/to/pom.xml} * @param manifestContent a byte array of the manifest's content * @return the deserialized Json report as an AnalysisReport wrapped in a CompletableFuture * @throws IOException when failed to load the manifest content */ - CompletableFuture componentAnalysis( - String manifestType, byte[] manifestContent, Path manifestPath) throws IOException; + CompletableFuture componentAnalysis(String manifest, byte[] manifestContet) + throws IOException; - CompletableFuture componentAnalysis(String manifestFile) throws IOException; + CompletableFuture componentAnalysis(String manifest) throws IOException; CompletableFuture> imageAnalysis(Set imageRefs) throws IOException; diff --git a/src/main/java/com/redhat/exhort/Provider.java b/src/main/java/com/redhat/exhort/Provider.java index a3b72753..aee3a1a5 100644 --- a/src/main/java/com/redhat/exhort/Provider.java +++ b/src/main/java/com/redhat/exhort/Provider.java @@ -25,6 +25,9 @@ * manifest type for constructing backend requests. */ public abstract class Provider { + + public static final String PROP_MATCH_MANIFEST_VERSIONS = "MATCH_MANIFEST_VERSIONS"; + /** * Content is used to aggregate a content buffer and a content type. These will be used to * construct the backend API request. @@ -42,29 +45,32 @@ public Content(byte[] buffer, String type) { /** The ecosystem of this provider, i.e. maven. */ public final Ecosystem.Type ecosystem; + public final Path manifest; + protected final ObjectMapper objectMapper = new ObjectMapper(); - protected Provider(Ecosystem.Type ecosystem) { + protected Provider(Ecosystem.Type ecosystem, Path manifest) { this.ecosystem = ecosystem; + this.manifest = manifest; } /** * Use for providing content for a stack analysis request. * - * @param manifestPath the Path for the manifest file * @return A Content record aggregating the body content and content type * @throws IOException when failed to load the manifest file */ - public abstract Content provideStack(Path manifestPath) throws IOException; + public abstract Content provideStack() throws IOException; /** * Use for providing content for a component analysis request. * - * @param manifestContent the content of the manifest file * @return A Content record aggregating the body content and content type * @throws IOException when failed to load the manifest content */ - public abstract Content provideComponent(byte[] manifestContent) throws IOException; + public abstract Content provideComponent() throws IOException; - public abstract Content provideComponent(Path manifestPath) throws IOException; + public boolean validateLockFile(Path lockFile) { + return true; + } } diff --git a/src/main/java/com/redhat/exhort/api/PackageRef.java b/src/main/java/com/redhat/exhort/api/PackageRef.java index 347d48bc..9f3e10cd 100644 --- a/src/main/java/com/redhat/exhort/api/PackageRef.java +++ b/src/main/java/com/redhat/exhort/api/PackageRef.java @@ -24,8 +24,6 @@ public class PackageRef { - private static final String MAVEN_TYPE = "maven"; - @JsonSerialize(using = PackageURLSerializer.class) @JsonValue private final PackageURL purl; diff --git a/src/main/java/com/redhat/exhort/image/ImageUtils.java b/src/main/java/com/redhat/exhort/image/ImageUtils.java index 8268393e..6debcb2b 100644 --- a/src/main/java/com/redhat/exhort/image/ImageUtils.java +++ b/src/main/java/com/redhat/exhort/image/ImageUtils.java @@ -24,7 +24,6 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.TextNode; import com.github.packageurl.MalformedPackageURLException; -import com.redhat.exhort.logging.LoggersFactory; import com.redhat.exhort.tools.Operations; import java.io.File; import java.io.IOException; @@ -36,7 +35,6 @@ import java.util.Map; import java.util.Objects; import java.util.function.Supplier; -import java.util.logging.Logger; import java.util.stream.Collectors; import java.util.stream.StreamSupport; @@ -59,8 +57,6 @@ public class ImageUtils { private static final String MEDIA_TYPE_OCI1_MANIFEST_LIST = "application/vnd.oci.image.index.v1+json"; - private static final Logger logger = LoggersFactory.getLogger(ImageUtils.class.getName()); - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private static final Map archMapping = diff --git a/src/main/java/com/redhat/exhort/impl/ExhortApi.java b/src/main/java/com/redhat/exhort/impl/ExhortApi.java index 0618e674..7e391d9c 100644 --- a/src/main/java/com/redhat/exhort/impl/ExhortApi.java +++ b/src/main/java/com/redhat/exhort/impl/ExhortApi.java @@ -40,7 +40,6 @@ import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; import java.nio.file.Path; -import java.nio.file.Paths; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; import java.util.AbstractMap; @@ -53,7 +52,6 @@ import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; -import java.util.concurrent.ExecutionException; import java.util.function.Function; import java.util.function.Supplier; import java.util.logging.Logger; @@ -63,8 +61,6 @@ /** Concrete implementation of the Exhort {@link Api} Service. */ public final class ExhortApi implements Api { - // private static final System.Logger LOG = System.getLogger(ExhortApi.class.getName()); - private static final Logger LOG = LoggersFactory.getLogger(ExhortApi.class.getName()); public static final String DEFAULT_ENDPOINT = "https://rhda.rhcloud.com"; @@ -80,27 +76,6 @@ public String getEndpoint() { return endpoint; } - public static final void main(String[] args) - throws IOException, InterruptedException, ExecutionException { - System.setProperty("EXHORT_DEV_MODE", "true"); - AnalysisReport analysisReport = - new ExhortApi() - .stackAnalysisMixed("/tmp/exhort_test_10582748308498949664/pom.xml") - .get() - .json; - // ObjectMapper om = new - // ObjectMapper().configure(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS, false); - // - // System.out.println(om.writerWithDefaultPrettyPrinter().writeValueAsString(analysisReport)); - // AnalysisReport analysisReport = new ExhortApi() - // byte[] analysisReport = new ExhortApi(). - // - // stackAnalysisHtml("/home/zgrinber/git/exhort-java-api/src/test/resources/tst_manifests/golang/go_mod_with_one_ignored_prefix_go/go.mod").get(); - // Path html = Files.createFile(Path.of("/","tmp", "golang0210.html")); - // Files.write(html,analysisReport); - - } - /** Enum for identifying token environment variables and their corresponding request headers. */ private enum TokenProvider { SNYK, @@ -195,7 +170,6 @@ static HttpClient.Version getHttpVersion() { private String commonHookBeginning(boolean startOfApi) { if (startOfApi) { - // generateClientRequestId(); if (debugLoggingIsNeeded()) { LOG.info("Start of exhort-java-api client"); LOG.info(String.format("Starting time of API: %s", LocalDateTime.now())); @@ -403,7 +377,7 @@ private AnalysisReport getAnalysisReportFromResponse( } } - private static void logExhortRequestId(HttpResponse response) { + private static void logExhortRequestId(HttpResponse response) { Optional headerExRequestId = response.headers().allValues(EXHORT_REQUEST_ID_HEADER_NAME).stream().findFirst(); headerExRequestId.ifPresent( @@ -421,12 +395,12 @@ public static boolean debugLoggingIsNeeded() { @Override public CompletableFuture componentAnalysis( - final String manifestType, final byte[] manifestContent, final Path manifestPath) - throws IOException { + final String manifest, final byte[] manifestContent) throws IOException { String exClientTraceId = commonHookBeginning(false); - var provider = Ecosystem.getProvider(manifestType, manifestPath); + var manifestPath = Path.of(manifest); + var provider = Ecosystem.getProvider(manifestPath); var uri = URI.create(String.format("%s/api/v4/analysis", this.endpoint)); - var content = provider.provideComponent(manifestContent); + var content = provider.provideComponent(); commonHookAfterProviderCreatedSbomAndBeforeExhort(); return getAnalysisReportForComponent(uri, content, exClientTraceId); } @@ -467,10 +441,10 @@ private void commonHookAfterExhortResponse() { public CompletableFuture componentAnalysis(String manifestFile) throws IOException { String exClientTraceId = commonHookBeginning(false); - var manifestPath = Paths.get(manifestFile); + var manifestPath = Path.of(manifestFile); var provider = Ecosystem.getProvider(manifestPath); var uri = URI.create(String.format("%s/api/v4/analysis", this.endpoint)); - var content = provider.provideComponent(manifestPath); + var content = provider.provideComponent(); commonHookAfterProviderCreatedSbomAndBeforeExhort(); return getAnalysisReportForComponent(uri, content, exClientTraceId); } @@ -508,10 +482,10 @@ private CompletableFuture getAnalysisReportForComponent( */ private HttpRequest buildStackRequest(final String manifestFile, final MediaType acceptType) throws IOException { - var manifestPath = Paths.get(manifestFile); + var manifestPath = Path.of(manifestFile); var provider = Ecosystem.getProvider(manifestPath); var uri = URI.create(String.format("%s/api/v4/analysis", this.endpoint)); - var content = provider.provideStack(manifestPath); + var content = provider.provideStack(); commonHookAfterProviderCreatedSbomAndBeforeExhort(); return buildRequest(content, uri, acceptType, "Stack Analysis"); diff --git a/src/main/java/com/redhat/exhort/logging/ClientTraceIdSimpleFormatter.java b/src/main/java/com/redhat/exhort/logging/ClientTraceIdSimpleFormatter.java index 2d067cfb..25b6f673 100644 --- a/src/main/java/com/redhat/exhort/logging/ClientTraceIdSimpleFormatter.java +++ b/src/main/java/com/redhat/exhort/logging/ClientTraceIdSimpleFormatter.java @@ -41,9 +41,6 @@ public ClientTraceIdSimpleFormatter() { @Override public String format(LogRecord record) { - // return String.format("%s, ex-client-trace-id: - // %s",super.format(record).trim(),RequestManager.getInstance().getTraceIdOfRequest() + - // System.lineSeparator()); Map messageKeysValues = new HashMap<>(); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); @@ -59,22 +56,14 @@ public String format(LogRecord record) { source = record.getLoggerName(); } String message = formatMessage(record); - String throwable = ""; if (record.getThrown() != null) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); pw.println(); record.getThrown().printStackTrace(pw); pw.close(); - throwable = sw.toString(); } - // return String.format(super.format, - // zdt, - // source, - // record.getLoggerName(), - // record.getLevel().getLocalizedLevelName(), - // message, - // throwable); + messageKeysValues.put("timestamp", zdt.toString()); messageKeysValues.put("ex-client-trace-id", RequestManager.getInstance().getTraceIdOfRequest()); messageKeysValues.put("methodName", source); diff --git a/src/main/java/com/redhat/exhort/providers/BaseJavaProvider.java b/src/main/java/com/redhat/exhort/providers/BaseJavaProvider.java index c6c4778f..292a5543 100644 --- a/src/main/java/com/redhat/exhort/providers/BaseJavaProvider.java +++ b/src/main/java/com/redhat/exhort/providers/BaseJavaProvider.java @@ -20,6 +20,7 @@ import com.redhat.exhort.Provider; import com.redhat.exhort.sbom.Sbom; import com.redhat.exhort.tools.Ecosystem; +import java.nio.file.Path; import java.util.Arrays; import java.util.Map; import java.util.Objects; @@ -27,8 +28,8 @@ public abstract class BaseJavaProvider extends Provider { - protected BaseJavaProvider(Ecosystem.Type ecosystem) { - super(ecosystem); + protected BaseJavaProvider(Ecosystem.Type ecosystem, Path manifest) { + super(ecosystem, manifest); } void parseDependencyTree(String src, int srcDepth, String[] lines, Sbom sbom) { diff --git a/src/main/java/com/redhat/exhort/providers/GoModulesProvider.java b/src/main/java/com/redhat/exhort/providers/GoModulesProvider.java index 80f3fc88..16fcc643 100644 --- a/src/main/java/com/redhat/exhort/providers/GoModulesProvider.java +++ b/src/main/java/com/redhat/exhort/providers/GoModulesProvider.java @@ -36,7 +36,6 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.*; import java.util.logging.Logger; import java.util.regex.Pattern; @@ -48,12 +47,12 @@ */ public final class GoModulesProvider extends Provider { + public static final String PROP_EXHORT_GO_MVS_LOGIC_ENABLED = "EXHORT_GO_MVS_LOGIC_ENABLED"; private Logger log = LoggersFactory.getLogger(this.getClass().getName()); - private static final String goHostArchitectureEnvName = "GOHOSTARCH"; - private static final String goHostOperationSystemEnvName = "GOHOSTOS"; - public static final String defaultMainVersion = "v0.0.0"; - private final TreeMap goEnvironmentVariableForPurl; - private final TreeMap goEnvironmentVariablesForRef; + private static final String GO_HOST_ARCHITECTURE_ENV_NAME = "GOHOSTARCH"; + private static final String GO_HOST_OPERATION_SYSTEM_ENV_NAME = "GOHOSTOS"; + public static final String DEFAULT_MAIN_VERSION = "v0.0.0"; + private final TreeMap goEnvironmentVariableForPurl; public String getMainModuleVersion() { return mainModuleVersion; @@ -61,78 +60,36 @@ public String getMainModuleVersion() { private String mainModuleVersion; - public static void main(String[] args) { - - TreeMap qualifiers = GoModulesProvider.getQualifiers(true); - // Path path = - // Path.of("/home/zgrinber/git/exhort-java-api/src/test/resources/tst_manifests/golang/go_mod_light_no_ignore/go.mod"); - Path path = Path.of("/tmp/xieshen/go.mod"); - Provider provider = new GoModulesProvider(); - GoModulesProvider goProvider = (GoModulesProvider) provider; - // boolean answer = goProvider.IgnoredLine(" github.com/davecgh/go-spew v1.1.1 // - // indirect - // //exhortignore"); - try { - // provider.provideStack(path); - byte[] bytes = Files.readAllBytes(path); - provider.provideComponent(bytes); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - public GoModulesProvider() { - super(Type.GOLANG); + public GoModulesProvider(Path manifest) { + super(Type.GOLANG, manifest); this.goEnvironmentVariableForPurl = getQualifiers(true); - this.goEnvironmentVariablesForRef = getQualifiers(false); this.mainModuleVersion = getDefaultMainModuleVersion(); } @Override - public Content provideStack(final Path manifestPath) throws IOException { + public Content provideStack() throws IOException { // check for custom npm executable - Sbom sbom = getDependenciesSbom(manifestPath, true); + Sbom sbom = getDependenciesSbom(manifest, true); return new Content( sbom.getAsJsonString().getBytes(StandardCharsets.UTF_8), Api.CYCLONEDX_MEDIA_TYPE); } @Override - public Content provideComponent(byte[] manifestContent) throws IOException { - // check for custom npm executable - return new Content( - getDependenciesSbomCa(manifestContent).getAsJsonString().getBytes(StandardCharsets.UTF_8), - Api.CYCLONEDX_MEDIA_TYPE); - } - - @Override - public Content provideComponent(Path manifestPath) throws IOException { - throw new IllegalArgumentException( - "provideComponent with file system path for GoModules package manager not implemented yet"); - } - - private Sbom getDependenciesSbomCa(byte[] manifestContent) { - Sbom sbom; - try { - Path tempRepository = Files.createTempDirectory("exhort-go"); - Path path = Paths.get(tempRepository.toAbsolutePath().normalize().toString(), "go.mod"); - Files.deleteIfExists(path); - Path manifestPath = Files.createFile(path); - Files.write(manifestPath, manifestContent); - sbom = getDependenciesSbom(manifestPath, false); - - Files.delete(manifestPath); - Files.delete(tempRepository); - } catch (IOException e) { - throw new RuntimeException(e); + public Content provideComponent() throws IOException { + if (!Files.exists(manifest)) { + throw new IllegalArgumentException("Missing required go.mod file: " + manifest); } - return sbom; - } - - private PackageURL getRoot(String DependenciesGolang) { - return null; + if (!Files.isRegularFile(manifest)) { + throw new IllegalArgumentException( + "The provided manifest is not a regular file: " + manifest); + } + var sbom = getDependenciesSbom(manifest, false); + return new Content( + sbom.getAsJsonString().getBytes(StandardCharsets.UTF_8), Api.CYCLONEDX_MEDIA_TYPE); } - private PackageURL toPurl(String dependency, String delimiter, TreeMap qualifiers) { + private PackageURL toPurl( + String dependency, String delimiter, TreeMap qualifiers) { try { int lastSlashIndex = dependency.lastIndexOf("/"); // there is no '/' char in module/package, so there is no namespace, only name @@ -168,12 +125,12 @@ private PackageURL toPurl(String dependency, String delimiter, TreeMap qualifier Sbom getDependenciesSbom(Path manifestPath, boolean buildTree) throws IOException { var goModulesResult = buildGoModulesDependencies(manifestPath); - calculateMainModuleVersion(manifestPath.getParent()); + determineMainModuleVersion(manifestPath.getParent()); Sbom sbom; List ignoredDeps = getIgnoredDeps(manifestPath); - boolean matchManifestVersions = getBooleanValueEnvironment("MATCH_MANIFEST_VERSIONS", "false"); + boolean matchManifestVersions = + getBooleanValueEnvironment(Provider.PROP_MATCH_MANIFEST_VERSIONS, "false"); if (matchManifestVersions) { - // String rootName = getParentVertex() String[] goModGraphLines = goModulesResult.split(System.lineSeparator()); performManifestVersionsCheck(goModGraphLines, manifestPath); } @@ -182,8 +139,6 @@ Sbom getDependenciesSbom(Path manifestPath, boolean buildTree) throws IOExceptio } else { sbom = buildSbomFromGraph(goModulesResult, ignoredDeps, manifestPath); } - // List ignoredDeps = getIgnoredDeps(manifestPath); - // sbom.filterIgnoredDeps(ignoredDeps); return sbom; } @@ -219,8 +174,11 @@ private void performManifestVersionsCheck(String[] goModGraphLines, Path manifes + " Version=%s, if you want to allow version mismatch for" + " analysis between installed and requested packages," + " set environment variable/setting -" - + " MATCH_MANIFEST_VERSIONS=false", - depName, currentVersion, version)); + + " %s=false", + depName, + currentVersion, + version, + Provider.PROP_MATCH_MANIFEST_VERSIONS)); } } }); @@ -281,10 +239,7 @@ private Map decideRequireBlockIndex(String currentSegmentOfGoMo } public void determineMainModuleVersion(Path directory) { - this.calculateMainModuleVersion(directory); - } - - private void calculateMainModuleVersion(Path directory) { + // TODO: The javascript api doesn't implement this method VersionControlSystem vcs = new GitVersionControlSystemImpl(); if (vcs.isDirectoryRepo(directory)) { TagInfo latestTagInfo = vcs.getLatestTag(directory); @@ -306,44 +261,33 @@ private void calculateMainModuleVersion(Path directory) { private Sbom buildSbomFromGraph( String goModulesResult, List ignoredDeps, Path manifestPath) throws IOException { - // Each entry contains a key of the module, and the list represents the module direct + // Each entry contains a key of the module, and the list represents the module direct // dependencies , so // pairing of the key with each of the dependencies in a list is basically an edge in the graph. - Map edges = new HashMap<>(); + Map> edges = new HashMap<>(); // iterate over go mod graph line by line and create map , with each entry to contain module as // a key , and // value of list of that module' dependencies. - String[] lines = goModulesResult.split(System.lineSeparator()); - List linesList = Arrays.asList(lines); - // System.out.print("Start time: " + LocalDateTime.now() + System.lineSeparator()); + List linesList = Arrays.asList(goModulesResult.split(System.lineSeparator())); Integer startingIndex = 0; - Integer EndingIndex = lines.length - 1; - String[] targetLines = Arrays.copyOfRange(lines, 0, lines.length - 1); for (String line : linesList) { - if (!edges.containsKey(getParentVertex(line))) { // Collect all direct dependencies of the current module into a list. - List deps = collectAllDirectDependencies(targetLines, line); + List deps = + collectAllDirectDependencies( + linesList.subList(startingIndex, linesList.size() - 1), line); edges.put(getParentVertex(line), deps); startingIndex += deps.size(); - // Because all the deps of the current module were collected, not need to search for next - // modules on - // these lines, so truncate these lines from search array to make the search more rapid and - // efficient. - if (startingIndex < EndingIndex) { - targetLines = Arrays.copyOfRange(lines, startingIndex, EndingIndex); - } } } - // DEBUG - // System.setProperty("EXHORT_GO_MVS_LOGIC_ENABLED","true"); - boolean goMvsLogicEnabled = getBooleanValueEnvironment("EXHORT_GO_MVS_LOGIC_ENABLED", "false"); + boolean goMvsLogicEnabled = + getBooleanValueEnvironment(PROP_EXHORT_GO_MVS_LOGIC_ENABLED, "false"); if (goMvsLogicEnabled) { edges = getFinalPackagesVersionsForModule(edges, manifestPath); } // Build Sbom - String rootPackage = getParentVertex(lines[0]); + String rootPackage = getParentVertex(linesList.get(0)); PackageURL root = toPurl(rootPackage, "@", this.goEnvironmentVariableForPurl); Sbom sbom = SbomFactory.newInstance(Sbom.BelongingCondition.PURL, "sensitive"); @@ -374,8 +318,8 @@ private Sbom buildSbomFromGraph( return sbom; } - private Map getFinalPackagesVersionsForModule( - Map edges, Path manifestPath) { + private Map> getFinalPackagesVersionsForModule( + Map> edges, Path manifestPath) { Operations.runProcessGetOutput(manifestPath.getParent(), "go", "mod", "download"); String finalVersionsForAllModules = Operations.runProcessGetOutput(manifestPath.getParent(), "go", "list", "-m", "all"); @@ -385,7 +329,7 @@ private Map getFinalPackagesVersionsForModule( .collect( Collectors.toMap( t -> t.split(" ")[0], t -> t.split(" ")[1], (first, second) -> second)); - Map listWithModifiedVersions = new HashMap<>(); + Map> listWithModifiedVersions = new HashMap<>(); edges.entrySet().stream() .filter(string -> string.getKey().trim().split("@").length == 2) .collect(Collectors.toList()) @@ -393,17 +337,17 @@ private Map getFinalPackagesVersionsForModule( (entry) -> { String packageWithSelectedVersion = getPackageWithFinalVersion(finalModulesVersions, entry.getKey()); - List packagesWithFinalVersions = - getListOfPackagesWithFinlVersions(finalModulesVersions, entry); + List packagesWithFinalVersions = + getListOfPackagesWithFinalVersions(finalModulesVersions, entry); listWithModifiedVersions.put(packageWithSelectedVersion, packagesWithFinalVersions); }); return listWithModifiedVersions; } - private List getListOfPackagesWithFinlVersions( - Map finalModulesVersions, Map.Entry entry) { - return (List) + private List getListOfPackagesWithFinalVersions( + Map finalModulesVersions, Map.Entry> entry) { + return (List) entry.getValue().stream() .map( (packageWithVersion) -> @@ -414,7 +358,6 @@ private List getListOfPackagesWithFinlVersions( public static String getPackageWithFinalVersion( Map finalModulesVersions, String packagePlusVersion) { String packageName = packagePlusVersion.split("@")[0]; - String originalVersion = packagePlusVersion.split("@")[1]; String finalVersion = finalModulesVersions.get(packageName); if (Objects.nonNull(finalVersion)) { return String.format("%s@%s", packageName, finalVersion); @@ -429,25 +372,27 @@ private boolean dependencyNotToBeIgnored(List ignoredDeps, PackageUR dependencyPurl -> dependencyPurl.getCoordinates().equals(checkedPurl.getCoordinates())); } - private static List collectAllDirectDependencies(String[] targetLines, String edge) { - return Arrays.stream(targetLines) - .filter(line2 -> getParentVertex(line2).equals(getParentVertex(edge))) + private static List collectAllDirectDependencies(List targetLines, String edge) { + return targetLines.stream() + .filter(line -> getParentVertex(line).equals(getParentVertex(edge))) .map(GoModulesProvider::getChildVertex) .collect(Collectors.toList()); } - private static TreeMap getQualifiers(boolean includeOsAndArch) { + private static TreeMap getQualifiers(boolean includeOsAndArch) { if (includeOsAndArch) { var go = Operations.getCustomPathOrElse("go"); - String goEnvironmentVariables = - Operations.runProcessGetOutput(null, new String[] {go, "env"}); - String hostArch = getEnvironmentVariable(goEnvironmentVariables, goHostArchitectureEnvName); - String hostOS = getEnvironmentVariable(goEnvironmentVariables, goHostOperationSystemEnvName); - return new TreeMap(Map.of("type", "module", "goos", hostOS, "goarch", hostArch)); + String goEnvironmentVariables = Operations.runProcessGetOutput(null, go, "env"); + String hostArch = + getEnvironmentVariable(goEnvironmentVariables, GO_HOST_ARCHITECTURE_ENV_NAME); + String hostOS = + getEnvironmentVariable(goEnvironmentVariables, GO_HOST_OPERATION_SYSTEM_ENV_NAME); + return new TreeMap( + Map.of("type", "module", "goos", hostOS, "goarch", hostArch)); } - return new TreeMap(Map.of("type", "module")); + return new TreeMap(Map.of("type", "module")); } private static String getEnvironmentVariable(String goEnvironmentVariables, String envName) { @@ -482,7 +427,7 @@ private Sbom buildSbomFromList(String golangDeps, List ignoredDeps) String parentVertex = getParentVertex(allModulesFlat[0]); PackageURL root = toPurl(parentVertex, "@", this.goEnvironmentVariableForPurl); // Get only direct dependencies of root package/module, and that's it. - List deps = collectAllDirectDependencies(allModulesFlat, parentVertex); + List deps = collectAllDirectDependencies(Arrays.asList(allModulesFlat), parentVertex); Sbom sbom = SbomFactory.newInstance(Sbom.BelongingCondition.PURL, "sensitive"); sbom.addRoot(root); @@ -569,6 +514,6 @@ private static String getChildVertex(String edge) { } private static String getDefaultMainModuleVersion() { - return defaultMainVersion; + return DEFAULT_MAIN_VERSION; } } diff --git a/src/main/java/com/redhat/exhort/providers/GradleProvider.java b/src/main/java/com/redhat/exhort/providers/GradleProvider.java index 2464a1bb..2da9f9f9 100644 --- a/src/main/java/com/redhat/exhort/providers/GradleProvider.java +++ b/src/main/java/com/redhat/exhort/providers/GradleProvider.java @@ -51,13 +51,13 @@ public final class GradleProvider extends BaseJavaProvider { }; private Logger log = LoggersFactory.getLogger(this.getClass().getName()); - public GradleProvider() { - super(Type.GRADLE); + public GradleProvider(Path manifest) { + super(Type.GRADLE, manifest); } @Override - public Content provideStack(final Path manifestPath) throws IOException { - Path tempFile = getDependencies(manifestPath); + public Content provideStack() throws IOException { + Path tempFile = getDependencies(manifest); if (debugLoggingIsNeeded()) { String stackAnalysisDependencyTree = Files.readString(tempFile); log.info( @@ -65,10 +65,10 @@ public Content provideStack(final Path manifestPath) throws IOException { "Package Manager Gradle Stack Analysis Dependency Tree Output: %s %s", System.lineSeparator(), stackAnalysisDependencyTree)); } - Map propertiesMap = extractProperties(manifestPath); + Map propertiesMap = extractProperties(manifest); var sbom = buildSbomFromTextFormat(tempFile, propertiesMap, new String[] {"runtimeClasspath"}); - var ignored = getIgnoredDeps(manifestPath); + var ignored = getIgnoredDeps(manifest); return new Content( sbom.filterIgnoredDeps(ignored).getAsJsonString().getBytes(), Api.CYCLONEDX_MEDIA_TYPE); @@ -433,7 +433,7 @@ private Map extractProperties(Path manifestPath) throws IOExcept if (!keyValueMap.isEmpty()) { return keyValueMap; } else { - return null; + return Collections.emptyMap(); } } @@ -461,25 +461,15 @@ private List extractLines(Path inputFilePath, String startMarker) throws } @Override - public Content provideComponent(byte[] manifestContent) throws IOException { - throw new IllegalArgumentException( - "Gradle Package Manager requires the full package directory, not just the manifest content," - + " to generate the dependency tree. Please provide the complete package directory" - + " path."); - } - - @Override - public Content provideComponent(Path manifestPath) throws IOException { + public Content provideComponent() throws IOException { - Path tempFile = getDependencies(manifestPath); - Map propertiesMap = extractProperties(manifestPath); + Path tempFile = getDependencies(manifest); + Map propertiesMap = extractProperties(manifest); String[] configurationNames = COMPONENT_ANALYSIS_CONFIGURATIONS; - String configName = null; - var sbom = buildSbomFromTextFormat(tempFile, propertiesMap, configurationNames); - var ignored = getIgnoredDeps(manifestPath); + var ignored = getIgnoredDeps(manifest); return new Content( sbom.filterIgnoredDeps(ignored).getAsJsonString().getBytes(), Api.CYCLONEDX_MEDIA_TYPE); diff --git a/src/main/java/com/redhat/exhort/providers/JavaMavenProvider.java b/src/main/java/com/redhat/exhort/providers/JavaMavenProvider.java index 26290196..da590f18 100644 --- a/src/main/java/com/redhat/exhort/providers/JavaMavenProvider.java +++ b/src/main/java/com/redhat/exhort/providers/JavaMavenProvider.java @@ -25,15 +25,17 @@ import com.redhat.exhort.logging.LoggersFactory; import com.redhat.exhort.sbom.Sbom; import com.redhat.exhort.sbom.SbomFactory; -import com.redhat.exhort.tools.Ecosystem; import com.redhat.exhort.tools.Ecosystem.Type; import com.redhat.exhort.tools.Operations; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.time.LocalDateTime; -import java.time.temporal.ChronoUnit; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.TreeMap; import java.util.logging.Logger; import java.util.stream.Collectors; import javax.xml.stream.XMLInputFactory; @@ -50,35 +52,16 @@ public final class JavaMavenProvider extends BaseJavaProvider { private Logger log = LoggersFactory.getLogger(this.getClass().getName()); - public static void main(String[] args) throws IOException { - JavaMavenProvider javaMavenProvider = new JavaMavenProvider(); - PackageURL packageURL = - javaMavenProvider.parseDep("+- org.assertj:assertj-core:jar:3.24.2:test"); - LocalDateTime start = LocalDateTime.now(); - System.out.print(start); - Content content = - javaMavenProvider.provideStack( - Path.of("/tmp/devfile-sample-java-springboot-basic/pom.xml")); - - // PackageURL packageURL = - // javaMavenProvider.parseDep("pom-with-deps-no-ignore:pom-with-dependency-not-ignored-common-paths:jar:0.0.1"); - // String report = new String(content.buffer); - System.out.println(new String(content.buffer)); - LocalDateTime end = LocalDateTime.now(); - System.out.print(end); - System.out.print("Total time elapsed = " + start.until(end, ChronoUnit.NANOS)); - } - - public JavaMavenProvider() { - super(Type.MAVEN); + public JavaMavenProvider(Path manifest) { + super(Type.MAVEN, manifest); } @Override - public Content provideStack(final Path manifestPath) throws IOException { + public Content provideStack() throws IOException { // check for custom mvn executable var mvn = Operations.getCustomPathOrElse("mvn"); // clean command used to clean build target - var mvnCleanCmd = new String[] {mvn, "clean", "-f", manifestPath.toString()}; + var mvnCleanCmd = new String[] {mvn, "clean", "-f", manifest.toString()}; var mvnEnvs = getMvnExecEnvs(); // execute the clean command Operations.runProcess(mvnCleanCmd, mvnEnvs); @@ -94,12 +77,12 @@ public Content provideStack(final Path manifestPath) throws IOException { add("-DoutputType=text"); add(String.format("-DoutputFile=%s", tmpFile.toString())); add("-f"); - add(manifestPath.toString()); + add(manifest.toString()); } }; // if we have dependencies marked as ignored, exclude them from the tree command var ignored = - getDependencies(manifestPath).stream() + getDependencies(manifest).stream() .filter(d -> d.ignored) .map(DependencyAggregator::toPurl) .map(PackageURL::getCoordinates) @@ -134,31 +117,13 @@ private Sbom buildSbomFromTextFormat(Path textFormatFile) throws IOException { return sbom; } - private PackageURL txtPkgToPurl(String dotPkg) { - var parts = dotPkg.replaceAll("\"", "").trim().split(":"); - if (parts.length >= 4) { - try { - return new PackageURL( - Ecosystem.Type.MAVEN.getType(), parts[0], parts[1], parts[3], null, null); - } catch (MalformedPackageURLException e) { - throw new IllegalArgumentException("Unable to parse dot package: " + dotPkg, e); - } - } - throw new IllegalArgumentException("Invalid dot package format: " + dotPkg); - } - @Override - public Content provideComponent(byte[] manifestContent) throws IOException { - // save content in temporary file - var originPom = Files.createTempFile("exhort_orig_pom_", ".xml"); - Files.write(originPom, manifestContent); + public Content provideComponent() throws IOException { // build effective pom command - Content content = generateSbomFromEffectivePom(originPom); - Files.delete(originPom); - return content; + return generateSbomFromEffectivePom(); } - private Content generateSbomFromEffectivePom(Path originPom) throws IOException { + private Content generateSbomFromEffectivePom() throws IOException { // check for custom mvn executable var mvn = Operations.getCustomPathOrElse("mvn"); var tmpEffPom = Files.createTempFile("exhort_eff_pom_", ".xml"); @@ -169,7 +134,7 @@ private Content generateSbomFromEffectivePom(Path originPom) throws IOException "help:effective-pom", String.format("-Doutput=%s", tmpEffPom.toString()), "-f", - originPom.toString() + manifest.toString() }; // execute the effective pom command Operations.runProcess(mvnEffPomCmd, getMvnExecEnvs()); @@ -182,7 +147,7 @@ private Content generateSbomFromEffectivePom(Path originPom) throws IOException } // if we have dependencies marked as ignored grab ignored dependencies from the original pom // the effective-pom goal doesn't carry comments - List dependencies = getDependencies(originPom); + List dependencies = getDependencies(manifest); var ignored = dependencies.stream() .filter(d -> d.ignored) @@ -210,12 +175,6 @@ private Content generateSbomFromEffectivePom(Path originPom) throws IOException return new Content(sbom.getAsJsonString().getBytes(), Api.CYCLONEDX_MEDIA_TYPE); } - @Override - public Content provideComponent(Path manifestPath) throws IOException { - Content content = generateSbomFromEffectivePom(manifestPath); - return content; - } - private PackageURL getRoot(final Path manifestPath) throws IOException { XMLStreamReader reader = null; try { diff --git a/src/main/java/com/redhat/exhort/providers/JavaScriptNpmProvider.java b/src/main/java/com/redhat/exhort/providers/JavaScriptNpmProvider.java index 54e4e7d4..8038c5d7 100644 --- a/src/main/java/com/redhat/exhort/providers/JavaScriptNpmProvider.java +++ b/src/main/java/com/redhat/exhort/providers/JavaScriptNpmProvider.java @@ -50,49 +50,27 @@ public final class JavaScriptNpmProvider extends Provider { private System.Logger log = System.getLogger(this.getClass().getName()); - public JavaScriptNpmProvider() { - super(Type.NPM); + public JavaScriptNpmProvider(Path manifest) { + super(Type.NPM, manifest); } @Override - public Content provideStack(final Path manifestPath) throws IOException { + public Content provideStack() throws IOException { // check for custom npm executable - Sbom sbom = getDependencySbom(manifestPath, true, false); + Sbom sbom = getDependencySbom(manifest, true, false); return new Content( sbom.getAsJsonString().getBytes(StandardCharsets.UTF_8), Api.CYCLONEDX_MEDIA_TYPE); } @Override - public Content provideComponent(byte[] manifestContent) throws IOException { - // check for custom npm executable - return new Content( - getDependencyTree(manifestContent).getAsJsonString().getBytes(StandardCharsets.UTF_8), - Api.CYCLONEDX_MEDIA_TYPE); - } - - @Override - public Content provideComponent(Path manifestPath) throws IOException { + public Content provideComponent() throws IOException { return new Content( - getDependencySbom(manifestPath, false, false) + getDependencySbom(manifest, false, false) .getAsJsonString() .getBytes(StandardCharsets.UTF_8), Api.CYCLONEDX_MEDIA_TYPE); } - private Sbom getDependencyTree(byte[] manifestContent) { - Sbom sbom; - try { - Path tempDir = Files.createTempDirectory("exhort_npm"); - Path path = Files.createFile(Path.of(tempDir.toString(), "package.json")); - Files.write(path, manifestContent); - sbom = getDependencySbom(path, false, true); - Files.delete(path); - } catch (IOException e) { - throw new RuntimeException(e); - } - return sbom; - } - private PackageURL getRoot(JsonNode jsonDependenciesNpm) throws MalformedPackageURLException { return toPurl( jsonDependenciesNpm.get("name").asText(), jsonDependenciesNpm.get("version").asText()); @@ -140,11 +118,20 @@ private JsonNode buildNpmDependencyTree( var npm = Operations.getCustomPathOrElse("npm"); var npmEnvs = getNpmExecEnv(); // clean command used to clean build target - Path packageLockJson = Path.of(manifestPath.getParent().toString(), "package-lock.json"); + Path manifestDir = null; + try { + // MacOS requires resolving to the CanonicalPath to avoid problems with /var being a symlink + // of /private/var + manifestDir = Path.of(manifestPath.getParent().toFile().getCanonicalPath()); + } catch (IOException e) { + throw new RuntimeException( + String.format( + "Unable to resolve manifest directory %s, got %s", + manifestPath.getParent(), e.getMessage())); + } + Path packageLockJson = manifestDir.resolve("package-lock.json"); var createPackageLock = - new String[] { - npm, "i", "--package-lock-only", "--prefix", manifestPath.getParent().toString() - }; + new String[] {npm, "i", "--package-lock-only", "--prefix", manifestDir.toString()}; // execute the clean command Operations.runProcess(createPackageLock, npmEnvs); String[] npmAllDeps; @@ -155,19 +142,19 @@ private JsonNode buildNpmDependencyTree( new String[] { npm, "ls", - includeTransitive ? "--all" : "", + includeTransitive ? "--all" : "--depth=0", "--omit=dev", "--package-lock-only", "--json", "--prefix", - manifestPath.getParent().toString() + manifestDir.toString() }; } else { npmAllDeps = new String[] { npm, "ls", - includeTransitive ? "--all" : "", + includeTransitive ? "--all" : "--depth=0", "--omit=dev", "--package-lock-only", "--json" diff --git a/src/main/java/com/redhat/exhort/providers/PythonPipProvider.java b/src/main/java/com/redhat/exhort/providers/PythonPipProvider.java index c9bdce0b..6f1634ff 100644 --- a/src/main/java/com/redhat/exhort/providers/PythonPipProvider.java +++ b/src/main/java/com/redhat/exhort/providers/PythonPipProvider.java @@ -34,7 +34,6 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.*; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -51,38 +50,22 @@ public void setPythonController(PythonControllerBase pythonController) { private PythonControllerBase pythonController; - public static void main(String[] args) { - try { - PythonPipProvider pythonPipProvider = new PythonPipProvider(); - // byte[] bytes = Files.readAllBytes(Path.of("/tmp/exhort_env/requirements.txt")); - // Content content = pythonPipProvider.provideComponent(bytes); - Content content = - pythonPipProvider.provideStack( - Path.of( - "/home/zgrinber/git/exhort-java-api/src/test/resources/tst_manifests/pip/pip_requirements_txt_ignore/requirements.txt")); - String s = new String(content.buffer); - System.out.print(s); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - public PythonPipProvider() { - super(Ecosystem.Type.PYTHON); + public PythonPipProvider(Path manifest) { + super(Ecosystem.Type.PYTHON, manifest); } @Override - public Content provideStack(Path manifestPath) throws IOException { + public Content provideStack() throws IOException { PythonControllerBase pythonController = getPythonController(); List> dependencies = - pythonController.getDependencies(manifestPath.toString(), true); + pythonController.getDependencies(manifest.toString(), true); printDependenciesTree(dependencies); Sbom sbom = SbomFactory.newInstance(Sbom.BelongingCondition.PURL, "sensitive"); sbom.addRoot(toPurl(DEFAULT_PIP_ROOT_COMPONENT_NAME, DEFAULT_PIP_ROOT_COMPONENT_VERSION)); for (Map component : dependencies) { addAllDependencies(sbom.getRoot(), component, sbom); } - byte[] requirementsFile = Files.readAllBytes(manifestPath); + byte[] requirementsFile = Files.readAllBytes(manifest); handleIgnoredDependencies(new String(requirementsFile), sbom); return new Content( sbom.getAsJsonString().getBytes(StandardCharsets.UTF_8), Api.CYCLONEDX_MEDIA_TYPE); @@ -104,16 +87,10 @@ private void addAllDependencies(PackageURL source, Map component } @Override - public Content provideComponent(byte[] manifestContent) throws IOException { + public Content provideComponent() throws IOException { PythonControllerBase pythonController = getPythonController(); - Path tempRepository = Files.createTempDirectory("exhort-pip"); - Path path = - Paths.get(tempRepository.toAbsolutePath().normalize().toString(), "requirements.txt"); - Files.deleteIfExists(path); - Path manifestPath = Files.createFile(path); - Files.write(manifestPath, manifestContent); List> dependencies = - pythonController.getDependencies(manifestPath.toString(), false); + pythonController.getDependencies(manifest.toString(), false); printDependenciesTree(dependencies); Sbom sbom = SbomFactory.newInstance(); sbom.addRoot(toPurl(DEFAULT_PIP_ROOT_COMPONENT_NAME, DEFAULT_PIP_ROOT_COMPONENT_VERSION)); @@ -124,9 +101,9 @@ public Content provideComponent(byte[] manifestContent) throws IOException { sbom.getRoot(), toPurl((String) component.get("name"), (String) component.get("version"))); }); - Files.delete(manifestPath); - Files.delete(tempRepository); - handleIgnoredDependencies(new String(manifestContent), sbom); + + var manifestContent = Files.readString(manifest); + handleIgnoredDependencies(manifestContent, sbom); return new Content( sbom.getAsJsonString().getBytes(StandardCharsets.UTF_8), Api.CYCLONEDX_MEDIA_TYPE); } @@ -145,12 +122,12 @@ private void printDependenciesTree(List> dependencies) private void handleIgnoredDependencies(String manifestContent, Sbom sbom) { Set ignoredDeps = getIgnoredDependencies(manifestContent); - Set ignoredDepsVersions = + Set ignoredDepsVersions = ignoredDeps.stream() .filter(dep -> !dep.getVersion().trim().equals("*")) .map(PackageURL::getCoordinates) .collect(Collectors.toSet()); - Set ignoredDepsNoVersions = + Set ignoredDepsNoVersions = ignoredDeps.stream() .filter(dep -> dep.getVersion().trim().equals("*")) .map(PackageURL::getCoordinates) @@ -160,7 +137,8 @@ private void handleIgnoredDependencies(String manifestContent, Sbom sbom) { // resolved by pip. sbom.setBelongingCriteriaBinaryAlgorithm(Sbom.BelongingCondition.NAME); sbom.filterIgnoredDeps(ignoredDepsNoVersions); - boolean matchManifestVersions = getBooleanValueEnvironment("MATCH_MANIFEST_VERSIONS", "true"); + boolean matchManifestVersions = + getBooleanValueEnvironment(PROP_MATCH_MANIFEST_VERSIONS, "true"); // filter out by purl from sbom all exhortignore dependencies that their version hardcoded in // requirements.txt - // in case all versions in manifest matching installed versions of packages in environment. @@ -171,8 +149,8 @@ private void handleIgnoredDependencies(String manifestContent, Sbom sbom) { // in case version mismatch is possible (MATCH_MANIFEST_VERSIONS=false) , need to parse the // name of package // from the purl, and remove the package name from sbom according to name only - Set deps = - (Set) + Set deps = + (Set) ignoredDepsVersions.stream() .map( purlString -> { @@ -188,7 +166,7 @@ private void handleIgnoredDependencies(String manifestContent, Sbom sbom) { } } - private Set getIgnoredDependencies(String requirementsDeps) { + private Set getIgnoredDependencies(String requirementsDeps) { String[] requirementsLines = requirementsDeps.split(System.lineSeparator()); Set collected = @@ -231,18 +209,21 @@ private PackageURL toPurl(String name, String version) { private PythonControllerBase getPythonController() { String pythonPipBinaries; String useVirtualPythonEnv; - if (!getStringValueEnvironment("EXHORT_PIP_SHOW", "").trim().equals("") - && !getStringValueEnvironment("EXHORT_PIP_FREEZE", "").trim().equals("")) { + if (!getStringValueEnvironment(PythonControllerBase.PROP_EXHORT_PIP_SHOW, "").trim().equals("") + && !getStringValueEnvironment(PythonControllerBase.PROP_EXHORT_PIP_FREEZE, "") + .trim() + .equals("")) { pythonPipBinaries = "python;;pip"; useVirtualPythonEnv = "false"; } else { pythonPipBinaries = getPythonPipBinaries(); useVirtualPythonEnv = Objects.requireNonNullElseGet( - System.getenv("EXHORT_PYTHON_VIRTUAL_ENV"), + System.getenv(PythonControllerBase.PROP_EXHORT_PYTHON_VIRTUAL_ENV), () -> Objects.requireNonNullElse( - System.getProperty("EXHORT_PYTHON_VIRTUAL_ENV"), "false")); + System.getProperty(PythonControllerBase.PROP_EXHORT_PYTHON_VIRTUAL_ENV), + "false")); } String[] parts = pythonPipBinaries.split(";;"); @@ -250,10 +231,11 @@ private PythonControllerBase getPythonController() { var pip = parts[1]; useVirtualPythonEnv = Objects.requireNonNullElseGet( - System.getenv("EXHORT_PYTHON_VIRTUAL_ENV"), + System.getenv(PythonControllerBase.PROP_EXHORT_PYTHON_VIRTUAL_ENV), () -> Objects.requireNonNullElse( - System.getProperty("EXHORT_PYTHON_VIRTUAL_ENV"), "false")); + System.getProperty(PythonControllerBase.PROP_EXHORT_PYTHON_VIRTUAL_ENV), + "false")); PythonControllerBase pythonController; if (this.pythonController == null) { if (Boolean.parseBoolean(useVirtualPythonEnv)) { @@ -281,10 +263,4 @@ private static String getPythonPipBinaries() { } return String.format("%s;;%s", python, pip); } - - @Override - public Content provideComponent(Path manifestPath) throws IOException { - throw new IllegalArgumentException( - "provideComponent with file system path for Python pip package manager is not supported"); - } } diff --git a/src/main/java/com/redhat/exhort/tools/Ecosystem.java b/src/main/java/com/redhat/exhort/tools/Ecosystem.java index 554ac12a..6c5808ff 100644 --- a/src/main/java/com/redhat/exhort/tools/Ecosystem.java +++ b/src/main/java/com/redhat/exhort/tools/Ecosystem.java @@ -21,7 +21,6 @@ import com.redhat.exhort.providers.JavaMavenProvider; import com.redhat.exhort.providers.JavaScriptNpmProvider; import com.redhat.exhort.providers.PythonPipProvider; -import java.nio.file.Files; import java.nio.file.Path; /** Utility class used for instantiating providers. * */ @@ -53,40 +52,33 @@ private Ecosystem() { * Utility function for instantiating {@link Provider} implementations. * * @param manifestPath the manifest Path - * @return a Manifest record + * @return a {@link Provider} suited for this manifest type */ public static Provider getProvider(final Path manifestPath) { - return Ecosystem.getProvider(manifestPath.getFileName().toString(), manifestPath); + var provider = resolveProvider(manifestPath); + if (!provider.validateLockFile(manifestPath)) { + throw new IllegalStateException( + "Missing lock file for manifest file: " + manifestPath.toString()); + } + return provider; } - /** - * Utility function for instantiating {@link Provider} implementations. - * - * @param manifestType the type (filename + type) of the manifest - * @return a Manifest record - */ - public static Provider getProvider(final String manifestType, final Path manifestPath) { - switch (manifestType) { + private static Provider resolveProvider(final Path manifestPath) { + var manifestFile = manifestPath.getFileName().toString(); + switch (manifestFile) { case "pom.xml": - return new JavaMavenProvider(); + return new JavaMavenProvider(manifestPath); case "package.json": - Path lockFile = manifestPath.getParent().resolve("package-lock.json"); - if (Files.exists(lockFile)) { - return new JavaScriptNpmProvider(); - } else { - throw new IllegalStateException( - String.format("NPM Lock file could not be found for %s", manifestType)); - } + return new JavaScriptNpmProvider(manifestPath); case "go.mod": - return new GoModulesProvider(); + return new GoModulesProvider(manifestPath); case "requirements.txt": - return new PythonPipProvider(); + return new PythonPipProvider(manifestPath); case "build.gradle": case "build.gradle.kts": - return new GradleProvider(); - + return new GradleProvider(manifestPath); default: - throw new IllegalStateException(String.format("Unknown manifest file %s", manifestType)); + throw new IllegalStateException(String.format("Unknown manifest file %s", manifestFile)); } } } diff --git a/src/main/java/com/redhat/exhort/tools/Operations.java b/src/main/java/com/redhat/exhort/tools/Operations.java index 4a4a3d13..7c85cac5 100644 --- a/src/main/java/com/redhat/exhort/tools/Operations.java +++ b/src/main/java/com/redhat/exhort/tools/Operations.java @@ -19,7 +19,6 @@ import java.io.BufferedReader; import java.io.IOException; -import java.io.InputStream; import java.io.InputStreamReader; import java.nio.file.Path; import java.util.Map; @@ -90,15 +89,24 @@ public static void runProcess(final String[] cmdList, final Map } // verify the command was executed successfully or throw a runtime exception if (exitCode != 0) { - String errMsg = - new BufferedReader(new InputStreamReader(process.getErrorStream())) - .lines() - .collect(Collectors.joining(System.lineSeparator())); + String errMsg = ""; + try (var reader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) { + errMsg = reader.lines().collect(Collectors.joining(System.lineSeparator())); + } catch (IOException e) { + throw new RuntimeException( + String.format( + "unable to process error output for '%s', got %s", + join(" ", cmdList), e.getMessage())); + } + if (errMsg.isEmpty()) { - errMsg = - new BufferedReader(new InputStreamReader(process.getInputStream())) - .lines() - .collect(Collectors.joining(System.lineSeparator())); + try (var reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { + errMsg = reader.lines().collect(Collectors.joining(System.lineSeparator())); + } catch (IOException e) { + throw new RuntimeException( + String.format( + "unable to process output for '%s', got %s", join(" ", cmdList), e.getMessage())); + } } if (errMsg.isEmpty()) { throw new RuntimeException( @@ -121,49 +129,34 @@ public static String runProcessGetOutput(Path dir, final String... cmdList) { } public static String runProcessGetOutput(Path dir, final String[] cmdList, String[] envList) { - StringBuilder sb = new StringBuilder(); try { Process process; - InputStream inputStream; if (dir == null) { if (envList != null) { - process = Runtime.getRuntime().exec(join(" ", cmdList), envList); + process = Runtime.getRuntime().exec(cmdList, envList); } else { - process = Runtime.getRuntime().exec(join(" ", cmdList)); + process = Runtime.getRuntime().exec(cmdList, null); } } else { if (envList != null) { - process = Runtime.getRuntime().exec(join(" ", cmdList), envList, dir.toFile()); + process = Runtime.getRuntime().exec(cmdList, envList, dir.toFile()); } else { - process = Runtime.getRuntime().exec(join(" ", cmdList), null, dir.toFile()); + process = Runtime.getRuntime().exec(cmdList, null, dir.toFile()); } } - inputStream = process.getInputStream(); + String stdout = new String(process.getInputStream().readAllBytes()); + String stderr = new String(process.getErrorStream().readAllBytes()); - BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); - String line; - while ((line = reader.readLine()) != null) { - sb.append(line); - if (!line.endsWith(System.lineSeparator())) { - sb.append("\n"); - } - } - if (sb.toString().trim().equals("")) { - inputStream = process.getErrorStream(); - reader = new BufferedReader(new InputStreamReader(inputStream)); - while ((line = reader.readLine()) != null) { - sb.append(line); - if (!line.endsWith(System.lineSeparator())) { - sb.append("\n"); - } - } + // TODO: This should throw an exception if the process fails + if (!stderr.isBlank()) { + return stderr.trim(); } + return stdout.trim(); } catch (IOException e) { throw new RuntimeException( String.format("Failed to execute command '%s' ", join(" ", cmdList)), e); } - return sb.toString(); } public static ProcessExecOutput runProcessGetFullOutput( @@ -172,15 +165,15 @@ public static ProcessExecOutput runProcessGetFullOutput( Process process; if (dir == null) { if (envList != null) { - process = Runtime.getRuntime().exec(join(" ", cmdList), envList); + process = Runtime.getRuntime().exec(cmdList, envList); } else { - process = Runtime.getRuntime().exec(join(" ", cmdList)); + process = Runtime.getRuntime().exec(cmdList); } } else { if (envList != null) { - process = Runtime.getRuntime().exec(join(" ", cmdList), envList, dir.toFile()); + process = Runtime.getRuntime().exec(cmdList, envList, dir.toFile()); } else { - process = Runtime.getRuntime().exec(join(" ", cmdList), null, dir.toFile()); + process = Runtime.getRuntime().exec(cmdList, null, dir.toFile()); } } diff --git a/src/main/java/com/redhat/exhort/utils/PythonControllerBase.java b/src/main/java/com/redhat/exhort/utils/PythonControllerBase.java index da28cdcb..12c07ce5 100644 --- a/src/main/java/com/redhat/exhort/utils/PythonControllerBase.java +++ b/src/main/java/com/redhat/exhort/utils/PythonControllerBase.java @@ -15,7 +15,10 @@ */ package com.redhat.exhort.utils; -import static com.redhat.exhort.impl.ExhortApi.*; +import static com.redhat.exhort.Provider.PROP_MATCH_MANIFEST_VERSIONS; +import static com.redhat.exhort.impl.ExhortApi.debugLoggingIsNeeded; +import static com.redhat.exhort.impl.ExhortApi.getBooleanValueEnvironment; +import static com.redhat.exhort.impl.ExhortApi.getStringValueEnvironment; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; @@ -26,57 +29,25 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.time.LocalDateTime; -import java.time.temporal.ChronoUnit; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Base64; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.logging.Logger; import java.util.stream.Collectors; import java.util.stream.Stream; public abstract class PythonControllerBase { - public static void main(String[] args) { - - PythonControllerBase pythonController; - // pythonController = new PythonControllerVirtualEnv("/usr/bin/python3"); - LocalDateTime start = LocalDateTime.now(); - List> dependencies; - // dependencies = pythonController.getDependencies("/tmp/requirements.txt",true); - LocalDateTime end = LocalDateTime.now(); - System.out.println("start time:" + start + "\n"); - System.out.println("end time:" + end + "\n"); - System.out.println("elapsed time: " + start.until(end, ChronoUnit.SECONDS) + "\n"); - pythonController = new PythonControllerRealEnv("/usr/bin/python3", "/usr/bin/pip3"); - start = LocalDateTime.now(); - try { - dependencies = - pythonController.getDependencies( - "/home/zgrinber/git/exhort-java-api/src/test/resources/tst_manifests/pip/pip_requirements_txt_ignore/requirements.txt", - true); - } catch (PackageNotInstalledException e) { - System.out.println(e.getMessage()); - dependencies = null; - } - end = LocalDateTime.now(); - // LocalDateTime startNaive = LocalDateTime.now(); - // List> dependenciesNaive = pythonController.getDependenciesNaive(); - // LocalDateTime endNaive = LocalDateTime.now(); - System.out.println("start time:" + start + "\n"); - System.out.println("end time:" + end + "\n"); - System.out.println("elapsed time: " + start.until(end, ChronoUnit.SECONDS) + "\n"); - // System.out.println("naive start time:" + startNaive + "\n" ); - // System.out.println("naive end time:" + endNaive + "\n"); - // System.out.println("elapsed time: " + startNaive.until(endNaive, ChronoUnit.SECONDS)); - - ObjectMapper om = new ObjectMapper(); - try { - String json = om.writerWithDefaultPrettyPrinter().writeValueAsString(dependencies); - System.out.println(json); - // System.out.println(pythonController.counter); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - } + public static final String PROP_EXHORT_PIP_PIPDEPTREE = "EXHORT_PIP_PIPDEPTREE"; + public static final String PROP_EXHORT_PIP_FREEZE = "EXHORT_PIP_FREEZE"; + public static final String PROP_EXHORT_PIP_USE_DEP_TREE = "EXHORT_PIP_USE_DEP_TREE"; + public static final String PROP_EXHORT_PYTHON_INSTALL_BEST_EFFORTS = + "EXHORT_PYTHON_INSTALL_BEST_EFFORTS"; + public static final String PROP_EXHORT_PIP_SHOW = "EXHORT_PIP_SHOW"; + public static final String PROP_EXHORT_PYTHON_VIRTUAL_ENV = "EXHORT_PYTHON_VIRTUAL_ENV"; private Logger log = LoggersFactory.getLogger(this.getClass().getName()); protected Path pythonEnvironmentDir; @@ -86,8 +57,6 @@ public static void main(String[] args) { protected String pipBinaryLocation; - // public int counter =0; - public abstract void prepareEnvironment(String pathToPythonBin); public abstract boolean automaticallyInstallPackageOnEnvironment(); @@ -103,99 +72,6 @@ void installPackages(String pathToRequirements) { public abstract void cleanEnvironment(boolean deleteEnvironment); - // public List> getDependenciesNaive() - // { - // List> dependencies = new ArrayList<>(); - // String freeze = Operations.runProcessGetOutput(pythonEnvironmentDir, pipBinaryLocation, - // "freeze"); - // String[] deps = freeze.split(System.lineSeparator()); - // Arrays.stream(deps).forEach(dep -> - // { - // Map component = new HashMap<>(); - // dependencies.add(component); - // bringAllDependenciesNaive(component, getDependencyName(dep)); - // }); - // - // - // - // return dependencies; - // } - // - // private void bringAllDependenciesNaive(Map dependencies, String depName) { - // - // if(dependencies == null || depName.trim().equals("")) - // return; - // counter++; - // LocalDateTime start = LocalDateTime.now(); - // String pipShowOutput = Operations.runProcessGetOutput(pythonEnvironmentDir, - // pipBinaryLocation, "show", - // depName); - // LocalDateTime end = LocalDateTime.now(); - // System.out.println("pip show start time:" + start + "\n"); - // System.out.println("pip show end time:" + end + "\n"); - // System.out.println("pip show elapsed time: " + start.until(end, ChronoUnit.SECONDS) + "\n" - // ); - // String depVersion = getDependencyVersion(pipShowOutput); - // List directDeps = getDepsList(pipShowOutput); - // dependencies.put("name", depName); - // dependencies.put("version",depVersion); - // List> targetDeps = new ArrayList<>(); - // directDeps.stream().forEach(d -> { - // Map myMap = new HashMap<>(); - // targetDeps.add(myMap); - // bringAllDependenciesNaive(myMap,d); - // }); - // dependencies.put("dependencies",targetDeps); - // - // } - // public List> getDependencies() - // { - // List> dependencies = new ArrayList<>(); - // String freeze = Operations.runProcessGetOutput(pythonEnvironmentDir, pipBinaryLocation, - // "freeze"); - // String[] deps = freeze.split(System.lineSeparator()); - // String depNames = - // Arrays.stream(deps).map(this::getDependencyName).collect(Collectors.joining(" ")); - // bringAllDependencies(dependencies, depNames); - // - // - // - // - // return dependencies; - // } - // - // private void bringAllDependencies(List> dependencies, String depName) { - // - // if (dependencies == null || depName.trim().equals("")) - // return; - // counter++; - // LocalDateTime start = LocalDateTime.now(); - // String pipShowOutput = Operations.runProcessGetOutput(pythonEnvironmentDir, - // pipBinaryLocation, "show", - // depName); - // LocalDateTime end = LocalDateTime.now(); - // System.out.println("pip show start time:" + start + "\n"); - // System.out.println("pip show end time:" + end + "\n"); - // System.out.println("pip show elapsed time: " + start.until(end, ChronoUnit.MILLIS) + "\n" - // ); - // List allLines = - // Arrays.stream(pipShowOutput.split("---")).collect(Collectors.toList()); - // allLines.stream().forEach(record -> { - // String depVersion = getDependencyVersion(record); - // List directDeps = getDepsList(record); - // getDependencyNameShow(record); - // Map entry = new HashMap(); - // dependencies.add(entry); - // entry.put("name", getDependencyNameShow(record)); - // entry.put("version", depVersion); - // List> targetDeps = new ArrayList<>(); - // String depsList = directDeps.stream().map(str -> str.replace(",", - // "")).collect(Collectors.joining(" ")); - // bringAllDependencies(targetDeps, depsList); - // entry.put("dependencies",targetDeps); - // }); - // } - public final List> getDependencies( String pathToRequirements, boolean includeTransitive) { if (isVirtualEnv() || isRealEnv()) { @@ -203,7 +79,7 @@ public final List> getDependencies( } if (automaticallyInstallPackageOnEnvironment()) { boolean installBestEfforts = - getBooleanValueEnvironment("EXHORT_PYTHON_INSTALL_BEST_EFFORTS", "false"); + getBooleanValueEnvironment(PROP_EXHORT_PYTHON_INSTALL_BEST_EFFORTS, "false"); /* make best efforts to install the requirements.txt on the virtual environment created from the python3 passed in. that means that it will install the packages without referring to @@ -212,11 +88,14 @@ public final List> getDependencies( */ if (installBestEfforts) { boolean matchManifestVersions = - getBooleanValueEnvironment("MATCH_MANIFEST_VERSIONS", "true"); + getBooleanValueEnvironment(PROP_MATCH_MANIFEST_VERSIONS, "true"); if (matchManifestVersions) { throw new RuntimeException( - "Conflicting settings, EXHORT_PYTHON_INSTALL_BEST_EFFORTS=true can only work with" - + " MATCH_MANIFEST_VERSIONS=false"); + "Conflicting settings, " + + PythonControllerBase.PROP_EXHORT_PYTHON_INSTALL_BEST_EFFORTS + + "=true can only work with " + + PROP_MATCH_MANIFEST_VERSIONS + + "=false"); } else { installingRequirementsOneByOne(pathToRequirements); } @@ -263,27 +142,38 @@ private void installingRequirementsOneByOne(String pathToRequirements) { } private List> getDependenciesImpl( - String pathToRequirements, boolean includeTransitive) { + String requirements, boolean includeTransitive) { List> dependencies = new ArrayList<>(); Map cachedEnvironmentDeps = new HashMap<>(); fillCacheWithEnvironmentDeps(cachedEnvironmentDeps); + var requirementsPath = Path.of(requirements); + if (!Files.isRegularFile(requirementsPath)) { + throw new IllegalArgumentException( + "The requirements.txt file does not exist or is not a regular file: " + requirements); + } List linesOfRequirements; try { linesOfRequirements = - Files.readAllLines(Path.of(pathToRequirements)).stream() + Files.readAllLines(requirementsPath).stream() .filter((line) -> !line.startsWith("#")) .map(String::trim) .collect(Collectors.toList()); } catch (IOException e) { - throw new RuntimeException(e); + log.warning( + "Error while trying to read the requirements.txt file, will not be able to install" + + " packages one by one"); + throw new RuntimeException("Unable to read requirements.txt file: " + e.getMessage()); } try { ObjectMapper om = new ObjectMapper(); om.writerWithDefaultPrettyPrinter().writeValueAsString(cachedEnvironmentDeps); } catch (JsonProcessingException e) { + log.warning( + "Error while trying to convert the cached environment dependencies to JSON string"); throw new RuntimeException(e); } - boolean matchManifestVersions = getBooleanValueEnvironment("MATCH_MANIFEST_VERSIONS", "true"); + boolean matchManifestVersions = + getBooleanValueEnvironment(PROP_MATCH_MANIFEST_VERSIONS, "true"); for (String dep : linesOfRequirements) { if (matchManifestVersions) { @@ -312,8 +202,11 @@ private List> getDependenciesImpl( + " name=%s, manifest version=%s, installed Version=%s, if you" + " want to allow version mismatch for analysis between installed" + " and requested packages, set environment variable/setting -" - + " MATCH_MANIFEST_VERSIONS=false", - dependencyName, manifestVersion, installedVersion)); + + " %s=false", + dependencyName, + manifestVersion, + installedVersion, + PROP_MATCH_MANIFEST_VERSIONS)); } } } @@ -328,27 +221,31 @@ private List> getDependenciesImpl( return dependencies; } - private String getPipShowFromEnvironment(String depNames) { - return executeCommandOrExtractFromEnv("EXHORT_PIP_SHOW", pipBinaryLocation, "show", depNames); + private String getPipShowFromEnvironment(List depNames) { + var args = new ArrayList<>(); + args.add(pipBinaryLocation); + args.add("show"); + args.addAll(depNames); + return executeCommandOrExtractFromEnv(PROP_EXHORT_PIP_SHOW, args.toArray(new String[] {})); } String getPipFreezeFromEnvironment() { return executeCommandOrExtractFromEnv( - "EXHORT_PIP_FREEZE", pipBinaryLocation, "freeze", "--all"); + PROP_EXHORT_PIP_FREEZE, pipBinaryLocation, "freeze", "--all"); } List getDependencyTreeJsonFromPipDepTree() { executeCommandOrExtractFromEnv( - "EXHORT_PIP_PIPDEPTREE", pipBinaryLocation, "install", "pipdeptree"); + PROP_EXHORT_PIP_PIPDEPTREE, pipBinaryLocation, "install", "pipdeptree"); String pipdeptreeJsonString = ""; if (isVirtualEnv()) { pipdeptreeJsonString = - executeCommandOrExtractFromEnv("EXHORT_PIP_PIPDEPTREE", "./bin/pipdeptree", "--json"); + executeCommandOrExtractFromEnv(PROP_EXHORT_PIP_PIPDEPTREE, "./bin/pipdeptree", "--json"); } else if (isRealEnv()) { pipdeptreeJsonString = executeCommandOrExtractFromEnv( - "EXHORT_PIP_PIPDEPTREE", pathToPythonBin, "-m", "pipdeptree", "--json"); + PROP_EXHORT_PIP_PIPDEPTREE, pathToPythonBin, "-m", "pipdeptree", "--json"); } if (debugLoggingIsNeeded()) { String pipdeptreeMessage = @@ -391,7 +288,7 @@ List mapToPythonDependencies(String jsonString) { private String executeCommandOrExtractFromEnv(String EnvVar, String... cmdList) { String envValue = getStringValueEnvironment(EnvVar, ""); - if (envValue.trim().equals("")) + if (envValue.trim().isBlank()) return Operations.runProcessGetOutput(pythonEnvironmentDir, cmdList); return new String(Base64.getDecoder().decode(envValue)); } @@ -411,9 +308,9 @@ private void bringAllDependencies( String.format( "Package name=>%s is not installed on your python environment, either install it (" + " better to install requirements.txt altogether) or turn on environment" - + " variable EXHORT_PYTHON_VIRTUAL_ENV=true to automatically installs it on" - + " virtual environment ( will slow down the analysis)", - depName)); + + " variable %s=true to automatically install it on" + + " virtual environment (will slow down the analysis)", + depName, PROP_EXHORT_PYTHON_VIRTUAL_ENV)); } Map dataMap = new HashMap<>(); @@ -517,7 +414,7 @@ private PythonDependency getPythonDependencyByShowStringBlock(String pipShowStri } private void fillCacheWithEnvironmentDeps(Map cache) { - boolean usePipDepTree = getBooleanValueEnvironment("EXHORT_PIP_USE_DEP_TREE", "false"); + boolean usePipDepTree = getBooleanValueEnvironment(PROP_EXHORT_PIP_USE_DEP_TREE, "false"); if (usePipDepTree) { getDependencyTreeJsonFromPipDepTree().forEach(d -> saveToCacheWithKeyVariations(cache, d)); } else { @@ -530,10 +427,11 @@ private void fillCacheWithEnvironmentDeps(Map !line.contains("@ file")) .map(PythonControllerBase::getDependencyName) - .collect(Collectors.joining(" ")); + .collect(Collectors.toList()); String pipShowOutput = getPipShowFromEnvironment(depNames); if (debugLoggingIsNeeded()) { String pipShowMessage = diff --git a/src/main/java/com/redhat/exhort/utils/PythonControllerTestEnv.java b/src/main/java/com/redhat/exhort/utils/PythonControllerTestEnv.java index 740e4506..0a79506b 100644 --- a/src/main/java/com/redhat/exhort/utils/PythonControllerTestEnv.java +++ b/src/main/java/com/redhat/exhort/utils/PythonControllerTestEnv.java @@ -19,7 +19,7 @@ import java.nio.file.Path; public class PythonControllerTestEnv extends PythonControllerRealEnv { - // private System.Logger log = System.getLogger("name"); + public PythonControllerTestEnv(String pathToPythonBin, String pathToPip) { super(pathToPythonBin, pathToPip); } @@ -27,12 +27,9 @@ public PythonControllerTestEnv(String pathToPythonBin, String pathToPip) { @Override public void prepareEnvironment(String pathToPythonBin) { super.prepareEnvironment(pathToPythonBin); - String output = - Operations.runProcessGetOutput( - Path.of("."), - new String[] {this.pathToPythonBin, "-m", "pip", "install", "--upgrade", "pip"}); - // log.log(System.Logger.Level.INFO,"Output from upgrading pip = " + System.lineSeparator() + - // output); + Operations.runProcessGetOutput( + Path.of("."), + new String[] {this.pathToPythonBin, "-m", "pip", "install", "--upgrade", "pip"}); } @Override diff --git a/src/main/java/com/redhat/exhort/utils/PythonControllerVirtualEnv.java b/src/main/java/com/redhat/exhort/utils/PythonControllerVirtualEnv.java index 7c8db244..b9c6946e 100644 --- a/src/main/java/com/redhat/exhort/utils/PythonControllerVirtualEnv.java +++ b/src/main/java/com/redhat/exhort/utils/PythonControllerVirtualEnv.java @@ -42,10 +42,9 @@ public void prepareEnvironment(String pathToPythonBin) { } catch (IOException e) { throw new RuntimeException(e); } - String output = - Operations.runProcessGetOutput( - Path.of("."), - new String[] {pathToPythonBin, "-m", "venv", pythonEnvironmentDir.toString()}); + Operations.runProcessGetOutput( + Path.of("."), + new String[] {pathToPythonBin, "-m", "venv", pythonEnvironmentDir.toString()}); String envBinDir = pipBinaryDir.toString(); if (pathToPythonBin.contains("python3")) { this.pipBinaryLocation = Path.of(envBinDir, "pip3").toString(); diff --git a/src/main/java/com/redhat/exhort/vcs/GitVersionControlSystemImpl.java b/src/main/java/com/redhat/exhort/vcs/GitVersionControlSystemImpl.java index 867c0130..5f50c9dd 100644 --- a/src/main/java/com/redhat/exhort/vcs/GitVersionControlSystemImpl.java +++ b/src/main/java/com/redhat/exhort/vcs/GitVersionControlSystemImpl.java @@ -91,11 +91,14 @@ public TagInfo getLatestTag(Path repoLocation) { @Override public boolean isDirectoryRepo(Path repoLocation) { - - String resultFromInvocation = - Operations.runProcessGetOutput( - repoLocation, gitBinary, "rev-parse", "--is-inside-work-tree"); - return resultFromInvocation.trim().equals("true"); + try { + String resultFromInvocation = + Operations.runProcessGetOutput( + repoLocation, gitBinary, "rev-parse", "--is-inside-work-tree"); + return resultFromInvocation.trim().equals("true"); + } catch (Exception e) { + return false; + } } @Override diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 0b7515d4..4b5134cf 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -8,7 +8,9 @@ requires jakarta.mail; requires cyclonedx.core.java; requires transitive packageurl.java; + requires transitive java.logging; requires org.tomlj; + requires java.base; opens com.redhat.exhort.api to com.fasterxml.jackson.databind; @@ -21,6 +23,7 @@ exports com.redhat.exhort.impl; exports com.redhat.exhort.sbom; exports com.redhat.exhort.tools; + exports com.redhat.exhort.utils; opens com.redhat.exhort.utils to com.fasterxml.jackson.databind; diff --git a/src/test/java/com/redhat/exhort/ExhortTest.java b/src/test/java/com/redhat/exhort/ExhortTest.java index 2f18e494..822bfab6 100644 --- a/src/test/java/com/redhat/exhort/ExhortTest.java +++ b/src/test/java/com/redhat/exhort/ExhortTest.java @@ -21,7 +21,6 @@ import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; -import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.function.Supplier; @@ -29,10 +28,10 @@ public class ExhortTest { - protected String getStringFromFile(String... list) { + protected String getStringFromFile(String path) { byte[] bytes = new byte[0]; try { - InputStream resourceAsStream = getResourceAsStreamDecision(this.getClass(), list); + InputStream resourceAsStream = getResourceAsStreamDecision(this.getClass(), path); bytes = resourceAsStream.readAllBytes(); resourceAsStream.close(); } catch (IOException e) { @@ -42,27 +41,29 @@ protected String getStringFromFile(String... list) { return new String(bytes); } - public static InputStream getResourceAsStreamDecision(Class theClass, String[] list) + public static InputStream getResourceAsStreamDecision(Class theClass, String path) throws IOException { - InputStream resourceAsStreamFromModule = - theClass.getModule().getResourceAsStream(String.join("/", list)); + InputStream resourceAsStreamFromModule = theClass.getModule().getResourceAsStream(path); if (Objects.isNull(resourceAsStreamFromModule)) { - return theClass.getClassLoader().getResourceAsStream(String.join("/", list)); + return theClass.getClassLoader().getResourceAsStream(path); } return resourceAsStreamFromModule; } - protected String getFileFromResource(String fileName, String... pathList) { + public static Path resolveFile(String path) { + return Path.of("src/test/resources", path); + } + + protected String getFileFromResource(String fileName, String path) { Path tmpFile; try { var tmpDir = Files.createTempDirectory("exhort_test_"); tmpFile = Files.createFile(tmpDir.resolve(fileName)); - try (var is = getResourceAsStreamDecision(this.getClass(), pathList)) { + try (var is = getResourceAsStreamDecision(this.getClass(), path)) { if (Objects.nonNull(is)) { Files.write(tmpFile, is.readAllBytes()); } else { - InputStream resourceIs = - getClass().getClassLoader().getResourceAsStream(String.join("/", pathList)); + InputStream resourceIs = getClass().getClassLoader().getResourceAsStream(path); Files.write(tmpFile, resourceIs.readAllBytes()); resourceIs.close(); } @@ -89,16 +90,15 @@ public AddPath(String fileName) { this.fileName = fileName; } - public TempDirFromResources fromResources(String... pathList) { + public TempDirFromResources fromResources(String path) { Path tmpFile; try { tmpFile = Files.createFile(tmpDir.resolve(this.fileName)); - try (var is = getResourceAsStreamDecision(super.getClass(), pathList)) { + try (var is = getResourceAsStreamDecision(super.getClass(), path)) { if (Objects.nonNull(is)) { Files.write(tmpFile, is.readAllBytes()); } else { - InputStream resourceIs = - getClass().getClassLoader().getResourceAsStream(String.join("/", pathList)); + InputStream resourceIs = getClass().getClassLoader().getResourceAsStream(path); Files.write(tmpFile, resourceIs.readAllBytes()); resourceIs.close(); } @@ -116,10 +116,9 @@ public AddPath addFile(String fileName) { return new AddPath(fileName); } - public TempDirFromResources addDirectory(String dirName, String... pathList) { + public TempDirFromResources addDirectory(String dirName, String path) { File target = this.tmpDir.resolve(dirName).toFile(); - String join = String.join("/", pathList); - URL resource = this.getClass().getClassLoader().getResource(join); + URL resource = this.getClass().getClassLoader().getResource(path); File source = new File(Objects.requireNonNull(resource).getFile()); try { FileUtils.copyDirectory(source, target); @@ -129,13 +128,12 @@ public TempDirFromResources addDirectory(String dirName, String... pathList) { return this; } - public TempDirFromResources addFile( - Optional fileName, Supplier> pathList) { + public TempDirFromResources addFile(Optional fileName, Supplier path) { if (fileName.isEmpty()) { return this; } - return new AddPath(fileName.get()).fromResources(pathList.get().toArray(new String[0])); + return new AddPath(fileName.get()).fromResources(path.get()); } public Path getTempDir() { diff --git a/src/test/java/com/redhat/exhort/image/ImageRefTest.java b/src/test/java/com/redhat/exhort/image/ImageRefTest.java index ba670fa6..d85d0313 100644 --- a/src/test/java/com/redhat/exhort/image/ImageRefTest.java +++ b/src/test/java/com/redhat/exhort/image/ImageRefTest.java @@ -67,7 +67,7 @@ void test_check_image_digest() throws IOException { try (MockedStatic mock = Mockito.mockStatic(Operations.class); var is = getResourceAsStreamDecision( - this.getClass(), new String[] {"msc", "image", "skopeo_inspect_multi_raw.json"})) { + this.getClass(), "msc/image/skopeo_inspect_multi_raw.json")) { var json = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)) .lines() diff --git a/src/test/java/com/redhat/exhort/image/ImageUtilsTest.java b/src/test/java/com/redhat/exhort/image/ImageUtilsTest.java index 708a8cdf..2ce13cd8 100644 --- a/src/test/java/com/redhat/exhort/image/ImageUtilsTest.java +++ b/src/test/java/com/redhat/exhort/image/ImageUtilsTest.java @@ -124,9 +124,7 @@ static Stream dockerVariantSources() { @ClearEnvironmentVariable(key = EXHORT_SYFT_IMAGE_SOURCE) void test_generate_image_sbom() throws IOException, MalformedPackageURLException { try (MockedStatic mock = Mockito.mockStatic(Operations.class); - var is = - getResourceAsStreamDecision( - this.getClass(), new String[] {"msc", "image", "image_sbom.json"})) { + var is = getResourceAsStreamDecision(this.getClass(), "msc/image/image_sbom.json")) { var json = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)) .lines() @@ -171,10 +169,9 @@ void test_get_image_digests_single() throws IOException { try (MockedStatic mock = Mockito.mockStatic(Operations.class); var isRaw = getResourceAsStreamDecision( - this.getClass(), new String[] {"msc", "image", "skopeo_inspect_single_raw.json"}); + this.getClass(), "msc/image/skopeo_inspect_single_raw.json"); var is = - getResourceAsStreamDecision( - this.getClass(), new String[] {"msc", "image", "skopeo_inspect_single.json"})) { + getResourceAsStreamDecision(this.getClass(), "msc/image/skopeo_inspect_single.json")) { var jsonRaw = new BufferedReader(new InputStreamReader(isRaw, StandardCharsets.UTF_8)) .lines() @@ -236,7 +233,7 @@ void test_get_image_digests_multiple() throws IOException { try (MockedStatic mock = Mockito.mockStatic(Operations.class); var is = getResourceAsStreamDecision( - this.getClass(), new String[] {"msc", "image", "skopeo_inspect_multi_raw.json"})) { + this.getClass(), "msc/image/skopeo_inspect_multi_raw.json")) { var json = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)) .lines() @@ -894,8 +891,7 @@ void test_exec_skopeo_inspect_no_config_no_daemon() { @Test void test_get_multi_image_digests() throws IOException { try (var is = - getResourceAsStreamDecision( - this.getClass(), new String[] {"msc", "image", "skopeo_inspect_multi_raw.json"})) { + getResourceAsStreamDecision(this.getClass(), "msc/image/skopeo_inspect_multi_raw.json")) { var mapper = new ObjectMapper(); var node = mapper.readTree(is); @@ -931,8 +927,7 @@ void test_get_multi_image_digests_empty() { @Test void test_filter_mediaType() throws IOException { try (var is = - getResourceAsStreamDecision( - this.getClass(), new String[] {"msc", "image", "skopeo_inspect_multi_raw.json"})) { + getResourceAsStreamDecision(this.getClass(), "msc/image/skopeo_inspect_multi_raw.json")) { var mapper = new ObjectMapper(); var node = mapper.readTree(is); @@ -943,8 +938,7 @@ void test_filter_mediaType() throws IOException { @Test void test_filter_digest() throws IOException { try (var is = - getResourceAsStreamDecision( - this.getClass(), new String[] {"msc", "image", "skopeo_inspect_multi_raw.json"})) { + getResourceAsStreamDecision(this.getClass(), "msc/image/skopeo_inspect_multi_raw.json")) { var mapper = new ObjectMapper(); var node = mapper.readTree(is); @@ -955,8 +949,7 @@ void test_filter_digest() throws IOException { @Test void test_filter_platform() throws IOException { try (var is = - getResourceAsStreamDecision( - this.getClass(), new String[] {"msc", "image", "skopeo_inspect_multi_raw.json"})) { + getResourceAsStreamDecision(this.getClass(), "msc/image/skopeo_inspect_multi_raw.json")) { var mapper = new ObjectMapper(); var node = mapper.readTree(is); @@ -968,8 +961,7 @@ void test_filter_platform() throws IOException { void test_get_single_image_digest() throws IOException { try (MockedStatic mock = Mockito.mockStatic(Operations.class); var is = - getResourceAsStreamDecision( - this.getClass(), new String[] {"msc", "image", "skopeo_inspect_single.json"})) { + getResourceAsStreamDecision(this.getClass(), "msc/image/skopeo_inspect_single.json")) { var json = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)) .lines() diff --git a/src/test/java/com/redhat/exhort/impl/ExhortApiIT.java b/src/test/java/com/redhat/exhort/impl/ExhortApiIT.java index b2d863f0..c6fc68b4 100644 --- a/src/test/java/com/redhat/exhort/impl/ExhortApiIT.java +++ b/src/test/java/com/redhat/exhort/impl/ExhortApiIT.java @@ -15,7 +15,12 @@ */ package com.redhat.exhort.impl; -import static org.junit.jupiter.api.Assertions.*; +import static com.redhat.exhort.Provider.PROP_MATCH_MANIFEST_VERSIONS; +import static com.redhat.exhort.utils.PythonControllerBase.PROP_EXHORT_PYTHON_INSTALL_BEST_EFFORTS; +import static com.redhat.exhort.utils.PythonControllerBase.PROP_EXHORT_PYTHON_VIRTUAL_ENV; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.AdditionalMatchers.aryEq; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -51,7 +56,6 @@ import java.util.concurrent.ExecutionException; import java.util.function.Function; import java.util.stream.Collectors; -import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -60,6 +64,9 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.MethodSource; +import org.junitpioneer.jupiter.RestoreEnvironmentVariables; +import org.junitpioneer.jupiter.RestoreSystemProperties; +import org.junitpioneer.jupiter.SetEnvironmentVariable; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; @@ -68,6 +75,9 @@ @Tag("IntegrationTest") @ExtendWith(HelperExtension.class) @ExtendWith(MockitoExtension.class) +@SetEnvironmentVariable(key = "RHDA_SOURCE", value = "exhort-java-api-it") +@SetEnvironmentVariable(key = "EXHORT_DEV_MODE", value = "false") +@RestoreEnvironmentVariables class ExhortApiIT extends ExhortTest { private static Api api; @@ -79,8 +89,6 @@ class ExhortApiIT extends ExhortTest { @BeforeAll static void beforeAll() { api = new ExhortApi(); - System.setProperty("RHDA_SOURCE", "exhort-java-api-it"); - System.setProperty("EXHORT_DEV_MODE", "false"); ecoSystemsManifestNames = Map.of( "golang", @@ -97,14 +105,6 @@ static void beforeAll() { new SimpleEntry<>("build.gradle.kts", Optional.empty())); } - @Tag("IntegrationTest") - @AfterAll - static void afterAll() { - System.clearProperty("RHDA_SOURCE"); - System.clearProperty("EXHORT_DEV_MODE"); - api = null; - } - private static List scenarios() { return ecoSystemsManifestNames.entrySet().stream() .map(e -> Arguments.of(e.getKey(), e.getValue().getKey(), e.getValue().getValue())) @@ -114,28 +114,28 @@ private static List scenarios() { @Tag("IntegrationTest") @ParameterizedTest(name = "StackAnalysis for: {0} with manifest: {1}") @MethodSource("scenarios") + @RestoreSystemProperties void Integration_Test_End_To_End_Stack_Analysis( String useCase, String manifestFileName, Optional lockFilename) throws IOException, ExecutionException, InterruptedException { Path manifestDirPath = new TempDirFromResources() .addFile(manifestFileName) - .fromResources("tst_manifests", "it", useCase, manifestFileName) + .fromResources(String.format("tst_manifests/it/%s/%s", useCase, manifestFileName)) .addFile( lockFilename, - () -> Arrays.asList("tst_manifests", "it", useCase, lockFilename.get())) + () -> String.format("tst_manifests/it/%s/%s", useCase, lockFilename.get())) .getTempDir(); Path manifestFileNamePath = manifestDirPath.resolve(manifestFileName); - var provider = Ecosystem.getProvider(manifestFileName, manifestFileNamePath); + var provider = Ecosystem.getProvider(manifestFileNamePath); var packageManager = provider.ecosystem; - String pathToManifest = manifestFileNamePath.toString(); preparePythonEnvironment(packageManager); // Github action runner with all maven and java versions seems to enter infinite loop in // integration tests of - // MAVEN when runnig dependency maven plugin to produce verbose text dependenct tree format. + // MAVEN when running dependency maven plugin to produce verbose text dependenct tree format. // locally it's not recreated with same versions mockMavenDependencyTree(packageManager); - AnalysisReport analysisReportResult = api.stackAnalysis(pathToManifest).get(); + AnalysisReport analysisReportResult = api.stackAnalysis(manifestFileNamePath.toString()).get(); handleJsonResponse(analysisReportResult, true); releaseStaticMock(packageManager); } @@ -149,20 +149,21 @@ private void releaseStaticMock(Ecosystem.Type packageManager) { @Tag("IntegrationTest") @ParameterizedTest(name = "StackAnalysis Mixed for: {0} with manifest: {1}") @MethodSource("scenarios") + @RestoreSystemProperties void Integration_Test_End_To_End_Stack_Analysis_Mixed( String useCase, String manifestFileName, Optional lockFilename) throws IOException, ExecutionException, InterruptedException { Path manifestDirPath = new TempDirFromResources() .addFile(manifestFileName) - .fromResources("tst_manifests", "it", useCase, manifestFileName) + .fromResources(String.format("tst_manifests/it/%s/%s", useCase, manifestFileName)) .addFile( lockFilename, - () -> Arrays.asList("tst_manifests", "it", useCase, lockFilename.get())) + () -> String.format("tst_manifests/it/%s/%s", useCase, lockFilename.get())) .getTempDir(); Path manifestFileNamePath = manifestDirPath.resolve(manifestFileName); String pathToManifest = manifestFileNamePath.toString(); - var provider = Ecosystem.getProvider(manifestFileName, Path.of(pathToManifest)); + var provider = Ecosystem.getProvider(Path.of(pathToManifest)); var packageManager = provider.ecosystem; preparePythonEnvironment(packageManager); // Github action runner with all maven and java versions seems to enter infinite loop in @@ -180,20 +181,21 @@ void Integration_Test_End_To_End_Stack_Analysis_Mixed( @Tag("IntegrationTest") @ParameterizedTest(name = "StackAnalysis HTML for: {0} with manifest: {1}") @MethodSource("scenarios") + @RestoreSystemProperties void Integration_Test_End_To_End_Stack_Analysis_Html( String useCase, String manifestFileName, Optional lockFilename) throws IOException, ExecutionException, InterruptedException { Path manifestDirPath = new TempDirFromResources() .addFile(manifestFileName) - .fromResources("tst_manifests", "it", useCase, manifestFileName) + .fromResources(String.format("tst_manifests/it/%s/%s", useCase, manifestFileName)) .addFile( lockFilename, - () -> Arrays.asList("tst_manifests", "it", useCase, lockFilename.get())) + () -> String.format("tst_manifests/it/%s/%s", useCase, lockFilename.get())) .getTempDir(); Path manifestFileNamePath = manifestDirPath.resolve(manifestFileName); String pathToManifest = manifestFileNamePath.toString(); - var provider = Ecosystem.getProvider(manifestFileName, Path.of(pathToManifest)); + var provider = Ecosystem.getProvider(Path.of(pathToManifest)); var packageManager = provider.ecosystem; preparePythonEnvironment(packageManager); // Github action runner with all maven and java versions seems to enter infinite loop in @@ -208,6 +210,7 @@ void Integration_Test_End_To_End_Stack_Analysis_Html( @Tag("IntegrationTest") @ParameterizedTest + @RestoreSystemProperties @EnumSource( value = Ecosystem.Type.class, names = {"GOLANG", "MAVEN", "NPM", "PYTHON"}) @@ -217,14 +220,16 @@ void Integration_Test_End_To_End_Component_Analysis(Ecosystem.Type packageManage Path tempDir = new TempDirFromResources() - .addDirectory(packageManager.getType(), "tst_manifests", "it", packageManager.getType()) + .addDirectory( + packageManager.getType(), + String.format("tst_manifests/it/%s", packageManager.getType())) .getTempDir(); Path manifestPath = tempDir.resolve(packageManager.getType()).resolve(manifestFileName); byte[] manifestContent = Files.readAllBytes(manifestPath); preparePythonEnvironment(packageManager); AnalysisReport analysisReportResult = - api.componentAnalysis(manifestFileName, manifestContent, manifestPath).get(); + api.componentAnalysis(manifestPath.toString(), manifestContent).get(); handleJsonResponse(analysisReportResult, false); } @@ -264,9 +269,7 @@ void Integration_Test_End_To_End_Image_Analysis_Html() throws IOException { private static T testImageAnalysis(Function, T> imageAnalysisFunction) throws IOException { try (MockedStatic mock = Mockito.mockStatic(Operations.class); - var sbomIS = - getResourceAsStreamDecision( - ExhortApiIT.class, new String[] {"msc", "image", "image_sbom.json"})) { + var sbomIS = getResourceAsStreamDecision(ExhortApiIT.class, "msc/image/image_sbom.json")) { var imageRef = new ImageRef( @@ -304,13 +307,9 @@ private static T testImageAnalysis(Function, T> imageAnalysisF private static void preparePythonEnvironment(Ecosystem.Type packageManager) { if (packageManager.equals(Ecosystem.Type.PYTHON)) { - System.setProperty("EXHORT_PYTHON_VIRTUAL_ENV", "true"); - System.setProperty("EXHORT_PYTHON_INSTALL_BEST_EFFORTS", "true"); - System.setProperty("MATCH_MANIFEST_VERSIONS", "false"); - } else { - System.clearProperty("EXHORT_PYTHON_VIRTUAL_ENV"); - System.clearProperty("EXHORT_PYTHON_INSTALL_BEST_EFFORTS"); - System.clearProperty("MATCH_MANIFEST_VERSIONS"); + System.setProperty(PROP_EXHORT_PYTHON_VIRTUAL_ENV, "true"); + System.setProperty(PROP_EXHORT_PYTHON_INSTALL_BEST_EFFORTS, "true"); + System.setProperty(PROP_MATCH_MANIFEST_VERSIONS, "false"); } } @@ -320,7 +319,8 @@ private static void handleJsonResponse( .forEach( provider -> { assertTrue(provider.getValue().getStatus().getOk()); - assertTrue(provider.getValue().getStatus().getCode() == HttpURLConnection.HTTP_OK); + assertThat(provider.getValue().getStatus().getCode()) + .isEqualTo(HttpURLConnection.HTTP_OK); }); analysisReportResult.getProviders().entrySet().stream() .map(Map.Entry::getValue) @@ -328,33 +328,31 @@ private static void handleJsonResponse( .map(Map::entrySet) .flatMap(Collection::stream) .map(Map.Entry::getValue) - .forEach(source -> assertTrue(source.getSummary().getTotal() > 0)); + .forEach(source -> assertThat(source.getSummary().getTotal()).isGreaterThan(0)); if (positiveNumberOfTransitives) { - assertTrue(analysisReportResult.getScanned().getTransitive() > 0); + assertThat(analysisReportResult.getScanned().getTransitive()).isGreaterThan(0); } else { - assertEquals(0, analysisReportResult.getScanned().getTransitive()); + assertThat(analysisReportResult.getScanned().getTransitive()).isZero(); } } private void handleHtmlResponse(String analysisReportHtml) throws JsonProcessingException { ObjectMapper om = new ObjectMapper(); - assertTrue(analysisReportHtml.contains("svg") && analysisReportHtml.contains("html")); + assertThat(analysisReportHtml).contains("svg", "html"); } private void handleHtmlResponseForImage(String analysisReportHtml) throws JsonProcessingException { ObjectMapper om = new ObjectMapper(); - assertTrue(analysisReportHtml.contains("svg") && analysisReportHtml.contains("html")); + assertThat(analysisReportHtml).contains("svg", "html"); } private void mockMavenDependencyTree(Ecosystem.Type packageManager) throws IOException { if (packageManager.equals(Ecosystem.Type.MAVEN)) { mockedOperations = mockStatic(Operations.class); String depTree; - try (var is = - getResourceAsStreamDecision( - getClass(), new String[] {"tst_manifests", "it", "maven", "depTree.txt"})) { + try (var is = getResourceAsStreamDecision(getClass(), "tst_manifests/it/maven/depTree.txt")) { depTree = new String(is.readAllBytes()); } mockedOperations diff --git a/src/test/java/com/redhat/exhort/impl/ExhortApiIT.java.orig b/src/test/java/com/redhat/exhort/impl/ExhortApiIT.java.orig deleted file mode 100644 index 97467047..00000000 --- a/src/test/java/com/redhat/exhort/impl/ExhortApiIT.java.orig +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright © 2023 Red Hat, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.redhat.exhort.impl; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.redhat.exhort.Api; -import com.redhat.exhort.ExhortTest; -import com.redhat.exhort.api.AnalysisReport; -import com.redhat.exhort.api.ProviderReport; -import com.redhat.exhort.providers.HelperExtension; -import com.redhat.exhort.tools.Ecosystem; -import com.redhat.exhort.tools.Operations; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.EnumSource; -import org.mockito.MockedStatic; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.junit.jupiter.MockitoExtension; - -import java.io.IOException; -import java.net.HttpURLConnection; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Arrays; -import java.util.Collection; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.ExecutionException; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mockStatic; - -@Tag("IntegrationTest") -@ExtendWith(HelperExtension.class) -@ExtendWith(MockitoExtension.class) -class ExhortApiIT extends ExhortTest { - - private static Api api; - private static Map ecoSystemsManifestNames; - - private MockedStatic mockedOperations; - @BeforeAll - static void beforeAll() { - api = new ExhortApi(); -<<<<<<< HEAD - System.setProperty("EXHORT_DEV_MODE","true"); -======= - System.setProperty("RHDA_SOURCE","exhort-java-api-it"); - System.setProperty("EXHORT_DEV_MODE","false"); ->>>>>>> java-enhanced-it-working - ecoSystemsManifestNames = Map.of("golang", "go.mod","maven","pom.xml","npm","package.json","pypi","requirements.txt"); - - } - - @Tag("IntegrationTest") - @AfterAll - static void afterAll() { -<<<<<<< HEAD -======= - System.clearProperty("RHDA_SOURCE"); ->>>>>>> java-enhanced-it-working - System.clearProperty("EXHORT_DEV_MODE"); - api = null; - } - @Tag("IntegrationTest") - @ParameterizedTest - @EnumSource(value = Ecosystem.Type.class, names = { "GOLANG", "MAVEN", "NPM", "PYTHON" }) - void Integration_Test_End_To_End_Stack_Analysis(Ecosystem.Type packageManager) throws IOException, ExecutionException, InterruptedException { - String manifestFileName = ecoSystemsManifestNames.get(packageManager.getType()); - String pathToManifest = getFileFromResource(manifestFileName, "tst_manifests", "it", packageManager.getType(), manifestFileName); - preparePythonEnvironment(packageManager); - // Github action runner with all maven and java versions seems to enter infinite loop in integration tests of MAVEN when runnig dependency maven plugin to produce verbose text dependenct tree format. - // locally it's not recreated with same versions - mockMavenDependencyTree(packageManager); - AnalysisReport analysisReportResult = api.stackAnalysis(pathToManifest).get(); - handleJsonResponse(analysisReportResult,true); - releaseStaticMock(packageManager); - } - - private void releaseStaticMock(Ecosystem.Type packageManager) { - if(packageManager.equals(Ecosystem.Type.MAVEN)) { - this.mockedOperations.close(); - } - } - - - @Tag("IntegrationTest") - @ParameterizedTest - @EnumSource(value = Ecosystem.Type.class, names = { "GOLANG", "MAVEN", "NPM", "PYTHON" }) - void Integration_Test_End_To_End_Stack_Analysis_Mixed(Ecosystem.Type packageManager) throws IOException, ExecutionException, InterruptedException { - String manifestFileName = ecoSystemsManifestNames.get(packageManager.getType()); - String pathToManifest = getFileFromResource(manifestFileName, "tst_manifests", "it", packageManager.getType(), manifestFileName); - preparePythonEnvironment(packageManager); - // Github action runner with all maven and java versions seems to enter infinite loop in integration tests of MAVEN when runnig dependency maven plugin to produce verbose text dependenct tree format. - // locally it's not recreated with same versions - mockMavenDependencyTree(packageManager); - AnalysisReport analysisReportJson = api.stackAnalysisMixed(pathToManifest).get().json; - String analysisReportHtml = new String(api.stackAnalysisMixed(pathToManifest).get().html); - handleJsonResponse(analysisReportJson,true); - handleHtmlResponse(analysisReportHtml); - releaseStaticMock(packageManager); - } - - @Tag("IntegrationTest") - @ParameterizedTest - @EnumSource(value = Ecosystem.Type.class, names = { "GOLANG", "MAVEN", "NPM", "PYTHON" }) - void Integration_Test_End_To_End_Stack_Analysis_Html(Ecosystem.Type packageManager) throws IOException, ExecutionException, InterruptedException { - String manifestFileName = ecoSystemsManifestNames.get(packageManager.getType()); - String pathToManifest = getFileFromResource(manifestFileName, "tst_manifests", "it", packageManager.getType(), manifestFileName); - preparePythonEnvironment(packageManager); - // Github action runner with all maven and java versions seems to enter infinite loop in integration tests of MAVEN when runnig dependency maven plugin to produce verbose text dependenct tree format. - // locally it's not recreated with same versions - mockMavenDependencyTree(packageManager); - String analysisReportHtml = new String(api.stackAnalysisHtml(pathToManifest).get()); - releaseStaticMock(packageManager); - handleHtmlResponse(analysisReportHtml); - } - - - @Tag("IntegrationTest") - @ParameterizedTest - @EnumSource(value = Ecosystem.Type.class, names = { "GOLANG", "MAVEN", "NPM", "PYTHON" }) - void Integration_Test_End_To_End_Component_Analysis(Ecosystem.Type packageManager) throws IOException, ExecutionException, InterruptedException { - String manifestFileName = ecoSystemsManifestNames.get(packageManager.getType()); - byte[] manifestContent = getStringFromFile("tst_manifests", "it", packageManager.getType(), manifestFileName).getBytes(); - preparePythonEnvironment(packageManager); - AnalysisReport analysisReportResult = api.componentAnalysis(manifestFileName,manifestContent).get(); - handleJsonResponse(analysisReportResult,false); - } - - - private static void preparePythonEnvironment(Ecosystem.Type packageManager) { - if(packageManager.equals(Ecosystem.Type.PYTHON)) { - System.setProperty("EXHORT_PYTHON_VIRTUAL_ENV","true"); - System.setProperty("EXHORT_PYTHON_INSTALL_BEST_EFFORTS","true"); - System.setProperty("MATCH_MANIFEST_VERSIONS","false"); - } - else { - System.clearProperty("EXHORT_PYTHON_VIRTUAL_ENV"); - System.clearProperty("EXHORT_PYTHON_INSTALL_BEST_EFFORTS"); - System.clearProperty("MATCH_MANIFEST_VERSIONS"); - } - } - - private static void handleJsonResponse(AnalysisReport analysisReportResult, boolean positiveNumberOfTransitives) { - analysisReportResult.getProviders().entrySet().stream().forEach(provider -> { assertTrue(provider.getValue().getStatus().getOk()); - assertTrue(provider.getValue().getStatus().getCode() == HttpURLConnection.HTTP_OK); - }); - analysisReportResult.getProviders().entrySet().stream() - .map(Map.Entry::getValue) - .map(ProviderReport::getSources) - .map(Map::entrySet) - .flatMap(Collection::stream) - .map(Map.Entry::getValue) - .forEach( source -> assertTrue(source.getSummary().getTotal() > 0 )); - - if(positiveNumberOfTransitives) { - assertTrue(analysisReportResult.getScanned().getTransitive() > 0); - } - else { - assertEquals(0,analysisReportResult.getScanned().getTransitive()); - } - } - - private void handleHtmlResponse(String analysisReportHtml) throws JsonProcessingException { - ObjectMapper om = new ObjectMapper(); - assertTrue(analysisReportHtml.contains("svg") && analysisReportHtml.contains("html")); - int jsonStart = analysisReportHtml.indexOf("\"report\":"); - int jsonEnd = analysisReportHtml.indexOf("}}}}}"); - String embeddedJson = analysisReportHtml.substring(jsonStart + 9 ,jsonEnd + 5); - JsonNode jsonInHtml = om.readTree(embeddedJson); - JsonNode scannedNode = jsonInHtml.get("scanned"); - assertTrue(scannedNode.get("total").asInt(0) > 0); - assertTrue(scannedNode.get("transitive").asInt(0) > 0); - JsonNode status = jsonInHtml.get("providers").get("snyk").get("status"); - assertTrue(status.get("code").asInt(0) == 200); - assertTrue(status.get("ok").asBoolean(false)); - - } - private void mockMavenDependencyTree(Ecosystem.Type packageManager) throws IOException { - if(packageManager.equals(Ecosystem.Type.MAVEN)) { - mockedOperations = mockStatic(Operations.class); - String depTree; - try (var is = getResourceAsStreamDecision(getClass(), new String [] { "tst_manifests", "it","maven", "depTree.txt"})) { - depTree = new String(is.readAllBytes()); - } - mockedOperations.when(() -> Operations.runProcess(any(),any())).thenAnswer(invocationOnMock -> { return getOutputFileAndOverwriteItWithMock(depTree, invocationOnMock, "-DoutputFile");}); - } - } - - public static String getOutputFileAndOverwriteItWithMock(String outputFileContent, InvocationOnMock invocationOnMock, String parameterPrefix) throws IOException { - String[] rawArguments = (String[]) invocationOnMock.getRawArguments()[0]; - Optional outputFileArg = Arrays.stream(rawArguments).filter(arg -> arg!= null && arg.startsWith(parameterPrefix)).findFirst(); - String outputFilePath=null; - if(outputFileArg.isPresent()) - { - String outputFile = outputFileArg.get(); - outputFilePath = outputFile.substring(outputFile.indexOf("=") + 1); - Files.writeString(Path.of(outputFilePath), outputFileContent); - } - return outputFilePath; - } - -} - - - diff --git a/src/test/java/com/redhat/exhort/impl/Exhort_Api_Test.java b/src/test/java/com/redhat/exhort/impl/Exhort_Api_Test.java index d812318e..a732b466 100644 --- a/src/test/java/com/redhat/exhort/impl/Exhort_Api_Test.java +++ b/src/test/java/com/redhat/exhort/impl/Exhort_Api_Test.java @@ -101,13 +101,12 @@ void stackAnalysisHtml_with_pom_xml_should_return_html_report_from_the_backend() // create a temporary pom.xml file var tmpFile = Files.createTempFile("exhort_test_pom_", ".xml"); try (var is = - getResourceAsStreamDecision( - this.getClass(), new String[] {"tst_manifests", "maven", "empty", "pom.xml"})) { + getResourceAsStreamDecision(this.getClass(), "tst_manifests/maven/empty/pom.xml")) { Files.write(tmpFile, is.readAllBytes()); } // stub the mocked provider with a fake content object - given(mockProvider.provideStack(tmpFile)) + given(mockProvider.provideStack()) .willReturn(new Provider.Content("fake-body-content".getBytes(), "fake-content-type")); // create an argument matcher to make sure we mock the response to for right request @@ -127,7 +126,7 @@ void stackAnalysisHtml_with_pom_xml_should_return_html_report_from_the_backend() byte[] expectedHtml; try (var is = getResourceAsStreamDecision( - this.getClass(), new String[] {"dummy_responses", "maven", "analysis-report.html"})) { + this.getClass(), "dummy_responses/maven/analysis-report.html")) { expectedHtml = is.readAllBytes(); } @@ -165,13 +164,12 @@ void stackAnalysis_with_pom_xml_should_return_json_object_from_the_backend() // create a temporary pom.xml file var tmpFile = Files.createTempFile("exhort_test_pom_", ".xml"); try (var is = - getResourceAsStreamDecision( - this.getClass(), new String[] {"tst_manifests", "maven", "empty", "pom.xml"})) { + getResourceAsStreamDecision(this.getClass(), "tst_manifests/maven/empty/pom.xml")) { Files.write(tmpFile, is.readAllBytes()); } // stub the mocked provider with a fake content object - given(mockProvider.provideStack(tmpFile)) + given(mockProvider.provideStack()) .willReturn(new Provider.Content("fake-body-content".getBytes(), "fake-content-type")); // we expect this to be ignored because tokens from env vars takes precedence @@ -197,7 +195,7 @@ void stackAnalysis_with_pom_xml_should_return_json_object_from_the_backend() AnalysisReport expectedAnalysis; try (var is = getResourceAsStreamDecision( - this.getClass(), new String[] {"dummy_responses", "maven", "analysis-report.json"})) { + this.getClass(), "dummy_responses/maven/analysis-report.json")) { expectedAnalysis = mapper.readValue(is, AnalysisReport.class); } @@ -231,12 +229,12 @@ void componentAnalysis_with_pom_xml_should_return_json_object_from_the_backend() Path tempDir = new TempDirFromResources() .addFile("pom.xml") - .fromResources(new String[] {"tst_manifests", "maven", "empty", "pom.xml"}) + .fromResources("tst_manifests/maven/empty/pom.xml") .getTempDir(); byte[] targetPom = Files.readAllBytes(tempDir.resolve("pom.xml")); // stub the mocked provider with a fake content object - given(mockProvider.provideComponent(targetPom)) + given(mockProvider.provideComponent()) .willReturn(new Provider.Content("fake-body-content".getBytes(), "fake-content-type")); // we expect this to picked up because no env var to take precedence @@ -263,7 +261,7 @@ void componentAnalysis_with_pom_xml_should_return_json_object_from_the_backend() AnalysisReport expectedReport; try (var is = getResourceAsStreamDecision( - this.getClass(), new String[] {"dummy_responses", "maven", "analysis-report.json"})) { + this.getClass(), "dummy_responses/maven/analysis-report.json")) { expectedReport = mapper.readValue(is, AnalysisReport.class); } @@ -275,14 +273,14 @@ void componentAnalysis_with_pom_xml_should_return_json_object_from_the_backend() // mock static getProvider utility function try (var ecosystemTool = mockStatic(Ecosystem.class)) { // stub static getProvider utility function to return our mock provider - ecosystemTool.when(() -> Ecosystem.getProvider("pom.xml", tempDir)).thenReturn(mockProvider); + ecosystemTool.when(() -> Ecosystem.getProvider(tempDir)).thenReturn(mockProvider); // stub the http client to return our mocked response when request matches our arg matcher given(mockHttpClient.sendAsync(argThat(matchesRequest), any())) .willReturn(CompletableFuture.completedFuture(mockHttpResponse)); // when invoking the api for a json stack analysis report - var responseAnalysis = exhortApiSut.componentAnalysis("pom.xml", targetPom, tempDir); + var responseAnalysis = exhortApiSut.componentAnalysis(tempDir.toString(), targetPom); // verify we got the correct analysis report then(responseAnalysis.get()).isEqualTo(expectedReport); } @@ -297,7 +295,7 @@ void componentAnalysis_with_pom_xml_should_return_json_object_from_the_backend() AnalysisReport expectedJson; try (var is = getResourceAsStreamDecision( - this.getClass(), new String[] {"dummy_responses", "maven", "analysis-report.json"})) { + this.getClass(), "dummy_responses/maven/analysis-report.json")) { expectedJson = mapper.readValue(is, AnalysisReport.class); } @@ -305,20 +303,19 @@ void componentAnalysis_with_pom_xml_should_return_json_object_from_the_backend() byte[] expectedHtml; try (var is = getResourceAsStreamDecision( - this.getClass(), new String[] {"dummy_responses", "maven", "analysis-report.html"})) { + this.getClass(), "dummy_responses/maven/analysis-report.html")) { expectedHtml = is.readAllBytes(); } // create a temporary pom.xml file var tmpFile = Files.createTempFile("exhort_test_pom_", ".xml"); try (var is = - getResourceAsStreamDecision( - this.getClass(), new String[] {"tst_manifests", "maven", "empty", "pom.xml"})) { + getResourceAsStreamDecision(this.getClass(), "tst_manifests/maven/empty/pom.xml")) { Files.write(tmpFile, is.readAllBytes()); } // stub the mocked provider with a fake content object - given(mockProvider.provideStack(tmpFile)) + given(mockProvider.provideStack()) .willReturn(new Provider.Content("fake-body-content".getBytes(), "fake-content-type")); // create an argument matcher to make sure we mock the response for the right request @@ -332,7 +329,7 @@ void componentAnalysis_with_pom_xml_should_return_json_object_from_the_backend() byte[] mixedResponse; try (var is = getResourceAsStreamDecision( - this.getClass(), new String[] {"dummy_responses", "maven", "analysis-report.mixed"})) { + this.getClass(), "dummy_responses/maven/analysis-report.mixed")) { mixedResponse = is.readAllBytes(); } @@ -366,13 +363,12 @@ void componentAnalysis_with_pom_xml_as_path_should_return_json_object_from_the_b // load pom.xml var tmpFile = Files.createTempFile("exhort_test_pom_", ".xml"); try (var is = - getResourceAsStreamDecision( - this.getClass(), new String[] {"tst_manifests", "maven", "empty", "pom.xml"})) { + getResourceAsStreamDecision(this.getClass(), "tst_manifests/maven/empty/pom.xml")) { Files.write(tmpFile, is.readAllBytes()); } // stub the mocked provider with a fake content object - given(mockProvider.provideComponent(tmpFile)) + given(mockProvider.provideComponent()) .willReturn(new Provider.Content("fake-body-content".getBytes(), "fake-content-type")); // we expect this to picked up because no env var to take precedence @@ -394,7 +390,7 @@ void componentAnalysis_with_pom_xml_as_path_should_return_json_object_from_the_b AnalysisReport expectedReport; try (var is = getResourceAsStreamDecision( - this.getClass(), new String[] {"dummy_responses", "maven", "analysis-report.json"})) { + this.getClass(), "dummy_responses/maven/analysis-report.json")) { expectedReport = mapper.readValue(is, AnalysisReport.class); } @@ -539,12 +535,9 @@ void check_Exhort_Url_When_Nothing_Set_Then_Default_Exhort_URL_Selected() { void test_image_analysis() throws IOException, ExecutionException, InterruptedException, MalformedPackageURLException { try (MockedStatic mock = Mockito.mockStatic(Operations.class); - var sbomIS = - getResourceAsStreamDecision( - this.getClass(), new String[] {"msc", "image", "image_sbom.json"}); + var sbomIS = getResourceAsStreamDecision(this.getClass(), "msc/image/image_sbom.json"); var reportIS = - getResourceAsStreamDecision( - this.getClass(), new String[] {"msc", "image", "image_reports.json"})) { + getResourceAsStreamDecision(this.getClass(), "msc/image/image_reports.json")) { var imageRef = new ImageRef( @@ -639,12 +632,9 @@ void test_image_analysis() void imageAnalysisHtml() throws IOException, ExecutionException, InterruptedException, MalformedPackageURLException { try (MockedStatic mock = Mockito.mockStatic(Operations.class); - var sbomIS = - getResourceAsStreamDecision( - this.getClass(), new String[] {"msc", "image", "image_sbom.json"}); + var sbomIS = getResourceAsStreamDecision(this.getClass(), "msc/image/image_sbom.json"); var reportIS = - getResourceAsStreamDecision( - this.getClass(), new String[] {"msc", "image", "image_reports.json"})) { + getResourceAsStreamDecision(this.getClass(), "msc/image/image_reports.json")) { var imageRef = new ImageRef( @@ -717,9 +707,7 @@ void imageAnalysisHtml() @SetEnvironmentVariable(key = "RHDA_SOURCE", value = "rhda-source-from-env-var") void test_perform_batch_analysis() throws IOException, MalformedPackageURLException, ExecutionException, InterruptedException { - try (var is = - getResourceAsStreamDecision( - this.getClass(), new String[] {"msc", "image", "image_sbom.json"})) { + try (var is = getResourceAsStreamDecision(this.getClass(), "msc/image/image_sbom.json")) { var sbomsGenerator = mock(Supplier.class); var responseBodyHandler = mock(HttpResponse.BodyHandler.class); var responseGenerator = mock(Function.class); @@ -777,9 +765,7 @@ void test_perform_batch_analysis() @Test void test_get_batch_image_sboms() throws IOException, MalformedPackageURLException { try (MockedStatic mock = Mockito.mockStatic(Operations.class); - var is = - getResourceAsStreamDecision( - this.getClass(), new String[] {"msc", "image", "image_sbom.json"})) { + var is = getResourceAsStreamDecision(this.getClass(), "msc/image/image_sbom.json")) { var imageRef = new ImageRef( "test.io/test/test-app:test-version@sha256:1fafb0905264413501df60d90a92ca32df8a2011cbfb4876ddff5ceb20c8f165", @@ -826,9 +812,7 @@ void test_get_batch_image_sboms() throws IOException, MalformedPackageURLExcepti @Test void test_get_batch_image_analysis_reports() throws IOException, MalformedPackageURLException { - try (var is = - getResourceAsStreamDecision( - this.getClass(), new String[] {"msc", "image", "image_reports.json"})) { + try (var is = getResourceAsStreamDecision(this.getClass(), "msc/image/image_reports.json")) { var json = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)) .lines() diff --git a/src/test/java/com/redhat/exhort/impl/Exhort_Api_Test.java.orig b/src/test/java/com/redhat/exhort/impl/Exhort_Api_Test.java.orig deleted file mode 100644 index 8b3c1527..00000000 --- a/src/test/java/com/redhat/exhort/impl/Exhort_Api_Test.java.orig +++ /dev/null @@ -1,517 +0,0 @@ -/* - * Copyright © 2023 Red Hat, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.redhat.exhort.impl; - -import static com.redhat.exhort.ExhortTest.getResourceAsStreamDecision; -import static org.assertj.core.api.BDDAssertions.then; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.mockStatic; - -import java.io.IOException; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; -import java.nio.file.Files; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; - -import com.redhat.exhort.ExhortTest; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junitpioneer.jupiter.ClearEnvironmentVariable; -import org.junitpioneer.jupiter.SetEnvironmentVariable; -import org.mockito.ArgumentMatcher; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.redhat.exhort.Provider; -import com.redhat.exhort.api.AnalysisReport; -import com.redhat.exhort.tools.Ecosystem; - -@ExtendWith(MockitoExtension.class) -@ClearEnvironmentVariable(key="EXHORT_SNYK_TOKEN") -@ClearEnvironmentVariable(key="EXHORT_DEV_MODE") -@ClearEnvironmentVariable(key="DEV_EXHORT_BACKEND_URL") -@ClearEnvironmentVariable(key="RHDA_TOKEN") -@ClearEnvironmentVariable(key="RHDA_SOURCE") -@SuppressWarnings("unchecked") -class Exhort_Api_Test extends ExhortTest { - @Mock - Provider mockProvider; - @Mock - HttpClient mockHttpClient; - @InjectMocks - ExhortApi exhortApiSut; - - @AfterEach - void cleanup() { - System.clearProperty("EXHORT_SNYK_TOKEN"); - } - - @Test - @SetEnvironmentVariable(key="EXHORT_SNYK_TOKEN", value="snyk-token-from-env-var") - @SetEnvironmentVariable(key="RHDA_TOKEN", value="rhda-token-from-env-var") - @SetEnvironmentVariable(key="RHDA_SOURCE", value="rhda-source-from-env-var") - void stackAnalysisHtml_with_pom_xml_should_return_html_report_from_the_backend() - throws IOException, ExecutionException, InterruptedException { - // create a temporary pom.xml file - var tmpFile = Files.createTempFile("exhort_test_pom_", ".xml"); -<<<<<<< HEAD - try (var is = getClass().getModule().getResourceAsStream("tst_manifests/maven/empty/pom.xml")) { -======= - try (var is = getResourceAsStreamDecision(this.getClass(), new String [] {"tst_manifests","maven","empty","pom.xml"})) { ->>>>>>> 73f7443 (test: fix and tailor tests also for new java versions) - Files.write(tmpFile, is.readAllBytes()); - } - - // stub the mocked provider with a fake content object - given(mockProvider.provideStack(tmpFile)) - .willReturn(new Provider.Content("fake-body-content".getBytes(), "fake-content-type")); - - // create an argument matcher to make sure we mock the response to for right request - ArgumentMatcher matchesRequest = r -> - r.headers().firstValue("Content-Type").get().equals("fake-content-type") && - r.headers().firstValue("Accept").get().equals("text/html") && - // snyk token is set using the environment variable (annotation) - r.headers().firstValue("ex-snyk-token").get().equals("snyk-token-from-env-var") && - r.headers().firstValue("rhda-token").get().equals("rhda-token-from-env-var") && - r.headers().firstValue("rhda-source").get().equals("rhda-source-from-env-var") && - r.headers().firstValue("rhda-operation-type").get().equals("Stack Analysis") && - - r.method().equals("POST"); - - // load dummy html and set as the expected analysis - byte[] expectedHtml; -<<<<<<< HEAD - try (var is = getClass().getModule().getResourceAsStream("dummy_responses/maven/analysis-report.html")) { -======= - try (var is = getResourceAsStreamDecision(this.getClass(), new String [] {"dummy_responses","maven","analysis-report.html"})) { ->>>>>>> 73f7443 (test: fix and tailor tests also for new java versions) - expectedHtml = is.readAllBytes(); - } - - // mock and http response object and stub it to return a fake body - var mockHttpResponse = mock(HttpResponse.class); - given(mockHttpResponse.body()).willReturn(expectedHtml); - given(mockHttpResponse.statusCode()).willReturn(200); - - // mock static getProvider utility function - try(var ecosystemTool = mockStatic(Ecosystem.class)) { - // stub static getProvider utility function to return our mock provider - ecosystemTool.when(() -> Ecosystem.getProvider(tmpFile)).thenReturn(mockProvider); - - // stub the http client to return our mocked response when request matches our arg matcher - given(mockHttpClient.sendAsync(argThat(matchesRequest), any())) - .willReturn(CompletableFuture.completedFuture(mockHttpResponse)); - - // when invoking the api for a html stack analysis report - var htmlTxt = exhortApiSut.stackAnalysisHtml(tmpFile.toString()); - // verify we got the correct html response - then(htmlTxt.get()).isEqualTo(expectedHtml); - } - // cleanup - Files.deleteIfExists(tmpFile); - } - - @Test -// System.setProperty("RHDA_TOKEN", "rhda-token-from-property"); -// System.setProperty("RHDA_SOURCE", "rhda-source-from-property"); - @SetEnvironmentVariable(key="EXHORT_SNYK_TOKEN", value="snyk-token-from-env-var") - @SetEnvironmentVariable(key="RHDA_TOKEN", value="rhda-token-from-env-var") - @SetEnvironmentVariable(key="RHDA_SOURCE", value="rhda-source-from-env-var") - void stackAnalysis_with_pom_xml_should_return_json_object_from_the_backend() - throws IOException, ExecutionException, InterruptedException { - // create a temporary pom.xml file - var tmpFile = Files.createTempFile("exhort_test_pom_", ".xml"); -<<<<<<< HEAD - try (var is = getClass().getModule().getResourceAsStream("tst_manifests/maven/empty/pom.xml")) { -======= - try (var is = getResourceAsStreamDecision(this.getClass(), new String [] {"tst_manifests","maven","empty","pom.xml"})) { ->>>>>>> 73f7443 (test: fix and tailor tests also for new java versions) - Files.write(tmpFile, is.readAllBytes()); - } - - // stub the mocked provider with a fake content object - given(mockProvider.provideStack(tmpFile)) - .willReturn(new Provider.Content("fake-body-content".getBytes(), "fake-content-type")); - - // we expect this to be ignored because tokens from env vars takes precedence - System.setProperty("EXHORT_SNYK_TOKEN", "snyk-token-from-property"); - - // create an argument matcher to make sure we mock the response for the right request - ArgumentMatcher matchesRequest = r -> - r.headers().firstValue("Content-Type").get().equals("fake-content-type") && - r.headers().firstValue("Accept").get().equals("application/json") && - // snyk token is set using the environment variable (annotation) - ignored the one set in properties - r.headers().firstValue("ex-snyk-token").get().equals("snyk-token-from-env-var") && - r.headers().firstValue("rhda-token").get().equals("rhda-token-from-env-var") && - r.headers().firstValue("rhda-source").get().equals("rhda-source-from-env-var") && - r.headers().firstValue("rhda-operation-type").get().equals("Stack Analysis") && - r.method().equals("POST"); - - // load dummy json and set as the expected analysis - var mapper = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); - AnalysisReport expectedAnalysis; -<<<<<<< HEAD - try (var is = getClass().getModule().getResourceAsStream("dummy_responses/maven/analysis-report.json")) { -======= - try (var is = getResourceAsStreamDecision(this.getClass(), new String [] {"dummy_responses","maven","analysis-report.json"})) { ->>>>>>> 73f7443 (test: fix and tailor tests also for new java versions) - expectedAnalysis = mapper.readValue(is, AnalysisReport.class); - } - - // mock and http response object and stub it to return the expected analysis - var mockHttpResponse = mock(HttpResponse.class); - given(mockHttpResponse.body()).willReturn(mapper.writeValueAsString(expectedAnalysis)); - given(mockHttpResponse.statusCode()).willReturn(200); - - // mock static getProvider utility function - try(var ecosystemTool = mockStatic(Ecosystem.class)) { - // stub static getProvider utility function to return our mock provider - ecosystemTool.when(() -> Ecosystem.getProvider(tmpFile)).thenReturn(mockProvider); - - // stub the http client to return our mocked response when request matches our arg matcher - given(mockHttpClient.sendAsync(argThat(matchesRequest), any())) - .willReturn(CompletableFuture.completedFuture(mockHttpResponse)); - - // when invoking the api for a json stack analysis report - var responseAnalysis = exhortApiSut.stackAnalysis(tmpFile.toString()); - // verify we got the correct analysis report - then(responseAnalysis.get()).isEqualTo(expectedAnalysis); - } - // cleanup - Files.deleteIfExists(tmpFile); - } - - @Test - void componentAnalysis_with_pom_xml_should_return_json_object_from_the_backend() - throws IOException, ExecutionException, InterruptedException { - // load pom.xml - byte[] targetPom; -<<<<<<< HEAD - try (var is = getClass().getModule().getResourceAsStream("tst_manifests/maven/empty/pom.xml")) { -======= - try (var is = getResourceAsStreamDecision(this.getClass(), new String [] {"tst_manifests","maven","empty","pom.xml"})) { ->>>>>>> 73f7443 (test: fix and tailor tests also for new java versions) - targetPom = is.readAllBytes(); - } - - // stub the mocked provider with a fake content object - given(mockProvider.provideComponent(targetPom)) - .willReturn(new Provider.Content("fake-body-content".getBytes(), "fake-content-type")); - - // we expect this to picked up because no env var to take precedence - System.setProperty("EXHORT_SNYK_TOKEN", "snyk-token-from-property"); - System.setProperty("RHDA_TOKEN", "rhda-token-from-property"); - System.setProperty("RHDA_SOURCE", "rhda-source-from-property"); - - // create an argument matcher to make sure we mock the response for the right request - ArgumentMatcher matchesRequest = r -> - r.headers().firstValue("Content-Type").get().equals("fake-content-type") && - r.headers().firstValue("Accept").get().equals("application/json") && - // snyk token is set using properties which is picked up because no env var specified - r.headers().firstValue("ex-snyk-token").get().equals("snyk-token-from-property") && - r.headers().firstValue("rhda-token").get().equals("rhda-token-from-property") && - r.headers().firstValue("rhda-source").get().equals("rhda-source-from-property") && - r.headers().firstValue("rhda-operation-type").get().equals("Component Analysis") && - r.method().equals("POST"); - - // load dummy json and set as the expected analysis - var mapper = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); - AnalysisReport expectedReport; -<<<<<<< HEAD - try (var is = getClass().getModule().getResourceAsStream("dummy_responses/maven/analysis-report.json")) { -======= - try (var is = getResourceAsStreamDecision(this.getClass(), new String [] {"dummy_responses","maven","analysis-report.json"})) { ->>>>>>> 73f7443 (test: fix and tailor tests also for new java versions) - expectedReport = mapper.readValue(is, AnalysisReport.class); - } - - // mock and http response object and stub it to return the expected analysis - var mockHttpResponse = mock(HttpResponse.class); - given(mockHttpResponse.body()).willReturn(mapper.writeValueAsString(expectedReport)); - given(mockHttpResponse.statusCode()).willReturn(200); - - // mock static getProvider utility function - try (var ecosystemTool = mockStatic(Ecosystem.class)) { - // stub static getProvider utility function to return our mock provider - ecosystemTool.when(() -> Ecosystem.getProvider("pom.xml")).thenReturn(mockProvider); - - // stub the http client to return our mocked response when request matches our arg matcher - given(mockHttpClient.sendAsync(argThat(matchesRequest), any())) - .willReturn(CompletableFuture.completedFuture(mockHttpResponse)); - - // when invoking the api for a json stack analysis report - var responseAnalysis = exhortApiSut.componentAnalysis("pom.xml", targetPom); - // verify we got the correct analysis report - then(responseAnalysis.get()).isEqualTo(expectedReport); - } - } - - @Test - void stackAnalysisMixed_with_pom_xml_should_return_both_html_text_and_json_object_from_the_backend() - throws IOException, ExecutionException, InterruptedException { - // load dummy json and set as the expected analysis - var mapper = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); - AnalysisReport expectedJson; -<<<<<<< HEAD - try (var is = getClass().getModule().getResourceAsStream("dummy_responses/maven/analysis-report.json")) { -======= - try (var is = getResourceAsStreamDecision(this.getClass(), new String [] {"dummy_responses","maven","analysis-report.json"})) { ->>>>>>> 73f7443 (test: fix and tailor tests also for new java versions) - expectedJson = mapper.readValue(is, AnalysisReport.class); - } - - // load dummy html and set as the expected analysis - byte[] expectedHtml; -<<<<<<< HEAD - try (var is = getClass().getModule().getResourceAsStream("dummy_responses/maven/analysis-report.html")) { -======= - try (var is = getResourceAsStreamDecision(this.getClass(), new String [] {"dummy_responses","maven","analysis-report.html"})) { ->>>>>>> 73f7443 (test: fix and tailor tests also for new java versions) - expectedHtml = is.readAllBytes(); - } - - // create a temporary pom.xml file - var tmpFile = Files.createTempFile("exhort_test_pom_", ".xml"); -<<<<<<< HEAD - try (var is = getClass().getModule().getResourceAsStream("tst_manifests/maven/empty/pom.xml")) { -======= - try (var is = getResourceAsStreamDecision(this.getClass(), new String [] {"tst_manifests","maven","empty","pom.xml"})) { ->>>>>>> 73f7443 (test: fix and tailor tests also for new java versions) - Files.write(tmpFile, is.readAllBytes()); - } - - // stub the mocked provider with a fake content object - given(mockProvider.provideStack(tmpFile)) - .willReturn(new Provider.Content("fake-body-content".getBytes(), "fake-content-type")); - - // create an argument matcher to make sure we mock the response for the right request - ArgumentMatcher matchesRequest = r -> - r.headers().firstValue("Content-Type").get().equals("fake-content-type") && - r.headers().firstValue("Accept").get().equals("multipart/mixed") && - r.method().equals("POST"); - - // load dummy mixed and set as the expected analysis - byte[] mixedResponse; -<<<<<<< HEAD - try (var is = getClass().getModule().getResourceAsStream("dummy_responses/maven/analysis-report.mixed")) { -======= - try (var is = getResourceAsStreamDecision(this.getClass(), new String [] {"dummy_responses","maven","analysis-report.mixed"})) { ->>>>>>> 73f7443 (test: fix and tailor tests also for new java versions) - mixedResponse = is.readAllBytes(); - } - - // mock and http response object and stub it to return the expected analysis - var mockHttpResponse = mock(HttpResponse.class); - given(mockHttpResponse.body()).willReturn(mixedResponse); - given(mockHttpResponse.statusCode()).willReturn(200); - - // mock static getProvider utility function - try(var ecosystemTool = mockStatic(Ecosystem.class)) { - // stub static getProvider utility function to return our mock provider - ecosystemTool.when(() -> Ecosystem.getProvider(tmpFile)).thenReturn(mockProvider); - - // stub the http client to return our mocked response when request matches our arg matcher - given(mockHttpClient.sendAsync(argThat(matchesRequest), any())) - .willReturn(CompletableFuture.completedFuture(mockHttpResponse)); - - // when invoking the api for a json stack analysis mixed report - var responseAnalysis = exhortApiSut.stackAnalysisMixed(tmpFile.toString()).get(); - // verify we got the correct mixed report - then(new String(responseAnalysis.html).trim()).isEqualTo(new String(expectedHtml).trim()); - then(responseAnalysis.json).isEqualTo(expectedJson); - } - // cleanup - Files.deleteIfExists(tmpFile); - } - - @Test - void componentAnalysis_with_pom_xml_as_path_should_return_json_object_from_the_backend() - throws IOException, ExecutionException, InterruptedException { - // load pom.xml - var tmpFile = Files.createTempFile("exhort_test_pom_", ".xml"); -<<<<<<< HEAD - try (var is = getClass().getModule().getResourceAsStream("tst_manifests/maven/empty/pom.xml")) { -======= - try (var is = getResourceAsStreamDecision(this.getClass(), new String [] {"tst_manifests","maven","empty","pom.xml"})) { ->>>>>>> 73f7443 (test: fix and tailor tests also for new java versions) - Files.write(tmpFile, is.readAllBytes()); - } - - // stub the mocked provider with a fake content object - given(mockProvider.provideComponent(tmpFile)) - .willReturn(new Provider.Content("fake-body-content".getBytes(), "fake-content-type")); - - // we expect this to picked up because no env var to take precedence - System.setProperty("EXHORT_SNYK_TOKEN", "snyk-token-from-property"); - - // create an argument matcher to make sure we mock the response for the right request - ArgumentMatcher matchesRequest = r -> - r.headers().firstValue("Content-Type").get().equals("fake-content-type") && - r.headers().firstValue("Accept").get().equals("application/json") && - // snyk token is set using properties which is picked up because no env var specified - r.headers().firstValue("ex-snyk-token").get().equals("snyk-token-from-property") && - r.method().equals("POST"); - - // load dummy json and set as the expected analysis - var mapper = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); - AnalysisReport expectedReport; -<<<<<<< HEAD - try (var is = getClass().getModule().getResourceAsStream("dummy_responses/maven/analysis-report.json")) { -======= - try (var is = getResourceAsStreamDecision(this.getClass(), new String [] {"dummy_responses","maven","analysis-report.json"})) { ->>>>>>> 73f7443 (test: fix and tailor tests also for new java versions) - expectedReport = mapper.readValue(is, AnalysisReport.class); - } - - // mock and http response object and stub it to return the expected analysis - var mockHttpResponse = mock(HttpResponse.class); - given(mockHttpResponse.body()).willReturn(mapper.writeValueAsString(expectedReport)); - given(mockHttpResponse.statusCode()).willReturn(200); - - // mock static getProvider utility function - try (var ecosystemTool = mockStatic(Ecosystem.class)) { - // stub static getProvider utility function to return our mock provider - ecosystemTool.when(() -> Ecosystem.getProvider(tmpFile)).thenReturn(mockProvider); - - // stub the http client to return our mocked response when request matches our arg matcher - given(mockHttpClient.sendAsync(argThat(matchesRequest), any())) - .willReturn(CompletableFuture.completedFuture(mockHttpResponse)); - - // when invoking the api for a json stack analysis report - var responseAnalysis = exhortApiSut.componentAnalysis(tmpFile.toString()); - // verify we got the correct analysis report - then(responseAnalysis.get()).isEqualTo(expectedReport); - //cleanup - Files.deleteIfExists(tmpFile); - } - } - - - @AfterEach - void afterEach() { - System.clearProperty("EXHORT_DEV_MODE"); - System.clearProperty("DEV_EXHORT_BACKEND_URL"); - System.clearProperty("RHDA_TOKEN"); - System.clearProperty("RHDA_SOURCE"); - - } - - @Test - @SetEnvironmentVariable(key="EXHORT_DEV_MODE", value="true") - @ClearEnvironmentVariable(key="DEV_EXHORT_BACKEND_URL") - void check_Exhort_Url_When_DEV_Mode_true_Both() { - System.setProperty("EXHORT_DEV_MODE","true"); - ExhortApi exhortApi = new ExhortApi(); - then(exhortApi.getEndpoint()).isEqualTo(ExhortApi.DEFAULT_ENDPOINT_DEV); - then(exhortApi.getEndpoint()).isNotEqualTo(ExhortApi.DEFAULT_ENDPOINT); - } -@Test - @SetEnvironmentVariable(key="EXHORT_DEV_MODE", value="true") - @ClearEnvironmentVariable(key="DEV_EXHORT_BACKEND_URL") - void check_Exhort_Url_When_env_DEV_Mode_true_property_DEV_Mode_false() { - System.setProperty("EXHORT_DEV_MODE","false"); - ExhortApi exhortApi = new ExhortApi(); - then(exhortApi.getEndpoint()).isEqualTo(ExhortApi.DEFAULT_ENDPOINT_DEV); - then(exhortApi.getEndpoint()).isNotEqualTo(ExhortApi.DEFAULT_ENDPOINT); - } - -@Test - @SetEnvironmentVariable(key="EXHORT_DEV_MODE", value="true") - @ClearEnvironmentVariable(key="DEV_EXHORT_BACKEND_URL") - void check_Exhort_Url_When_env_DEV_Mode_true_And_DEV_Exhort_Url_Set_Then_Default_DEV_Exhort_URL_Not_Selected() { - String dummyUrl = "http://dummy-url"; - System.setProperty("DEV_EXHORT_BACKEND_URL", dummyUrl); - ExhortApi exhortApi = new ExhortApi(); - then(exhortApi.getEndpoint()).isEqualTo(dummyUrl); - then(exhortApi.getEndpoint()).isNotEqualTo(ExhortApi.DEFAULT_ENDPOINT_DEV); - } - -@Test - @SetEnvironmentVariable(key="EXHORT_DEV_MODE", value="false") - @ClearEnvironmentVariable(key="DEV_EXHORT_BACKEND_URL") -void check_Exhort_Url_When_env_DEV_Mode_false_And_DEV_Exhort_Url_Set_Then_Default_DEV_Exhort_URL_Not_Selected() { - System.setProperty("EXHORT_DEV_MODE", "false"); - ExhortApi exhortApi = new ExhortApi(); - then(exhortApi.getEndpoint()).isEqualTo(ExhortApi.DEFAULT_ENDPOINT); - then(exhortApi.getEndpoint()).isNotEqualTo(ExhortApi.DEFAULT_ENDPOINT_DEV); - } - - - @Test - @SetEnvironmentVariable(key="EXHORT_DEV_MODE", value= "false") - void check_Exhort_Url_When_env_DEV_Mode_false_And_Property_Dev_Mode_true_Default_Exhort_URL_Selected() { - System.setProperty("EXHORT_DEV_MODE", "true"); - ExhortApi exhortApi = new ExhortApi(); - then(exhortApi.getEndpoint()).isEqualTo(ExhortApi.DEFAULT_ENDPOINT); - then(exhortApi.getEndpoint()).isNotEqualTo(ExhortApi.DEFAULT_ENDPOINT_DEV); - } - - @Test - @SetEnvironmentVariable(key="EXHORT_DEV_MODE", value="false") - @SetEnvironmentVariable(key="DEV_EXHORT_BACKEND_URL", value="http://dummy-route") - void check_Exhort_Url_When_env_DEV_Mode_false_And_DEV_Exhort_Url_Set_Then_Default_Exhort_URL_Selected_Anyway() { - System.setProperty("EXHORT_DEV_MODE", "true"); - System.setProperty("DEV_EXHORT_BACKEND_URL","http://dummy-route2"); - ExhortApi exhortApi = new ExhortApi(); - then(exhortApi.getEndpoint()).isEqualTo(ExhortApi.DEFAULT_ENDPOINT); - then(exhortApi.getEndpoint()).isNotEqualTo(System.getenv("DEV_EXHORT_BACKEND_URL")); - then(exhortApi.getEndpoint()).isNotEqualTo(System.getProperty("DEV_EXHORT_BACKEND_URL")); - - } - @Test - void check_Exhort_Url_When_env_DEV_Mode_not_set_And_Property_Exhort_Dev_Mode_false_Then_Default_Exhort_URL_Selected() { - System.setProperty("EXHORT_DEV_MODE", "false"); - ExhortApi exhortApi = new ExhortApi(); - then(exhortApi.getEndpoint()).isEqualTo(ExhortApi.DEFAULT_ENDPOINT); - then(exhortApi.getEndpoint()).isNotEqualTo(ExhortApi.DEFAULT_ENDPOINT_DEV); - } - @Test - void check_Exhort_Url_When_env_DEV_Mode_not_set_And_Property_Exhort_Dev_Mode_true_Then_Default_DEV_Exhort_URL_Selected() { - System.setProperty("EXHORT_DEV_MODE", "true"); - ExhortApi exhortApi = new ExhortApi(); - then(exhortApi.getEndpoint()).isNotEqualTo(ExhortApi.DEFAULT_ENDPOINT); - then(exhortApi.getEndpoint()).isEqualTo(ExhortApi.DEFAULT_ENDPOINT_DEV); - } - @Test - @SetEnvironmentVariable(key="DEV_EXHORT_BACKEND_URL", value="http://dummy-route") - void check_Exhort_Url_When_env_DEV_Mode_not_set_And_Property_Exhort_Dev_Mode_true_and_Env_DEV_Exhort_Backend_Url_Set_Then_DEV_ENV_Exhort_URL_Selected() { - System.setProperty("EXHORT_DEV_MODE", "true"); - System.setProperty("DEV_EXHORT_BACKEND_URL", "http://dummy-route2"); - ExhortApi exhortApi = new ExhortApi(); - then(exhortApi.getEndpoint()).isNotEqualTo(ExhortApi.DEFAULT_ENDPOINT); - then(exhortApi.getEndpoint()).isNotEqualTo(ExhortApi.DEFAULT_ENDPOINT_DEV); - then(exhortApi.getEndpoint()).isNotEqualTo("http://dummy-route2"); - then(exhortApi.getEndpoint()).isEqualTo("http://dummy-route"); - } - - @Test - void check_Exhort_Url_When_Nothing_Set_Then_Default_Exhort_URL_Selected() { - ExhortApi exhortApi = new ExhortApi(); - then(exhortApi.getEndpoint()).isEqualTo(ExhortApi.DEFAULT_ENDPOINT); - - } - -} diff --git a/src/test/java/com/redhat/exhort/providers/GoModulesMainModuleVersionTest.java b/src/test/java/com/redhat/exhort/providers/GoModulesMainModuleVersionTest.java index 31833a0b..34556cc8 100644 --- a/src/test/java/com/redhat/exhort/providers/GoModulesMainModuleVersionTest.java +++ b/src/test/java/com/redhat/exhort/providers/GoModulesMainModuleVersionTest.java @@ -35,13 +35,9 @@ class GoModulesMainModuleVersionTest { @BeforeEach void setUp() { try { - this.goModulesProvider = new GoModulesProvider(); + this.goModulesProvider = new GoModulesProvider(null); this.testGitRepo = Files.createTempDirectory("exhort_tmp"); - Operations.runProcessGetOutput(this.testGitRepo, "git", "init"); - Operations.runProcessGetOutput( - this.testGitRepo, "git", "config", "user.email", "tester@exhort-java-api.com"); - Operations.runProcessGetOutput( - this.testGitRepo, "git", "config", "user.name", "exhort-java-api-tester"); + gitInit(); this.noGitRepo = Files.createTempDirectory("exhort_tmp"); } catch (IOException e) { throw new RuntimeException(e); @@ -62,21 +58,19 @@ void tearDown() { @Test void determine_Main_Module_Version_NoRepo() { goModulesProvider.determineMainModuleVersion(noGitRepo); - assertEquals(goModulesProvider.defaultMainVersion, goModulesProvider.getMainModuleVersion()); + assertEquals(GoModulesProvider.DEFAULT_MAIN_VERSION, goModulesProvider.getMainModuleVersion()); } @Test void determine_Main_Module_Version_GitRepo() { goModulesProvider.determineMainModuleVersion(testGitRepo); - assertEquals(goModulesProvider.defaultMainVersion, goModulesProvider.getMainModuleVersion()); + assertEquals(GoModulesProvider.DEFAULT_MAIN_VERSION, goModulesProvider.getMainModuleVersion()); } @Test void determine_Main_Module_Version_GitRepo_commit_is_tag() { - - Operations.runProcessGetOutput( - this.testGitRepo, "git", "commit", "-m \"sample\"", "--allow-empty"); - Operations.runProcessGetOutput(this.testGitRepo, "git", "tag", "v1.0.0"); + gitCommit("sample"); + gitTag("v1.0.0", "sample tag"); goModulesProvider.determineMainModuleVersion(testGitRepo); assertEquals("v1.0.0", goModulesProvider.getMainModuleVersion()); @@ -84,11 +78,8 @@ void determine_Main_Module_Version_GitRepo_commit_is_tag() { @Test void determine_Main_Module_Version_GitRepo_commit_is_annotated_tag() { - - Operations.runProcessGetOutput( - this.testGitRepo, "git", "commit", "-m \"sample\"", "--allow-empty"); - Operations.runProcessGetOutput( - this.testGitRepo, "git", "tag", "-a", "-m", "annotatedTag", "v1.0.0a"); + gitCommit("sample"); + gitTag("v1.0.0a", "annotatedTag"); goModulesProvider.determineMainModuleVersion(testGitRepo); assertEquals("v1.0.0a", goModulesProvider.getMainModuleVersion()); @@ -97,15 +88,32 @@ void determine_Main_Module_Version_GitRepo_commit_is_annotated_tag() { @Test void determine_Main_Module_Version_GitRepo_commit_is_after_tag() { - Operations.runProcessGetOutput( - this.testGitRepo, "git", "commit", "-m \"sample\"", "--allow-empty"); - Operations.runProcessGetOutput(this.testGitRepo, "git", "tag", "v1.0.0"); - Operations.runProcessGetOutput( - this.testGitRepo, "git", "commit", "-m \"sample2\"", "--allow-empty"); + gitCommit("sample"); + gitTag("v1.0.0", "sample tag"); + gitCommit("sample-2"); goModulesProvider.determineMainModuleVersion(testGitRepo); assertTrue( Pattern.matches( "v1.0.1-0.[0-9]{14}-[a-f0-9]{12}", goModulesProvider.getMainModuleVersion())); } + + private void gitInit() { + Operations.runProcessGetOutput(testGitRepo, "git", "-c", getConfigParam(), "init"); + } + + private void gitCommit(String message) { + Operations.runProcessGetOutput( + testGitRepo, "git", "-c", getConfigParam(), "commit", "-m", message, "--allow-empty"); + } + + private void gitTag(String tag, String message) { + Operations.runProcessGetOutput( + testGitRepo, "git", "-c", getConfigParam(), "tag", "-a", tag, "-m", message); + } + + private String getConfigParam() { + String absPath = Path.of("src/test/resources/git_config").toAbsolutePath().toString(); + return "include.path=" + absPath; + } } diff --git a/src/test/java/com/redhat/exhort/providers/Golang_Modules_Provider_Test.java b/src/test/java/com/redhat/exhort/providers/Golang_Modules_Provider_Test.java index 8142736b..9beb49cf 100644 --- a/src/test/java/com/redhat/exhort/providers/Golang_Modules_Provider_Test.java +++ b/src/test/java/com/redhat/exhort/providers/Golang_Modules_Provider_Test.java @@ -15,10 +15,10 @@ */ package com.redhat.exhort.providers; +import static com.redhat.exhort.Provider.PROP_MATCH_MANIFEST_VERSIONS; import static org.assertj.core.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*; -import com.fasterxml.jackson.databind.ObjectMapper; import com.redhat.exhort.Api; import com.redhat.exhort.ExhortTest; import java.io.IOException; @@ -56,7 +56,7 @@ void test_the_provideStack(String testFolder) throws IOException, InterruptedExc var tmpGolangFile = Files.createFile(tmpGoModulesDir.resolve("go.mod")); try (var is = getResourceAsStreamDecision( - this.getClass(), new String[] {"tst_manifests", "golang", testFolder, "go.mod"})) { + this.getClass(), String.format("tst_manifests/golang/%s/go.mod", testFolder))) { Files.write(tmpGolangFile, is.readAllBytes()); } // load expected SBOM @@ -64,13 +64,12 @@ void test_the_provideStack(String testFolder) throws IOException, InterruptedExc try (var is = getResourceAsStreamDecision( this.getClass(), - new String[] { - "tst_manifests", "golang", testFolder, "expected_sbom_stack_analysis.json" - })) { + String.format( + "tst_manifests/golang/%s/expected_sbom_stack_analysis.json", testFolder))) { expectedSbom = new String(is.readAllBytes()); } // when providing stack content for our pom - var content = new GoModulesProvider().provideStack(tmpGolangFile); + var content = new GoModulesProvider(tmpGolangFile).provideStack(); // cleanup Files.deleteIfExists(tmpGolangFile); // verify expected SBOM is returned @@ -81,25 +80,27 @@ void test_the_provideStack(String testFolder) throws IOException, InterruptedExc @ParameterizedTest @MethodSource("testFolders") void test_the_provideComponent(String testFolder) throws IOException, InterruptedException { - // load the pom target pom file - byte[] targetPom; + // create temp file hosting our sut package.json + var tmpGoModulesDir = Files.createTempDirectory("exhort_test_"); + var tmpGolangFile = Files.createFile(tmpGoModulesDir.resolve("go.mod")); try (var is = getResourceAsStreamDecision( - this.getClass(), new String[] {"tst_manifests", "golang", testFolder, "go.mod"})) { - targetPom = is.readAllBytes(); + this.getClass(), String.format("tst_manifests/golang/%s/go.mod", testFolder))) { + Files.write(tmpGolangFile, is.readAllBytes()); } // load expected SBOM String expectedSbom = ""; try (var is = getResourceAsStreamDecision( this.getClass(), - new String[] { - "tst_manifests", "golang", testFolder, "expected_sbom_component_analysis.json" - })) { + String.format( + "tst_manifests/golang/%s/expected_sbom_component_analysis.json", testFolder))) { expectedSbom = new String(is.readAllBytes()); } // when providing component content for our pom - var content = new GoModulesProvider().provideComponent(targetPom); + var content = new GoModulesProvider(tmpGolangFile).provideComponent(); + // cleanup + Files.deleteIfExists(tmpGolangFile); // verify expected SBOM is returned assertThat(content.type).isEqualTo(Api.CYCLONEDX_MEDIA_TYPE); assertThat(dropIgnored(new String(content.buffer))).isEqualTo(dropIgnored(expectedSbom)); @@ -108,25 +109,22 @@ void test_the_provideComponent(String testFolder) throws IOException, Interrupte @Test void Test_The_ProvideComponent_Path_Should_Throw_Exception() { - GoModulesProvider goModulesProvider = new GoModulesProvider(); + GoModulesProvider goModulesProvider = new GoModulesProvider(Path.of(".")); assertThatIllegalArgumentException() .isThrownBy( () -> { - goModulesProvider.provideComponent(Path.of(".")); - }) - .withMessage( - "provideComponent with file system path for GoModules package manager not implemented" - + " yet"); + goModulesProvider.provideComponent(); + }); } @ParameterizedTest @ValueSource(booleans = {true, false}) void Test_Golang_Modules_with_Match_Manifest_Version(boolean MatchManifestVersionsEnabled) { - String goModPath = getFileFromResource("go.mod", "msc", "golang", "go.mod"); - GoModulesProvider goModulesProvider = new GoModulesProvider(); + String goModPath = getFileFromResource("go.mod", "msc/golang/go.mod"); + GoModulesProvider goModulesProvider = new GoModulesProvider(Path.of(goModPath)); if (MatchManifestVersionsEnabled) { - System.setProperty("MATCH_MANIFEST_VERSIONS", "true"); + System.setProperty(PROP_MATCH_MANIFEST_VERSIONS, "true"); RuntimeException runtimeException = assertThrows( RuntimeException.class, @@ -140,7 +138,7 @@ void Test_Golang_Modules_with_Match_Manifest_Version(boolean MatchManifestVersio "Can't continue with analysis - versions mismatch for dependency" + " name=github.com/google/uuid, manifest version=v1.1.0, installed" + " Version=v1.1.1")); - System.clearProperty("MATCH_MANIFEST_VERSIONS"); + System.clearProperty(PROP_MATCH_MANIFEST_VERSIONS); } else { String sbomString = assertDoesNotThrow( @@ -150,8 +148,7 @@ void Test_Golang_Modules_with_Match_Manifest_Version(boolean MatchManifestVersio .getAsJsonString()); String actualSbomWithTSStripped = dropIgnoredKeepFormat(sbomString); assertEquals( - getStringFromFile("msc", "golang", "expected_sbom_ca.json").trim(), - actualSbomWithTSStripped); + getStringFromFile("msc/golang/expected_sbom_ca.json").trim(), actualSbomWithTSStripped); System.out.println(sbomString); } @@ -159,15 +156,14 @@ void Test_Golang_Modules_with_Match_Manifest_Version(boolean MatchManifestVersio @Test void Test_Golang_MvS_Logic_Enabled() throws IOException { - ObjectMapper om = new ObjectMapper(); - System.setProperty("EXHORT_GO_MVS_LOGIC_ENABLED", "true"); - String goModPath = getFileFromResource("go.mod", "msc", "golang", "mvs_logic", "go.mod"); - GoModulesProvider goModulesProvider = new GoModulesProvider(); + System.setProperty(GoModulesProvider.PROP_EXHORT_GO_MVS_LOGIC_ENABLED, "true"); + String goModPath = getFileFromResource("go.mod", "msc/golang/mvs_logic/go.mod"); + GoModulesProvider goModulesProvider = new GoModulesProvider(Path.of(goModPath)); String resultSbom = dropIgnoredKeepFormat( goModulesProvider.getDependenciesSbom(Path.of(goModPath), true).getAsJsonString()); String expectedSbom = - getStringFromFile("msc", "golang", "mvs_logic", "expected_sbom_stack_analysis.json").trim(); + getStringFromFile("msc/golang/mvs_logic/expected_sbom_stack_analysis.json").trim(); assertEquals(expectedSbom, resultSbom); @@ -179,7 +175,7 @@ void Test_Golang_MvS_Logic_Enabled() throws IOException { .count() == 1); - System.clearProperty("EXHORT_GO_MVS_LOGIC_ENABLED"); + System.clearProperty(GoModulesProvider.PROP_EXHORT_GO_MVS_LOGIC_ENABLED); resultSbom = dropIgnoredKeepFormat( diff --git a/src/test/java/com/redhat/exhort/providers/Gradle_Provider_Test.java b/src/test/java/com/redhat/exhort/providers/Gradle_Provider_Test.java index 7a093098..2a06e3b8 100644 --- a/src/test/java/com/redhat/exhort/providers/Gradle_Provider_Test.java +++ b/src/test/java/com/redhat/exhort/providers/Gradle_Provider_Test.java @@ -16,7 +16,6 @@ package com.redhat.exhort.providers; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.mockStatic; @@ -62,7 +61,6 @@ void test_the_provideStack(String testFolder) throws IOException, InterruptedExc // create temp file hosting our sut build.gradle var tmpGradleDir = Files.createTempDirectory("exhort_test_"); var tmpGradleFile = Files.createFile(tmpGradleDir.resolve(getManifestName())); - // log.log(System.Logger.Level.INFO,"the test folder is : " + testFolder); try (var is = getClass() .getClassLoader() @@ -71,7 +69,7 @@ void test_the_provideStack(String testFolder) throws IOException, InterruptedExc "/", "tst_manifests", getProviderFolder(), testFolder, getManifestName()))) { Files.write(tmpGradleFile, is.readAllBytes()); } - var settingsFile = Files.createFile(tmpGradleDir.resolve("settings.gradle")); + var settingsFile = Files.createFile(tmpGradleDir.resolve(getSettingsName())); try (var is = getClass() .getClassLoader() @@ -128,68 +126,41 @@ void test_the_provideStack(String testFolder) throws IOException, InterruptedExc gradleProperties = new String(is.readAllBytes()); } - MockedStatic mockedOperations = mockStatic(Operations.class); ArgumentMatcher gradle = string -> string.equals("gradle"); ArgumentMatcher dependencies = string -> string.equals("dependencies"); ArgumentMatcher properties = string -> string.equals("properties"); - mockedOperations.when(() -> Operations.getCustomPathOrElse("gradle")).thenReturn("gradle"); - mockedOperations - .when( - () -> - Operations.runProcessGetOutput( - any(Path.class), argThat(gradle), argThat(dependencies))) - .thenReturn(depTree); - mockedOperations - .when( - () -> - Operations.runProcessGetOutput( - any(Path.class), argThat(gradle), argThat(properties))) - .thenReturn(gradleProperties); + try (MockedStatic mockedOperations = mockStatic(Operations.class)) { + mockedOperations.when(() -> Operations.getCustomPathOrElse("gradle")).thenReturn("gradle"); + mockedOperations + .when( + () -> + Operations.runProcessGetOutput( + any(Path.class), argThat(gradle), argThat(dependencies))) + .thenReturn(depTree); + mockedOperations + .when( + () -> + Operations.runProcessGetOutput( + any(Path.class), argThat(gradle), argThat(properties))) + .thenReturn(gradleProperties); - // when providing stack content for our pom - var content = new GradleProvider().provideStack(tmpGradleFile); - // cleanup - Files.deleteIfExists(tmpGradleFile); - // verify expected SBOM is returned - mockedOperations.close(); - assertThat(content.type).isEqualTo(Api.CYCLONEDX_MEDIA_TYPE); - assertThat(dropIgnored(new String(content.buffer))).isEqualTo(dropIgnored(expectedSbom)); - } + // when providing stack content for our pom + var content = new GradleProvider(tmpGradleFile).provideStack(); + // cleanup + Files.deleteIfExists(tmpGradleFile); - @ParameterizedTest - @MethodSource("testFolders") - void test_the_provideComponent(String testFolder) throws IOException, InterruptedException { - // load the pom target pom file - byte[] targetGradleBuild; - try (var is = - getClass() - .getClassLoader() - .getResourceAsStream( - String.join( - "/", "tst_manifests", getProviderFolder(), testFolder, getManifestName()))) { - targetGradleBuild = is.readAllBytes(); + // verify expected SBOM is returned + assertThat(content.type).isEqualTo(Api.CYCLONEDX_MEDIA_TYPE); + assertThat(dropIgnored(new String(content.buffer))).isEqualTo(dropIgnored(expectedSbom)); } - - GradleProvider gradleProvider = new GradleProvider(); - assertThatIllegalArgumentException() - .isThrownBy( - () -> { - gradleProvider.provideComponent(targetGradleBuild); - }) - .withMessage( - "Gradle Package Manager requires the full package directory, not just the manifest" - + " content, to generate the dependency tree. Please provide the complete package" - + " directory path."); } @ParameterizedTest @MethodSource("testFolders") - void test_the_provideComponent_With_Path(String testFolder) - throws IOException, InterruptedException { + void test_the_provideComponent(String testFolder) throws IOException, InterruptedException { // create temp file hosting our sut build.gradle var tmpGradleDir = Files.createTempDirectory("exhort_test_"); var tmpGradleFile = Files.createFile(tmpGradleDir.resolve(getManifestName())); - // log.log(System.Logger.Level.INFO,"the test folder is : " + testFolder); try (var is = getClass() .getClassLoader() @@ -255,32 +226,32 @@ void test_the_provideComponent_With_Path(String testFolder) gradleProperties = new String(is.readAllBytes()); } - MockedStatic mockedOperations = mockStatic(Operations.class); - ArgumentMatcher gradle = string -> string.equals("gradle"); - ArgumentMatcher dependencies = string -> string.equals("dependencies"); - ArgumentMatcher properties = string -> string.equals("properties"); - mockedOperations.when(() -> Operations.getCustomPathOrElse("gradle")).thenReturn("gradle"); - mockedOperations - .when( - () -> - Operations.runProcessGetOutput( - any(Path.class), argThat(gradle), argThat(dependencies))) - .thenReturn(depTree); - mockedOperations - .when( - () -> - Operations.runProcessGetOutput( - any(Path.class), argThat(gradle), argThat(properties))) - .thenReturn(gradleProperties); + try (MockedStatic mockedOperations = mockStatic(Operations.class)) { + ArgumentMatcher gradle = string -> string.equals("gradle"); + ArgumentMatcher dependencies = string -> string.equals("dependencies"); + ArgumentMatcher properties = string -> string.equals("properties"); + mockedOperations.when(() -> Operations.getCustomPathOrElse("gradle")).thenReturn("gradle"); + mockedOperations + .when( + () -> + Operations.runProcessGetOutput( + any(Path.class), argThat(gradle), argThat(dependencies))) + .thenReturn(depTree); + mockedOperations + .when( + () -> + Operations.runProcessGetOutput( + any(Path.class), argThat(gradle), argThat(properties))) + .thenReturn(gradleProperties); - // when providing component content for our pom - var content = new GradleProvider().provideComponent(tmpGradleFile); - // cleanup - Files.deleteIfExists(tmpGradleFile); - // verify expected SBOM is returned - mockedOperations.close(); - assertThat(content.type).isEqualTo(Api.CYCLONEDX_MEDIA_TYPE); - assertThat(dropIgnored(new String(content.buffer))).isEqualTo(dropIgnored(expectedSbom)); + // when providing component content for our pom + var content = new GradleProvider(tmpGradleFile).provideComponent(); + // cleanup + Files.deleteIfExists(tmpGradleFile); + // verify expected SBOM is returned + assertThat(content.type).isEqualTo(Api.CYCLONEDX_MEDIA_TYPE); + assertThat(dropIgnored(new String(content.buffer))).isEqualTo(dropIgnored(expectedSbom)); + } } private String dropIgnored(String s) { diff --git a/src/test/java/com/redhat/exhort/providers/HelperExtension.java b/src/test/java/com/redhat/exhort/providers/HelperExtension.java index 5464dbe8..6632c9b3 100644 --- a/src/test/java/com/redhat/exhort/providers/HelperExtension.java +++ b/src/test/java/com/redhat/exhort/providers/HelperExtension.java @@ -15,29 +15,28 @@ */ package com.redhat.exhort.providers; -import java.util.List; -import org.junit.jupiter.api.extension.*; +import com.redhat.exhort.impl.ExhortApi; +import com.redhat.exhort.logging.LoggersFactory; +import java.util.logging.Logger; +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; public class HelperExtension implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback { - private System.Logger log = System.getLogger(this.getClass().getName()); - - // public PythonEnvironmentExtension(List requirementsFiles) { - // this.requirementsFiles = requirementsFiles; - // } - - private List requirementsFiles; + private static final Logger LOG = LoggersFactory.getLogger(ExhortApi.class.getName()); @Override public void afterAll(ExtensionContext extensionContext) throws Exception { - log.log(System.Logger.Level.INFO, "Finished all tests!!"); + LOG.info("Finished all tests!!"); } @Override public void afterEach(ExtensionContext extensionContext) throws Exception { - log.log( - System.Logger.Level.INFO, + LOG.info( String.format( "Finished Test Method: %s_%s", extensionContext.getRequiredTestMethod().getName(), extensionContext.getDisplayName())); @@ -46,13 +45,12 @@ public void afterEach(ExtensionContext extensionContext) throws Exception { @Override public void beforeAll(ExtensionContext extensionContext) throws Exception { - log.log(System.Logger.Level.INFO, "Before all tests"); + LOG.info("Before all tests"); } @Override public void beforeEach(ExtensionContext extensionContext) throws Exception { - log.log( - System.Logger.Level.INFO, + LOG.info( String.format( "Started Test Method: %s_%s", extensionContext.getRequiredTestMethod().getName(), extensionContext.getDisplayName())); diff --git a/src/test/java/com/redhat/exhort/providers/Java_Envs_Test.java b/src/test/java/com/redhat/exhort/providers/Java_Envs_Test.java index cb38b864..e857f137 100644 --- a/src/test/java/com/redhat/exhort/providers/Java_Envs_Test.java +++ b/src/test/java/com/redhat/exhort/providers/Java_Envs_Test.java @@ -28,21 +28,21 @@ public class Java_Envs_Test { @Test @SetEnvironmentVariable(key = "JAVA_HOME", value = "test-java-home") void test_java_get_envs() { - var envs = new JavaMavenProvider().getMvnExecEnvs(); + var envs = new JavaMavenProvider(null).getMvnExecEnvs(); assertEquals(Collections.singletonMap("JAVA_HOME", "test-java-home"), envs); } @Test @SetEnvironmentVariable(key = "JAVA_HOME", value = "") void test_java_get_envs_empty_java_home() { - var envs = new JavaMavenProvider().getMvnExecEnvs(); + var envs = new JavaMavenProvider(null).getMvnExecEnvs(); assertNull(envs); } @Test @ClearEnvironmentVariable(key = "JAVA_HOME") void test_java_get_envs_no_java_home() { - var envs = new JavaMavenProvider().getMvnExecEnvs(); + var envs = new JavaMavenProvider(null).getMvnExecEnvs(); assertNull(envs); } } diff --git a/src/test/java/com/redhat/exhort/providers/Java_Maven_Provider_Test.java b/src/test/java/com/redhat/exhort/providers/Java_Maven_Provider_Test.java index 3e1615fa..8daeb4d7 100644 --- a/src/test/java/com/redhat/exhort/providers/Java_Maven_Provider_Test.java +++ b/src/test/java/com/redhat/exhort/providers/Java_Maven_Provider_Test.java @@ -62,10 +62,9 @@ static Stream testFolders() { void test_the_provideStack(String testFolder) throws IOException, InterruptedException { // create temp file hosting our sut pom.xml var tmpPomFile = Files.createTempFile("exhort_test_", ".xml"); - // log.log(System.Logger.Level.INFO,"the test folder is : " + testFolder); try (var is = getResourceAsStreamDecision( - getClass(), new String[] {"tst_manifests", "maven", testFolder, "pom.xml"})) { + getClass(), String.format("tst_manifests/maven/%s/pom.xml", testFolder))) { Files.write(tmpPomFile, is.readAllBytes()); } // load expected SBOM @@ -73,32 +72,32 @@ void test_the_provideStack(String testFolder) throws IOException, InterruptedExc try (var is = getResourceAsStreamDecision( getClass(), - new String[] {"tst_manifests", "maven", testFolder, "expected_stack_sbom.json"})) { + String.format("tst_manifests/maven/%s/expected_stack_sbom.json", testFolder))) { expectedSbom = new String(is.readAllBytes()); } String depTree; try (var is = getResourceAsStreamDecision( - getClass(), new String[] {"tst_manifests", "maven", testFolder, "depTree.txt"})) { + getClass(), String.format("tst_manifests/maven/%s/depTree.txt", testFolder))) { depTree = new String(is.readAllBytes()); } - - MockedStatic mockedOperations = mockStatic(Operations.class); - mockedOperations - .when(() -> Operations.runProcess(any(), any())) - .thenAnswer( - invocationOnMock -> { - return getOutputFileAndOverwriteItWithMock(depTree, invocationOnMock, "-DoutputFile"); - }); - - // when providing stack content for our pom - var content = new JavaMavenProvider().provideStack(tmpPomFile); - // cleanup - Files.deleteIfExists(tmpPomFile); - // verify expected SBOM is returned - mockedOperations.close(); - assertThat(content.type).isEqualTo(Api.CYCLONEDX_MEDIA_TYPE); - assertThat(dropIgnored(new String(content.buffer))).isEqualTo(dropIgnored(expectedSbom)); + try (MockedStatic mockedOperations = mockStatic(Operations.class)) { + mockedOperations + .when(() -> Operations.runProcess(any(), any())) + .thenAnswer( + invocationOnMock -> { + return getOutputFileAndOverwriteItWithMock( + depTree, invocationOnMock, "-DoutputFile"); + }); + + // when providing stack content for our pom + var content = new JavaMavenProvider(tmpPomFile).provideStack(); + // cleanup + Files.deleteIfExists(tmpPomFile); + // verify expected SBOM is returned + assertThat(content.type).isEqualTo(Api.CYCLONEDX_MEDIA_TYPE); + assertThat(dropIgnored(new String(content.buffer))).isEqualTo(dropIgnored(expectedSbom)); + } } public static String getOutputFileAndOverwriteItWithMock( @@ -122,43 +121,38 @@ public static String getOutputFileAndOverwriteItWithMock( @MethodSource("testFolders") void test_the_provideComponent(String testFolder) throws IOException, InterruptedException { // load the pom target pom file - byte[] targetPom; - try (var is = - getResourceAsStreamDecision( - getClass(), new String[] {"tst_manifests", "maven", testFolder, "pom.xml"})) { - targetPom = is.readAllBytes(); - } + var targetPom = resolveFile(String.format("tst_manifests/maven/%s/pom.xml", testFolder)); + // load expected SBOM String expectedSbom = ""; try (var is = getResourceAsStreamDecision( getClass(), - new String[] {"tst_manifests", "maven", testFolder, "expected_component_sbom.json"})) { + String.format("tst_manifests/maven/%s/expected_component_sbom.json", testFolder))) { expectedSbom = new String(is.readAllBytes()); } String effectivePom; try (var is = getResourceAsStreamDecision( - getClass(), new String[] {"tst_manifests", "maven", testFolder, "effectivePom.xml"})) { + getClass(), String.format("tst_manifests/maven/%s/effectivePom.xml", testFolder))) { effectivePom = new String(is.readAllBytes()); } - - MockedStatic mockedOperations = mockStatic(Operations.class); - mockedOperations - .when(() -> Operations.runProcess(any(), any())) - .thenAnswer( - invocationOnMock -> { - return getOutputFileAndOverwriteItWithMock( - effectivePom, invocationOnMock, "-Doutput"); - }); - - // when providing component content for our pom - var content = new JavaMavenProvider().provideComponent(targetPom); - mockedOperations.close(); - // verify expected SBOM is returned - assertThat(content.type).isEqualTo(Api.CYCLONEDX_MEDIA_TYPE); - assertThat(dropIgnored(new String(content.buffer))).isEqualTo(dropIgnored(expectedSbom)); + try (MockedStatic mockedOperations = mockStatic(Operations.class)) { + mockedOperations + .when(() -> Operations.runProcess(any(), any())) + .thenAnswer( + invocationOnMock -> { + return getOutputFileAndOverwriteItWithMock( + effectivePom, invocationOnMock, "-Doutput"); + }); + + // when providing component content for our pom + var content = new JavaMavenProvider(targetPom).provideComponent(); + // verify expected SBOM is returned + assertThat(content.type).isEqualTo(Api.CYCLONEDX_MEDIA_TYPE); + assertThat(dropIgnored(new String(content.buffer))).isEqualTo(dropIgnored(expectedSbom)); + } } @ParameterizedTest @@ -170,7 +164,7 @@ void test_the_provideComponent_With_Path(String testFolder) var tmpPomFile = Files.createTempFile("exhort_test_", ".xml"); try (var is = getResourceAsStreamDecision( - getClass(), new String[] {"tst_manifests", "maven", testFolder, "pom.xml"})) { + getClass(), String.format("tst_manifests/maven/%s/pom.xml", testFolder))) { Files.write(tmpPomFile, is.readAllBytes()); } // load expected SBOM @@ -178,32 +172,31 @@ void test_the_provideComponent_With_Path(String testFolder) try (var is = getResourceAsStreamDecision( getClass(), - new String[] {"tst_manifests", "maven", testFolder, "expected_component_sbom.json"})) { + String.format("tst_manifests/maven/%s/expected_component_sbom.json", testFolder))) { expectedSbom = new String(is.readAllBytes()); } String effectivePom; try (var is = getResourceAsStreamDecision( - getClass(), new String[] {"tst_manifests", "maven", testFolder, "effectivePom.xml"})) { + getClass(), String.format("tst_manifests/maven/%s/effectivePom.xml", testFolder))) { effectivePom = new String(is.readAllBytes()); } - - MockedStatic mockedOperations = mockStatic(Operations.class); - mockedOperations - .when(() -> Operations.runProcess(any(), any())) - .thenAnswer( - invocationOnMock -> { - return getOutputFileAndOverwriteItWithMock( - effectivePom, invocationOnMock, "-Doutput"); - }); - - // when providing component content for our pom - var content = new JavaMavenProvider().provideComponent(tmpPomFile); - // verify expected SBOM is returned - mockedOperations.close(); - assertThat(content.type).isEqualTo(Api.CYCLONEDX_MEDIA_TYPE); - assertThat(dropIgnored(new String(content.buffer))).isEqualTo(dropIgnored(expectedSbom)); + try (MockedStatic mockedOperations = mockStatic(Operations.class)) { + mockedOperations + .when(() -> Operations.runProcess(any(), any())) + .thenAnswer( + invocationOnMock -> { + return getOutputFileAndOverwriteItWithMock( + effectivePom, invocationOnMock, "-Doutput"); + }); + + // when providing component content for our pom + var content = new JavaMavenProvider(tmpPomFile).provideComponent(); + // verify expected SBOM is returned + assertThat(content.type).isEqualTo(Api.CYCLONEDX_MEDIA_TYPE); + assertThat(dropIgnored(new String(content.buffer))).isEqualTo(dropIgnored(expectedSbom)); + } } private String dropIgnored(String s) { diff --git a/src/test/java/com/redhat/exhort/providers/Javascript_Envs_Test.java b/src/test/java/com/redhat/exhort/providers/Javascript_Envs_Test.java index 4410f03c..4f5c53ea 100644 --- a/src/test/java/com/redhat/exhort/providers/Javascript_Envs_Test.java +++ b/src/test/java/com/redhat/exhort/providers/Javascript_Envs_Test.java @@ -31,7 +31,7 @@ public class Javascript_Envs_Test { @SetSystemProperty(key = "NODE_HOME", value = "test-node-home") @SetEnvironmentVariable(key = "PATH", value = "test-path") void test_javascript_get_envs() { - var envs = new JavaScriptNpmProvider().getNpmExecEnv(); + var envs = new JavaScriptNpmProvider(null).getNpmExecEnv(); assertEquals( Collections.singletonMap("PATH", "test-path" + File.pathSeparator + "test-node-home"), envs); @@ -41,7 +41,7 @@ void test_javascript_get_envs() { @SetSystemProperty(key = "NODE_HOME", value = "test-node-home") @ClearEnvironmentVariable(key = "PATH") void test_javascript_get_envs_no_path() { - var envs = new JavaScriptNpmProvider().getNpmExecEnv(); + var envs = new JavaScriptNpmProvider(null).getNpmExecEnv(); assertEquals(Collections.singletonMap("PATH", "test-node-home"), envs); } @@ -49,7 +49,7 @@ void test_javascript_get_envs_no_path() { @SetSystemProperty(key = "NODE_HOME", value = "") @SetEnvironmentVariable(key = "PATH", value = "test-path") void test_javascript_get_envs_empty_java_home() { - var envs = new JavaScriptNpmProvider().getNpmExecEnv(); + var envs = new JavaScriptNpmProvider(null).getNpmExecEnv(); assertNull(envs); } @@ -57,7 +57,7 @@ void test_javascript_get_envs_empty_java_home() { @ClearSystemProperty(key = "NODE_HOME") @SetEnvironmentVariable(key = "PATH", value = "test-path") void test_javascript_get_envs_no_java_home() { - var envs = new JavaScriptNpmProvider().getNpmExecEnv(); + var envs = new JavaScriptNpmProvider(null).getNpmExecEnv(); assertNull(envs); } } diff --git a/src/test/java/com/redhat/exhort/providers/Javascript_Npm_Provider_Test.java b/src/test/java/com/redhat/exhort/providers/Javascript_Npm_Provider_Test.java index 2a1e51e0..11222a0c 100644 --- a/src/test/java/com/redhat/exhort/providers/Javascript_Npm_Provider_Test.java +++ b/src/test/java/com/redhat/exhort/providers/Javascript_Npm_Provider_Test.java @@ -50,14 +50,13 @@ void test_the_provideStack(String testFolder) throws IOException, InterruptedExc var tmpLockFile = Files.createFile(tmpNpmFolder.resolve("package-lock.json")); try (var is = getResourceAsStreamDecision( - this.getClass(), new String[] {"tst_manifests", "npm", testFolder, "package.json"})) { + this.getClass(), String.format("tst_manifests/npm/%s/package.json", testFolder))) { Files.write(tmpNpmFile, is.readAllBytes()); } try (var is = getResourceAsStreamDecision( - this.getClass(), - new String[] {"tst_manifests", "npm", testFolder, "package-lock.json"})) { + this.getClass(), String.format("tst_manifests/npm/%s/package-lock.json", testFolder))) { Files.write(tmpLockFile, is.readAllBytes()); } // load expected SBOM @@ -65,87 +64,78 @@ void test_the_provideStack(String testFolder) throws IOException, InterruptedExc try (var is = getResourceAsStreamDecision( this.getClass(), - new String[] {"tst_manifests", "npm", testFolder, "expected_stack_sbom.json"})) { + String.format("tst_manifests/npm/%s/expected_stack_sbom.json", testFolder))) { expectedSbom = new String(is.readAllBytes()); } String npmListingStack; try (var is = getResourceAsStreamDecision( - this.getClass(), - new String[] {"tst_manifests", "npm", testFolder, "npm-ls-stack.json"})) { + this.getClass(), String.format("tst_manifests/npm/%s/npm-ls-stack.json", testFolder))) { npmListingStack = new String(is.readAllBytes()); } - MockedStatic mockedOperations = mockStatic(Operations.class); - // Operations.runProcess(contains("npm i"),any()) + ArgumentMatcher matchPath = path -> path == null; - mockedOperations - .when(() -> Operations.runProcessGetOutput(argThat(matchPath), any(String[].class))) - .thenReturn(npmListingStack); - // when providing stack content for our pom - var content = new JavaScriptNpmProvider().provideStack(tmpNpmFile); - // cleanup - Files.deleteIfExists(tmpNpmFile); - Files.deleteIfExists(tmpLockFile); - Files.deleteIfExists(tmpNpmFolder); - mockedOperations.close(); - // verify expected SBOM is returned - assertThat(content.type).isEqualTo(Api.CYCLONEDX_MEDIA_TYPE); - assertThat(dropIgnored(new String(content.buffer))).isEqualTo(dropIgnored(expectedSbom)); + try (MockedStatic mockedOperations = mockStatic(Operations.class)) { + mockedOperations + .when(() -> Operations.runProcessGetOutput(argThat(matchPath), any(String[].class))) + .thenReturn(npmListingStack); + // when providing stack content for our pom + var content = new JavaScriptNpmProvider(tmpNpmFile).provideStack(); + // cleanup + Files.deleteIfExists(tmpNpmFile); + Files.deleteIfExists(tmpLockFile); + Files.deleteIfExists(tmpNpmFolder); + // verify expected SBOM is returned + assertThat(content.type).isEqualTo(Api.CYCLONEDX_MEDIA_TYPE); + assertThat(dropIgnored(new String(content.buffer))).isEqualTo(dropIgnored(expectedSbom)); + } } @ParameterizedTest @MethodSource("testFolders") void test_the_provideComponent(String testFolder) throws IOException, InterruptedException { // load the pom target pom file - byte[] targetPom; - try (var is = - getResourceAsStreamDecision( - this.getClass(), new String[] {"tst_manifests", "npm", testFolder, "package.json"})) { - targetPom = is.readAllBytes(); - } + var targetPom = + String.format("src/test/resources/tst_manifests/npm/%s/package.json", testFolder); + // load expected SBOM String expectedSbom = ""; try (var is = getResourceAsStreamDecision( this.getClass(), - new String[] {"tst_manifests", "npm", testFolder, "expected_component_sbom.json"})) { + String.format("tst_manifests/npm/%s/expected_component_sbom.json", testFolder))) { expectedSbom = new String(is.readAllBytes()); } String npmListingComponent; try (var is = getResourceAsStreamDecision( this.getClass(), - new String[] {"tst_manifests", "npm", testFolder, "npm-ls-component.json"})) { + String.format("tst_manifests/npm/%s/npm-ls-component.json", testFolder))) { npmListingComponent = new String(is.readAllBytes()); } - // MockedStatic javaFiles = mockStatic(Files.class); - // Operations.runProcess(contains("npm i"),any()) - // mockedOperations.when(() -> - // Operations.runProcessGetOutput(eq(null),any())).thenReturn(npmListingComponent); - MockedStatic mockedOperations = mockStatic(Operations.class); - mockedOperations - .when(() -> Operations.runProcess(any(), any())) - .thenAnswer( - (invocationOnMock) -> { - String[] commandParts = (String[]) invocationOnMock.getRawArguments()[0]; - int lastElementIsDir = commandParts.length - 1; - String packageLockJson = commandParts[lastElementIsDir] + "/package-lock.json"; - Files.createFile(Path.of(packageLockJson)); - return packageLockJson; - }); - ArgumentMatcher matchPath = path -> path == null; + try (MockedStatic mockedOperations = mockStatic(Operations.class)) { + mockedOperations + .when(() -> Operations.runProcess(any(), any())) + .thenAnswer( + (invocationOnMock) -> { + String[] commandParts = (String[]) invocationOnMock.getRawArguments()[0]; + int lastElementIsDir = commandParts.length - 1; + String packageLockJson = commandParts[lastElementIsDir] + "/package-lock.json"; - mockedOperations - .when(() -> Operations.runProcessGetOutput(argThat(matchPath), any(String[].class))) - .thenReturn(npmListingComponent); - // when providing component content for our pom - var content = new JavaScriptNpmProvider().provideComponent(targetPom); - mockedOperations.close(); - // javaFiles.close(); - // verify expected SBOM is returned - assertThat(content.type).isEqualTo(Api.CYCLONEDX_MEDIA_TYPE); - assertThat(dropIgnored(new String(content.buffer))).isEqualTo(dropIgnored(expectedSbom)); + return packageLockJson; + }); + ArgumentMatcher matchPath = path -> path == null; + + mockedOperations + .when(() -> Operations.runProcessGetOutput(argThat(matchPath), any(String[].class))) + .thenReturn(npmListingComponent); + // when providing component content for our pom + var content = new JavaScriptNpmProvider(Path.of(targetPom)).provideComponent(); + // verify expected SBOM is returned + assertThat(content.type).isEqualTo(Api.CYCLONEDX_MEDIA_TYPE); + assertThat(dropIgnored(new String(content.buffer))).isEqualTo(dropIgnored(expectedSbom)); + } } @ParameterizedTest @@ -158,44 +148,44 @@ void test_the_provideComponent_with_Path(String testFolder) throws Exception { var tmpNpmFile = Files.createFile(tmpNpmFolder.resolve("package.json")); try (var is = getResourceAsStreamDecision( - this.getClass(), new String[] {"tst_manifests", "npm", testFolder, "package.json"})) { + this.getClass(), String.format("tst_manifests/npm/%s/package.json", testFolder))) { Files.write(tmpNpmFile, is.readAllBytes()); } String expectedSbom = ""; try (var is = getResourceAsStreamDecision( this.getClass(), - new String[] {"tst_manifests", "npm", testFolder, "expected_component_sbom.json"})) { + String.format("tst_manifests/npm/%s/expected_component_sbom.json", testFolder))) { expectedSbom = new String(is.readAllBytes()); } String npmListingComponent; try (var is = getResourceAsStreamDecision( this.getClass(), - new String[] {"tst_manifests", "npm", testFolder, "npm-ls-component.json"})) { + String.format("tst_manifests/npm/%s/npm-ls-component.json", testFolder))) { npmListingComponent = new String(is.readAllBytes()); } ArgumentMatcher matchPath = path -> path == null; - MockedStatic mockedOperations = mockStatic(Operations.class); - mockedOperations - .when(() -> Operations.runProcess(any(), any())) - .thenAnswer( - (invocationOnMock) -> { - String[] commandParts = (String[]) invocationOnMock.getRawArguments()[0]; - int lastElementIsDir = commandParts.length - 1; - String packageLockJson = commandParts[lastElementIsDir] + "/package-lock.json"; - Files.createFile(Path.of(packageLockJson)); - return packageLockJson; - }); - mockedOperations - .when(() -> Operations.runProcessGetOutput(argThat(matchPath), any(String[].class))) - .thenReturn(npmListingComponent); - // when providing component content for our pom - var content = new JavaScriptNpmProvider().provideComponent(tmpNpmFile); - mockedOperations.close(); - // verify expected SBOM is returned - assertThat(content.type).isEqualTo(Api.CYCLONEDX_MEDIA_TYPE); - assertThat(dropIgnored(new String(content.buffer))).isEqualTo(dropIgnored(expectedSbom)); + try (MockedStatic mockedOperations = mockStatic(Operations.class)) { + mockedOperations + .when(() -> Operations.runProcess(any(), any())) + .thenAnswer( + (invocationOnMock) -> { + String[] commandParts = (String[]) invocationOnMock.getRawArguments()[0]; + int lastElementIsDir = commandParts.length - 1; + String packageLockJson = commandParts[lastElementIsDir] + "/package-lock.json"; + Files.createFile(Path.of(packageLockJson)); + return packageLockJson; + }); + mockedOperations + .when(() -> Operations.runProcessGetOutput(argThat(matchPath), any(String[].class))) + .thenReturn(npmListingComponent); + // when providing component content for our pom + var content = new JavaScriptNpmProvider(tmpNpmFile).provideComponent(); + // verify expected SBOM is returned + assertThat(content.type).isEqualTo(Api.CYCLONEDX_MEDIA_TYPE); + assertThat(dropIgnored(new String(content.buffer))).isEqualTo(dropIgnored(expectedSbom)); + } } private String dropIgnored(String s) { diff --git a/src/test/java/com/redhat/exhort/providers/Python_Provider_Test.java b/src/test/java/com/redhat/exhort/providers/Python_Provider_Test.java index 23411a87..d736e80b 100644 --- a/src/test/java/com/redhat/exhort/providers/Python_Provider_Test.java +++ b/src/test/java/com/redhat/exhort/providers/Python_Provider_Test.java @@ -15,6 +15,10 @@ */ package com.redhat.exhort.providers; +import static com.redhat.exhort.utils.PythonControllerBase.PROP_EXHORT_PIP_FREEZE; +import static com.redhat.exhort.utils.PythonControllerBase.PROP_EXHORT_PIP_PIPDEPTREE; +import static com.redhat.exhort.utils.PythonControllerBase.PROP_EXHORT_PIP_SHOW; +import static com.redhat.exhort.utils.PythonControllerBase.PROP_EXHORT_PIP_USE_DEP_TREE; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; @@ -31,6 +35,8 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; +import org.junitpioneer.jupiter.RestoreSystemProperties; +import org.junitpioneer.jupiter.SetSystemProperty; @ExtendWith(PythonEnvironmentExtension.class) class Python_Provider_Test extends ExhortTest { @@ -39,18 +45,11 @@ static Stream testFolders() { return Stream.of("pip_requirements_txt_no_ignore", "pip_requirements_txt_ignore"); } - // @RegisterExtension - // private PythonEnvironmentExtension pythonEnvironmentExtension = new - // PythonEnvironmentExtension(); - public Python_Provider_Test(PythonControllerBase pythonController) { this.pythonController = pythonController; - this.pythonPipProvider = new PythonPipProvider(); - this.pythonPipProvider.setPythonController(pythonController); } private PythonControllerBase pythonController; - private PythonPipProvider pythonPipProvider; @EnabledIfEnvironmentVariable(named = "RUN_PYTHON_BIN", matches = "true") @ParameterizedTest @@ -59,10 +58,11 @@ void test_the_provideStack(String testFolder) throws IOException, InterruptedExc // create temp file hosting our sut package.json var tmpPythonModuleDir = Files.createTempDirectory("exhort_test_"); var tmpPythonFile = Files.createFile(tmpPythonModuleDir.resolve("requirements.txt")); + var provider = new PythonPipProvider(tmpPythonFile); + provider.setPythonController(pythonController); try (var is = getResourceAsStreamDecision( - this.getClass(), - new String[] {"tst_manifests", "pip", testFolder, "requirements.txt"})) { + this.getClass(), String.format("tst_manifests/pip/%s/requirements.txt", testFolder))) { Files.write(tmpPythonFile, is.readAllBytes()); } // load expected SBOM @@ -70,11 +70,11 @@ void test_the_provideStack(String testFolder) throws IOException, InterruptedExc try (var is = getResourceAsStreamDecision( this.getClass(), - new String[] {"tst_manifests", "pip", testFolder, "expected_stack_sbom.json"})) { + String.format("tst_manifests/pip/%s/expected_stack_sbom.json", testFolder))) { expectedSbom = new String(is.readAllBytes()); } // when providing stack content for our pom - var content = this.pythonPipProvider.provideStack(tmpPythonFile); + var content = provider.provideStack(); // cleanup Files.deleteIfExists(tmpPythonFile); Files.deleteIfExists(tmpPythonModuleDir); @@ -88,23 +88,20 @@ void test_the_provideStack(String testFolder) throws IOException, InterruptedExc @MethodSource("testFolders") void test_the_provideComponent(String testFolder) throws IOException, InterruptedException { // load the pom target pom file - byte[] targetRequirementsTxt; - try (var is = - getResourceAsStreamDecision( - this.getClass(), - new String[] {"tst_manifests", "pip", testFolder, "requirements.txt"})) { - targetRequirementsTxt = is.readAllBytes(); - } + var requirementsFile = + Path.of( + String.format("src/test/resources/tst_manifests/pip/%s/requirements.txt", testFolder)); + // load expected SBOM String expectedSbom = ""; try (var is = getResourceAsStreamDecision( this.getClass(), - new String[] {"tst_manifests", "pip", testFolder, "expected_component_sbom.json"})) { + String.format("tst_manifests/pip/%s/expected_component_sbom.json", testFolder))) { expectedSbom = new String(is.readAllBytes()); } // when providing component content for our pom - var content = this.pythonPipProvider.provideComponent(targetRequirementsTxt); + var content = new PythonPipProvider(requirementsFile).provideComponent(); // verify expected SBOM is returned assertThat(content.type).isEqualTo(Api.CYCLONEDX_MEDIA_TYPE); assertThat(dropIgnored(new String(content.buffer))).isEqualTo(dropIgnored(expectedSbom)); @@ -112,6 +109,8 @@ void test_the_provideComponent(String testFolder) throws IOException, Interrupte @ParameterizedTest @MethodSource("testFolders") + @SetSystemProperty(key = PythonControllerBase.PROP_EXHORT_PYTHON_VIRTUAL_ENV, value = "true") + @RestoreSystemProperties void test_the_provideStack_with_properties(String testFolder) throws IOException, InterruptedException { // create temp file hosting our sut package.json @@ -119,8 +118,7 @@ void test_the_provideStack_with_properties(String testFolder) var tmpPythonFile = Files.createFile(tmpPythonModuleDir.resolve("requirements.txt")); try (var is = getResourceAsStreamDecision( - this.getClass(), - new String[] {"tst_manifests", "pip", testFolder, "requirements.txt"})) { + this.getClass(), String.format("tst_manifests/pip/%s/requirements.txt", testFolder))) { Files.write(tmpPythonFile, is.readAllBytes()); } // load expected SBOM @@ -128,22 +126,20 @@ void test_the_provideStack_with_properties(String testFolder) try (var is = getResourceAsStreamDecision( this.getClass(), - new String[] {"tst_manifests", "pip", testFolder, "expected_stack_sbom.json"})) { + String.format("tst_manifests/pip/%s/expected_stack_sbom.json", testFolder))) { expectedSbom = new String(is.readAllBytes()); } // when providing stack content for our pom - var content = this.pythonPipProvider.provideStack(tmpPythonFile); - String pipShowContent = this.getStringFromFile("tst_manifests", "pip", "pip-show.txt"); - String pipFreezeContent = this.getStringFromFile("tst_manifests", "pip", "pip-freeze-all.txt"); + var content = new PythonPipProvider(tmpPythonFile).provideStack(); + String pipShowContent = this.getStringFromFile("tst_manifests/pip/pip-show.txt"); + String pipFreezeContent = this.getStringFromFile("tst_manifests/pip/pip-freeze-all.txt"); String base64PipShow = new String(Base64.getEncoder().encode(pipShowContent.getBytes())); String base64PipFreeze = new String(Base64.getEncoder().encode(pipFreezeContent.getBytes())); - System.setProperty("EXHORT_PIP_SHOW", base64PipShow); - System.setProperty("EXHORT_PIP_FREEZE", base64PipFreeze); + System.setProperty(PROP_EXHORT_PIP_SHOW, base64PipShow); + System.setProperty(PROP_EXHORT_PIP_FREEZE, base64PipFreeze); // cleanup Files.deleteIfExists(tmpPythonFile); Files.deleteIfExists(tmpPythonModuleDir); - System.clearProperty("EXHORT_PIP_SHOW"); - System.clearProperty("EXHORT_PIP_FREEZE"); // verify expected SBOM is returned assertThat(content.type).isEqualTo(Api.CYCLONEDX_MEDIA_TYPE); assertThat(dropIgnored(new String(content.buffer))).isEqualTo(dropIgnored(expectedSbom)); @@ -151,16 +147,17 @@ void test_the_provideStack_with_properties(String testFolder) @ParameterizedTest @MethodSource("testFolders") + @SetSystemProperty(key = PythonControllerBase.PROP_EXHORT_PYTHON_VIRTUAL_ENV, value = "true") + @SetSystemProperty(key = PROP_EXHORT_PIP_USE_DEP_TREE, value = "true") + @RestoreSystemProperties void test_the_provideStack_with_pipdeptree(String testFolder) throws IOException, InterruptedException { // create temp file hosting our sut package.json - System.setProperty("EXHORT_PIP_USE_DEP_TREE", "true"); var tmpPythonModuleDir = Files.createTempDirectory("exhort_test_"); var tmpPythonFile = Files.createFile(tmpPythonModuleDir.resolve("requirements.txt")); try (var is = getResourceAsStreamDecision( - this.getClass(), - new String[] {"tst_manifests", "pip", testFolder, "requirements.txt"})) { + this.getClass(), String.format("tst_manifests/pip/%s/requirements.txt", testFolder))) { Files.write(tmpPythonFile, is.readAllBytes()); } // load expected SBOM @@ -168,18 +165,17 @@ void test_the_provideStack_with_pipdeptree(String testFolder) try (var is = getResourceAsStreamDecision( this.getClass(), - new String[] {"tst_manifests", "pip", testFolder, "expected_stack_sbom.json"})) { + String.format("tst_manifests/pip/%s/expected_stack_sbom.json", testFolder))) { expectedSbom = new String(is.readAllBytes()); } // when providing stack content for our pom - var content = this.pythonPipProvider.provideStack(tmpPythonFile); - String pipdeptreeContent = this.getStringFromFile("tst_manifests", "pip", "pipdeptree.json"); + var content = new PythonPipProvider(tmpPythonFile).provideStack(); + String pipdeptreeContent = this.getStringFromFile("tst_manifests/pip/pipdeptree.json"); String base64Pipdeptree = new String(Base64.getEncoder().encode(pipdeptreeContent.getBytes())); - System.setProperty("EXHORT_PIP_PIPDEPTREE", base64Pipdeptree); + System.setProperty(PROP_EXHORT_PIP_PIPDEPTREE, base64Pipdeptree); // cleanup Files.deleteIfExists(tmpPythonFile); Files.deleteIfExists(tmpPythonModuleDir); - System.clearProperty("EXHORT_PIP_USE_DEP_TREE"); // verify expected SBOM is returned assertThat(content.type).isEqualTo(Api.CYCLONEDX_MEDIA_TYPE); assertThat(dropIgnored(new String(content.buffer))).isEqualTo(dropIgnored(expectedSbom)); @@ -187,37 +183,32 @@ void test_the_provideStack_with_pipdeptree(String testFolder) @ParameterizedTest @MethodSource("testFolders") + @RestoreSystemProperties void test_the_provideComponent_with_properties(String testFolder) throws IOException, InterruptedException { // load the pom target pom file - byte[] targetRequirementsTxt; - try (var is = - getResourceAsStreamDecision( - this.getClass(), - new String[] {"tst_manifests", "pip", testFolder, "requirements.txt"})) { - targetRequirementsTxt = is.readAllBytes(); - } + var targetRequirements = + String.format("src/test/resources/tst_manifests/pip/%s/requirements.txt", testFolder); + // load expected SBOM String expectedSbom = ""; try (var is = getResourceAsStreamDecision( this.getClass(), - new String[] {"tst_manifests", "pip", testFolder, "expected_component_sbom.json"})) { + String.format("tst_manifests/pip/%s/expected_component_sbom.json", testFolder))) { expectedSbom = new String(is.readAllBytes()); } - String pipShowContent = this.getStringFromFile("tst_manifests", "pip", "pip-show.txt"); - String pipFreezeContent = this.getStringFromFile("tst_manifests", "pip", "pip-freeze-all.txt"); + String pipShowContent = this.getStringFromFile("tst_manifests/pip/pip-show.txt"); + String pipFreezeContent = this.getStringFromFile("tst_manifests/pip/pip-freeze-all.txt"); String base64PipShow = new String(Base64.getEncoder().encode(pipShowContent.getBytes())); String base64PipFreeze = new String(Base64.getEncoder().encode(pipFreezeContent.getBytes())); - System.setProperty("EXHORT_PIP_SHOW", base64PipShow); - System.setProperty("EXHORT_PIP_FREEZE", base64PipFreeze); + System.setProperty(PROP_EXHORT_PIP_SHOW, base64PipShow); + System.setProperty(PROP_EXHORT_PIP_FREEZE, base64PipFreeze); // when providing component content for our pom - var content = this.pythonPipProvider.provideComponent(targetRequirementsTxt); + var content = new PythonPipProvider(Path.of(targetRequirements)).provideComponent(); // verify expected SBOM is returned assertThat(content.type).isEqualTo(Api.CYCLONEDX_MEDIA_TYPE); assertThat(dropIgnored(new String(content.buffer))).isEqualTo(dropIgnored(expectedSbom)); - System.clearProperty("EXHORT_PIP_SHOW"); - System.clearProperty("EXHORT_PIP_FREEZE"); } @Test @@ -225,11 +216,8 @@ void Test_The_ProvideComponent_Path_Should_Throw_Exception() { assertThatIllegalArgumentException() .isThrownBy( () -> { - this.pythonPipProvider.provideComponent(Path.of(".")); - }) - .withMessage( - "provideComponent with file system path for Python pip package manager is not" - + " supported"); + new PythonPipProvider(Path.of(".")).provideComponent(); + }); } private String dropIgnored(String s) { diff --git a/src/test/java/com/redhat/exhort/tools/Ecosystem_Test.java b/src/test/java/com/redhat/exhort/tools/Ecosystem_Test.java index 4f728773..05be587e 100644 --- a/src/test/java/com/redhat/exhort/tools/Ecosystem_Test.java +++ b/src/test/java/com/redhat/exhort/tools/Ecosystem_Test.java @@ -19,21 +19,21 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import com.redhat.exhort.providers.JavaMavenProvider; -import java.nio.file.Paths; +import java.nio.file.Path; import org.junit.jupiter.api.Test; class Ecosystem_Test { @Test void get_a_provider_for_an_unknown_package_file_should_throw_an_exception() { - var manifestPath = Paths.get("/not/a/supported/mani.fest"); + var manifestPath = Path.of("/not/a/supported/mani.fest"); assertThatExceptionOfType(IllegalStateException.class) .isThrownBy(() -> Ecosystem.getProvider(manifestPath)); } @Test void get_a_provider_for_a_pom_xml_file_should_return_java_maven_manifest() { - var manifestPath = Paths.get("/supported/manifest/pom.xml"); + var manifestPath = Path.of("/supported/manifest/pom.xml"); assertThat(Ecosystem.getProvider(manifestPath)).isInstanceOf(JavaMavenProvider.class); } } diff --git a/src/test/java/com/redhat/exhort/utils/PythonControllerRealEnvTest.java b/src/test/java/com/redhat/exhort/utils/PythonControllerRealEnvTest.java index 8b004967..bed8d604 100644 --- a/src/test/java/com/redhat/exhort/utils/PythonControllerRealEnvTest.java +++ b/src/test/java/com/redhat/exhort/utils/PythonControllerRealEnvTest.java @@ -15,21 +15,31 @@ */ package com.redhat.exhort.utils; +import static com.redhat.exhort.Provider.PROP_MATCH_MANIFEST_VERSIONS; import static com.redhat.exhort.utils.PythonControllerBaseTest.matchCommandPipFreeze; import static com.redhat.exhort.utils.PythonControllerBaseTest.matchCommandPipShow; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; import com.redhat.exhort.ExhortTest; import com.redhat.exhort.tools.Operations; import java.nio.file.Path; -import java.util.*; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import org.junitpioneer.jupiter.RestoreSystemProperties; +import org.junitpioneer.jupiter.SetSystemProperty; import org.mockito.MockedStatic; import org.mockito.Mockito; @@ -37,39 +47,9 @@ class PythonControllerRealEnvTest extends ExhortTest { private static PythonControllerRealEnv pythonControllerRealEnv; private final String PIP_FREEZE_LINES_CYCLIC = - getStringFromFile("msc", "python", "pip_freeze_lines_cyclic.txt"); + getStringFromFile("msc/python/pip_freeze_lines_cyclic.txt"); private final String PIP_SHOW_LINES_CYCLIC = - getStringFromFile("msc", "python", "pip_show_lines_cyclic.txt"); - - // ArgumentMatcher matchCommandPipFreeze = new ArgumentMatcher() { - // @Override - // public boolean matches(String[] command) { - // return Arrays.stream(command).anyMatch(word -> word.contains("freeze")); - // } - // // in var args, must override type default method' void.class in argumentMatcher interface - // in order to let - // custom ArgumentMatcher work correctly. - // @Override - // public Class type() - // { - // return String[].class; - // } - // - // }; - // - // ArgumentMatcher matchCommandPipShow = new ArgumentMatcher() { - // @Override - // public boolean matches(String[] command) { - // return Arrays.stream(command).anyMatch(word -> word.contains("show")); - // } - // - // @Override - // public Class type() - // { - // return String[].class; - // } - // - // }; + getStringFromFile("msc/python/pip_show_lines_cyclic.txt"); @BeforeEach void setUp() { @@ -227,7 +207,7 @@ void get_Dependencies_With_Match_Manifest_Versions(boolean MatchManifestVersions .when(() -> Operations.runProcessGetOutput(any(Path.class), argThat(matchCommandPipShow))) .thenReturn(pipShowResults); if (!MatchManifestVersionsEnabled) { - System.setProperty("MATCH_MANIFEST_VERSIONS", "false"); + System.setProperty(PROP_MATCH_MANIFEST_VERSIONS, "false"); } if (MatchManifestVersionsEnabled) { RuntimeException runtimeException = @@ -247,10 +227,10 @@ void get_Dependencies_With_Match_Manifest_Versions(boolean MatchManifestVersions List> dependencies = pythonControllerRealEnv.getDependencies(requirementsPath, true); - System.clearProperty("MATCH_MANIFEST_VERSIONS"); + System.clearProperty(PROP_MATCH_MANIFEST_VERSIONS); // collect all packages returned from getDependencies into Set. System.out.println(dependencies); - Set actualSetOfPackages = new HashSet(); + Set actualSetOfPackages = new HashSet<>(); dependencies.forEach( entry -> { accumulateAllPackages(entry, actualSetOfPackages); @@ -287,26 +267,21 @@ record -> { } @Test + @RestoreSystemProperties + @SetSystemProperty(key = PROP_MATCH_MANIFEST_VERSIONS, value = "false") void get_Dependencies_from_Cyclic_Tree() { MockedStatic operationsMockedStatic = Mockito.mockStatic(Operations.class); - // ArgumentMatcher matchCommandPipFreeze = command -> - // Arrays.stream(command).anyMatch(word -> - // word.contains("freeze")); operationsMockedStatic .when(() -> Operations.runProcessGetOutput(any(Path.class), argThat(matchCommandPipFreeze))) .thenReturn(PIP_FREEZE_LINES_CYCLIC); - // operationsMockedStatic.when(() -> - // Operations.runProcessGetOutput(any(Path.class),any(String[].class))).thenReturn(PIP_FREEZE_LINES_CYCLIC); operationsMockedStatic .when(() -> Operations.runProcessGetOutput(any(Path.class), argThat(matchCommandPipShow))) .thenReturn(PIP_SHOW_LINES_CYCLIC); String requirementsTxt = - getFileFromResource("requirements.txt", "msc", "python", "requirements-cyclic-test.txt"); - System.setProperty("MATCH_MANIFEST_VERSIONS", "false"); + getFileFromResource("requirements.txt", "msc/python/requirements-cyclic-test.txt"); List> dependencies = pythonControllerRealEnv.getDependencies(requirementsTxt, true); - System.clearProperty("MATCH_MANIFEST_VERSIONS"); assertEquals(104, dependencies.size()); operationsMockedStatic.close(); @@ -314,7 +289,6 @@ void get_Dependencies_from_Cyclic_Tree() { @Test void get_Dependency_Name_requirements() { - assertEquals("something", PythonControllerRealEnv.getDependencyName("something==2.0.5")); assertEquals("something", PythonControllerRealEnv.getDependencyName("something == 2.0.5")); assertEquals("something", PythonControllerRealEnv.getDependencyName("something>=2.0.5")); @@ -322,17 +296,17 @@ void get_Dependency_Name_requirements() { @Test void automaticallyInstallPackageOnEnvironment() { - assertFalse(this.pythonControllerRealEnv.automaticallyInstallPackageOnEnvironment()); + assertFalse(pythonControllerRealEnv.automaticallyInstallPackageOnEnvironment()); } @Test void isRealEnv() { - assertTrue(this.pythonControllerRealEnv.isRealEnv()); + assertTrue(pythonControllerRealEnv.isRealEnv()); } @Test void isVirtualEnv() { - assertFalse(this.pythonControllerRealEnv.isVirtualEnv()); + assertFalse(pythonControllerRealEnv.isVirtualEnv()); } } diff --git a/src/test/java/com/redhat/exhort/utils/PythonControllerVirtualEnvTest.java b/src/test/java/com/redhat/exhort/utils/PythonControllerVirtualEnvTest.java index 920e5c8f..e9b0a8ea 100644 --- a/src/test/java/com/redhat/exhort/utils/PythonControllerVirtualEnvTest.java +++ b/src/test/java/com/redhat/exhort/utils/PythonControllerVirtualEnvTest.java @@ -15,6 +15,8 @@ */ package com.redhat.exhort.utils; +import static com.redhat.exhort.Provider.PROP_MATCH_MANIFEST_VERSIONS; +import static com.redhat.exhort.utils.PythonControllerBase.PROP_EXHORT_PYTHON_INSTALL_BEST_EFFORTS; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; @@ -28,6 +30,8 @@ import java.util.Map; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junitpioneer.jupiter.RestoreSystemProperties; +import org.junitpioneer.jupiter.SetSystemProperty; import org.mockito.Mockito; class PythonControllerVirtualEnvTest extends ExhortTest { @@ -45,22 +49,22 @@ static void setUp() { } @Test + @RestoreSystemProperties void test_Virtual_Environment_Install_Best_Efforts() throws JsonProcessingException { - System.setProperty("EXHORT_PYTHON_INSTALL_BEST_EFFORTS", "true"); - System.setProperty("MATCH_MANIFEST_VERSIONS", "false"); + System.setProperty(PROP_EXHORT_PYTHON_INSTALL_BEST_EFFORTS, "true"); + System.setProperty(PROP_MATCH_MANIFEST_VERSIONS, "false"); String requirementsTxt = getFileFromString("requirements.txt", "flask==9.9.9\ndeprecated==15.15.99\n"); List> dependencies = spiedPythonControllerVirtualEnv.getDependencies(requirementsTxt, true); System.out.println(om.writerWithDefaultPrettyPrinter().writeValueAsString(dependencies)); - System.clearProperty("EXHORT_PYTHON_INSTALL_BEST_EFFORTS"); - System.clearProperty("MATCH_MANIFEST_VERSIONS"); } @Test + @RestoreSystemProperties + @SetSystemProperty(key = PROP_EXHORT_PYTHON_INSTALL_BEST_EFFORTS, value = "true") void test_Virtual_Environment_Install_Best_Efforts_Conflict_MMV_Should_Throw_Runtime_Exception() { - System.setProperty("EXHORT_PYTHON_INSTALL_BEST_EFFORTS", "true"); String requirementsTxt = getFileFromString("requirements.txt", "flask==9.9.9\ndeprecated==15.15.99\n"); RuntimeException runtimeException = @@ -68,7 +72,6 @@ void test_Virtual_Environment_Install_Best_Efforts_Conflict_MMV_Should_Throw_Run RuntimeException.class, () -> spiedPythonControllerVirtualEnv.getDependencies(requirementsTxt, true)); assertTrue(runtimeException.getMessage().contains("Conflicting settings")); - System.clearProperty("EXHORT_PYTHON_INSTALL_BEST_EFFORTS"); } @Test @@ -78,10 +81,8 @@ void test_Virtual_Environment_Flow() throws IOException { Path requirementsFilePath = Path.of(System.getProperty("user.dir").toString(), "requirements.txt"); Files.write(requirementsFilePath, requirementsTxt.getBytes()); - // MockedStatic operationsMockedStatic = mockStatic(Operations.class); - // when(spiedPythonControllerVirtualEnv.) - List> dependencies = - spiedPythonControllerVirtualEnv.getDependencies(requirementsFilePath.toString(), true); + + spiedPythonControllerVirtualEnv.getDependencies(requirementsFilePath.toString(), true); verify(spiedPythonControllerVirtualEnv).prepareEnvironment(anyString()); verify(spiedPythonControllerVirtualEnv).installPackages(anyString()); verify(spiedPythonControllerVirtualEnv).cleanEnvironment(anyBoolean()); @@ -89,16 +90,16 @@ void test_Virtual_Environment_Flow() throws IOException { verify(spiedPythonControllerVirtualEnv).automaticallyInstallPackageOnEnvironment(); verify(spiedPythonControllerVirtualEnv, never()).isRealEnv(); verify(spiedPythonControllerVirtualEnv, times(2)).isVirtualEnv(); + Files.delete(requirementsFilePath); } @Test void isRealEnv() { - - assertFalse(this.spiedPythonControllerVirtualEnv.isRealEnv()); + assertFalse(spiedPythonControllerVirtualEnv.isRealEnv()); } @Test void isVirtualEnv() { - assertTrue(this.spiedPythonControllerVirtualEnv.isVirtualEnv()); + assertTrue(spiedPythonControllerVirtualEnv.isVirtualEnv()); } } diff --git a/src/test/java/module-info.test b/src/test/java/module-info.test index e2ca1d20..70b4d39b 100644 --- a/src/test/java/module-info.test +++ b/src/test/java/module-info.test @@ -1,10 +1,12 @@ --add-modules - jdk.attach,jdk.unsupported,org.assertj.core,org.junit.jupiter.params,org.mockito,net.bytebuddy,net.bytebuddy.agent + jdk.attach,jdk.unsupported,org.assertj.core,org.junit.jupiter.params,org.mockito,org.mockito.junit.jupiter,net.bytebuddy,net.bytebuddy.agent --add-reads com.redhat.exhort=org.assertj.core,org.junit.jupiter.api --add-reads - com.redhat.exhort=org.junit.jupiter.params + com.redhat.exhort=org.junit.jupiter.params,org.mockito +--add-reads + org.mockito=java.net.http --add-opens com.redhat.exhort/com.redhat.exhort.impl=org.junit.platform.commons @@ -15,11 +17,7 @@ --add-opens com.redhat.exhort/com.redhat.exhort.image=org.junit.platform.commons --add-opens - com.redhat.exhort/com.redhat.exhort=org.mockito ---add-opens - com.redhat.exhort.providers/com.redhat.exhort.providers=org.mockito ---add-opens - com.redhat.exhort.providers/com.redhat.exhort.image=org.mockito + com.redhat.exhort/com.redhat.exhort.image=org.mockito --add-opens java.base/java.lang=org.junitpioneer --add-opens diff --git a/src/test/resources/git_config b/src/test/resources/git_config new file mode 100644 index 00000000..4f73a25c --- /dev/null +++ b/src/test/resources/git_config @@ -0,0 +1,5 @@ +[user] + email = default@example.com + name = Exhort API Tests +[init] + defaultBranch = main \ No newline at end of file diff --git a/src/test/resources/tst_manifests/golang/go_mod_light_no_ignore/go.mod b/src/test/resources/tst_manifests/golang/go_mod_light_no_ignore/go.mod index ecbbc2b9..7ba64b70 100644 --- a/src/test/resources/tst_manifests/golang/go_mod_light_no_ignore/go.mod +++ b/src/test/resources/tst_manifests/golang/go_mod_light_no_ignore/go.mod @@ -6,5 +6,3 @@ require golang.org/x/tools v0.0.0-20210112183307-1e6ecd4bf1b0 require github.com/spf13/cobra v0.0.5 require gopkg.in/yaml.v3 v3.0.1 // indirect - - diff --git a/src/test/resources/tst_manifests/gradle-kotlin/deps_with_duplicate_no_version/build.gradle.kts b/src/test/resources/tst_manifests/gradle-kotlin/deps_with_duplicate_no_version/build.gradle.kts index 43e0e1f1..d2862a12 100644 --- a/src/test/resources/tst_manifests/gradle-kotlin/deps_with_duplicate_no_version/build.gradle.kts +++ b/src/test/resources/tst_manifests/gradle-kotlin/deps_with_duplicate_no_version/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("java") + id ("java") } group = "org.acme.dbaas" @@ -10,9 +10,9 @@ repositories { } dependencies { - implementation("log4j:log4j:1.2.17") - implementation(group: "log4j", name: "log4j") + implementation ("log4j:log4j:1.2.17") + implementation (group: "log4j", name: "log4j") } -tasks.test { +test { useJUnitPlatform() } diff --git a/src/test/resources/tst_manifests/gradle-kotlin/deps_with_duplicate_no_version/settings.gradle.kts b/src/test/resources/tst_manifests/gradle-kotlin/deps_with_duplicate_no_version/settings.gradle.kts index 2d781048..57f8def6 100644 --- a/src/test/resources/tst_manifests/gradle-kotlin/deps_with_duplicate_no_version/settings.gradle.kts +++ b/src/test/resources/tst_manifests/gradle-kotlin/deps_with_duplicate_no_version/settings.gradle.kts @@ -1,5 +1,5 @@ /* - * This file was generated by the Gradle 'init' task. + * This file was generated by the Gradle "init' task. */ rootProject.name = "postgresql-orm-quarkus" From e24ab223d10c7bac4722476f46842794028b3fc5 Mon Sep 17 00:00:00 2001 From: Ruben Romero Montes Date: Tue, 8 Apr 2025 15:41:32 +0200 Subject: [PATCH 3/5] fix: update module-info Signed-off-by: Ruben Romero Montes --- src/main/java/module-info.java | 2 +- src/test/java/module-info.test | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 4b5134cf..b1c895c7 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -7,7 +7,7 @@ requires java.xml; requires jakarta.mail; requires cyclonedx.core.java; - requires transitive packageurl.java; + requires packageurl.java; requires transitive java.logging; requires org.tomlj; requires java.base; diff --git a/src/test/java/module-info.test b/src/test/java/module-info.test index 70b4d39b..87103d26 100644 --- a/src/test/java/module-info.test +++ b/src/test/java/module-info.test @@ -22,3 +22,7 @@ java.base/java.lang=org.junitpioneer --add-opens java.base/java.util=org.junitpioneer +--add-opens + java.base/java.lang=ALL-UNNAMED +--add-opens + java.base/java.util=ALL-UNNAMED From 7f3836ce547ee4f1a3356b6fb35512397764032d Mon Sep 17 00:00:00 2001 From: Ruben Romero Montes Date: Tue, 8 Apr 2025 17:48:32 +0200 Subject: [PATCH 4/5] refactor: avoid mocking system.getenv Signed-off-by: Ruben Romero Montes --- .../com/redhat/exhort/image/ImageUtils.java | 31 +- .../com/redhat/exhort/impl/ExhortApi.java | 58 ++- .../exhort/providers/GoModulesProvider.java | 7 +- .../exhort/providers/JavaMavenProvider.java | 7 +- .../providers/JavaScriptNpmProvider.java | 10 +- .../exhort/providers/PythonPipProvider.java | 30 +- .../com/redhat/exhort/sbom/CycloneDXSbom.java | 14 +- .../com/redhat/exhort/tools/Operations.java | 6 +- .../com/redhat/exhort/utils/Environment.java | 41 +++ .../exhort/utils/PythonControllerBase.java | 14 +- .../exhort/utils/PythonControllerRealEnv.java | 2 +- .../redhat/exhort/image/ImageUtilsTest.java | 348 +++++++++--------- .../com/redhat/exhort/impl/ExhortApiIT.java | 9 +- .../redhat/exhort/impl/Exhort_Api_Test.java | 157 ++------ .../exhort/providers/Java_Envs_Test.java | 27 +- .../providers/Javascript_Envs_Test.java | 20 +- .../redhat/exhort/tools/Operations_Test.java | 24 +- .../utils/PythonControllerVirtualEnvTest.java | 3 +- src/test/java/module-info.test | 4 - 19 files changed, 364 insertions(+), 448 deletions(-) create mode 100644 src/main/java/com/redhat/exhort/utils/Environment.java diff --git a/src/main/java/com/redhat/exhort/image/ImageUtils.java b/src/main/java/com/redhat/exhort/image/ImageUtils.java index 6debcb2b..25b40782 100644 --- a/src/main/java/com/redhat/exhort/image/ImageUtils.java +++ b/src/main/java/com/redhat/exhort/image/ImageUtils.java @@ -16,7 +16,6 @@ package com.redhat.exhort.image; import static com.redhat.exhort.image.Platform.EMPTY_PLATFORM; -import static com.redhat.exhort.impl.ExhortApi.getStringValueEnvironment; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; @@ -25,10 +24,10 @@ import com.fasterxml.jackson.databind.node.TextNode; import com.github.packageurl.MalformedPackageURLException; import com.redhat.exhort.tools.Operations; +import com.redhat.exhort.utils.Environment; import java.io.File; import java.io.IOException; import java.util.AbstractMap; -import java.util.ArrayList; import java.util.Collections; import java.util.EnumSet; import java.util.List; @@ -91,7 +90,7 @@ public class ImageUtils { new AbstractMap.SimpleEntry<>("aarch64", "v8")); static String updatePATHEnv(String execPath) { - String path = System.getenv("PATH"); + String path = Environment.get("PATH"); if (path != null) { return String.format("PATH=%s%s%s", path, File.pathSeparator, execPath); } else { @@ -129,8 +128,8 @@ static Operations.ProcessExecOutput execSyft(ImageRef imageRef) { var docker = Operations.getCustomPathOrElse("docker"); var podman = Operations.getCustomPathOrElse("podman"); - var syftConfigPath = getStringValueEnvironment(EXHORT_SYFT_CONFIG_PATH, ""); - var imageSource = getStringValueEnvironment(EXHORT_SYFT_IMAGE_SOURCE, ""); + var syftConfigPath = Environment.get(EXHORT_SYFT_CONFIG_PATH, ""); + var imageSource = Environment.get(EXHORT_SYFT_IMAGE_SOURCE, ""); SyftImageSource.getImageSource(imageSource); var dockerPath = @@ -195,29 +194,23 @@ static List getSyftEnvs(String dockerPath, String podmanPath) { } else if (!podmanPath.isEmpty()) { path = podmanPath; } - var envPath = path != null ? updatePATHEnv(path) : null; - - List envs = new ArrayList<>(1); - if (envPath != null) { - envs.add(envPath); - } - return envs; + return path != null ? List.of(updatePATHEnv(path)) : Collections.emptyList(); } public static Platform getImagePlatform() { - var platform = getStringValueEnvironment(EXHORT_IMAGE_PLATFORM, ""); + var platform = Environment.get(EXHORT_IMAGE_PLATFORM, ""); if (!platform.isEmpty()) { return new Platform(platform); } - var imageSource = getStringValueEnvironment(EXHORT_SYFT_IMAGE_SOURCE, ""); + var imageSource = Environment.get(EXHORT_SYFT_IMAGE_SOURCE, ""); SyftImageSource source = SyftImageSource.getImageSource(imageSource); - var os = getStringValueEnvironment(EXHORT_IMAGE_OS, ""); + var os = Environment.get(EXHORT_IMAGE_OS, ""); if (os.isEmpty()) { os = source.getOs(); } - var arch = getStringValueEnvironment(EXHORT_IMAGE_ARCH, ""); + var arch = Environment.get(EXHORT_IMAGE_ARCH, ""); if (arch.isEmpty()) { arch = source.getArch(); } @@ -226,7 +219,7 @@ public static Platform getImagePlatform() { return new Platform(os, arch, null); } - var variant = getStringValueEnvironment(EXHORT_IMAGE_VARIANT, ""); + var variant = Environment.get(EXHORT_IMAGE_VARIANT, ""); if (variant.isEmpty()) { variant = source.getVariant(); } @@ -425,8 +418,8 @@ static Map getSingleImageDigest(ImageRef imageRef) static Operations.ProcessExecOutput execSkopeoInspect(ImageRef imageRef, boolean raw) { var skopeo = Operations.getCustomPathOrElse("skopeo"); - var configPath = getStringValueEnvironment(EXHORT_SKOPEO_CONFIG_PATH, ""); - var daemonHost = getStringValueEnvironment(EXHORT_IMAGE_SERVICE_ENDPOINT, ""); + var configPath = Environment.get(EXHORT_SKOPEO_CONFIG_PATH, ""); + var daemonHost = Environment.get(EXHORT_IMAGE_SERVICE_ENDPOINT, ""); String[] cmd; if (daemonHost.isEmpty()) { diff --git a/src/main/java/com/redhat/exhort/impl/ExhortApi.java b/src/main/java/com/redhat/exhort/impl/ExhortApi.java index 7e391d9c..17c7fc92 100644 --- a/src/main/java/com/redhat/exhort/impl/ExhortApi.java +++ b/src/main/java/com/redhat/exhort/impl/ExhortApi.java @@ -28,6 +28,7 @@ import com.redhat.exhort.image.ImageUtils; import com.redhat.exhort.logging.LoggersFactory; import com.redhat.exhort.tools.Ecosystem; +import com.redhat.exhort.utils.Environment; import jakarta.mail.MessagingException; import jakarta.mail.internet.MimeMultipart; import jakarta.mail.util.ByteArrayDataSource; @@ -61,6 +62,12 @@ /** Concrete implementation of the Exhort {@link Api} Service. */ public final class ExhortApi implements Api { + private static final String DEV_EXHORT_BACKEND_URL = "DEV_EXHORT_BACKEND_URL"; + + private static final String EXHORT_DEV_MODE = "EXHORT_DEV_MODE"; + + private static final String HTTP_VERSION_EXHORT_CLIENT = "HTTP_VERSION_EXHORT_CLIENT"; + private static final Logger LOG = LoggersFactory.getLogger(ExhortApi.class.getName()); public static final String DEFAULT_ENDPOINT = "https://rhda.rhcloud.com"; @@ -126,8 +133,8 @@ public ExhortApi() { * @return i.e. HttpClient.Version.HTTP_1.1 */ static HttpClient.Version getHttpVersion() { - return (System.getenv("HTTP_VERSION_EXHORT_CLIENT") != null - && System.getenv("HTTP_VERSION_EXHORT_CLIENT").contains("2")) + var version = Environment.get(HTTP_VERSION_EXHORT_CLIENT); + return (version != null && version.contains("2")) ? HttpClient.Version.HTTP_2 : HttpClient.Version.HTTP_1_1; } @@ -141,7 +148,7 @@ static HttpClient.Version getHttpVersion() { this.client = client; this.mapper = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); // Take default from config.properties in case client didn't override DEV MODE - if (System.getProperty("EXHORT_DEV_MODE") == null) { + if (Environment.get(EXHORT_DEV_MODE) == null) { try { InputStream exhortConfig = this.getClass().getClassLoader().getResourceAsStream("config.properties"); @@ -149,11 +156,11 @@ static HttpClient.Version getHttpVersion() { LOG.info( "config.properties not found on the class path, fallback to default DEV MODE =" + " false"); - System.setProperty("EXHORT_DEV_MODE", "false"); + System.setProperty(EXHORT_DEV_MODE, "false"); } else { Properties properties = new Properties(); properties.load(exhortConfig); - System.setProperty("EXHORT_DEV_MODE", (String) properties.get("EXHORT_DEV_MODE")); + System.setProperty(EXHORT_DEV_MODE, (String) properties.get(EXHORT_DEV_MODE)); } } catch (IOException e) { LOG.info( @@ -161,7 +168,7 @@ static HttpClient.Version getHttpVersion() { "Error loading config.properties , fallback to set default property DEV MODE =" + " false, Error message = %s", e.getMessage())); - System.setProperty("EXHORT_DEV_MODE", "false"); + System.setProperty(EXHORT_DEV_MODE, "false"); } } @@ -198,8 +205,8 @@ private static String getClientRequestId() { public String getExhortUrl() { String endpoint; - if (getBooleanValueEnvironment("EXHORT_DEV_MODE", "false")) { - endpoint = getStringValueEnvironment("DEV_EXHORT_BACKEND_URL", DEFAULT_ENDPOINT_DEV); + if (Environment.getBoolean(EXHORT_DEV_MODE, false)) { + endpoint = Environment.get(DEV_EXHORT_BACKEND_URL, DEFAULT_ENDPOINT_DEV); } else { endpoint = DEFAULT_ENDPOINT; @@ -209,8 +216,8 @@ public String getExhortUrl() { String.format( "EXHORT_DEV_MODE=%s,DEV_EXHORT_BACKEND_URL=%s, Chosen Backend URL=%s ," + " DEFAULT_ENDPOINT_DEV=%s , DEFAULT_ENDPOINT=%s", - getBooleanValueEnvironment("EXHORT_DEV_MODE", "false"), - getStringValueEnvironment("DEV_EXHORT_BACKEND_URL", DEFAULT_ENDPOINT_DEV), + Environment.getBoolean(EXHORT_DEV_MODE, false), + Environment.get(DEV_EXHORT_BACKEND_URL, DEFAULT_ENDPOINT_DEV), endpoint, DEFAULT_ENDPOINT_DEV, DEFAULT_ENDPOINT)); @@ -218,20 +225,6 @@ public String getExhortUrl() { return endpoint; } - public static boolean getBooleanValueEnvironment(String key, String defaultValue) { - String result = - Objects.requireNonNullElse( - System.getenv(key), Objects.requireNonNullElse(System.getProperty(key), defaultValue)); - return Boolean.parseBoolean(result.trim().toLowerCase()); - } - - public static String getStringValueEnvironment(String key, String defaultValue) { - String result = - Objects.requireNonNullElse( - System.getenv(key), Objects.requireNonNullElse(System.getProperty(key), defaultValue)); - return result; - } - @Override public CompletableFuture stackAnalysisMixed(final String manifestFile) throws IOException { @@ -390,7 +383,7 @@ private static void logExhortRequestId(HttpResponse response) { } public static boolean debugLoggingIsNeeded() { - return Boolean.parseBoolean(getStringValueEnvironment("EXHORT_DEBUG", "false")); + return Environment.getBoolean("EXHORT_DEBUG", false); } @Override @@ -639,20 +632,20 @@ private HttpRequest buildRequest( Stream.of(ExhortApi.TokenProvider.values()) .forEach( p -> { - var envToken = System.getenv(p.getVarName()); + var envToken = Environment.get(p.getVarName()); if (Objects.nonNull(envToken)) { request.setHeader(p.getHeaderName(), envToken); } else { - var propToken = System.getProperty(p.getVarName()); + var propToken = Environment.get(p.getVarName()); if (Objects.nonNull(propToken)) { request.setHeader(p.getHeaderName(), propToken); } } - var envUser = System.getenv(p.getUserHeaderName()); + var envUser = Environment.get(p.getUserHeaderName()); if (Objects.nonNull(envUser)) { request.setHeader(p.getUserHeaderName(), envUser); } else { - var propUser = System.getProperty(p.getUserVarName()); + var propUser = Environment.get(p.getUserVarName()); if (Objects.nonNull(propUser)) { request.setHeader(p.getUserHeaderName(), propUser); } @@ -685,11 +678,6 @@ private String calculateHeaderValue(String headerName) { } private String calculateHeaderValueActual(String headerName) { - String result = null; - result = System.getenv(headerName); - if (result == null) { - result = System.getProperty(headerName); - } - return result; + return Environment.get(headerName); } } diff --git a/src/main/java/com/redhat/exhort/providers/GoModulesProvider.java b/src/main/java/com/redhat/exhort/providers/GoModulesProvider.java index 16fcc643..000cef00 100644 --- a/src/main/java/com/redhat/exhort/providers/GoModulesProvider.java +++ b/src/main/java/com/redhat/exhort/providers/GoModulesProvider.java @@ -16,7 +16,6 @@ package com.redhat.exhort.providers; import static com.redhat.exhort.impl.ExhortApi.debugLoggingIsNeeded; -import static com.redhat.exhort.impl.ExhortApi.getBooleanValueEnvironment; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonMappingException; @@ -29,6 +28,7 @@ import com.redhat.exhort.sbom.SbomFactory; import com.redhat.exhort.tools.Ecosystem.Type; import com.redhat.exhort.tools.Operations; +import com.redhat.exhort.utils.Environment; import com.redhat.exhort.vcs.GitVersionControlSystemImpl; import com.redhat.exhort.vcs.TagInfo; import com.redhat.exhort.vcs.VersionControlSystem; @@ -129,7 +129,7 @@ Sbom getDependenciesSbom(Path manifestPath, boolean buildTree) throws IOExceptio Sbom sbom; List ignoredDeps = getIgnoredDeps(manifestPath); boolean matchManifestVersions = - getBooleanValueEnvironment(Provider.PROP_MATCH_MANIFEST_VERSIONS, "false"); + Environment.getBoolean(Provider.PROP_MATCH_MANIFEST_VERSIONS, false); if (matchManifestVersions) { String[] goModGraphLines = goModulesResult.split(System.lineSeparator()); performManifestVersionsCheck(goModGraphLines, manifestPath); @@ -281,8 +281,7 @@ private Sbom buildSbomFromGraph( startingIndex += deps.size(); } } - boolean goMvsLogicEnabled = - getBooleanValueEnvironment(PROP_EXHORT_GO_MVS_LOGIC_ENABLED, "false"); + boolean goMvsLogicEnabled = Environment.getBoolean(PROP_EXHORT_GO_MVS_LOGIC_ENABLED, false); if (goMvsLogicEnabled) { edges = getFinalPackagesVersionsForModule(edges, manifestPath); } diff --git a/src/main/java/com/redhat/exhort/providers/JavaMavenProvider.java b/src/main/java/com/redhat/exhort/providers/JavaMavenProvider.java index da590f18..ae304a50 100644 --- a/src/main/java/com/redhat/exhort/providers/JavaMavenProvider.java +++ b/src/main/java/com/redhat/exhort/providers/JavaMavenProvider.java @@ -21,12 +21,12 @@ import com.github.packageurl.PackageURL; import com.redhat.exhort.Api; import com.redhat.exhort.Provider; -import com.redhat.exhort.impl.ExhortApi; import com.redhat.exhort.logging.LoggersFactory; import com.redhat.exhort.sbom.Sbom; import com.redhat.exhort.sbom.SbomFactory; import com.redhat.exhort.tools.Ecosystem.Type; import com.redhat.exhort.tools.Operations; +import com.redhat.exhort.utils.Environment; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -50,6 +50,7 @@ */ public final class JavaMavenProvider extends BaseJavaProvider { + private static final String PROP_JAVA_HOME = "JAVA_HOME"; private Logger log = LoggersFactory.getLogger(this.getClass().getName()); public JavaMavenProvider(Path manifest) { @@ -303,9 +304,9 @@ private List getDependencies(final Path manifestPath) thro } Map getMvnExecEnvs() { - var javaHome = ExhortApi.getStringValueEnvironment("JAVA_HOME", ""); + var javaHome = Environment.get(PROP_JAVA_HOME); if (javaHome != null && !javaHome.isBlank()) { - return Collections.singletonMap("JAVA_HOME", javaHome); + return Collections.singletonMap(PROP_JAVA_HOME, javaHome); } return null; } diff --git a/src/main/java/com/redhat/exhort/providers/JavaScriptNpmProvider.java b/src/main/java/com/redhat/exhort/providers/JavaScriptNpmProvider.java index 8038c5d7..4f06102f 100644 --- a/src/main/java/com/redhat/exhort/providers/JavaScriptNpmProvider.java +++ b/src/main/java/com/redhat/exhort/providers/JavaScriptNpmProvider.java @@ -30,6 +30,7 @@ import com.redhat.exhort.tools.Ecosystem; import com.redhat.exhort.tools.Ecosystem.Type; import com.redhat.exhort.tools.Operations; +import com.redhat.exhort.utils.Environment; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -48,6 +49,7 @@ */ public final class JavaScriptNpmProvider extends Provider { + private static final String PROP_PATH = "PATH"; private System.Logger log = System.getLogger(this.getClass().getName()); public JavaScriptNpmProvider(Path manifest) { @@ -219,13 +221,13 @@ private List getIgnoredDeps(Path manifestPath) throws IOException { } Map getNpmExecEnv() { - String nodeHome = System.getProperty("NODE_HOME"); + String nodeHome = Environment.get("NODE_HOME"); if (nodeHome != null && !nodeHome.isBlank()) { - String path = System.getenv("PATH"); + String path = Environment.get(PROP_PATH); if (path != null) { - return Collections.singletonMap("PATH", path + File.pathSeparator + nodeHome); + return Collections.singletonMap(PROP_PATH, path + File.pathSeparator + nodeHome); } else { - return Collections.singletonMap("PATH", nodeHome); + return Collections.singletonMap(PROP_PATH, nodeHome); } } return null; diff --git a/src/main/java/com/redhat/exhort/providers/PythonPipProvider.java b/src/main/java/com/redhat/exhort/providers/PythonPipProvider.java index 6f1634ff..9e566bc3 100644 --- a/src/main/java/com/redhat/exhort/providers/PythonPipProvider.java +++ b/src/main/java/com/redhat/exhort/providers/PythonPipProvider.java @@ -27,6 +27,7 @@ import com.redhat.exhort.sbom.SbomFactory; import com.redhat.exhort.tools.Ecosystem; import com.redhat.exhort.tools.Operations; +import com.redhat.exhort.utils.Environment; import com.redhat.exhort.utils.PythonControllerBase; import com.redhat.exhort.utils.PythonControllerRealEnv; import com.redhat.exhort.utils.PythonControllerVirtualEnv; @@ -137,8 +138,7 @@ private void handleIgnoredDependencies(String manifestContent, Sbom sbom) { // resolved by pip. sbom.setBelongingCriteriaBinaryAlgorithm(Sbom.BelongingCondition.NAME); sbom.filterIgnoredDeps(ignoredDepsNoVersions); - boolean matchManifestVersions = - getBooleanValueEnvironment(PROP_MATCH_MANIFEST_VERSIONS, "true"); + boolean matchManifestVersions = Environment.getBoolean(PROP_MATCH_MANIFEST_VERSIONS, true); // filter out by purl from sbom all exhortignore dependencies that their version hardcoded in // requirements.txt - // in case all versions in manifest matching installed versions of packages in environment. @@ -208,37 +208,23 @@ private PackageURL toPurl(String name, String version) { private PythonControllerBase getPythonController() { String pythonPipBinaries; - String useVirtualPythonEnv; - if (!getStringValueEnvironment(PythonControllerBase.PROP_EXHORT_PIP_SHOW, "").trim().equals("") - && !getStringValueEnvironment(PythonControllerBase.PROP_EXHORT_PIP_FREEZE, "") - .trim() - .equals("")) { + boolean useVirtualPythonEnv; + if (!Environment.get(PythonControllerBase.PROP_EXHORT_PIP_SHOW, "").trim().equals("") + && !Environment.get(PythonControllerBase.PROP_EXHORT_PIP_FREEZE, "").trim().equals("")) { pythonPipBinaries = "python;;pip"; - useVirtualPythonEnv = "false"; + useVirtualPythonEnv = false; } else { pythonPipBinaries = getPythonPipBinaries(); useVirtualPythonEnv = - Objects.requireNonNullElseGet( - System.getenv(PythonControllerBase.PROP_EXHORT_PYTHON_VIRTUAL_ENV), - () -> - Objects.requireNonNullElse( - System.getProperty(PythonControllerBase.PROP_EXHORT_PYTHON_VIRTUAL_ENV), - "false")); + Environment.getBoolean(PythonControllerBase.PROP_EXHORT_PYTHON_VIRTUAL_ENV, false); } String[] parts = pythonPipBinaries.split(";;"); var python = parts[0]; var pip = parts[1]; - useVirtualPythonEnv = - Objects.requireNonNullElseGet( - System.getenv(PythonControllerBase.PROP_EXHORT_PYTHON_VIRTUAL_ENV), - () -> - Objects.requireNonNullElse( - System.getProperty(PythonControllerBase.PROP_EXHORT_PYTHON_VIRTUAL_ENV), - "false")); PythonControllerBase pythonController; if (this.pythonController == null) { - if (Boolean.parseBoolean(useVirtualPythonEnv)) { + if (useVirtualPythonEnv) { pythonController = new PythonControllerVirtualEnv(python); } else { pythonController = new PythonControllerRealEnv(python, pip); diff --git a/src/main/java/com/redhat/exhort/sbom/CycloneDXSbom.java b/src/main/java/com/redhat/exhort/sbom/CycloneDXSbom.java index 891b63c5..ffb2d9c4 100644 --- a/src/main/java/com/redhat/exhort/sbom/CycloneDXSbom.java +++ b/src/main/java/com/redhat/exhort/sbom/CycloneDXSbom.java @@ -20,6 +20,7 @@ import com.github.packageurl.MalformedPackageURLException; import com.github.packageurl.PackageURL; import com.redhat.exhort.logging.LoggersFactory; +import com.redhat.exhort.utils.Environment; import java.util.*; import java.util.function.BiPredicate; import java.util.function.Predicate; @@ -35,6 +36,7 @@ public class CycloneDXSbom implements Sbom { + private static final String EXHORT_IGNORE_METHOD = "EXHORT_IGNORE_METHOD"; private Logger log = LoggersFactory.getLogger(this.getClass().getName()); private static final Version VERSION = Version.VERSION_14; private String exhortIgnoreMethod; @@ -109,16 +111,8 @@ public Sbom filterIgnoredDeps(Collection ignoredDeps) { } private String getExhortIgnoreMethod() { - boolean result; - return System.getenv("EXHORT_IGNORE_METHOD") != null - ? System.getenv("EXHORT_IGNORE_METHOD").trim().toLowerCase() - : getExhortIgnoreProperty(); - } - - private String getExhortIgnoreProperty() { - return System.getProperty("EXHORT_IGNORE_METHOD") != null - ? System.getProperty("EXHORT_IGNORE_METHOD").trim().toLowerCase() - : null; + var val = Environment.get(EXHORT_IGNORE_METHOD); + return val != null ? val.trim().toLowerCase() : null; } private Component newRootComponent(PackageURL ref) { diff --git a/src/main/java/com/redhat/exhort/tools/Operations.java b/src/main/java/com/redhat/exhort/tools/Operations.java index 7c85cac5..acd3647e 100644 --- a/src/main/java/com/redhat/exhort/tools/Operations.java +++ b/src/main/java/com/redhat/exhort/tools/Operations.java @@ -17,12 +17,12 @@ import static java.lang.String.join; +import com.redhat.exhort.utils.Environment; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.nio.file.Path; import java.util.Map; -import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -46,9 +46,7 @@ private Operations() { public static String getCustomPathOrElse(String defaultExecutable) { var target = defaultExecutable.toUpperCase().replaceAll(" ", "_").replaceAll("-", "_"); var executableKey = String.format("EXHORT_%s_PATH", target); - return Objects.requireNonNullElseGet( - System.getenv(executableKey), - () -> Objects.requireNonNullElse(System.getProperty(executableKey), defaultExecutable)); + return Environment.get(executableKey, defaultExecutable); } /** diff --git a/src/main/java/com/redhat/exhort/utils/Environment.java b/src/main/java/com/redhat/exhort/utils/Environment.java new file mode 100644 index 00000000..9263e769 --- /dev/null +++ b/src/main/java/com/redhat/exhort/utils/Environment.java @@ -0,0 +1,41 @@ +/* + * Copyright © 2023 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.redhat.exhort.utils; + +import java.util.Optional; + +public final class Environment { + + private Environment() {} + + public static final String get(String name, String defaultValue) { + return Optional.ofNullable(System.getProperty(name)) + .or(() -> Optional.ofNullable(System.getenv(name))) + .orElse(defaultValue); + } + + public static final String get(String name) { + return get(name, null); + } + + public static final boolean getBoolean(String key, Boolean defaultValue) { + var val = get(key); + if (val != null) { + return Boolean.parseBoolean(val.trim().toLowerCase()); + } + return defaultValue != null ? defaultValue : false; + } +} diff --git a/src/main/java/com/redhat/exhort/utils/PythonControllerBase.java b/src/main/java/com/redhat/exhort/utils/PythonControllerBase.java index 12c07ce5..e3664935 100644 --- a/src/main/java/com/redhat/exhort/utils/PythonControllerBase.java +++ b/src/main/java/com/redhat/exhort/utils/PythonControllerBase.java @@ -17,8 +17,6 @@ import static com.redhat.exhort.Provider.PROP_MATCH_MANIFEST_VERSIONS; import static com.redhat.exhort.impl.ExhortApi.debugLoggingIsNeeded; -import static com.redhat.exhort.impl.ExhortApi.getBooleanValueEnvironment; -import static com.redhat.exhort.impl.ExhortApi.getStringValueEnvironment; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; @@ -79,7 +77,7 @@ public final List> getDependencies( } if (automaticallyInstallPackageOnEnvironment()) { boolean installBestEfforts = - getBooleanValueEnvironment(PROP_EXHORT_PYTHON_INSTALL_BEST_EFFORTS, "false"); + Environment.getBoolean(PROP_EXHORT_PYTHON_INSTALL_BEST_EFFORTS, false); /* make best efforts to install the requirements.txt on the virtual environment created from the python3 passed in. that means that it will install the packages without referring to @@ -87,8 +85,7 @@ public final List> getDependencies( environment( and of pip package manager) for each package. */ if (installBestEfforts) { - boolean matchManifestVersions = - getBooleanValueEnvironment(PROP_MATCH_MANIFEST_VERSIONS, "true"); + boolean matchManifestVersions = Environment.getBoolean(PROP_MATCH_MANIFEST_VERSIONS, true); if (matchManifestVersions) { throw new RuntimeException( "Conflicting settings, " @@ -172,8 +169,7 @@ private List> getDependenciesImpl( "Error while trying to convert the cached environment dependencies to JSON string"); throw new RuntimeException(e); } - boolean matchManifestVersions = - getBooleanValueEnvironment(PROP_MATCH_MANIFEST_VERSIONS, "true"); + boolean matchManifestVersions = Environment.getBoolean(PROP_MATCH_MANIFEST_VERSIONS, true); for (String dep : linesOfRequirements) { if (matchManifestVersions) { @@ -287,7 +283,7 @@ List mapToPythonDependencies(String jsonString) { } private String executeCommandOrExtractFromEnv(String EnvVar, String... cmdList) { - String envValue = getStringValueEnvironment(EnvVar, ""); + String envValue = Environment.get(EnvVar, ""); if (envValue.trim().isBlank()) return Operations.runProcessGetOutput(pythonEnvironmentDir, cmdList); return new String(Base64.getDecoder().decode(envValue)); @@ -414,7 +410,7 @@ private PythonDependency getPythonDependencyByShowStringBlock(String pipShowStri } private void fillCacheWithEnvironmentDeps(Map cache) { - boolean usePipDepTree = getBooleanValueEnvironment(PROP_EXHORT_PIP_USE_DEP_TREE, "false"); + boolean usePipDepTree = Environment.getBoolean(PROP_EXHORT_PIP_USE_DEP_TREE, false); if (usePipDepTree) { getDependencyTreeJsonFromPipDepTree().forEach(d -> saveToCacheWithKeyVariations(cache, d)); } else { diff --git a/src/main/java/com/redhat/exhort/utils/PythonControllerRealEnv.java b/src/main/java/com/redhat/exhort/utils/PythonControllerRealEnv.java index ff693425..14baec01 100644 --- a/src/main/java/com/redhat/exhort/utils/PythonControllerRealEnv.java +++ b/src/main/java/com/redhat/exhort/utils/PythonControllerRealEnv.java @@ -25,7 +25,7 @@ public PythonControllerRealEnv(String pathToPythonBin, String pathToPip) { if (this.pipBinaryDir == null) { this.pipBinaryDir = pipPath; } - this.pythonEnvironmentDir = Path.of(System.getProperty("user.dir")); + this.pythonEnvironmentDir = Path.of(Environment.get("user.dir")); this.pathToPythonBin = pathToPythonBin; } diff --git a/src/test/java/com/redhat/exhort/image/ImageUtilsTest.java b/src/test/java/com/redhat/exhort/image/ImageUtilsTest.java index 2ce13cd8..91fc4e88 100644 --- a/src/test/java/com/redhat/exhort/image/ImageUtilsTest.java +++ b/src/test/java/com/redhat/exhort/image/ImageUtilsTest.java @@ -39,6 +39,7 @@ import com.github.packageurl.MalformedPackageURLException; import com.redhat.exhort.ExhortTest; import com.redhat.exhort.tools.Operations; +import com.redhat.exhort.utils.Environment; import java.io.BufferedReader; import java.io.File; import java.io.IOException; @@ -55,8 +56,8 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import org.junitpioneer.jupiter.ClearEnvironmentVariable; -import org.junitpioneer.jupiter.SetEnvironmentVariable; +import org.junitpioneer.jupiter.ClearSystemProperty; +import org.junitpioneer.jupiter.SetSystemProperty; import org.mockito.MockedStatic; import org.mockito.Mockito; @@ -116,12 +117,12 @@ static Stream dockerVariantSources() { } @Test - @ClearEnvironmentVariable(key = "PATH") - @ClearEnvironmentVariable(key = "EXHORT_SYFT_PATH") - @ClearEnvironmentVariable(key = EXHORT_SYFT_CONFIG_PATH) - @ClearEnvironmentVariable(key = "EXHORT_DOCKER_PATH") - @ClearEnvironmentVariable(key = "EXHORT_PODMAN_PATH") - @ClearEnvironmentVariable(key = EXHORT_SYFT_IMAGE_SOURCE) + @ClearSystemProperty(key = "PATH") + @ClearSystemProperty(key = "EXHORT_SYFT_PATH") + @ClearSystemProperty(key = EXHORT_SYFT_CONFIG_PATH) + @ClearSystemProperty(key = "EXHORT_DOCKER_PATH") + @ClearSystemProperty(key = "EXHORT_PODMAN_PATH") + @ClearSystemProperty(key = EXHORT_SYFT_IMAGE_SOURCE) void test_generate_image_sbom() throws IOException, MalformedPackageURLException { try (MockedStatic mock = Mockito.mockStatic(Operations.class); var is = getResourceAsStreamDecision(this.getClass(), "msc/image/image_sbom.json")) { @@ -162,9 +163,9 @@ void test_generate_image_sbom() throws IOException, MalformedPackageURLException } @Test - @ClearEnvironmentVariable(key = "EXHORT_SKOPEO_PATH") - @ClearEnvironmentVariable(key = EXHORT_SKOPEO_CONFIG_PATH) - @ClearEnvironmentVariable(key = EXHORT_IMAGE_SERVICE_ENDPOINT) + @ClearSystemProperty(key = "EXHORT_SKOPEO_PATH") + @ClearSystemProperty(key = EXHORT_SKOPEO_CONFIG_PATH) + @ClearSystemProperty(key = EXHORT_IMAGE_SERVICE_ENDPOINT) void test_get_image_digests_single() throws IOException { try (MockedStatic mock = Mockito.mockStatic(Operations.class); var isRaw = @@ -226,9 +227,9 @@ void test_get_image_digests_single() throws IOException { } @Test - @ClearEnvironmentVariable(key = "EXHORT_SKOPEO_PATH") - @ClearEnvironmentVariable(key = EXHORT_SKOPEO_CONFIG_PATH) - @ClearEnvironmentVariable(key = EXHORT_IMAGE_SERVICE_ENDPOINT) + @ClearSystemProperty(key = "EXHORT_SKOPEO_PATH") + @ClearSystemProperty(key = EXHORT_SKOPEO_CONFIG_PATH) + @ClearSystemProperty(key = EXHORT_IMAGE_SERVICE_ENDPOINT) void test_get_image_digests_multiple() throws IOException { try (MockedStatic mock = Mockito.mockStatic(Operations.class); var is = @@ -277,33 +278,33 @@ void test_get_image_digests_multiple() throws IOException { } @Test - @SetEnvironmentVariable(key = EXHORT_IMAGE_PLATFORM, value = mockImagePlatform) - @SetEnvironmentVariable(key = EXHORT_SYFT_IMAGE_SOURCE, value = mockSyftSource) - @SetEnvironmentVariable(key = EXHORT_IMAGE_OS, value = mockOs) - @SetEnvironmentVariable(key = EXHORT_IMAGE_ARCH, value = mockArch) - @SetEnvironmentVariable(key = EXHORT_IMAGE_VARIANT, value = mockVariant) + @SetSystemProperty(key = EXHORT_IMAGE_PLATFORM, value = mockImagePlatform) + @SetSystemProperty(key = EXHORT_SYFT_IMAGE_SOURCE, value = mockSyftSource) + @SetSystemProperty(key = EXHORT_IMAGE_OS, value = mockOs) + @SetSystemProperty(key = EXHORT_IMAGE_ARCH, value = mockArch) + @SetSystemProperty(key = EXHORT_IMAGE_VARIANT, value = mockVariant) void test_get_image_platform() { var platform = ImageUtils.getImagePlatform(); assertEquals(new Platform(mockImagePlatform), platform); } @Test - @ClearEnvironmentVariable(key = EXHORT_IMAGE_PLATFORM) - @SetEnvironmentVariable(key = EXHORT_SYFT_IMAGE_SOURCE, value = mockSyftSource) - @SetEnvironmentVariable(key = EXHORT_IMAGE_OS, value = mockOs) - @SetEnvironmentVariable(key = EXHORT_IMAGE_ARCH, value = mockArch) - @SetEnvironmentVariable(key = EXHORT_IMAGE_VARIANT, value = mockVariant) + @ClearSystemProperty(key = EXHORT_IMAGE_PLATFORM) + @SetSystemProperty(key = EXHORT_SYFT_IMAGE_SOURCE, value = mockSyftSource) + @SetSystemProperty(key = EXHORT_IMAGE_OS, value = mockOs) + @SetSystemProperty(key = EXHORT_IMAGE_ARCH, value = mockArch) + @SetSystemProperty(key = EXHORT_IMAGE_VARIANT, value = mockVariant) void test_get_image_platform_no_default() { var platform = ImageUtils.getImagePlatform(); assertEquals(new Platform(mockOs, mockArch, mockVariant), platform); } @Test - @ClearEnvironmentVariable(key = EXHORT_IMAGE_PLATFORM) - @SetEnvironmentVariable(key = EXHORT_SYFT_IMAGE_SOURCE, value = "podman") - @SetEnvironmentVariable(key = EXHORT_IMAGE_OS, value = mockOs) - @SetEnvironmentVariable(key = EXHORT_IMAGE_ARCH, value = mockArch) - @ClearEnvironmentVariable(key = EXHORT_IMAGE_VARIANT) + @ClearSystemProperty(key = EXHORT_IMAGE_PLATFORM) + @SetSystemProperty(key = EXHORT_SYFT_IMAGE_SOURCE, value = "podman") + @SetSystemProperty(key = EXHORT_IMAGE_OS, value = mockOs) + @SetSystemProperty(key = EXHORT_IMAGE_ARCH, value = mockArch) + @ClearSystemProperty(key = EXHORT_IMAGE_VARIANT) void test_get_image_platform_no_default_no_variant() { try (MockedStatic mock = Mockito.mockStatic(Operations.class)) { mock.when(() -> Operations.getCustomPathOrElse(eq("podman"))).thenReturn("podman"); @@ -320,12 +321,12 @@ void test_get_image_platform_no_default_no_variant() { } @Test - @ClearEnvironmentVariable(key = EXHORT_IMAGE_PLATFORM) - @SetEnvironmentVariable(key = EXHORT_SYFT_IMAGE_SOURCE, value = "podman") - @ClearEnvironmentVariable(key = EXHORT_IMAGE_OS) - @ClearEnvironmentVariable(key = EXHORT_IMAGE_ARCH) - @ClearEnvironmentVariable(key = EXHORT_IMAGE_VARIANT) - @ClearEnvironmentVariable(key = "PATH") + @ClearSystemProperty(key = EXHORT_IMAGE_PLATFORM) + @SetSystemProperty(key = EXHORT_SYFT_IMAGE_SOURCE, value = "podman") + @ClearSystemProperty(key = EXHORT_IMAGE_OS) + @ClearSystemProperty(key = EXHORT_IMAGE_ARCH) + @ClearSystemProperty(key = EXHORT_IMAGE_VARIANT) + @ClearSystemProperty(key = "PATH") void test_get_image_platform_no_defaults() { try (MockedStatic mock = Mockito.mockStatic(Operations.class)) { mock.when(() -> Operations.getCustomPathOrElse(eq("podman"))).thenReturn("podman"); @@ -343,127 +344,132 @@ void test_get_image_platform_no_defaults() { } @Test - @ClearEnvironmentVariable(key = "PATH") - @SetEnvironmentVariable(key = "EXHORT_SYFT_PATH", value = mockSyftPath) - @SetEnvironmentVariable(key = EXHORT_SYFT_CONFIG_PATH, value = mockSyftConfig) - @SetEnvironmentVariable(key = "EXHORT_DOCKER_PATH", value = mockDockerPath) - @SetEnvironmentVariable(key = "EXHORT_PODMAN_PATH", value = mockPodmanPath) - @SetEnvironmentVariable(key = EXHORT_SYFT_IMAGE_SOURCE, value = mockSyftSource) + @SetSystemProperty(key = EXHORT_SYFT_CONFIG_PATH, value = mockSyftConfig) + @SetSystemProperty(key = EXHORT_SYFT_IMAGE_SOURCE, value = mockSyftSource) void test_exec_syft() { - try (MockedStatic mock = Mockito.mockStatic(Operations.class)) { - var output = new Operations.ProcessExecOutput("test-output", "test-error", 0); - - mock.when(() -> Operations.getCustomPathOrElse(eq("syft"))).thenReturn(mockSyftPath); - - mock.when(() -> Operations.getCustomPathOrElse(eq("docker"))).thenReturn(mockDockerPath); - - mock.when(() -> Operations.getCustomPathOrElse(eq("podman"))).thenReturn(mockPodmanPath); - - mock.when( - () -> - Operations.runProcessGetFullOutput( - isNull(), - aryEq( - new String[] { - mockSyftPath, - mockImageRef.getImage().getFullName(), - "--from", - mockSyftSource, - "-c", - mockSyftConfig, - "-s", - "all-layers", - "-o", - "cyclonedx-json", - "-q" - }), - eq( - new String[] { - "PATH=" + "test-path/" + File.pathSeparator + "test-path/" - }))) - .thenReturn(output); - - assertThat(ImageUtils.execSyft(mockImageRef)).isEqualTo(output); + try (MockedStatic mockEnv = + Mockito.mockStatic(Environment.class, Mockito.CALLS_REAL_METHODS)) { + mockEnv.when(() -> Environment.get("PATH")).thenReturn(null); + try (MockedStatic opsMock = Mockito.mockStatic(Operations.class)) { + var output = new Operations.ProcessExecOutput("test-output", "test-error", 0); + + opsMock.when(() -> Operations.getCustomPathOrElse(eq("syft"))).thenReturn(mockSyftPath); + + opsMock.when(() -> Operations.getCustomPathOrElse(eq("docker"))).thenReturn(mockDockerPath); + + opsMock.when(() -> Operations.getCustomPathOrElse(eq("podman"))).thenReturn(mockPodmanPath); + + opsMock + .when( + () -> + Operations.runProcessGetFullOutput( + isNull(), + aryEq( + new String[] { + mockSyftPath, + mockImageRef.getImage().getFullName(), + "--from", + mockSyftSource, + "-c", + mockSyftConfig, + "-s", + "all-layers", + "-o", + "cyclonedx-json", + "-q" + }), + eq( + new String[] { + "PATH=" + "test-path/" + File.pathSeparator + "test-path/" + }))) + .thenReturn(output); + + assertThat(ImageUtils.execSyft(mockImageRef)).isEqualTo(output); + } } } @Test - @ClearEnvironmentVariable(key = "PATH") - @ClearEnvironmentVariable(key = "EXHORT_SYFT_PATH") - @ClearEnvironmentVariable(key = EXHORT_SYFT_CONFIG_PATH) - @ClearEnvironmentVariable(key = "EXHORT_DOCKER_PATH") - @ClearEnvironmentVariable(key = "EXHORT_PODMAN_PATH") - @ClearEnvironmentVariable(key = EXHORT_SYFT_IMAGE_SOURCE) + @ClearSystemProperty(key = EXHORT_SYFT_CONFIG_PATH) + @ClearSystemProperty(key = EXHORT_SYFT_IMAGE_SOURCE) void test_exec_syft_no_config_no_source() { - try (MockedStatic mock = Mockito.mockStatic(Operations.class)) { - var output = new Operations.ProcessExecOutput("test-output", "test-error", 0); - - mock.when(() -> Operations.getCustomPathOrElse(eq("docker"))).thenReturn("docker"); - - mock.when(() -> Operations.getCustomPathOrElse(eq("podman"))).thenReturn("podman"); - - mock.when(() -> Operations.getCustomPathOrElse(eq("syft"))).thenReturn("syft"); - - mock.when( - () -> - Operations.runProcessGetFullOutput( - isNull(), - aryEq( - new String[] { - "syft", - mockImageRef.getImage().getFullName(), - "-s", - "all-layers", - "-o", - "cyclonedx-json", - "-q" - }), - isNull())) - .thenReturn(output); - - assertThat(ImageUtils.execSyft(mockImageRef)).isEqualTo(output); + try (MockedStatic mockEnv = + Mockito.mockStatic(Environment.class, Mockito.CALLS_REAL_METHODS)) { + mockEnv.when(() -> Environment.get("PATH")).thenReturn(null); + try (MockedStatic mock = Mockito.mockStatic(Operations.class)) { + var output = new Operations.ProcessExecOutput("test-output", "test-error", 0); + + mock.when(() -> Operations.getCustomPathOrElse(eq("docker"))).thenReturn("docker"); + + mock.when(() -> Operations.getCustomPathOrElse(eq("podman"))).thenReturn("podman"); + + mock.when(() -> Operations.getCustomPathOrElse(eq("syft"))).thenReturn("syft"); + + mock.when( + () -> + Operations.runProcessGetFullOutput( + isNull(), + aryEq( + new String[] { + "syft", + mockImageRef.getImage().getFullName(), + "-s", + "all-layers", + "-o", + "cyclonedx-json", + "-q" + }), + isNull())) + .thenReturn(output); + + assertThat(ImageUtils.execSyft(mockImageRef)).isEqualTo(output); + } } } @Test - @ClearEnvironmentVariable(key = "PATH") void test_get_syft_envs() { - var envs1 = ImageUtils.getSyftEnvs("", ""); - assertTrue(envs1.isEmpty()); - - var envs2 = ImageUtils.getSyftEnvs("test-docker-path", ""); - var expected_envs2 = new ArrayList<>(); - expected_envs2.add("PATH=test-docker-path"); - assertEquals(expected_envs2, envs2); - - var envs3 = ImageUtils.getSyftEnvs("", "test-podman-path"); - var expected_envs3 = new ArrayList<>(); - expected_envs3.add("PATH=test-podman-path"); - assertEquals(expected_envs3, envs3); - - var envs4 = ImageUtils.getSyftEnvs("test-docker-path", "test-podman-path"); - var expected_envs4 = new ArrayList<>(); - expected_envs4.add("PATH=test-docker-path" + File.pathSeparator + "test-podman-path"); - assertEquals(expected_envs4, envs4); + try (MockedStatic mockEnv = Mockito.mockStatic(Environment.class)) { + mockEnv.when(() -> Environment.get("PATH")).thenReturn(null); + + var envs1 = ImageUtils.getSyftEnvs("", ""); + assertTrue(envs1.isEmpty()); + + var envs2 = ImageUtils.getSyftEnvs("test-docker-path", ""); + var expected_envs2 = new ArrayList<>(); + expected_envs2.add("PATH=test-docker-path"); + assertEquals(expected_envs2, envs2); + + var envs3 = ImageUtils.getSyftEnvs("", "test-podman-path"); + var expected_envs3 = new ArrayList<>(); + expected_envs3.add("PATH=test-podman-path"); + assertEquals(expected_envs3, envs3); + + var envs4 = ImageUtils.getSyftEnvs("test-docker-path", "test-podman-path"); + var expected_envs4 = new ArrayList<>(); + expected_envs4.add("PATH=test-docker-path" + File.pathSeparator + "test-podman-path"); + assertEquals(expected_envs4, envs4); + } } @Test - @SetEnvironmentVariable(key = "PATH", value = mockPath) + @SetSystemProperty(key = "PATH", value = mockPath) void test_update_PATH_env() { var path = ImageUtils.updatePATHEnv("test-exec-path"); assertEquals("PATH=test-path" + File.pathSeparator + "test-exec-path", path); } @Test - @ClearEnvironmentVariable(key = "PATH") void test_update_PATH_env_no_PATH() { - var path = ImageUtils.updatePATHEnv("test-exec-path"); - assertEquals("PATH=test-exec-path", path); + try (MockedStatic mockEnv = Mockito.mockStatic(Environment.class)) { + var path = ImageUtils.updatePATHEnv("test-exec-path"); + assertEquals("PATH=test-exec-path", path); + } } @Test - @SetEnvironmentVariable(key = "EXHORT_DOCKER_PATH", value = mockDockerPath) - @SetEnvironmentVariable(key = "PATH", value = mockPath) + @SetSystemProperty(key = "EXHORT_DOCKER_PATH", value = mockDockerPath) + @SetSystemProperty(key = "PATH", value = mockPath) void test_host_info_docker() { try (MockedStatic mock = Mockito.mockStatic(Operations.class)) { var output = new Operations.ProcessExecOutput("info0: test\n info: test-output", "", 0); @@ -481,7 +487,7 @@ void test_host_info_docker() { } @Test - @ClearEnvironmentVariable(key = "EXHORT_DOCKER_PATH") + @ClearSystemProperty(key = "EXHORT_DOCKER_PATH") void test_host_info_no_docker_path() { try (MockedStatic mock = Mockito.mockStatic(Operations.class)) { var output = new Operations.ProcessExecOutput("", "test-error", 0); @@ -505,8 +511,8 @@ void test_host_info_no_docker_path() { } @Test - @SetEnvironmentVariable(key = "EXHORT_DOCKER_PATH", value = mockDockerPath) - @SetEnvironmentVariable(key = "PATH", value = mockPath) + @SetSystemProperty(key = "EXHORT_DOCKER_PATH", value = mockDockerPath) + @SetSystemProperty(key = "PATH", value = mockPath) void test_docker_get_os() { try (MockedStatic mock = Mockito.mockStatic(Operations.class)) { var output = new Operations.ProcessExecOutput("OSType: test-output", "", 0); @@ -524,8 +530,8 @@ void test_docker_get_os() { } @ParameterizedTest(name = "{0}") - @SetEnvironmentVariable(key = "EXHORT_DOCKER_PATH", value = mockDockerPath) - @SetEnvironmentVariable(key = "PATH", value = mockPath) + @SetSystemProperty(key = "EXHORT_DOCKER_PATH", value = mockDockerPath) + @SetSystemProperty(key = "PATH", value = mockPath) @MethodSource("dockerArchSources") void test_docker_get_arch(String sysArch, String arch) { try (MockedStatic mock = Mockito.mockStatic(Operations.class)) { @@ -544,8 +550,8 @@ void test_docker_get_arch(String sysArch, String arch) { } @ParameterizedTest(name = "{0}") - @SetEnvironmentVariable(key = "EXHORT_DOCKER_PATH", value = mockDockerPath) - @SetEnvironmentVariable(key = "PATH", value = mockPath) + @SetSystemProperty(key = "EXHORT_DOCKER_PATH", value = mockDockerPath) + @SetSystemProperty(key = "PATH", value = mockPath) @MethodSource("dockerVariantSources") void test_docker_get_variant(String sysArch, String variant) { try (MockedStatic mock = Mockito.mockStatic(Operations.class)) { @@ -564,8 +570,8 @@ void test_docker_get_variant(String sysArch, String variant) { } @Test - @SetEnvironmentVariable(key = "EXHORT_PODMAN_PATH", value = mockPodmanPath) - @SetEnvironmentVariable(key = "PATH", value = mockPath) + @SetSystemProperty(key = "EXHORT_PODMAN_PATH", value = mockPodmanPath) + @SetSystemProperty(key = "PATH", value = mockPath) void test_host_info_podman() { try (MockedStatic mock = Mockito.mockStatic(Operations.class)) { var output = new Operations.ProcessExecOutput("info: test-output\nabcdesss", "", 0); @@ -583,8 +589,8 @@ void test_host_info_podman() { } @Test - @SetEnvironmentVariable(key = "EXHORT_PODMAN_PATH", value = mockPodmanPath) - @SetEnvironmentVariable(key = "PATH", value = mockPath) + @SetSystemProperty(key = "EXHORT_PODMAN_PATH", value = mockPodmanPath) + @SetSystemProperty(key = "PATH", value = mockPath) void test_podman_get_os() { try (MockedStatic mock = Mockito.mockStatic(Operations.class)) { var output = new Operations.ProcessExecOutput("os: test-output", "", 0); @@ -602,8 +608,8 @@ void test_podman_get_os() { } @Test - @SetEnvironmentVariable(key = "EXHORT_PODMAN_PATH", value = mockPodmanPath) - @SetEnvironmentVariable(key = "PATH", value = mockPath) + @SetSystemProperty(key = "EXHORT_PODMAN_PATH", value = mockPodmanPath) + @SetSystemProperty(key = "PATH", value = mockPath) void test_podman_get_arch() { try (MockedStatic mock = Mockito.mockStatic(Operations.class)) { var output = new Operations.ProcessExecOutput("arch: test-output", "", 0); @@ -621,8 +627,8 @@ void test_podman_get_arch() { } @Test - @SetEnvironmentVariable(key = "EXHORT_PODMAN_PATH", value = mockPodmanPath) - @SetEnvironmentVariable(key = "PATH", value = mockPath) + @SetSystemProperty(key = "EXHORT_PODMAN_PATH", value = mockPodmanPath) + @SetSystemProperty(key = "PATH", value = mockPath) void test_podman_get_variant() { try (MockedStatic mock = Mockito.mockStatic(Operations.class)) { var output = new Operations.ProcessExecOutput("variant: test-output", "", 0); @@ -649,9 +655,9 @@ void test_docker_podman_info() { } @Test - @SetEnvironmentVariable(key = "EXHORT_SKOPEO_PATH", value = mockSkopeoPath) - @SetEnvironmentVariable(key = EXHORT_SKOPEO_CONFIG_PATH, value = mockSkopeoConfig) - @SetEnvironmentVariable(key = EXHORT_IMAGE_SERVICE_ENDPOINT, value = mockSkopeoDaemon) + @SetSystemProperty(key = "EXHORT_SKOPEO_PATH", value = mockSkopeoPath) + @SetSystemProperty(key = EXHORT_SKOPEO_CONFIG_PATH, value = mockSkopeoConfig) + @SetSystemProperty(key = EXHORT_IMAGE_SERVICE_ENDPOINT, value = mockSkopeoDaemon) void test_exec_skopeo_inspect_raw() { try (MockedStatic mock = Mockito.mockStatic(Operations.class)) { var output = new Operations.ProcessExecOutput("test-output", "test-error", 0); @@ -681,9 +687,9 @@ void test_exec_skopeo_inspect_raw() { } @Test - @SetEnvironmentVariable(key = "EXHORT_SKOPEO_PATH", value = mockSkopeoPath) - @ClearEnvironmentVariable(key = EXHORT_SKOPEO_CONFIG_PATH) - @SetEnvironmentVariable(key = EXHORT_IMAGE_SERVICE_ENDPOINT, value = mockSkopeoDaemon) + @SetSystemProperty(key = "EXHORT_SKOPEO_PATH", value = mockSkopeoPath) + @ClearSystemProperty(key = EXHORT_SKOPEO_CONFIG_PATH) + @SetSystemProperty(key = EXHORT_IMAGE_SERVICE_ENDPOINT, value = mockSkopeoDaemon) void test_exec_skopeo_inspect_raw_no_config() { try (MockedStatic mock = Mockito.mockStatic(Operations.class)) { var output = new Operations.ProcessExecOutput("test-output", "test-error", 0); @@ -711,9 +717,9 @@ void test_exec_skopeo_inspect_raw_no_config() { } @Test - @ClearEnvironmentVariable(key = "EXHORT_SKOPEO_PATH") - @SetEnvironmentVariable(key = EXHORT_SKOPEO_CONFIG_PATH, value = mockSkopeoConfig) - @ClearEnvironmentVariable(key = EXHORT_IMAGE_SERVICE_ENDPOINT) + @ClearSystemProperty(key = "EXHORT_SKOPEO_PATH") + @SetSystemProperty(key = EXHORT_SKOPEO_CONFIG_PATH, value = mockSkopeoConfig) + @ClearSystemProperty(key = EXHORT_IMAGE_SERVICE_ENDPOINT) void test_exec_skopeo_inspect_raw_no_daemon() { try (MockedStatic mock = Mockito.mockStatic(Operations.class)) { var output = new Operations.ProcessExecOutput("test-output", "test-error", 0); @@ -741,9 +747,9 @@ void test_exec_skopeo_inspect_raw_no_daemon() { } @Test - @ClearEnvironmentVariable(key = "EXHORT_SKOPEO_PATH") - @ClearEnvironmentVariable(key = EXHORT_SKOPEO_CONFIG_PATH) - @ClearEnvironmentVariable(key = EXHORT_IMAGE_SERVICE_ENDPOINT) + @ClearSystemProperty(key = "EXHORT_SKOPEO_PATH") + @ClearSystemProperty(key = EXHORT_SKOPEO_CONFIG_PATH) + @ClearSystemProperty(key = EXHORT_IMAGE_SERVICE_ENDPOINT) void test_exec_skopeo_inspect_raw_no_config_no_daemon() { try (MockedStatic mock = Mockito.mockStatic(Operations.class)) { var output = new Operations.ProcessExecOutput("test-output", "test-error", 0); @@ -769,9 +775,9 @@ void test_exec_skopeo_inspect_raw_no_config_no_daemon() { } @Test - @SetEnvironmentVariable(key = "EXHORT_SKOPEO_PATH", value = mockSkopeoPath) - @SetEnvironmentVariable(key = EXHORT_SKOPEO_CONFIG_PATH, value = mockSkopeoConfig) - @SetEnvironmentVariable(key = EXHORT_IMAGE_SERVICE_ENDPOINT, value = mockSkopeoDaemon) + @SetSystemProperty(key = "EXHORT_SKOPEO_PATH", value = mockSkopeoPath) + @SetSystemProperty(key = EXHORT_SKOPEO_CONFIG_PATH, value = mockSkopeoConfig) + @SetSystemProperty(key = EXHORT_IMAGE_SERVICE_ENDPOINT, value = mockSkopeoDaemon) void test_exec_skopeo_inspect() { try (MockedStatic mock = Mockito.mockStatic(Operations.class)) { var output = new Operations.ProcessExecOutput("test-output", "test-error", 0); @@ -801,9 +807,9 @@ void test_exec_skopeo_inspect() { } @Test - @SetEnvironmentVariable(key = "EXHORT_SKOPEO_PATH", value = mockSkopeoPath) - @ClearEnvironmentVariable(key = EXHORT_SKOPEO_CONFIG_PATH) - @SetEnvironmentVariable(key = EXHORT_IMAGE_SERVICE_ENDPOINT, value = mockSkopeoDaemon) + @SetSystemProperty(key = "EXHORT_SKOPEO_PATH", value = mockSkopeoPath) + @ClearSystemProperty(key = EXHORT_SKOPEO_CONFIG_PATH) + @SetSystemProperty(key = EXHORT_IMAGE_SERVICE_ENDPOINT, value = mockSkopeoDaemon) void test_exec_skopeo_inspect_no_config() { try (MockedStatic mock = Mockito.mockStatic(Operations.class)) { var output = new Operations.ProcessExecOutput("test-output", "test-error", 0); @@ -831,9 +837,9 @@ void test_exec_skopeo_inspect_no_config() { } @Test - @ClearEnvironmentVariable(key = "EXHORT_SKOPEO_PATH") - @SetEnvironmentVariable(key = EXHORT_SKOPEO_CONFIG_PATH, value = mockSkopeoConfig) - @ClearEnvironmentVariable(key = EXHORT_IMAGE_SERVICE_ENDPOINT) + @ClearSystemProperty(key = "EXHORT_SKOPEO_PATH") + @SetSystemProperty(key = EXHORT_SKOPEO_CONFIG_PATH, value = mockSkopeoConfig) + @ClearSystemProperty(key = EXHORT_IMAGE_SERVICE_ENDPOINT) void test_exec_skopeo_inspect_no_daemon() { try (MockedStatic mock = Mockito.mockStatic(Operations.class)) { var output = new Operations.ProcessExecOutput("test-output", "test-error", 0); @@ -861,9 +867,9 @@ void test_exec_skopeo_inspect_no_daemon() { } @Test - @ClearEnvironmentVariable(key = "EXHORT_SKOPEO_PATH") - @ClearEnvironmentVariable(key = EXHORT_SKOPEO_CONFIG_PATH) - @ClearEnvironmentVariable(key = EXHORT_IMAGE_SERVICE_ENDPOINT) + @ClearSystemProperty(key = "EXHORT_SKOPEO_PATH") + @ClearSystemProperty(key = EXHORT_SKOPEO_CONFIG_PATH) + @ClearSystemProperty(key = EXHORT_IMAGE_SERVICE_ENDPOINT) void test_exec_skopeo_inspect_no_config_no_daemon() { try (MockedStatic mock = Mockito.mockStatic(Operations.class)) { var output = new Operations.ProcessExecOutput("test-output", "test-error", 0); diff --git a/src/test/java/com/redhat/exhort/impl/ExhortApiIT.java b/src/test/java/com/redhat/exhort/impl/ExhortApiIT.java index c6fc68b4..b0a8db3d 100644 --- a/src/test/java/com/redhat/exhort/impl/ExhortApiIT.java +++ b/src/test/java/com/redhat/exhort/impl/ExhortApiIT.java @@ -64,9 +64,8 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.MethodSource; -import org.junitpioneer.jupiter.RestoreEnvironmentVariables; import org.junitpioneer.jupiter.RestoreSystemProperties; -import org.junitpioneer.jupiter.SetEnvironmentVariable; +import org.junitpioneer.jupiter.SetSystemProperty; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; @@ -75,9 +74,9 @@ @Tag("IntegrationTest") @ExtendWith(HelperExtension.class) @ExtendWith(MockitoExtension.class) -@SetEnvironmentVariable(key = "RHDA_SOURCE", value = "exhort-java-api-it") -@SetEnvironmentVariable(key = "EXHORT_DEV_MODE", value = "false") -@RestoreEnvironmentVariables +@SetSystemProperty(key = "RHDA_SOURCE", value = "exhort-java-api-it") +@SetSystemProperty(key = "EXHORT_DEV_MODE", value = "false") +@RestoreSystemProperties class ExhortApiIT extends ExhortTest { private static Api api; diff --git a/src/test/java/com/redhat/exhort/impl/Exhort_Api_Test.java b/src/test/java/com/redhat/exhort/impl/Exhort_Api_Test.java index a732b466..c819d2cc 100644 --- a/src/test/java/com/redhat/exhort/impl/Exhort_Api_Test.java +++ b/src/test/java/com/redhat/exhort/impl/Exhort_Api_Test.java @@ -64,8 +64,9 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.junitpioneer.jupiter.ClearEnvironmentVariable; -import org.junitpioneer.jupiter.SetEnvironmentVariable; +import org.junitpioneer.jupiter.ClearSystemProperty; +import org.junitpioneer.jupiter.RestoreSystemProperties; +import org.junitpioneer.jupiter.SetSystemProperty; import org.mockito.ArgumentMatcher; import org.mockito.InjectMocks; import org.mockito.Mock; @@ -74,11 +75,11 @@ import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) -@ClearEnvironmentVariable(key = "EXHORT_SNYK_TOKEN") -@ClearEnvironmentVariable(key = "EXHORT_DEV_MODE") -@ClearEnvironmentVariable(key = "DEV_EXHORT_BACKEND_URL") -@ClearEnvironmentVariable(key = "RHDA_TOKEN") -@ClearEnvironmentVariable(key = "RHDA_SOURCE") +@ClearSystemProperty(key = "EXHORT_SNYK_TOKEN") +@ClearSystemProperty(key = "EXHORT_DEV_MODE") +@ClearSystemProperty(key = "DEV_EXHORT_BACKEND_URL") +@ClearSystemProperty(key = "RHDA_TOKEN") +@ClearSystemProperty(key = "RHDA_SOURCE") @SuppressWarnings("unchecked") class Exhort_Api_Test extends ExhortTest { @Mock Provider mockProvider; @@ -93,9 +94,9 @@ void cleanup() { } @Test - @SetEnvironmentVariable(key = "EXHORT_SNYK_TOKEN", value = "snyk-token-from-env-var") - @SetEnvironmentVariable(key = "RHDA_TOKEN", value = "rhda-token-from-env-var") - @SetEnvironmentVariable(key = "RHDA_SOURCE", value = "rhda-source-from-env-var") + @SetSystemProperty(key = "EXHORT_SNYK_TOKEN", value = "snyk-token-from-env-var") + @SetSystemProperty(key = "RHDA_TOKEN", value = "rhda-token-from-env-var") + @SetSystemProperty(key = "RHDA_SOURCE", value = "rhda-source-from-env-var") void stackAnalysisHtml_with_pom_xml_should_return_html_report_from_the_backend() throws IOException, ExecutionException, InterruptedException { // create a temporary pom.xml file @@ -154,11 +155,9 @@ void stackAnalysisHtml_with_pom_xml_should_return_html_report_from_the_backend() } @Test - // System.setProperty("RHDA_TOKEN", "rhda-token-from-property"); - // System.setProperty("RHDA_SOURCE", "rhda-source-from-property"); - @SetEnvironmentVariable(key = "EXHORT_SNYK_TOKEN", value = "snyk-token-from-env-var") - @SetEnvironmentVariable(key = "RHDA_TOKEN", value = "rhda-token-from-env-var") - @SetEnvironmentVariable(key = "RHDA_SOURCE", value = "rhda-source-from-env-var") + @SetSystemProperty(key = "EXHORT_SNYK_TOKEN", value = "snyk-token") + @SetSystemProperty(key = "RHDA_TOKEN", value = "rhda-token") + @SetSystemProperty(key = "RHDA_SOURCE", value = "rhda-source") void stackAnalysis_with_pom_xml_should_return_json_object_from_the_backend() throws IOException, ExecutionException, InterruptedException { // create a temporary pom.xml file @@ -172,21 +171,14 @@ void stackAnalysis_with_pom_xml_should_return_json_object_from_the_backend() given(mockProvider.provideStack()) .willReturn(new Provider.Content("fake-body-content".getBytes(), "fake-content-type")); - // we expect this to be ignored because tokens from env vars takes precedence - System.setProperty("EXHORT_SNYK_TOKEN", "snyk-token-from-property"); - // create an argument matcher to make sure we mock the response for the right request ArgumentMatcher matchesRequest = r -> r.headers().firstValue("Content-Type").get().equals("fake-content-type") && r.headers().firstValue("Accept").get().equals("application/json") - && - // snyk token is set using the environment variable (annotation) - ignored the one - // set in - // properties - r.headers().firstValue("ex-snyk-token").get().equals("snyk-token-from-env-var") - && r.headers().firstValue("rhda-token").get().equals("rhda-token-from-env-var") - && r.headers().firstValue("rhda-source").get().equals("rhda-source-from-env-var") + && r.headers().firstValue("ex-snyk-token").get().equals("snyk-token") + && r.headers().firstValue("rhda-token").get().equals("rhda-token") + && r.headers().firstValue("rhda-source").get().equals("rhda-source") && r.headers().firstValue("rhda-operation-type").get().equals("Stack Analysis") && r.method().equals("POST"); @@ -223,6 +215,7 @@ void stackAnalysis_with_pom_xml_should_return_json_object_from_the_backend() } @Test + @RestoreSystemProperties void componentAnalysis_with_pom_xml_should_return_json_object_from_the_backend() throws IOException, ExecutionException, InterruptedException { // load pom.xml @@ -358,6 +351,7 @@ void componentAnalysis_with_pom_xml_should_return_json_object_from_the_backend() } @Test + @RestoreSystemProperties void componentAnalysis_with_pom_xml_as_path_should_return_json_object_from_the_backend() throws IOException, ExecutionException, InterruptedException { // load pom.xml @@ -417,109 +411,36 @@ void componentAnalysis_with_pom_xml_as_path_should_return_json_object_from_the_b } } - @AfterEach - void afterEach() { - System.clearProperty("EXHORT_DEV_MODE"); - System.clearProperty("DEV_EXHORT_BACKEND_URL"); - System.clearProperty("RHDA_TOKEN"); - System.clearProperty("RHDA_SOURCE"); - } - @Test - @SetEnvironmentVariable(key = "EXHORT_DEV_MODE", value = "true") - @ClearEnvironmentVariable(key = "DEV_EXHORT_BACKEND_URL") - void check_Exhort_Url_When_DEV_Mode_true_Both() { - System.setProperty("EXHORT_DEV_MODE", "true"); - ExhortApi exhortApi = new ExhortApi(); - then(exhortApi.getEndpoint()).isEqualTo(ExhortApi.DEFAULT_ENDPOINT_DEV); - then(exhortApi.getEndpoint()).isNotEqualTo(ExhortApi.DEFAULT_ENDPOINT); - } - - @Test - @SetEnvironmentVariable(key = "EXHORT_DEV_MODE", value = "true") - @ClearEnvironmentVariable(key = "DEV_EXHORT_BACKEND_URL") - void check_Exhort_Url_When_env_DEV_Mode_true_property_DEV_Mode_false() { - System.setProperty("EXHORT_DEV_MODE", "false"); - ExhortApi exhortApi = new ExhortApi(); - then(exhortApi.getEndpoint()).isEqualTo(ExhortApi.DEFAULT_ENDPOINT_DEV); - then(exhortApi.getEndpoint()).isNotEqualTo(ExhortApi.DEFAULT_ENDPOINT); - } - - @Test - @SetEnvironmentVariable(key = "EXHORT_DEV_MODE", value = "true") - @ClearEnvironmentVariable(key = "DEV_EXHORT_BACKEND_URL") - void - check_Exhort_Url_When_env_DEV_Mode_true_And_DEV_Exhort_Url_Set_Then_Default_DEV_Exhort_URL_Not_Selected() { + @SetSystemProperty(key = "EXHORT_DEV_MODE", value = "true") + @ClearSystemProperty(key = "DEV_EXHORT_BACKEND_URL") + @RestoreSystemProperties + void check_Exhort_Url_When_DEV_Mode_true_And_DEV_Exhort_Url_Set() { String dummyUrl = "http://dummy-url"; System.setProperty("DEV_EXHORT_BACKEND_URL", dummyUrl); ExhortApi exhortApi = new ExhortApi(); then(exhortApi.getEndpoint()).isEqualTo(dummyUrl); - then(exhortApi.getEndpoint()).isNotEqualTo(ExhortApi.DEFAULT_ENDPOINT_DEV); - } - - @Test - @SetEnvironmentVariable(key = "EXHORT_DEV_MODE", value = "false") - @ClearEnvironmentVariable(key = "DEV_EXHORT_BACKEND_URL") - void - check_Exhort_Url_When_env_DEV_Mode_false_And_DEV_Exhort_Url_Set_Then_Default_DEV_Exhort_URL_Not_Selected() { - System.setProperty("EXHORT_DEV_MODE", "false"); - ExhortApi exhortApi = new ExhortApi(); - then(exhortApi.getEndpoint()).isEqualTo(ExhortApi.DEFAULT_ENDPOINT); - then(exhortApi.getEndpoint()).isNotEqualTo(ExhortApi.DEFAULT_ENDPOINT_DEV); } @Test - @SetEnvironmentVariable(key = "EXHORT_DEV_MODE", value = "false") - void - check_Exhort_Url_When_env_DEV_Mode_false_And_Property_Dev_Mode_true_Default_Exhort_URL_Selected() { - System.setProperty("EXHORT_DEV_MODE", "true"); + @SetSystemProperty(key = "EXHORT_DEV_MODE", value = "false") + void check_Exhort_Url_When_DEV_Mode_false_And_DEV_Exhort_Url_Set() { ExhortApi exhortApi = new ExhortApi(); then(exhortApi.getEndpoint()).isEqualTo(ExhortApi.DEFAULT_ENDPOINT); - then(exhortApi.getEndpoint()).isNotEqualTo(ExhortApi.DEFAULT_ENDPOINT_DEV); } @Test - @SetEnvironmentVariable(key = "EXHORT_DEV_MODE", value = "false") - @SetEnvironmentVariable(key = "DEV_EXHORT_BACKEND_URL", value = "http://dummy-route") - void - check_Exhort_Url_When_env_DEV_Mode_false_And_DEV_Exhort_Url_Set_Then_Default_Exhort_URL_Selected_Anyway() { - System.setProperty("EXHORT_DEV_MODE", "true"); - System.setProperty("DEV_EXHORT_BACKEND_URL", "http://dummy-route2"); + @SetSystemProperty(key = "EXHORT_DEV_MODE", value = "true") + void check_Exhort_Url_When_DEV_Mode_true_Dev_Exhort_URL_Selected() { ExhortApi exhortApi = new ExhortApi(); - then(exhortApi.getEndpoint()).isEqualTo(ExhortApi.DEFAULT_ENDPOINT); - then(exhortApi.getEndpoint()).isNotEqualTo(System.getenv("DEV_EXHORT_BACKEND_URL")); - then(exhortApi.getEndpoint()).isNotEqualTo(System.getProperty("DEV_EXHORT_BACKEND_URL")); - } - - @Test - void - check_Exhort_Url_When_env_DEV_Mode_not_set_And_Property_Exhort_Dev_Mode_false_Then_Default_Exhort_URL_Selected() { - System.setProperty("EXHORT_DEV_MODE", "false"); - ExhortApi exhortApi = new ExhortApi(); - then(exhortApi.getEndpoint()).isEqualTo(ExhortApi.DEFAULT_ENDPOINT); - then(exhortApi.getEndpoint()).isNotEqualTo(ExhortApi.DEFAULT_ENDPOINT_DEV); - } - - @Test - void - check_Exhort_Url_When_env_DEV_Mode_not_set_And_Property_Exhort_Dev_Mode_true_Then_Default_DEV_Exhort_URL_Selected() { - System.setProperty("EXHORT_DEV_MODE", "true"); - ExhortApi exhortApi = new ExhortApi(); - then(exhortApi.getEndpoint()).isNotEqualTo(ExhortApi.DEFAULT_ENDPOINT); then(exhortApi.getEndpoint()).isEqualTo(ExhortApi.DEFAULT_ENDPOINT_DEV); } @Test - @SetEnvironmentVariable(key = "DEV_EXHORT_BACKEND_URL", value = "http://dummy-route") - void - check_Exhort_Url_When_env_DEV_Mode_not_set_And_Property_Exhort_Dev_Mode_true_and_Env_DEV_Exhort_Backend_Url_Set_Then_DEV_ENV_Exhort_URL_Selected() { - System.setProperty("EXHORT_DEV_MODE", "true"); - System.setProperty("DEV_EXHORT_BACKEND_URL", "http://dummy-route2"); + @SetSystemProperty(key = "EXHORT_DEV_MODE", value = "false") + void check_Exhort_Url_When_DEV_Mode_not_set_Then_Default_Exhort_URL_Selected() { ExhortApi exhortApi = new ExhortApi(); - then(exhortApi.getEndpoint()).isNotEqualTo(ExhortApi.DEFAULT_ENDPOINT); - then(exhortApi.getEndpoint()).isNotEqualTo(ExhortApi.DEFAULT_ENDPOINT_DEV); - then(exhortApi.getEndpoint()).isNotEqualTo("http://dummy-route2"); - then(exhortApi.getEndpoint()).isEqualTo("http://dummy-route"); + then(exhortApi.getEndpoint()).isEqualTo(ExhortApi.DEFAULT_ENDPOINT); } @Test @@ -529,9 +450,9 @@ void check_Exhort_Url_When_Nothing_Set_Then_Default_Exhort_URL_Selected() { } @Test - @SetEnvironmentVariable(key = "EXHORT_SNYK_TOKEN", value = "snyk-token-from-env-var") - @SetEnvironmentVariable(key = "RHDA_TOKEN", value = "rhda-token-from-env-var") - @SetEnvironmentVariable(key = "RHDA_SOURCE", value = "rhda-source-from-env-var") + @SetSystemProperty(key = "EXHORT_SNYK_TOKEN", value = "snyk-token-from-env-var") + @SetSystemProperty(key = "RHDA_TOKEN", value = "rhda-token-from-env-var") + @SetSystemProperty(key = "RHDA_SOURCE", value = "rhda-source-from-env-var") void test_image_analysis() throws IOException, ExecutionException, InterruptedException, MalformedPackageURLException { try (MockedStatic mock = Mockito.mockStatic(Operations.class); @@ -626,9 +547,9 @@ void test_image_analysis() } @Test - @SetEnvironmentVariable(key = "EXHORT_SNYK_TOKEN", value = "snyk-token-from-env-var") - @SetEnvironmentVariable(key = "RHDA_TOKEN", value = "rhda-token-from-env-var") - @SetEnvironmentVariable(key = "RHDA_SOURCE", value = "rhda-source-from-env-var") + @SetSystemProperty(key = "EXHORT_SNYK_TOKEN", value = "snyk-token-from-env-var") + @SetSystemProperty(key = "RHDA_TOKEN", value = "rhda-token-from-env-var") + @SetSystemProperty(key = "RHDA_SOURCE", value = "rhda-source-from-env-var") void imageAnalysisHtml() throws IOException, ExecutionException, InterruptedException, MalformedPackageURLException { try (MockedStatic mock = Mockito.mockStatic(Operations.class); @@ -702,9 +623,9 @@ void imageAnalysisHtml() } @Test - @SetEnvironmentVariable(key = "EXHORT_SNYK_TOKEN", value = "snyk-token-from-env-var") - @SetEnvironmentVariable(key = "RHDA_TOKEN", value = "rhda-token-from-env-var") - @SetEnvironmentVariable(key = "RHDA_SOURCE", value = "rhda-source-from-env-var") + @SetSystemProperty(key = "EXHORT_SNYK_TOKEN", value = "snyk-token-from-env-var") + @SetSystemProperty(key = "RHDA_TOKEN", value = "rhda-token-from-env-var") + @SetSystemProperty(key = "RHDA_SOURCE", value = "rhda-source-from-env-var") void test_perform_batch_analysis() throws IOException, MalformedPackageURLException, ExecutionException, InterruptedException { try (var is = getResourceAsStreamDecision(this.getClass(), "msc/image/image_sbom.json")) { diff --git a/src/test/java/com/redhat/exhort/providers/Java_Envs_Test.java b/src/test/java/com/redhat/exhort/providers/Java_Envs_Test.java index e857f137..fbf71fb7 100644 --- a/src/test/java/com/redhat/exhort/providers/Java_Envs_Test.java +++ b/src/test/java/com/redhat/exhort/providers/Java_Envs_Test.java @@ -18,31 +18,40 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; +import com.redhat.exhort.utils.Environment; import java.util.Collections; import org.junit.jupiter.api.Test; -import org.junitpioneer.jupiter.ClearEnvironmentVariable; -import org.junitpioneer.jupiter.SetEnvironmentVariable; +import org.junitpioneer.jupiter.SetSystemProperty; +import org.mockito.MockedStatic; +import org.mockito.Mockito; public class Java_Envs_Test { @Test - @SetEnvironmentVariable(key = "JAVA_HOME", value = "test-java-home") + @SetSystemProperty(key = "JAVA_HOME", value = "test-java-home") void test_java_get_envs() { var envs = new JavaMavenProvider(null).getMvnExecEnvs(); assertEquals(Collections.singletonMap("JAVA_HOME", "test-java-home"), envs); } @Test - @SetEnvironmentVariable(key = "JAVA_HOME", value = "") + @SetSystemProperty(key = "JAVA_HOME", value = "") void test_java_get_envs_empty_java_home() { - var envs = new JavaMavenProvider(null).getMvnExecEnvs(); - assertNull(envs); + try (MockedStatic mockEnv = + Mockito.mockStatic(Environment.class, Mockito.CALLS_REAL_METHODS)) { + mockEnv.when(() -> Environment.get("JAVA_HOME")).thenReturn(null); + var envs = new JavaMavenProvider(null).getMvnExecEnvs(); + assertNull(envs); + } } @Test - @ClearEnvironmentVariable(key = "JAVA_HOME") void test_java_get_envs_no_java_home() { - var envs = new JavaMavenProvider(null).getMvnExecEnvs(); - assertNull(envs); + try (MockedStatic mockEnv = + Mockito.mockStatic(Environment.class, Mockito.CALLS_REAL_METHODS)) { + mockEnv.when(() -> Environment.get("JAVA_HOME")).thenReturn(null); + var envs = new JavaMavenProvider(null).getMvnExecEnvs(); + assertNull(envs); + } } } diff --git a/src/test/java/com/redhat/exhort/providers/Javascript_Envs_Test.java b/src/test/java/com/redhat/exhort/providers/Javascript_Envs_Test.java index 4f5c53ea..cd128ac4 100644 --- a/src/test/java/com/redhat/exhort/providers/Javascript_Envs_Test.java +++ b/src/test/java/com/redhat/exhort/providers/Javascript_Envs_Test.java @@ -18,18 +18,19 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; +import com.redhat.exhort.utils.Environment; import java.io.File; import java.util.Collections; import org.junit.jupiter.api.Test; -import org.junitpioneer.jupiter.ClearEnvironmentVariable; import org.junitpioneer.jupiter.ClearSystemProperty; -import org.junitpioneer.jupiter.SetEnvironmentVariable; import org.junitpioneer.jupiter.SetSystemProperty; +import org.mockito.MockedStatic; +import org.mockito.Mockito; public class Javascript_Envs_Test { @Test @SetSystemProperty(key = "NODE_HOME", value = "test-node-home") - @SetEnvironmentVariable(key = "PATH", value = "test-path") + @SetSystemProperty(key = "PATH", value = "test-path") void test_javascript_get_envs() { var envs = new JavaScriptNpmProvider(null).getNpmExecEnv(); assertEquals( @@ -39,15 +40,18 @@ void test_javascript_get_envs() { @Test @SetSystemProperty(key = "NODE_HOME", value = "test-node-home") - @ClearEnvironmentVariable(key = "PATH") void test_javascript_get_envs_no_path() { - var envs = new JavaScriptNpmProvider(null).getNpmExecEnv(); - assertEquals(Collections.singletonMap("PATH", "test-node-home"), envs); + try (MockedStatic mockEnv = + Mockito.mockStatic(Environment.class, Mockito.CALLS_REAL_METHODS)) { + mockEnv.when(() -> Environment.get("PATH")).thenReturn(null); + var envs = new JavaScriptNpmProvider(null).getNpmExecEnv(); + assertEquals(Collections.singletonMap("PATH", "test-node-home"), envs); + } } @Test @SetSystemProperty(key = "NODE_HOME", value = "") - @SetEnvironmentVariable(key = "PATH", value = "test-path") + @SetSystemProperty(key = "PATH", value = "test-path") void test_javascript_get_envs_empty_java_home() { var envs = new JavaScriptNpmProvider(null).getNpmExecEnv(); assertNull(envs); @@ -55,7 +59,7 @@ void test_javascript_get_envs_empty_java_home() { @Test @ClearSystemProperty(key = "NODE_HOME") - @SetEnvironmentVariable(key = "PATH", value = "test-path") + @SetSystemProperty(key = "PATH", value = "test-path") void test_javascript_get_envs_no_java_home() { var envs = new JavaScriptNpmProvider(null).getNpmExecEnv(); assertNull(envs); diff --git a/src/test/java/com/redhat/exhort/tools/Operations_Test.java b/src/test/java/com/redhat/exhort/tools/Operations_Test.java index 14f4b6e9..0a3ea33f 100644 --- a/src/test/java/com/redhat/exhort/tools/Operations_Test.java +++ b/src/test/java/com/redhat/exhort/tools/Operations_Test.java @@ -19,11 +19,9 @@ import static org.assertj.core.api.Assertions.assertThatNoException; import static org.assertj.core.api.Assertions.assertThatRuntimeException; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.junitpioneer.jupiter.ClearEnvironmentVariable; -import org.junitpioneer.jupiter.SetEnvironmentVariable; +import org.junitpioneer.jupiter.SetSystemProperty; class Operations_Test { @Nested @@ -40,26 +38,12 @@ void when_running_process_for_non_existing_command_should_throw_runtime_exceptio } @Nested - @ClearEnvironmentVariable(key = "EXHORT_MADE_UP_CMD_PATH") class Test_getCustomPathOrElse { - @AfterEach - void cleanup() { - System.clearProperty("EXHORT_MADE_UP_CMD_PATH"); - } - - @Test - @SetEnvironmentVariable(key = "EXHORT_MADE_UP_CMD_PATH", value = "/path/to/env/made_up_cmd") - void when_custom_path_exists_in_env_vars_and_properties_should_return_from_env_vars() { - System.setProperty("EXHORT_MADE_UP_CMD_PATH", "/path/to/property/made_up_cmd"); - assertThat(Operations.getCustomPathOrElse("made-up cmd")) - .isEqualTo("/path/to/env/made_up_cmd"); - } @Test - void when_custom_path_not_in_env_var_but_exists_in_properties_should_return_from_properties() { - System.setProperty("EXHORT_MADE_UP_CMD_PATH", "/path/to/property/made_up_cmd"); - assertThat(Operations.getCustomPathOrElse("made-up_cmd")) - .isEqualTo("/path/to/property/made_up_cmd"); + @SetSystemProperty(key = "EXHORT_MADE_UP_CMD_PATH", value = "/path/to/made_up_cmd") + void when_custom_path_exists_as_property() { + assertThat(Operations.getCustomPathOrElse("made-up cmd")).isEqualTo("/path/to/made_up_cmd"); } @Test diff --git a/src/test/java/com/redhat/exhort/utils/PythonControllerVirtualEnvTest.java b/src/test/java/com/redhat/exhort/utils/PythonControllerVirtualEnvTest.java index e9b0a8ea..dbb71b35 100644 --- a/src/test/java/com/redhat/exhort/utils/PythonControllerVirtualEnvTest.java +++ b/src/test/java/com/redhat/exhort/utils/PythonControllerVirtualEnvTest.java @@ -78,8 +78,7 @@ void test_Virtual_Environment_Install_Best_Efforts_Conflict_MMV_Should_Throw_Run void test_Virtual_Environment_Flow() throws IOException { // Mockito String requirementsTxt = "Jinja2==3.0.3"; - Path requirementsFilePath = - Path.of(System.getProperty("user.dir").toString(), "requirements.txt"); + Path requirementsFilePath = Path.of(Environment.get("user.dir").toString(), "requirements.txt"); Files.write(requirementsFilePath, requirementsTxt.getBytes()); spiedPythonControllerVirtualEnv.getDependencies(requirementsFilePath.toString(), true); diff --git a/src/test/java/module-info.test b/src/test/java/module-info.test index 87103d26..70b4d39b 100644 --- a/src/test/java/module-info.test +++ b/src/test/java/module-info.test @@ -22,7 +22,3 @@ java.base/java.lang=org.junitpioneer --add-opens java.base/java.util=org.junitpioneer ---add-opens - java.base/java.lang=ALL-UNNAMED ---add-opens - java.base/java.util=ALL-UNNAMED From a2de89745e0f0b1460c591133926278c9a6651b1 Mon Sep 17 00:00:00 2001 From: Ruben Romero Montes Date: Tue, 8 Apr 2025 20:58:21 +0200 Subject: [PATCH 5/5] chore: use primitive value for the defaultValue Signed-off-by: Ruben Romero Montes --- src/main/java/com/redhat/exhort/utils/Environment.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/redhat/exhort/utils/Environment.java b/src/main/java/com/redhat/exhort/utils/Environment.java index 9263e769..70aa6d2e 100644 --- a/src/main/java/com/redhat/exhort/utils/Environment.java +++ b/src/main/java/com/redhat/exhort/utils/Environment.java @@ -31,11 +31,11 @@ public static final String get(String name) { return get(name, null); } - public static final boolean getBoolean(String key, Boolean defaultValue) { + public static final boolean getBoolean(String key, boolean defaultValue) { var val = get(key); if (val != null) { return Boolean.parseBoolean(val.trim().toLowerCase()); } - return defaultValue != null ? defaultValue : false; + return defaultValue; } }