diff --git a/doc/quick-start-guide-and-build.md b/doc/quick-start-guide-and-build.md index bdb7d1c4..6b237413 100644 --- a/doc/quick-start-guide-and-build.md +++ b/doc/quick-start-guide-and-build.md @@ -245,34 +245,53 @@ _LPVS_ is now built and running. You can create a new pull request or update an #### 4.2 Single scan mode -Alternatively, you can perform a one-time scan on a specific pull request using the single scan mode. Follow these steps: +Alternatively, you can perform a one-time scan on a specific pull request or local files using the single scan mode. Follow these steps: -4.2.1 Begin by running the installation and navigating to the target directory, similar to the process in service mode (refer to steps 4.1.1 and 4.1.2): +4.2.1 Install and navigate to the target directory as described in step 4.1.1 and 4.1.2: ```bash mvn clean install cd target/ ``` -4.2.2 Execute the single scan with the following command: +4.2.2 Choose either to scan a specific pull request from GitHub or local files. + +4.2.2.1 To scan a specific pull request from GitHub, execute the following command: ```bash java -jar -Dgithub.token= lpvs-*.jar --github.pull.request= ``` -4.2.3 By default, the above command requires a pre-configured MySQL database. To avoid setting up the database, use the "singlescan" profile: +4.2.2.2 To scan local files or directories, execute the following command: + + ```bash + java -jar lpvs-*.jar --local.path= + ``` + +4.2.3 By default, the above commands require a pre-configured MySQL database. Use the "singlescan" profile to skip setting up a pre-configured MySQL database: + ```bash java -jar -Dspring.profiles.active=singlescan -Dgithub.token= lpvs-*.jar --github.pull.request= + java -jar -Dspring.profiles.active=singlescan lpvs-*.jar --local.path= ``` -These steps streamline the process, allowing you to run a scan on a single pull request without the need for a preconfigured database. +4.2.4 Optionally, generate an HTML report and save it in a specified folder. Replace `path/to/your/folder` with the full path to the folder where you want to save the HTML report, and `your_report_filename.html` with the desired filename for the report. -4.2.4 Available option to generate an HTML report and save it in a specified folder. Replace `/path/to/your/folder` with the full path to the folder where you want to save the HTML report, and `your_report_filename.html` with the desired filename for the report. ```bash - java -jar -Dgithub.token= lpvs-*.jar --github.pull.request= --build.html.report= + java -jar -Dspring.profiles.active=singlescan -Dgithub.token= lpvs-*.jar --github.pull.request= --build.html.report= + java -jar -Dspring.profiles.active=singlescan lpvs-*.jar --local.path= --build.html.report= ``` -These steps streamline the process, allowing you to run a scan on a single pull request without the need for a preconfigured database. +Note: Ensure that the specified folder exists before generating the HTML report. + +4.2.5 Examples of commands: + + ```bash + java -jar -Dspring.profiles.active=singlescan lpvs-*.jar --github.pull.request=https://github.com/Samsung/LPVS/pull/2 + java -jar -Dspring.profiles.active=singlescan lpvs-*.jar --github.pull.request=https://github.com/Samsung/LPVS/pull/2 --build.html.report=report.html + java -jar -Dspring.profiles.active=singlescan lpvs-*.jar --local.path=test.c + java -jar -Dspring.profiles.active=singlescan lpvs-*.jar --local.path=test --build.html.report=test/report.html + ``` #### 4.3 Use of _LPVS_ JAR `lpvs-x.y.z.jar` in your project diff --git a/src/main/java/com/lpvs/service/LPVSLicenseService.java b/src/main/java/com/lpvs/service/LPVSLicenseService.java index 74043219..f4496e9a 100644 --- a/src/main/java/com/lpvs/service/LPVSLicenseService.java +++ b/src/main/java/com/lpvs/service/LPVSLicenseService.java @@ -15,6 +15,7 @@ import com.lpvs.util.LPVSExitHandler; import com.lpvs.util.LPVSPayloadUtil; +import io.micrometer.common.util.StringUtils; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -230,7 +231,7 @@ protected LPVSLicense findLicenseByName(String name) { if (license.getLicenseName().equalsIgnoreCase(name)) { return license; } - if (license.getAlternativeNames() != null && !license.getAlternativeNames().isBlank()) { + if (!StringUtils.isBlank(license.getAlternativeNames())) { String[] names = license.getAlternativeNames().split(","); for (String n : names) { if (n.trim().equalsIgnoreCase(name)) { @@ -258,15 +259,10 @@ public LPVSLicense getLicenseBySpdxIdAndName( lic = findLicenseInOsoriDB(licenseSpdxId); // If not found, create new license with default field values if (lic == null) { - lic = - new LPVSLicense() { - { - setSpdxId(licenseSpdxId); - setLicenseName(licName); - setAlternativeNames(null); - setAccess("UNREVIEWED"); - } - }; + lic = new LPVSLicense(); + lic.setSpdxId(licenseSpdxId); + lic.setLicenseName(licName); + lic.setAccess("UNREVIEWED"); } // Save new license lic = lpvsLicenseRepository.saveAndFlush(lic); diff --git a/src/main/java/com/lpvs/service/LPVSQueueProcessorService.java b/src/main/java/com/lpvs/service/LPVSQueueProcessorService.java index 4008e053..c568ec73 100644 --- a/src/main/java/com/lpvs/service/LPVSQueueProcessorService.java +++ b/src/main/java/com/lpvs/service/LPVSQueueProcessorService.java @@ -7,6 +7,7 @@ package com.lpvs.service; import com.lpvs.entity.LPVSQueue; +import io.micrometer.common.util.StringUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -29,11 +30,17 @@ public class LPVSQueueProcessorService { private LPVSQueueService queueService; /** - * Trigger value obtained from application properties. + * Trigger value to start a single scan of a pull request (optional). */ @Value("${github.pull.request:}") private String trigger; + /** + * Trigger value to start a single scan of local files or folder (optional). + */ + @Value("${local.path:}") + private String localPath; + /** * Constructor for LPVSQueueProcessorService. * @@ -51,12 +58,12 @@ public class LPVSQueueProcessorService { * @throws Exception If an exception occurs during queue processing. */ @EventListener(ApplicationReadyEvent.class) - private void queueProcessor() throws Exception { + protected void queueProcessor() throws Exception { // Check for any pending elements in the LPVSQueue. queueService.checkForQueue(); // Process LPVSQueue elements until the trigger is set. - while (trigger == null || trigger.isEmpty()) { + while (StringUtils.isBlank(trigger) && StringUtils.isBlank(localPath)) { // Get the first element from the LPVSQueue. LPVSQueue webhookConfig = queueService.getQueueFirstElement(); log.info("PROCESS Webhook id = " + webhookConfig.getId()); diff --git a/src/main/java/com/lpvs/service/scan/LPVSDetectService.java b/src/main/java/com/lpvs/service/scan/LPVSDetectService.java index 7fa3e9ae..8b3fb110 100644 --- a/src/main/java/com/lpvs/service/scan/LPVSDetectService.java +++ b/src/main/java/com/lpvs/service/scan/LPVSDetectService.java @@ -7,15 +7,16 @@ package com.lpvs.service.scan; import java.io.File; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; import java.util.List; import com.lpvs.service.LPVSGitHubConnectionService; import com.lpvs.service.LPVSGitHubService; import com.lpvs.service.LPVSLicenseService; +import com.lpvs.util.LPVSFileUtil; +import io.micrometer.common.util.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; @@ -59,11 +60,17 @@ public class LPVSDetectService { private LPVSScanService scanService; /** - * GitHub pull request used to trigger a single license scan (optional). + * Trigger value to start a single scan of a pull request (optional). */ @Value("${github.pull.request:}") private String trigger; + /** + * Trigger value to start a single scan of local files or folder (optional). + */ + @Value("${local.path:}") + private String localPath; + /** * Optional parameter to save html report to specified location. */ @@ -105,52 +112,117 @@ public LPVSDetectService( */ @EventListener(ApplicationReadyEvent.class) public void runSingleScan() { - if (trigger != null && !HtmlUtils.htmlEscape(trigger).isEmpty()) { - log.info("Triggered single scan operation"); + // generateReport indicates that a report should be generated (HTML or command line output) + boolean generateReport = false; + LPVSQueue webhookConfig = null; + List scanResult = null; + List> detectedConflicts = null; + + // Error case when both pull request scan and local files scan are set to true + if (!StringUtils.isBlank(trigger) && !StringUtils.isBlank(localPath)) { + log.error( + "Incorrect settings: both pull request scan and local files scan are set to true."); + SpringApplication.exit(ctx, () -> 0); + + // Scan option - single pull request scan + } else if (!StringUtils.isBlank(trigger)) { + log.info("Triggered single scan of pull request."); try { licenseService.reloadFromTables(); - LPVSQueue webhookConfig = + webhookConfig = gitHubService.getInternalQueueByPullRequest(HtmlUtils.htmlEscape(trigger)); - List scanResult = + scanResult = this.runScan( webhookConfig, gitHubService.getPullRequestFiles(webhookConfig)); - List> detectedConflicts = - licenseService.findConflicts(webhookConfig, scanResult); - - if (htmlReport != null && !HtmlUtils.htmlEscape(htmlReport).isEmpty()) { - Path buildReportPath = Paths.get(htmlReport); - Path parentDirectory = buildReportPath.getParent(); - - if (parentDirectory != null && Files.isDirectory(parentDirectory)) { - String report = - LPVSCommentUtil.buildHTMLComment( - webhookConfig, scanResult, detectedConflicts); - LPVSCommentUtil.saveHTMLToFile(report, buildReportPath.toString()); - } else { - log.error( - "Error: The parent directory '" - + parentDirectory - + "' does not exist."); - } + detectedConflicts = licenseService.findConflicts(webhookConfig, scanResult); + generateReport = true; + log.info("Single scan of pull request completed."); + } catch (Exception ex) { + log.error("Single scan of pull request failed with error: " + ex.getMessage()); + SpringApplication.exit(ctx, () -> 0); + } + + // Scan option - single scan of local file or folder + } else if (!StringUtils.isBlank(localPath)) { + log.info("Triggered single scan of local file(s)."); + try { + licenseService.reloadFromTables(); + localPath = HtmlUtils.htmlEscape(localPath); + File localFile = new File(localPath); + if (localFile.exists()) { + // 1. Generate webhook config + webhookConfig = getInternalQueueByLocalPath(); + // 2. Copy files + LPVSFileUtil.copyFiles( + localPath, LPVSFileUtil.getLocalDirectoryPath(webhookConfig)); + // 3. Trigger scan + scanResult = + this.runScan( + webhookConfig, + LPVSFileUtil.getLocalDirectoryPath(webhookConfig)); + + detectedConflicts = licenseService.findConflicts(webhookConfig, scanResult); + generateReport = true; + log.info("Single scan of local file(s) completed."); } else { - String report = - LPVSCommentUtil.reportCommentBuilder( - webhookConfig, scanResult, detectedConflicts); - if (report != null && !report.isEmpty()) { - log.info(report); - } + throw new Exception("File path does not exist: " + localPath); } - log.info("Single scan completed."); + } catch (Exception ex) { - log.error("Single scan finished with errors."); - log.error("Can't trigger single scan: " + ex.getMessage()); + log.error("Single scan of local file(s) failed with error: " + ex.getMessage()); + SpringApplication.exit(ctx, () -> 0); + } + } + + // Report generation + // 1. HTML format + if (generateReport && !StringUtils.isBlank(htmlReport)) { + File report = new File(HtmlUtils.htmlEscape(htmlReport)); + String folderPath = report.getParent(); + if (folderPath == null) { + folderPath = "."; + } + File folder = new File(folderPath); + if (folder.exists() && folder.isDirectory()) { + String reportFile = + LPVSCommentUtil.buildHTMLComment( + webhookConfig, scanResult, detectedConflicts); + LPVSCommentUtil.saveHTMLToFile(reportFile, report.getAbsolutePath()); + } else { + log.error("Error: The parent directory '" + folder.getPath() + "' does not exist."); + } + SpringApplication.exit(ctx, () -> 0); + } else if (generateReport) { + // 2. Command line output + String report = + LPVSCommentUtil.reportCommentBuilder( + webhookConfig, scanResult, detectedConflicts); + if (!report.isEmpty()) { + log.info(report); } SpringApplication.exit(ctx, () -> 0); } } + /** + * Creates a new LPVSQueue object with default values for a local scan. + * + * @return the new LPVSQueue object + */ + private LPVSQueue getInternalQueueByLocalPath() { + LPVSQueue queue = new LPVSQueue(); + queue.setDate(new Date()); + queue.setUserId("Single scan of local files run"); + queue.setReviewSystemType("local_scan"); + SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd-HHmmss"); + String repoUrl = "local_scan_" + sdf.format(queue.getDate()); + queue.setRepositoryUrl(repoUrl); + queue.setPullRequestUrl(repoUrl); + return queue; + } + /** * Runs a license scan based on the selected scanner type. * diff --git a/src/main/java/com/lpvs/util/LPVSFileUtil.java b/src/main/java/com/lpvs/util/LPVSFileUtil.java index 06d4b7ad..0eed4846 100644 --- a/src/main/java/com/lpvs/util/LPVSFileUtil.java +++ b/src/main/java/com/lpvs/util/LPVSFileUtil.java @@ -10,6 +10,8 @@ import lombok.extern.slf4j.Slf4j; import org.kohsuke.github.GHPullRequestFileDetail; import org.springframework.util.FileSystemUtils; +import org.springframework.web.util.HtmlUtils; +import io.micrometer.common.util.StringUtils; import java.io.File; import java.io.FileWriter; @@ -19,6 +21,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; import java.util.Arrays; import java.util.List; @@ -125,6 +128,43 @@ public static String saveGithubDiffs( return directoryPath; } + /** + * Copies files from a source path to a destination directory path. + * + * @param sourcePath the path to the source file or directory to be copied + * @param directoryPath the path to the destination directory where the files will be copied + * @throws IOException if an I/O error occurs + */ + public static void copyFiles(String sourcePath, String directoryPath) throws IOException { + deleteIfExists(directoryPath); + File destination = new File(directoryPath); + File source = new File(sourcePath); + if (destination.mkdirs()) { + if (source.isDirectory()) { + File[] files = source.listFiles(); + if (files != null) { + for (File file : files) { + if (file.isFile()) { + Files.copy( + file.toPath(), + new File(destination, file.getName()).toPath(), + StandardCopyOption.REPLACE_EXISTING); + } else if (file.isDirectory()) { + File destinationSubdir = new File(destination, file.getName()); + destinationSubdir.mkdirs(); + copyFiles(file.getAbsolutePath(), destinationSubdir.getAbsolutePath()); + } + } + } + } else if (source.isFile()) { + Files.copy( + source.toPath(), + new File(destination, source.getName()).toPath(), + StandardCopyOption.REPLACE_EXISTING); + } + } + } + /** * Deletes the specified directory if it exists. * @@ -176,11 +216,10 @@ public static String getLocalDirectoryPath(LPVSQueue webhookConfig) { */ public static String getScanResultsJsonFilePath(LPVSQueue webhookConfig) { String fileName = null; - if (webhookConfig.getHeadCommitSHA() == null - || webhookConfig.getHeadCommitSHA().isBlank()) { + if (StringUtils.isBlank(webhookConfig.getHeadCommitSHA())) { fileName = LPVSPayloadUtil.getPullRequestId(webhookConfig); } else { - fileName = webhookConfig.getHeadCommitSHA(); + fileName = HtmlUtils.htmlEscape(webhookConfig.getHeadCommitSHA()); } return System.getProperty("user.home") diff --git a/src/main/java/com/lpvs/util/LPVSPayloadUtil.java b/src/main/java/com/lpvs/util/LPVSPayloadUtil.java index aad54950..49574709 100644 --- a/src/main/java/com/lpvs/util/LPVSPayloadUtil.java +++ b/src/main/java/com/lpvs/util/LPVSPayloadUtil.java @@ -66,23 +66,30 @@ public static LPVSLicense convertOsoriDbResponseToLicense(String payload) { JsonObject messageList = json.getAsJsonObject("messageList"); JsonArray detailInfoArray = messageList.getAsJsonArray("detailInfo"); - LPVSLicense lic = new LPVSLicense(); - lic.setLicenseName(detailInfoArray.get(0).getAsJsonObject().get("name").getAsString()); - lic.setSpdxId( - detailInfoArray.get(0).getAsJsonObject().get("spdx_identifier").getAsString()); - lic.setAccess("UNREVIEWED"); - - List nicknameList = new ArrayList<>(); - JsonElement nicknameListArray = - detailInfoArray.get(0).getAsJsonObject().get("nicknameList"); - if (nicknameListArray != null && nicknameListArray.isJsonArray()) { - nicknameListArray - .getAsJsonArray() - .forEach(element -> nicknameList.add(element.getAsString())); + if (!detailInfoArray.isEmpty()) { + LPVSLicense lic = new LPVSLicense(); + lic.setLicenseName( + detailInfoArray.get(0).getAsJsonObject().get("name").getAsString()); + lic.setSpdxId( + detailInfoArray + .get(0) + .getAsJsonObject() + .get("spdx_identifier") + .getAsString()); + lic.setAccess("UNREVIEWED"); + + List nicknameList = new ArrayList<>(); + JsonElement nicknameListArray = + detailInfoArray.get(0).getAsJsonObject().get("nicknameList"); + if (nicknameListArray != null && nicknameListArray.isJsonArray()) { + nicknameListArray + .getAsJsonArray() + .forEach(element -> nicknameList.add(element.getAsString())); + } + lic.setAlternativeNames(String.join(",", nicknameList)); + + return lic; } - lic.setAlternativeNames(String.join(",", nicknameList)); - - return lic; } catch (Exception e) { log.error("Error parsing OSORI DB payload: " + e.getMessage()); } @@ -335,7 +342,7 @@ public static LPVSQueue getGitHubWebhookConfig(GHRepository repo, GHPullRequest webhookConfig.setPullRequestAPIUrl(pR.getUrl() != null ? pR.getUrl().toString() : null); webhookConfig.setRepositoryUrl( repo.getHtmlUrl() != null ? repo.getHtmlUrl().toString() : null); - webhookConfig.setUserId("Single scan run"); + webhookConfig.setUserId("Single scan of pull request run"); webhookConfig.setHeadCommitSHA(pR.getHead() != null ? pR.getHead().getSha() : null); return webhookConfig; } diff --git a/src/test/java/com/lpvs/service/LPVSGitHubServiceTest.java b/src/test/java/com/lpvs/service/LPVSGitHubServiceTest.java index 14b054cc..42012194 100644 --- a/src/test/java/com/lpvs/service/LPVSGitHubServiceTest.java +++ b/src/test/java/com/lpvs/service/LPVSGitHubServiceTest.java @@ -4401,7 +4401,7 @@ void testGetInternalQueueByPullRequest() throws IOException { LPVSQueue result = gitHubService.getInternalQueueByPullRequest(pullRequest); assertNotNull(result); - assertEquals(result.getUserId(), "Single scan run"); + assertEquals(result.getUserId(), "Single scan of pull request run"); } @Test diff --git a/src/test/java/com/lpvs/service/scan/LPVSDetectServiceTest.java b/src/test/java/com/lpvs/service/scan/LPVSDetectServiceTest.java index 654ad808..6fd0c860 100644 --- a/src/test/java/com/lpvs/service/scan/LPVSDetectServiceTest.java +++ b/src/test/java/com/lpvs/service/scan/LPVSDetectServiceTest.java @@ -7,6 +7,7 @@ package com.lpvs.service.scan; import com.lpvs.entity.LPVSFile; +import com.lpvs.entity.LPVSLicense; import com.lpvs.entity.LPVSQueue; import com.lpvs.service.LPVSGitHubConnectionService; import com.lpvs.service.LPVSGitHubService; @@ -32,7 +33,9 @@ import java.io.IOException; import java.lang.reflect.Field; import java.net.URL; +import java.nio.file.Files; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; @@ -68,6 +71,7 @@ class TestRunScan__Scanoss { final String test_path = "test_path"; LPVSFile lpvs_file_1, lpvs_file_2; + LPVSLicense lpvs_license; @BeforeEach void setUp() throws IOException { @@ -83,47 +87,65 @@ void setUp() throws IOException { webhookConfig = new LPVSQueue(); webhookConfig.setId(1L); webhookConfig.setRepositoryUrl("https://github.com/Samsung/LPVS"); + webhookConfig.setPullRequestUrl("https://github.com/Samsung/LPVS/pull/1"); + + lpvs_license = + new LPVSLicense( + 1L, + "MIT License", + "MIT", + "PERMITTED", + "", + "https://opensource.org/licenses/MIT"); lpvs_file_1 = new LPVSFile( 1L, "test_path" + File.separator + "some_path", null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null); + "snippet", + "100%", + "1-10", + new HashSet<>() { + { + add(lpvs_license); + } + }, + "src/main/java/com/internal/lpvs/util/LPVSWebhookUtilExtension.java", + "main/java/com/lpvs/util/LPVSWebhookUtil.java", + "LPVS", + "1-10", + "https://github.com/Samsung/LPVS", + "1.0.1", + "Samsung"); lpvs_file_2 = new LPVSFile( 2L, "some_path", null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null); - - when(scanoss_mock.checkLicenses(webhookConfig)) - .thenReturn(List.of(lpvs_file_1, lpvs_file_2)); + "snippet", + "100%", + "34-45", + new HashSet<>() { + { + add(lpvs_license); + } + }, + "src/main/java/com/internal/lpvs/util/LPVSWebhookUtilExtension.java", + "main/java/com/lpvs/util/LPVSWebhookUtil.java", + "LPVS", + "34-45", + "https://github.com/Samsung/LPVS", + "1.5.0", + "Samsung"); + + when(scanoss_mock.checkLicenses(any())).thenReturn(List.of(lpvs_file_1, lpvs_file_2)); when(github_mock.connectToGitHubApi()).thenReturn(mockGitHub); } @Test - void testRunOneScanWithNullTrigger() throws NoSuchFieldException, IllegalAccessException { + void testRunOneScanPullRequestWithNullTrigger() + throws NoSuchFieldException, IllegalAccessException { lpvsDetectService = spy( new LPVSDetectService( @@ -139,7 +161,55 @@ void testRunOneScanWithNullTrigger() throws NoSuchFieldException, IllegalAccessE } @Test - void testRunOneScan_Default() throws NoSuchFieldException, IllegalAccessException { + void testRunOneScanLocalFileWithNullTrigger() + throws NoSuchFieldException, IllegalAccessException { + lpvsDetectService = + spy( + new LPVSDetectService( + "scanoss", false, null, null, null, scanServiceFactory_mock)); + + setPrivateField(lpvsDetectService, "localPath", null); + + assertDoesNotThrow(() -> lpvsDetectService.runSingleScan()); + + setPrivateField(lpvsDetectService, "localPath", ""); + + assertDoesNotThrow(() -> lpvsDetectService.runSingleScan()); + } + + @Test + void testRunOneScanBothPullRequestAndLocalFile() + throws NoSuchFieldException, IllegalAccessException { + lpvsDetectService = + spy( + new LPVSDetectService( + "scanoss", false, null, null, null, scanServiceFactory_mock)); + + setPrivateField(lpvsDetectService, "ctx", mockApplicationContext); + setPrivateField(lpvsDetectService, "trigger", ""); + setPrivateField(lpvsDetectService, "localPath", ""); + + assertDoesNotThrow(() -> lpvsDetectService.runSingleScan()); + } + + @Test + void testRunOneScanBothPullRequestAndLocalFile2() + throws NoSuchFieldException, IllegalAccessException { + lpvsDetectService = + spy( + new LPVSDetectService( + "scanoss", false, null, null, null, scanServiceFactory_mock)); + + setPrivateField(lpvsDetectService, "ctx", mockApplicationContext); + setPrivateField(lpvsDetectService, "trigger", "some-pull-request"); + setPrivateField(lpvsDetectService, "localPath", "some-local-path"); + + assertDoesNotThrow(() -> lpvsDetectService.runSingleScan()); + } + + @Test + void testRunOneScan_PullRequest_Default() + throws NoSuchFieldException, IllegalAccessException { lpvsDetectService = spy( @@ -148,12 +218,13 @@ void testRunOneScan_Default() throws NoSuchFieldException, IllegalAccessExceptio setPrivateField(lpvsDetectService, "trigger", "fake-trigger-value"); setPrivateField(lpvsDetectService, "ctx", mockApplicationContext); + setPrivateField(lpvsDetectService, "gitHubService", githubservice_mock); assertDoesNotThrow(() -> lpvsDetectService.runSingleScan()); } @Test - void testRunOneScan_Branch2() + void testRunOneScan_PullRequest_Branch2() throws NoSuchFieldException, IllegalAccessException, IOException { LPVSLicenseService.Conflict conflict_1 = new LPVSLicenseService.Conflict<>("MIT", "Apache-2.0"); @@ -175,20 +246,30 @@ void testRunOneScan_Branch2() when(mockCommitPointer.getRepository()).thenReturn(mockHeadRepository); when(mockHeadRepository.getHtmlUrl()) .thenReturn(new URL("https://example.com/repo/files")); + when(githubservice_mock.getInternalQueueByPullRequest(anyString())) + .thenReturn(webhookConfig); // Set up expected values String expectedPullRequestUrl = "https://example.com/pull/1"; when(mockPullRequest.getHtmlUrl()).thenReturn(new URL(expectedPullRequestUrl)); + when(githubservice_mock.getPullRequestFiles(any())) + .thenReturn( + System.getProperty("user.home") + + File.separator + + "LPVS" + + File.separator + + "Projects"); setPrivateField(detectService, "trigger", "github/owner/repo/branch/123"); setPrivateField(detectService, "htmlReport", null); setPrivateField(detectService, "ctx", mockApplicationContext); + setPrivateField(detectService, "scanService", scanoss_mock); - detectService.runSingleScan(); + assertDoesNotThrow(() -> detectService.runSingleScan()); setPrivateField(detectService, "htmlReport", ""); - detectService.runSingleScan(); + assertDoesNotThrow(() -> detectService.runSingleScan()); } @Test @@ -208,6 +289,7 @@ void testRunOneScan_Branch3() setPrivateField(detectService, "trigger", "github/owner/repo/branch/123"); setPrivateField(detectService, "htmlReport", "build"); setPrivateField(detectService, "ctx", mockApplicationContext); + setPrivateField(detectService, "scanService", scanoss_mock); // Mock the necessary GitHub objects for LPVSQueue when(mockGitHub.getRepository(any())).thenReturn(mockRepository); @@ -218,14 +300,178 @@ void testRunOneScan_Branch3() when(mockCommitPointer.getRepository()).thenReturn(mockHeadRepository); when(mockHeadRepository.getHtmlUrl()) .thenReturn(new URL("https://example.com/repo/files")); + when(githubservice_mock.getInternalQueueByPullRequest(anyString())) + .thenReturn(webhookConfig); // Set up expected values String expectedPullRequestUrl = "https://example.com/pull/1"; when(mockPullRequest.getHtmlUrl()).thenReturn(new URL(expectedPullRequestUrl)); + when(githubservice_mock.getPullRequestFiles(any())) + .thenReturn( + System.getProperty("user.home") + + File.separator + + "LPVS" + + File.separator + + "Projects"); - detectService.runSingleScan(); + assertDoesNotThrow(() -> detectService.runSingleScan()); + + deleteFile("build"); + } + + @Test + void testRunOneScan_LocalFiles_WithConsoleReport() + throws NoSuchFieldException, IllegalAccessException, IOException { + LPVSLicenseService.Conflict conflict_1 = + new LPVSLicenseService.Conflict<>("MIT", "Apache-2.0"); + + List> expected = + List.of(conflict_1, conflict_1); + + lpvsDetectService = + spy( + new LPVSDetectService( + "scanoss", false, null, null, null, scanServiceFactory_mock)); + + File sourceDir = Files.createTempDirectory("source").toFile(); + File sourceFile1 = new File(sourceDir, "file1.txt"); + sourceFile1.createNewFile(); + + setPrivateField(detectService, "localPath", sourceFile1.getAbsolutePath()); + setPrivateField(detectService, "ctx", mockApplicationContext); + setPrivateField(detectService, "scanService", scanoss_mock); + + // Mock the necessary GitHub objects for LPVSQueue + when(mockGitHub.getRepository(any())).thenReturn(mockRepository); + when(mockRepository.getPullRequest(anyInt())).thenReturn(mockPullRequest); + when(mockRepository.getPullRequest(anyInt())).thenReturn(mockPullRequest); + when(mockPullRequest.getHead()).thenReturn(mockCommitPointer); + when(licenseservice_mock.findConflicts(webhookConfig, null)).thenReturn(expected); + when(mockCommitPointer.getRepository()).thenReturn(mockHeadRepository); + when(mockHeadRepository.getHtmlUrl()) + .thenReturn(new URL("https://example.com/repo/files")); + when(githubservice_mock.getInternalQueueByPullRequest(anyString())) + .thenReturn(webhookConfig); + + // Set up expected values + String expectedPullRequestUrl = "https://example.com/pull/1"; + when(mockPullRequest.getHtmlUrl()).thenReturn(new URL(expectedPullRequestUrl)); + when(githubservice_mock.getPullRequestFiles(any())) + .thenReturn( + System.getProperty("user.home") + + File.separator + + "LPVS" + + File.separator + + "Projects"); assertDoesNotThrow(() -> detectService.runSingleScan()); + + deleteDirectory(sourceDir.getAbsolutePath()); + } + + @Test + void testRunOneScan_LocalFiles_WithHtmlReport() + throws NoSuchFieldException, IllegalAccessException, IOException { + LPVSLicenseService.Conflict conflict_1 = + new LPVSLicenseService.Conflict<>("MIT", "Apache-2.0"); + + List> expected = + List.of(conflict_1, conflict_1); + + lpvsDetectService = + spy( + new LPVSDetectService( + "scanoss", false, null, null, null, scanServiceFactory_mock)); + + File sourceDir = Files.createTempDirectory("source").toFile(); + File sourceFile1 = new File(sourceDir, "file1.txt"); + File sourceFile2 = new File(sourceDir, "file2.txt"); + sourceFile1.createNewFile(); + sourceFile2.createNewFile(); + + setPrivateField(detectService, "localPath", sourceDir.getAbsolutePath()); + setPrivateField(detectService, "ctx", mockApplicationContext); + setPrivateField(detectService, "scanService", scanoss_mock); + setPrivateField(detectService, "htmlReport", "report/test.html"); + + new File("report").mkdir(); + + // Mock the necessary GitHub objects for LPVSQueue + when(mockGitHub.getRepository(any())).thenReturn(mockRepository); + when(mockRepository.getPullRequest(anyInt())).thenReturn(mockPullRequest); + when(mockRepository.getPullRequest(anyInt())).thenReturn(mockPullRequest); + when(mockPullRequest.getHead()).thenReturn(mockCommitPointer); + when(licenseservice_mock.findConflicts(webhookConfig, null)).thenReturn(expected); + when(mockCommitPointer.getRepository()).thenReturn(mockHeadRepository); + when(mockHeadRepository.getHtmlUrl()) + .thenReturn(new URL("https://example.com/repo/files")); + when(githubservice_mock.getInternalQueueByPullRequest(anyString())) + .thenReturn(webhookConfig); + + // Set up expected values + String expectedPullRequestUrl = "https://example.com/pull/1"; + when(mockPullRequest.getHtmlUrl()).thenReturn(new URL(expectedPullRequestUrl)); + when(githubservice_mock.getPullRequestFiles(any())) + .thenReturn( + System.getProperty("user.home") + + File.separator + + "LPVS" + + File.separator + + "Projects"); + + assertDoesNotThrow(() -> detectService.runSingleScan()); + + deleteDirectory(sourceDir.getAbsolutePath()); + deleteDirectory("report"); + } + + @Test + void testRunOneScan_LocalFiles_NoFile() + throws NoSuchFieldException, IllegalAccessException, IOException { + LPVSLicenseService.Conflict conflict_1 = + new LPVSLicenseService.Conflict<>("MIT", "Apache-2.0"); + + List> expected = + List.of(conflict_1, conflict_1); + + lpvsDetectService = + spy( + new LPVSDetectService( + "scanoss", false, null, null, null, scanServiceFactory_mock)); + + File sourceDir = Files.createTempDirectory("source").toFile(); + File sourceFile1 = new File(sourceDir, "file1.txt"); + + setPrivateField(detectService, "localPath", sourceFile1.getAbsolutePath()); + setPrivateField(detectService, "ctx", mockApplicationContext); + setPrivateField(detectService, "scanService", scanoss_mock); + + // Mock the necessary GitHub objects for LPVSQueue + when(mockGitHub.getRepository(any())).thenReturn(mockRepository); + when(mockRepository.getPullRequest(anyInt())).thenReturn(mockPullRequest); + when(mockRepository.getPullRequest(anyInt())).thenReturn(mockPullRequest); + when(mockPullRequest.getHead()).thenReturn(mockCommitPointer); + when(licenseservice_mock.findConflicts(webhookConfig, null)).thenReturn(expected); + when(mockCommitPointer.getRepository()).thenReturn(mockHeadRepository); + when(mockHeadRepository.getHtmlUrl()) + .thenReturn(new URL("https://example.com/repo/files")); + when(githubservice_mock.getInternalQueueByPullRequest(anyString())) + .thenReturn(webhookConfig); + + // Set up expected values + String expectedPullRequestUrl = "https://example.com/pull/1"; + when(mockPullRequest.getHtmlUrl()).thenReturn(new URL(expectedPullRequestUrl)); + when(githubservice_mock.getPullRequestFiles(any())) + .thenReturn( + System.getProperty("user.home") + + File.separator + + "LPVS" + + File.separator + + "Projects"); + + assertDoesNotThrow(() -> detectService.runSingleScan()); + + deleteDirectory(sourceDir.getAbsolutePath()); } @Test @@ -248,8 +494,11 @@ void testRunOneScan_TriggerNotNull() throws Exception { List.of(conflict_1, conflict_1); setPrivateField(detectService, "trigger", "github/owner/repo/branch/123"); - setPrivateField(detectService, "htmlReport", "build/report/test.html"); + setPrivateField(detectService, "htmlReport", "report/test.html"); setPrivateField(detectService, "ctx", mockApplicationContext); + setPrivateField(detectService, "scanService", scanoss_mock); + + new File("report").mkdir(); // Mock the necessary GitHub objects for LPVSQueue when(mockGitHub.getRepository(any())).thenReturn(mockRepository); @@ -260,14 +509,23 @@ void testRunOneScan_TriggerNotNull() throws Exception { when(mockCommitPointer.getRepository()).thenReturn(mockHeadRepository); when(mockHeadRepository.getHtmlUrl()) .thenReturn(new URL("https://example.com/repo/files")); + when(githubservice_mock.getInternalQueueByPullRequest(anyString())) + .thenReturn(webhookConfig); // Set up expected values String expectedPullRequestUrl = "https://example.com/pull/1"; when(mockPullRequest.getHtmlUrl()).thenReturn(new URL(expectedPullRequestUrl)); - - detectService.runSingleScan(); + when(githubservice_mock.getPullRequestFiles(any())) + .thenReturn( + System.getProperty("user.home") + + File.separator + + "LPVS" + + File.separator + + "Projects"); assertDoesNotThrow(() -> detectService.runSingleScan()); + + deleteDirectory("report"); } @Test @@ -280,8 +538,11 @@ void testRunOneScan_TriggerNotNull_Branch2() throws Exception { List.of(conflict_1, conflict_1); setPrivateField(detectService, "trigger", "github/owner/repo/branch/123"); - setPrivateField(detectService, "htmlReport", "build/report/test.html"); + setPrivateField(detectService, "htmlReport", "report/test.html"); setPrivateField(detectService, "ctx", mockApplicationContext); + setPrivateField(detectService, "scanService", scanoss_mock); + + new File("report").mkdir(); // Mock the necessary GitHub objects for LPVSQueue when(mockGitHub.getRepository(any())).thenReturn(mockRepository); @@ -292,14 +553,23 @@ void testRunOneScan_TriggerNotNull_Branch2() throws Exception { when(mockCommitPointer.getRepository()).thenReturn(null); when(mockHeadRepository.getHtmlUrl()) .thenReturn(new URL("https://example.com/repo/files")); + when(githubservice_mock.getInternalQueueByPullRequest(anyString())) + .thenReturn(webhookConfig); // Set up expected values String expectedPullRequestUrl = "https://example.com/pull/1"; when(mockPullRequest.getHtmlUrl()).thenReturn(new URL(expectedPullRequestUrl)); - - detectService.runSingleScan(); + when(githubservice_mock.getPullRequestFiles(any())) + .thenReturn( + System.getProperty("user.home") + + File.separator + + "LPVS" + + File.separator + + "Projects"); assertDoesNotThrow(() -> detectService.runSingleScan()); + + deleteDirectory("report"); } @Test @@ -313,8 +583,11 @@ void testRunOneScan_TriggerNotNull_Branch3() throws Exception { List.of(conflict_1, conflict_1); setPrivateField(detectService, "trigger", "github/owner/repo/branch/123"); - setPrivateField(detectService, "htmlReport", "build/report/test.html"); + setPrivateField(detectService, "htmlReport", "report/test.html"); setPrivateField(detectService, "ctx", mockApplicationContext); + setPrivateField(detectService, "scanService", scanoss_mock); + + new File("report").mkdir(); // Mock the necessary GitHub objects for LPVSQueue when(mockGitHub.getRepository(any())).thenReturn(mockRepository); @@ -323,18 +596,66 @@ void testRunOneScan_TriggerNotNull_Branch3() throws Exception { when(mockPullRequest.getHead()).thenReturn(mockCommitPointer); when(licenseservice_mock.findConflicts(webhookConfig, null)).thenReturn(expected); when(mockCommitPointer.getRepository()).thenReturn(mockHeadRepository2); + when(githubservice_mock.getInternalQueueByPullRequest(anyString())) + .thenReturn(webhookConfig); // Set up expected values String expectedPullRequestUrl = "https://example.com/pull/1"; when(mockRepository.getHtmlUrl()).thenReturn(new URL(expectedPullRequestUrl)); + when(githubservice_mock.getPullRequestFiles(any())) + .thenReturn( + System.getProperty("user.home") + + File.separator + + "LPVS" + + File.separator + + "Projects"); + + assertDoesNotThrow(() -> detectService.runSingleScan()); + + deleteDirectory("report"); + } + + @Test + void testRunOneScan_TriggerNotNull_NoDirectory() throws Exception { + GHRepository mockHeadRepository2 = mock(GHRepository.class); + + LPVSLicenseService.Conflict conflict_1 = + new LPVSLicenseService.Conflict<>("MIT", "Apache-2.0"); + + List> expected = + List.of(conflict_1, conflict_1); + + setPrivateField(detectService, "trigger", "github/owner/repo/branch/123"); + setPrivateField(detectService, "htmlReport", "report/test.html"); + setPrivateField(detectService, "ctx", mockApplicationContext); + setPrivateField(detectService, "scanService", scanoss_mock); + + // Mock the necessary GitHub objects for LPVSQueue + when(mockGitHub.getRepository(any())).thenReturn(mockRepository); + when(mockRepository.getPullRequest(anyInt())).thenReturn(mockPullRequest); + when(mockRepository.getPullRequest(anyInt())).thenReturn(mockPullRequest); + when(mockPullRequest.getHead()).thenReturn(mockCommitPointer); + when(licenseservice_mock.findConflicts(webhookConfig, null)).thenReturn(expected); + when(mockCommitPointer.getRepository()).thenReturn(mockHeadRepository2); + when(githubservice_mock.getInternalQueueByPullRequest(anyString())) + .thenReturn(webhookConfig); - detectService.runSingleScan(); + // Set up expected values + String expectedPullRequestUrl = "https://example.com/pull/1"; + when(mockRepository.getHtmlUrl()).thenReturn(new URL(expectedPullRequestUrl)); + when(githubservice_mock.getPullRequestFiles(any())) + .thenReturn( + System.getProperty("user.home") + + File.separator + + "LPVS" + + File.separator + + "Projects"); assertDoesNotThrow(() -> detectService.runSingleScan()); } @Test - void testCommentBuilder_ConflictFilePresent() throws Exception { + void testCommentBuilder_ConflictFilePresent() { LPVSLicenseService.Conflict conflict_1 = new LPVSLicenseService.Conflict<>("MIT", "Apache-2.0"); @@ -502,4 +823,26 @@ private void setPrivateField(Object target, String fieldName, Object value) field.set(target, value); field.setAccessible(false); } + + private void deleteFile(String path) { + File file = new File(path); + if (file.exists() && file.isFile()) { + file.delete(); + } + } + + private void deleteDirectory(String directoryPath) { + File directory = new File(directoryPath); + File[] files = directory.listFiles(); + if (files != null) { + for (File file : files) { + if (file.isDirectory()) { + deleteDirectory(file.getPath()); + } else { + file.delete(); + } + } + } + directory.delete(); + } } diff --git a/src/test/java/com/lpvs/util/LPVSCommentUtilTest.java b/src/test/java/com/lpvs/util/LPVSCommentUtilTest.java index f5e11ce6..c94a99d0 100644 --- a/src/test/java/com/lpvs/util/LPVSCommentUtilTest.java +++ b/src/test/java/com/lpvs/util/LPVSCommentUtilTest.java @@ -194,7 +194,6 @@ void testSaveHTMLToFile() throws IOException { assertEquals(htmlContent, fileContent); // Clean up: delete the created file - // TODO: need to switch to temp folder option Files.deleteIfExists(Paths.get(filePath)); } } diff --git a/src/test/java/com/lpvs/util/LPVSFileUtilTest.java b/src/test/java/com/lpvs/util/LPVSFileUtilTest.java index f8abea27..6f3b6fab 100644 --- a/src/test/java/com/lpvs/util/LPVSFileUtilTest.java +++ b/src/test/java/com/lpvs/util/LPVSFileUtilTest.java @@ -13,15 +13,20 @@ import org.springframework.test.util.ReflectionTestUtils; import java.io.File; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; +import static com.lpvs.util.LPVSFileUtil.copyFiles; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class LPVSFileUtilTest { private LPVSQueue webhookConfig = null; + private File sourceDir; + private File destinationDir; @BeforeEach public void setUp() { @@ -87,6 +92,64 @@ public void testSaveGithubDiffsEmptyPatchLines() { webhookConfig)); } + @Test + public void testCopyFilesDirectory() throws IOException { + sourceDir = Files.createTempDirectory("source").toFile(); + destinationDir = Files.createTempDirectory("destination").toFile(); + + File sourceFile1 = new File(sourceDir, "file1.txt"); + File sourceFile2 = new File(sourceDir, "file2.txt"); + File sourceSubdir = new File(sourceDir, "subdir"); + File sourceSubfile = new File(sourceSubdir, "subfile.txt"); + + sourceFile1.createNewFile(); + sourceFile2.createNewFile(); + sourceSubdir.mkdirs(); + sourceSubfile.createNewFile(); + + copyFiles(sourceDir.getAbsolutePath(), destinationDir.getAbsolutePath()); + + assertTrue(new File(destinationDir, "file1.txt").exists()); + assertTrue(new File(destinationDir, "file2.txt").exists()); + assertTrue(new File(destinationDir, "subdir").exists()); + assertTrue(new File(destinationDir, "subdir/subfile.txt").exists()); + + deleteDirectory(sourceDir); + deleteDirectory(destinationDir); + } + + @Test + public void testCopyFilesFile() throws IOException { + sourceDir = Files.createTempDirectory("source").toFile(); + destinationDir = Files.createTempDirectory("destination").toFile(); + + File sourceFile = new File(sourceDir, "file.txt"); + sourceFile.createNewFile(); + + copyFiles(sourceFile.getAbsolutePath(), destinationDir.getAbsolutePath()); + + assertTrue(new File(destinationDir, "file.txt").exists()); + + deleteDirectory(sourceDir); + deleteDirectory(destinationDir); + } + + @Test + public void testCopyFilesDirectoryWithNullFiles() throws IOException { + sourceDir = Files.createTempDirectory("source").toFile(); + destinationDir = Files.createTempDirectory("destination").toFile(); + + File sourceSubdir = new File(sourceDir, "subdir"); + sourceSubdir.mkdirs(); + + copyFiles(sourceDir.getAbsolutePath(), destinationDir.getAbsolutePath()); + + assertTrue(new File(destinationDir, "subdir").exists()); + + deleteDirectory(sourceDir); + deleteDirectory(destinationDir); + } + @Test public void testGetLocalDirectoryPathWithHeadCommitSHA() { webhookConfig.setHeadCommitSHA("aaaa"); @@ -199,4 +262,18 @@ private static String getExpectedJsonFilePathWithPullRequestId() { private static String getExpectedJsonFilePathWithCommitSHA() { return getExpectedResultsPath() + File.separator + "aaaa.json"; } + + private void deleteDirectory(File directory) { + File[] files = directory.listFiles(); + if (files != null) { + for (File file : files) { + if (file.isDirectory()) { + deleteDirectory(file); + } else { + file.delete(); + } + } + } + directory.delete(); + } } diff --git a/src/test/java/com/lpvs/util/LPVSPayloadUtilTest.java b/src/test/java/com/lpvs/util/LPVSPayloadUtilTest.java index 3652d71b..b643cf28 100644 --- a/src/test/java/com/lpvs/util/LPVSPayloadUtilTest.java +++ b/src/test/java/com/lpvs/util/LPVSPayloadUtilTest.java @@ -75,6 +75,14 @@ public void testConvertOsoriDbResponseToLicense_withInvalidPayload_N() { LPVSLicense actualLicense = LPVSPayloadUtil.convertOsoriDbResponseToLicense(payload); assertNull(actualLicense); } + + @Test + public void testConvertOsoriDbResponseToLicense_emptyPayload_N() { + String payload = + "{\"code\":\"200\",\"messageList\":{\"detailInfo\":[]},\"success\":true}"; + LPVSLicense actualLicense = LPVSPayloadUtil.convertOsoriDbResponseToLicense(payload); + assertNull(actualLicense); + } } @Nested