From 47e4c1c8aa37d310c66882315c6790db3d60a1cd Mon Sep 17 00:00:00 2001 From: Aleksandr Danchenko Date: Tue, 19 Apr 2022 14:50:24 +0300 Subject: [PATCH] fix: fix root directory for open source scans (#136) --- CHANGELOG.md | 7 +- .../SnykCode/AnalysisServiceTest.cs | 16 +- .../SnykCode/BundleServiceTest.cs | 60 ++-- .../SnykCode/CodeCacheServiceTest.cs | 36 +- .../SnykCode/FiltersServiceTest.cs | 12 +- .../SnykCode/SnykCodeServiceTest.cs | 44 +-- Snyk.Code.Library/Service/CodeCacheService.cs | 51 ++- .../Service/ICodeCacheService.cs | 5 +- .../Service/SnykCodeFileProvider.cs | 4 +- Snyk.Code.Library/Service/SnykCodeService.cs | 8 +- Snyk.Common/IFileProvider.cs | 2 +- Snyk.Common/ISolutionService.cs | 15 +- .../CLI/ICli.cs | 4 +- .../CLI/SnykCli.cs | 19 +- .../Service/IOssService.cs | 3 +- .../Service/OssService.cs | 9 +- .../Service/SnykSolutionService.cs | 193 ++++++++--- .../Service/SnykTasksService.cs | 105 +++--- .../Settings/ISnykOptions.cs | 25 +- .../Settings/SnykGeneralOptionsDialogPage.cs | 29 +- .../SnykSolutionOptionsUserControl.cs | 22 +- .../SnykUserStorageSettingsService.cs | 21 +- .../SnykCode/DataFlowStepsControl.xaml.cs | 42 ++- .../SnykCodeDescriptionControl.xaml.cs | 2 +- .../Toolwindow/SnykToolWindowControl.xaml.cs | 7 +- .../OssServiceTest.cs | 28 +- .../SnykCliTest.cs | 314 +++++++----------- .../SnykSolutionServiceTest.cs | 33 ++ .../SnykUserStorageSettingsServiceTest.cs | 11 +- 29 files changed, 636 insertions(+), 491 deletions(-) create mode 100644 Snyk.VisualStudio.Extension.Tests/SnykSolutionServiceTest.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f046a465..ce20514d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,15 @@ # Snyk Changelog -## [1.1.9] +## [1.1.10] ### Changed - Snyk Code: add support for Single Tenant setups. +### Fixed +- Scan of solutions in which *.sln file is not in the root directory. + +## [1.1.9] + ### Fixed - "Object reference not set to an instance of an object" when launching extension in Visual Studio 2022. diff --git a/Snyk.Code.Library.Tests/SnykCode/AnalysisServiceTest.cs b/Snyk.Code.Library.Tests/SnykCode/AnalysisServiceTest.cs index 4787c1136..69f32d320 100644 --- a/Snyk.Code.Library.Tests/SnykCode/AnalysisServiceTest.cs +++ b/Snyk.Code.Library.Tests/SnykCode/AnalysisServiceTest.cs @@ -33,8 +33,8 @@ public void AnalysisService_FailedStatusProvided_GetAnalysisThrowException() }; codeClientMock - .Setup(codeClient => codeClient.GetAnalysisAsync(dummyBundleId, It.IsAny()).Result) - .Returns(dummyAnalysisResultDto); + .Setup(codeClient => codeClient.GetAnalysisAsync(dummyBundleId, It.IsAny())) + .ReturnsAsync(dummyAnalysisResultDto); _ = Assert.ThrowsAsync(() => analysisService.GetAnalysisAsync(dummyBundleId, scanCodeProgressUpdate)); @@ -61,8 +61,8 @@ public async Task AnalysisService_WaitingStatusProvided_GetAnalysisSuccessInTwoA var mockMethodCallsCount = 0; codeClientMock - .Setup(codeClient => codeClient.GetAnalysisAsync(dummyBundleId, It.IsAny()).Result) - .Returns(dummyAnalysisResultDto) + .Setup(codeClient => codeClient.GetAnalysisAsync(dummyBundleId, It.IsAny())) + .ReturnsAsync(dummyAnalysisResultDto) .Callback((str, cancellationToken) => { mockMethodCallsCount++; @@ -99,8 +99,8 @@ public async Task AnalysisService_InfiniteWaitingStatusProvided_GetAnalysisRetur }; codeClientMock - .Setup(codeClient => codeClient.GetAnalysisAsync(dummyBundleId, It.IsAny()).Result) - .Returns(dummyAnalysisResultDto); + .Setup(codeClient => codeClient.GetAnalysisAsync(dummyBundleId, It.IsAny())) + .ReturnsAsync(dummyAnalysisResultDto); var analysisResult = await analysisService.GetAnalysisAsync(dummyBundleId, this.scanCodeProgressUpdate, requestAttempts: 5); @@ -282,8 +282,8 @@ public async Task AnalysisService_TwoFilesWithIssuesProvided_GetAnalysisSuccessA }; codeClientMock - .Setup(codeClient => codeClient.GetAnalysisAsync(dummyBundleId, It.IsAny()).Result) - .Returns(dummyAnalysisResultDto); + .Setup(codeClient => codeClient.GetAnalysisAsync(dummyBundleId, It.IsAny())) + .ReturnsAsync(dummyAnalysisResultDto); var analysisResult = await analysisService.GetAnalysisAsync(dummyBundleId, this.scanCodeProgressUpdate); diff --git a/Snyk.Code.Library.Tests/SnykCode/BundleServiceTest.cs b/Snyk.Code.Library.Tests/SnykCode/BundleServiceTest.cs index 20d4b2628..92128084b 100644 --- a/Snyk.Code.Library.Tests/SnykCode/BundleServiceTest.cs +++ b/Snyk.Code.Library.Tests/SnykCode/BundleServiceTest.cs @@ -38,8 +38,8 @@ public async Task BundleService_TwoFilesProvided_UploadSuccessfullAfterTreeAttem }; codeClientMock - .Setup(codeClient => codeClient.CheckBundleAsync(bundle.Id, It.IsAny()).Result) - .Returns(bundleDto); + .Setup(codeClient => codeClient.CheckBundleAsync(bundle.Id, It.IsAny())) + .ReturnsAsync(bundleDto); string fileContent1 = TestResource.GetFileContent("app1.js"); string fileContent2 = TestResource.GetFileContent("app2.js"); @@ -56,8 +56,8 @@ public async Task BundleService_TwoFilesProvided_UploadSuccessfullAfterTreeAttem var mockMethodCallsCount = 1; codeClientMock - .Setup(codeClient => codeClient.ExtendBundleAsync(bundle.Id, It.IsAny>(), It.IsAny()).Result) - .Returns(new BundleResponseDto()) + .Setup(codeClient => codeClient.ExtendBundleAsync(bundle.Id, It.IsAny>(), It.IsAny())) + .ReturnsAsync(new BundleResponseDto()) .Callback, CancellationToken>((str, codeFilesDict, cancellationToken) => { mockMethodCallsCount++; @@ -103,8 +103,8 @@ public async Task BundleService_ActiveBundleProvided_CheckBundleSuccessfullAsync var dummyBundleDto = new BundleResponseDto { Hash = "dummy id" }; codeClientMock - .Setup(codeClient => codeClient.CheckBundleAsync(dummyBundleDto.Hash, It.IsAny()).Result) - .Returns(dummyBundleDto); + .Setup(codeClient => codeClient.CheckBundleAsync(dummyBundleDto.Hash, It.IsAny())) + .ReturnsAsync(dummyBundleDto); var bundleService = new BundleService(codeClientMock.Object); @@ -142,8 +142,8 @@ public async Task BundleService_ThreeFilesProvided_UploadedSuccessfullyAsync() var dummyBundleDto = new BundleResponseDto { Hash = "dummy id" }; codeClientMock - .Setup(codeClient => codeClient.CreateBundleAsync(It.IsAny>(), It.IsAny()).Result) - .Returns(dummyBundleDto); + .Setup(codeClient => codeClient.CreateBundleAsync(It.IsAny>(), It.IsAny())) + .ReturnsAsync(dummyBundleDto); var bundleService = new BundleService(codeClientMock.Object); @@ -159,8 +159,8 @@ public async Task BundleService_ThreeFilesProvided_UploadedSuccessfullyAsync() fileHashToContentDict.Add(filePath3, (fileHash3, fileContent3)); codeClientMock - .Setup(codeClient => codeClient.ExtendBundleAsync(dummyBundleDto.Hash, It.IsAny>(), It.IsAny()).Result) - .Returns(new BundleResponseDto()); + .Setup(codeClient => codeClient.ExtendBundleAsync(dummyBundleDto.Hash, It.IsAny>(), It.IsAny())) + .ReturnsAsync(new BundleResponseDto()); bool isSuccess = await bundleService.UploadFilesAsync(createdBundle.Id, fileHashToContentDict, (state, progress) => { }, 200); @@ -191,8 +191,8 @@ public async Task BundleService_TwoFilesAndRemoveOneFileProvided_ExtendBundleChe var dummyBundleDto = new BundleResponseDto { Hash = "dummy id" }; codeClientMock - .Setup(codeClient => codeClient.CreateBundleAsync(filePathToHashDict, It.IsAny()).Result) - .Returns(dummyBundleDto); + .Setup(codeClient => codeClient.CreateBundleAsync(filePathToHashDict, It.IsAny())) + .ReturnsAsync(dummyBundleDto); var firstBundleDto = await bundleService.CreateBundleAsync(filePathToHashDict); @@ -215,8 +215,8 @@ public async Task BundleService_TwoFilesAndRemoveOneFileProvided_ExtendBundleChe }; codeClientMock - .Setup(codeClient => codeClient.ExtendBundleAsync(dummyBundleDto.Hash, It.IsAny>(), It.IsAny>(), It.IsAny()).Result) - .Returns(resultExtendBundleDto); + .Setup(codeClient => codeClient.ExtendBundleAsync(dummyBundleDto.Hash, It.IsAny>(), It.IsAny>(), It.IsAny())) + .ReturnsAsync(resultExtendBundleDto); var uploadedBundle = await bundleService.ExtendBundleAsync(firstBundleDto.Id, extendFilePathToHashDict, filesToRemovePaths, 200); @@ -247,8 +247,8 @@ public async Task BundleService_FiveFilesProvided_ExtendBundleChecksPassAsync() var dummyBundleDto = new BundleResponseDto { Hash = "dummy id" }; codeClientMock - .Setup(codeClient => codeClient.CreateBundleAsync(filePathToHashDict, It.IsAny()).Result) - .Returns(dummyBundleDto); + .Setup(codeClient => codeClient.CreateBundleAsync(filePathToHashDict, It.IsAny())) + .ReturnsAsync(dummyBundleDto); var firstBundleDto = await bundleService.CreateBundleAsync(filePathToHashDict); @@ -273,8 +273,8 @@ public async Task BundleService_FiveFilesProvided_ExtendBundleChecksPassAsync() }; codeClientMock - .Setup(codeClient => codeClient.ExtendBundleAsync(dummyBundleDto.Hash, It.IsAny>(), It.IsAny>(), It.IsAny()).Result) - .Returns(resultExtendBundleDto); + .Setup(codeClient => codeClient.ExtendBundleAsync(dummyBundleDto.Hash, It.IsAny>(), It.IsAny>(), It.IsAny())) + .ReturnsAsync(resultExtendBundleDto); var extendedBundle = await bundleService.ExtendBundleAsync(firstBundleDto.Id, extendFilePathToHashDict, new List(), 150); @@ -305,8 +305,8 @@ public async Task BundleService_FiveFilesProvided_ProcessExtendLargeBundleChecks var dummyBundleDto = new BundleResponseDto { Hash = "dummy id" }; codeClientMock - .Setup(codeClient => codeClient.CreateBundleAsync(filePathToHashDict, It.IsAny()).Result) - .Returns(dummyBundleDto); + .Setup(codeClient => codeClient.CreateBundleAsync(filePathToHashDict, It.IsAny())) + .ReturnsAsync(dummyBundleDto); var firstBundleDto = await bundleService.CreateBundleAsync(filePathToHashDict); @@ -331,8 +331,8 @@ public async Task BundleService_FiveFilesProvided_ProcessExtendLargeBundleChecks }; codeClientMock - .Setup(codeClient => codeClient.ExtendBundleAsync(dummyBundleDto.Hash, It.IsAny>(), It.IsAny>(), It.IsAny()).Result) - .Returns(resultExtendBundleDto); + .Setup(codeClient => codeClient.ExtendBundleAsync(dummyBundleDto.Hash, It.IsAny>(), It.IsAny>(), It.IsAny())) + .ReturnsAsync(resultExtendBundleDto); var extendedBundle = await bundleService.ProcessExtendLargeBundleAsync(firstBundleDto.Id, extendFilePathToHashDict, null, 150); @@ -366,8 +366,8 @@ public async Task BundleService_FiftyFilesProvided_CreateBundleChecksPassAsync() var dummyBundleDto = new BundleResponseDto { Hash = "dummy id" }; codeClientMock - .Setup(codeClient => codeClient.CreateBundleAsync(It.IsAny>(), It.IsAny()).Result) - .Returns(dummyBundleDto); + .Setup(codeClient => codeClient.CreateBundleAsync(It.IsAny>(), It.IsAny())) + .ReturnsAsync(dummyBundleDto); var resultExtendBundleDto = new BundleResponseDto { @@ -376,8 +376,8 @@ public async Task BundleService_FiftyFilesProvided_CreateBundleChecksPassAsync() }; codeClientMock - .Setup(codeClient => codeClient.ExtendBundleAsync(dummyBundleDto.Hash, It.IsAny>(), It.IsAny>(), It.IsAny()).Result) - .Returns(resultExtendBundleDto); + .Setup(codeClient => codeClient.ExtendBundleAsync(dummyBundleDto.Hash, It.IsAny>(), It.IsAny>(), It.IsAny())) + .ReturnsAsync(resultExtendBundleDto); var bundleDto = await bundleService.CreateBundleAsync(filePathToHashDict, 150); @@ -412,8 +412,8 @@ public async Task BundleService_ThreeFilesProvided_ProcessCreateLargeBundleCheck var dummyBundleDto = new BundleResponseDto { Hash = "dummy id" }; codeClientMock - .Setup(codeClient => codeClient.CreateBundleAsync(It.IsAny>(), It.IsAny()).Result) - .Returns(dummyBundleDto); + .Setup(codeClient => codeClient.CreateBundleAsync(It.IsAny>(), It.IsAny())) + .ReturnsAsync(dummyBundleDto); var resultExtendBundleDto = new BundleResponseDto { @@ -422,8 +422,8 @@ public async Task BundleService_ThreeFilesProvided_ProcessCreateLargeBundleCheck }; codeClientMock - .Setup(codeClient => codeClient.ExtendBundleAsync(dummyBundleDto.Hash, It.IsAny>(), It.IsAny>(), It.IsAny()).Result) - .Returns(resultExtendBundleDto); + .Setup(codeClient => codeClient.ExtendBundleAsync(dummyBundleDto.Hash, It.IsAny>(), It.IsAny>(), It.IsAny())) + .ReturnsAsync(resultExtendBundleDto); var bundleDto = await bundleService.ProcessCreateLargeBundleAsync(filePathToHashDict, 175); diff --git a/Snyk.Code.Library.Tests/SnykCode/CodeCacheServiceTest.cs b/Snyk.Code.Library.Tests/SnykCode/CodeCacheServiceTest.cs index 747214f15..f85a47a38 100644 --- a/Snyk.Code.Library.Tests/SnykCode/CodeCacheServiceTest.cs +++ b/Snyk.Code.Library.Tests/SnykCode/CodeCacheServiceTest.cs @@ -41,12 +41,12 @@ public void CodeCacheService_CodeCacheExists_RemoveDeletedFiles() var solutionServiceMock = new Mock(); solutionServiceMock - .Setup(solutionService => solutionService.GetPath()) - .Returns(this.projectPath); + .Setup(solutionService => solutionService.GetSolutionFolderAsync()) + .ReturnsAsync(this.projectPath); solutionServiceMock - .Setup(solutionService => solutionService.GetFilesAsync().Result) - .Returns(new List()); + .Setup(solutionService => solutionService.GetFilesAsync()) + .ReturnsAsync(new List()); var fileProvider = new SnykCodeFileProvider(solutionServiceMock.Object); var codeCacheService = new CodeCacheService(fileProvider); @@ -68,8 +68,8 @@ public void CodeCacheService_CodeCacheExists_ValidCache() var solutionServiceMock = new Mock(); solutionServiceMock - .Setup(solutionService => solutionService.GetPath()) - .Returns(this.projectPath); + .Setup(solutionService => solutionService.GetSolutionFolderAsync()) + .ReturnsAsync(this.projectPath); var fileProvider = new SnykCodeFileProvider(solutionServiceMock.Object); @@ -86,12 +86,12 @@ public void CodeCacheService_CodeCacheExists_InvalidCache() var solutionServiceMock = new Mock(); solutionServiceMock - .Setup(solutionService => solutionService.GetPath()) - .Returns(this.projectPath); + .Setup(solutionService => solutionService.GetSolutionFolderAsync()) + .ReturnsAsync(this.projectPath); solutionServiceMock - .Setup(solutionService => solutionService.GetFilesAsync().Result) - .Returns(new List()); + .Setup(solutionService => solutionService.GetFilesAsync()) + .ReturnsAsync(new List()); var fileProvider = new SnykCodeFileProvider(solutionServiceMock.Object); var codeCacheService = new CodeCacheService(fileProvider); @@ -114,12 +114,12 @@ public void CodeCacheService_UpdateFile_CacheUpdated() var filtersServiceMock = new Mock(); solutionServiceMock - .Setup(solutionService => solutionService.GetPath()) - .Returns(this.projectPath); + .Setup(solutionService => solutionService.GetSolutionFolderAsync()) + .ReturnsAsync(this.projectPath); solutionServiceMock - .Setup(solutionService => solutionService.GetFilesAsync().Result) - .Returns(new List()); + .Setup(solutionService => solutionService.GetFilesAsync()) + .ReturnsAsync(new List()); var fileProvider = new SnykCodeFileProvider(solutionServiceMock.Object); var codeCacheService = new CodeCacheService(fileProvider); @@ -149,12 +149,12 @@ public void CodeCacheService_AddFiles_CacheContainsFiles() var filtersServiceMock = new Mock(); solutionServiceMock - .Setup(solutionService => solutionService.GetPath()) - .Returns(this.projectPath); + .Setup(solutionService => solutionService.GetSolutionFolderAsync()) + .ReturnsAsync(this.projectPath); solutionServiceMock - .Setup(solutionService => solutionService.GetFilesAsync().Result) - .Returns(new List()); + .Setup(solutionService => solutionService.GetFilesAsync()) + .ReturnsAsync(new List()); var fileProvider = new SnykCodeFileProvider(solutionServiceMock.Object); var codeCacheService = new CodeCacheService(fileProvider); diff --git a/Snyk.Code.Library.Tests/SnykCode/FiltersServiceTest.cs b/Snyk.Code.Library.Tests/SnykCode/FiltersServiceTest.cs index af6771953..26d004a3c 100644 --- a/Snyk.Code.Library.Tests/SnykCode/FiltersServiceTest.cs +++ b/Snyk.Code.Library.Tests/SnykCode/FiltersServiceTest.cs @@ -46,8 +46,8 @@ public async Task FiltersService_FiveCodeFilesProvided_FilterFilesCheckPassAsync }; codeClientMock - .Setup(codeClient => codeClient.GetFiltersAsync().Result) - .Returns(filtersDto); + .Setup(codeClient => codeClient.GetFiltersAsync()) + .ReturnsAsync(filtersDto); var filterService = new FiltersService(codeClientMock.Object); @@ -97,8 +97,8 @@ public async Task FiltersService_FiveConfigFilesProvided_FilterFilesCheckPassAsy var codeClientMock = new Mock(); codeClientMock - .Setup(codeClient => codeClient.GetFiltersAsync().Result) - .Returns(filtersDto); + .Setup(codeClient => codeClient.GetFiltersAsync()) + .ReturnsAsync(filtersDto); var filterService = new FiltersService(codeClientMock.Object); @@ -141,8 +141,8 @@ public async Task FiltersService_FiveCodeFilesAndFiveConfigFilesProvided_FilterF var filtersDto = GetFiltersDto(); codeClientMock - .Setup(codeClient => codeClient.GetFiltersAsync().Result) - .Returns(filtersDto); + .Setup(codeClient => codeClient.GetFiltersAsync()) + .ReturnsAsync(filtersDto); var filterService = new FiltersService(codeClientMock.Object); diff --git a/Snyk.Code.Library.Tests/SnykCode/SnykCodeServiceTest.cs b/Snyk.Code.Library.Tests/SnykCode/SnykCodeServiceTest.cs index 20966e6de..35ab87bd9 100644 --- a/Snyk.Code.Library.Tests/SnykCode/SnykCodeServiceTest.cs +++ b/Snyk.Code.Library.Tests/SnykCode/SnykCodeServiceTest.cs @@ -93,8 +93,8 @@ public async Task SnykCodeService_CodeCacheAndFileChangesExists_UpdatePreviousSc }; this.codeCacheServiceMock - .Setup(codeCacheService => codeCacheService.GetFilePathToHashDictionary(It.IsAny>())) - .Returns(extendFilePathToHashDict); + .Setup(codeCacheService => codeCacheService.GetFilePathToHashDictionaryAsync(It.IsAny>())) + .ReturnsAsync(extendFilePathToHashDict); var analysisResults = new AnalysisResult { @@ -124,8 +124,8 @@ public async Task SnykCodeService_CodeCacheAndFileChangesExists_UpdatePreviousSc .Setup(codeCacheService => codeCacheService.SetCachedBundleId(bundleId)); this.filtersServiceMock - .Setup(filtersService => filtersService.FilterFilesAsync(changedFiles, It.IsAny()).Result) - .Returns(changedFiles); + .Setup(filtersService => filtersService.FilterFilesAsync(changedFiles, It.IsAny())) + .ReturnsAsync(changedFiles); this.dcIgnoreServiceMock .Setup(dcIgnoreService => dcIgnoreService.FilterFiles(It.IsAny(), changedFiles, It.IsAny())) @@ -139,8 +139,8 @@ public async Task SnykCodeService_CodeCacheAndFileChangesExists_UpdatePreviousSc It.IsAny>(), It.IsAny>(), It.IsAny(), - It.IsAny()).Result) - .Returns(extendedBundle); + It.IsAny())) + .ReturnsAsync(extendedBundle); this.bundleServiceMock .Setup(bundleService => bundleService.UploadMissingFilesAsync( @@ -154,8 +154,8 @@ public async Task SnykCodeService_CodeCacheAndFileChangesExists_UpdatePreviousSc extendedBundle.Id, It.IsAny(), It.IsAny(), - It.IsAny()).Result) - .Returns(analysisResults); + It.IsAny())) + .ReturnsAsync(analysisResults); var analysisResult = await this.snykCodeService.ScanAsync(this.fileProviderMock.Object); @@ -198,8 +198,8 @@ public async Task SnykCodeService_CodeCacheExists_ReturnWithoutRemoteQueryAsync( var solutionServiceMock = new Mock(); solutionServiceMock - .Setup(solutionService => solutionService.GetPath()) - .Returns(TestResource.GetResourcesPath()); + .Setup(solutionService => solutionService.GetSolutionFolderAsync()) + .ReturnsAsync(TestResource.GetResourcesPath()); var fileProvider = new SnykCodeFileProvider(solutionServiceMock.Object); @@ -256,29 +256,29 @@ public async Task SnykCodeService_TwoFilesWithIssuesProvided_ScanSuccessAsync() }; this.fileProviderMock - .Setup(fileProvider => fileProvider.GetFilesAsync().Result) - .Returns(filePaths); + .Setup(fileProvider => fileProvider.GetFilesAsync()) + .ReturnsAsync(filePaths); this.filtersServiceMock - .Setup(filtersService => filtersService.FilterFilesAsync(It.IsAny>(), It.IsAny()).Result) - .Returns(filePaths); + .Setup(filtersService => filtersService.FilterFilesAsync(It.IsAny>(), It.IsAny())) + .ReturnsAsync(filePaths); this.bundleServiceMock - .Setup(bundleService => bundleService.CreateBundleAsync(It.IsAny>(), It.IsAny(), It.IsAny()).Result) - .Returns(bundle); + .Setup(bundleService => bundleService.CreateBundleAsync(It.IsAny>(), It.IsAny(), It.IsAny())) + .ReturnsAsync(bundle); this.bundleServiceMock - .Setup(bundleService => bundleService.UploadFilesAsync(bundleId, It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny()).Result) - .Returns(true); + .Setup(bundleService => bundleService.UploadFilesAsync(bundleId, It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(true); this.bundleServiceMock - .Setup(bundleService => bundleService.CheckBundleAsync(bundleId, It.IsAny()).Result) + .Setup(bundleService => bundleService.CheckBundleAsync(bundleId, It.IsAny())) .Callback((id, cancellationToken) => bundle.MissingFiles = new string[] { }) - .Returns(bundle); + .ReturnsAsync(bundle); this.analysisServiceMock - .Setup(analysisService => analysisService.GetAnalysisAsync(bundleId, It.IsAny(), It.IsAny(), It.IsAny()).Result) - .Returns(analysisResults); + .Setup(analysisService => analysisService.GetAnalysisAsync(bundleId, It.IsAny(), It.IsAny(), It.IsAny())) + .ReturnsAsync(analysisResults); this.codeCacheServiceMock .Setup(analysisService => analysisService.GetCachedAnalysisResult()) diff --git a/Snyk.Code.Library/Service/CodeCacheService.cs b/Snyk.Code.Library/Service/CodeCacheService.cs index 230dd5e55..8730d6936 100644 --- a/Snyk.Code.Library/Service/CodeCacheService.cs +++ b/Snyk.Code.Library/Service/CodeCacheService.cs @@ -5,6 +5,7 @@ using System.IO; using System.Runtime.Caching; using System.Text; + using System.Threading.Tasks; using Serilog; using Snyk.Code.Library.Domain.Analysis; using Snyk.Common; @@ -96,7 +97,7 @@ public IDictionary GetFilePathToHashDictionary() } /// - public IDictionary GetFilePathToHashDictionary(IEnumerable files) + public async Task> GetFilePathToHashDictionaryAsync(IEnumerable files) { var filePathToHashDict = new Dictionary(); @@ -104,11 +105,11 @@ public IDictionary GetFilePathToHashDictionary(IEnumerable files) { foreach (string filePath in files) { - this.AddFile(filePath); + this.AddFileAsync(filePath); } } @@ -161,18 +162,20 @@ public void Update(IFileProvider fileProvider) foreach (string file in fileProvider.GetRemovedFiles()) { - this.RemoveFile(file); + this.RemoveFileAsync(file); } } /// - public IEnumerable GetRelativeFilePaths(IEnumerable files) + public async Task> GetRelativeFilePathsAsync(IEnumerable files) { IList relateFilePaths = new List(); foreach (string fileFullPath in files) { - relateFilePaths.Add(FileUtil.GetRelativeFilePath(this.fileProvider.GetSolutionPath(), fileFullPath)); + var solutionPath = await this.fileProvider.GetSolutionPathAsync(); + + relateFilePaths.Add(FileUtil.GetRelativeFilePath(solutionPath, fileFullPath)); } return relateFilePaths; @@ -182,7 +185,7 @@ public IEnumerable GetRelativeFilePaths(IEnumerable files) /// Add (or update if it already exists) file hash and content to cache. /// /// Source file path. - private void AddFile(string filePath) + private async Task AddFileAsync(string filePath) { try { @@ -190,35 +193,43 @@ private void AddFile(string filePath) if (string.IsNullOrEmpty(fileContent)) { - return; + return string.Empty; } string fileHash = Sha256.ComputeHash(fileContent); - string relativeFilePath = FileUtil.GetRelativeFilePath(this.fileProvider.GetSolutionPath(), filePath); + var solutionPath = await this.fileProvider.GetSolutionPathAsync(); + + string relativeFilePath = FileUtil.GetRelativeFilePath(solutionPath, filePath); this.AddToFilePathToHashCache(relativeFilePath, fileHash); this.AddToFilePathToContentCache(relativeFilePath, fileContent); this.Invalidate(); + + return fileHash; } - catch (Exception ex) + catch (Exception e) { - Logger.Error(ex.Message, "Failed to update cache."); + Logger.Error(e, "Failed to update cache."); } + + return string.Empty; } private void UpdateFile(string file) { - this.AddFile(file); + this.AddFileAsync(file); this.Invalidate(); } - private void RemoveFile(string file) + private async Task RemoveFileAsync(string file) { - string relativeFilePath = FileUtil.GetRelativeFilePath(this.fileProvider.GetSolutionPath(), file); + var solutionPath = await this.fileProvider.GetSolutionPathAsync(); + + string relativeFilePath = FileUtil.GetRelativeFilePath(solutionPath, file); this.filePathToHashCache.Remove(relativeFilePath); this.filePathToContentCache.Remove(relativeFilePath); @@ -228,9 +239,13 @@ private void RemoveFile(string file) private void Invalidate() => this.isCacheValid = false; - private string GetRelativeFilePathIfFullPath(string filePath) - => string.IsNullOrEmpty(filePath) || !filePath.StartsWith(this.fileProvider.GetSolutionPath()) - ? filePath : FileUtil.GetRelativeFilePath(this.fileProvider.GetSolutionPath(), filePath); + private async Task GetRelativeFilePathIfFullPathAsync(string filePath) + { + var solutionPath = await this.fileProvider.GetSolutionPathAsync(); + + return string.IsNullOrEmpty(filePath) || !filePath.StartsWith(solutionPath) + ? filePath : FileUtil.GetRelativeFilePath(solutionPath, filePath); + } private void AddToFilePathToHashCache(string filePath, string fileHash) => this.filePathToHashCache.Set(filePath, fileHash, this.New24HoursExpirationTimeCacheItemPolicy()); diff --git a/Snyk.Code.Library/Service/ICodeCacheService.cs b/Snyk.Code.Library/Service/ICodeCacheService.cs index a71a898c6..45f669341 100644 --- a/Snyk.Code.Library/Service/ICodeCacheService.cs +++ b/Snyk.Code.Library/Service/ICodeCacheService.cs @@ -1,6 +1,7 @@ namespace Snyk.Code.Library.Service { using System.Collections.Generic; + using System.Threading.Tasks; using Snyk.Code.Library.Domain.Analysis; using Snyk.Common; @@ -32,7 +33,7 @@ public interface ICodeCacheService /// /// Provided files list. /// IDictionary. - IDictionary GetFilePathToHashDictionary(IEnumerable files); + Task> GetFilePathToHashDictionaryAsync(IEnumerable files); /// /// Create file path to file hash and content dictionary. @@ -103,6 +104,6 @@ public interface ICodeCacheService /// /// List of absolute file paths. /// List of relative file paths. - IEnumerable GetRelativeFilePaths(IEnumerable files); + Task> GetRelativeFilePathsAsync(IEnumerable files); } } diff --git a/Snyk.Code.Library/Service/SnykCodeFileProvider.cs b/Snyk.Code.Library/Service/SnykCodeFileProvider.cs index 468b473e5..0287b7103 100644 --- a/Snyk.Code.Library/Service/SnykCodeFileProvider.cs +++ b/Snyk.Code.Library/Service/SnykCodeFileProvider.cs @@ -34,11 +34,11 @@ public SnykCodeFileProvider(ISolutionService solutionService) public async Task> GetFilesAsync() => await this.solutionService.GetFilesAsync(); /// - public string GetSolutionPath() + public async Task GetSolutionPathAsync() { if (string.IsNullOrEmpty(this.solutionPath)) { - this.solutionPath = this.solutionService.GetPath(); + this.solutionPath = await this.solutionService.GetSolutionFolderAsync(); } return this.solutionPath; diff --git a/Snyk.Code.Library/Service/SnykCodeService.cs b/Snyk.Code.Library/Service/SnykCodeService.cs index 25d14f6b6..27b6b5c1b 100644 --- a/Snyk.Code.Library/Service/SnykCodeService.cs +++ b/Snyk.Code.Library/Service/SnykCodeService.cs @@ -97,7 +97,7 @@ public async Task ScanAsync(IFileProvider fileProvider, Cancella return await this.NewScanAsync(fileProvider, cancellationToken); } - var filteredChangedFiles = await this.GetFilteredFilesAsync(fileProvider.GetSolutionPath(), fileProvider.GetAllChangedFiles()); + var filteredChangedFiles = await this.GetFilteredFilesAsync(await fileProvider.GetSolutionPathAsync(), fileProvider.GetAllChangedFiles()); if (this.AnyFilesChangedInSolution(filteredChangedFiles)) { @@ -118,7 +118,7 @@ private async Task NewScanAsync(IFileProvider fileProvider, Canc var solutionFiles = await fileProvider.GetFilesAsync(); - var files = await this.GetFilteredFilesAsync(fileProvider.GetSolutionPath(), solutionFiles); + var files = await this.GetFilteredFilesAsync(await fileProvider.GetSolutionPathAsync(), solutionFiles); if (files == null || files.Count() == 0) { @@ -162,11 +162,11 @@ private async Task UpdatePreviousScanAsync(IFileProvider filePro { this.codeCacheService.Update(fileProvider); - var extendFilePathToHashDict = this.codeCacheService.GetFilePathToHashDictionary(changedFiles); + var extendFilePathToHashDict = await this.codeCacheService.GetFilePathToHashDictionaryAsync(changedFiles); string bundleId = this.codeCacheService.GetCachedBundleId(); - var removedFiles = this.codeCacheService.GetRelativeFilePaths(fileProvider.GetRemovedFiles()); + var removedFiles = await this.codeCacheService.GetRelativeFilePathsAsync(fileProvider.GetRemovedFiles()); var extendedBundle = await this.bundleService.ExtendBundleAsync(bundleId, extendFilePathToHashDict, removedFiles); diff --git a/Snyk.Common/IFileProvider.cs b/Snyk.Common/IFileProvider.cs index b040bf6b5..2f6ccb97c 100644 --- a/Snyk.Common/IFileProvider.cs +++ b/Snyk.Common/IFileProvider.cs @@ -18,7 +18,7 @@ public interface IFileProvider /// Get solution path. /// /// Path to solution. - string GetSolutionPath(); + Task GetSolutionPathAsync(); /// /// Save changed file path. diff --git a/Snyk.Common/ISolutionService.cs b/Snyk.Common/ISolutionService.cs index 1170bff8c..8e440194d 100644 --- a/Snyk.Common/ISolutionService.cs +++ b/Snyk.Common/ISolutionService.cs @@ -14,10 +14,10 @@ public interface ISolutionService IFileProvider FileProvider { get; } /// - /// Get solution path. + /// Get solution folder path. /// /// Path string. - string GetPath(); + Task GetSolutionFolderAsync(); /// /// Get all solution files. @@ -41,11 +41,20 @@ public interface ISolutionService /// /// Relative file path. /// Full file path. - string GetFileFullPath(string file); + Task GetFileFullPathAsync(string file); /// /// Gets a value indicating whether is solution open. /// + /// True if solution open. bool IsSolutionOpen(); + + /// + /// Find root directory for all paths. + /// + /// Initial root directory. + /// All paths. + /// Root directory for all paths. + string FindRootDirectoryForSolutionProjects(string rootDir, IList paths); } } diff --git a/Snyk.VisualStudio.Extension.Shared/CLI/ICli.cs b/Snyk.VisualStudio.Extension.Shared/CLI/ICli.cs index b5c190b3f..1862904ed 100644 --- a/Snyk.VisualStudio.Extension.Shared/CLI/ICli.cs +++ b/Snyk.VisualStudio.Extension.Shared/CLI/ICli.cs @@ -1,5 +1,7 @@ namespace Snyk.VisualStudio.Extension.Shared.CLI { + using System.Threading.Tasks; + /// /// Describe Snyk CLI interface common methods. /// @@ -15,7 +17,7 @@ public interface ICli /// /// Path for run scan. /// object. - CliResult Scan(string basePath); + Task ScanAsync(string basePath); /// /// Get Snyk API token from settings. diff --git a/Snyk.VisualStudio.Extension.Shared/CLI/SnykCli.cs b/Snyk.VisualStudio.Extension.Shared/CLI/SnykCli.cs index 8460d6e70..b18306dd8 100644 --- a/Snyk.VisualStudio.Extension.Shared/CLI/SnykCli.cs +++ b/Snyk.VisualStudio.Extension.Shared/CLI/SnykCli.cs @@ -4,6 +4,7 @@ using System.Collections.Specialized; using System.IO; using System.Linq; + using System.Threading.Tasks; using Serilog; using Snyk.Common; using Snyk.VisualStudio.Extension.Shared.Settings; @@ -120,7 +121,7 @@ public string Authenticate() } /// - public CliResult Scan(string basePath) + public async Task ScanAsync(string basePath) { Logger.Information("Path to scan {BasePath}", basePath); @@ -128,7 +129,9 @@ public CliResult Scan(string basePath) Logger.Information("CLI path is {CliPath}", cliPath); - this.ConsoleRunner.CreateProcess(cliPath, this.BuildScanArguments(), this.BuildScanEnvironmentVariables(), basePath); + var arguments = await this.BuildScanArgumentsAsync(); + + this.ConsoleRunner.CreateProcess(cliPath, arguments, this.BuildScanEnvironmentVariables(), basePath); Logger.Information("Start run console process"); @@ -161,7 +164,7 @@ public StringDictionary BuildScanEnvironmentVariables() /// Build arguments (options) for snyk cli depending on user settings. /// /// arguments string. - public string BuildScanArguments() + public async Task BuildScanArgumentsAsync() { Logger.Information("Enter BuildArguments method"); @@ -186,12 +189,16 @@ public string BuildScanArguments() arguments.Add($"--org={this.Options.Organization}"); } - if (!string.IsNullOrEmpty(this.Options.AdditionalOptions)) + var additionalOptions = await this.Options.GetAdditionalOptionsAsync(); + + if (!string.IsNullOrEmpty(additionalOptions)) { - arguments.Add($"{this.Options.AdditionalOptions}"); + arguments.Add($"{additionalOptions}"); } - if (this.Options.IsScanAllProjects) + var isScanAllProjects = await this.Options.IsScanAllProjectsAsync(); + + if (isScanAllProjects) { arguments.Add("--all-projects"); } diff --git a/Snyk.VisualStudio.Extension.Shared/Service/IOssService.cs b/Snyk.VisualStudio.Extension.Shared/Service/IOssService.cs index 7e37bd6e1..24f2ce655 100644 --- a/Snyk.VisualStudio.Extension.Shared/Service/IOssService.cs +++ b/Snyk.VisualStudio.Extension.Shared/Service/IOssService.cs @@ -1,6 +1,7 @@ namespace Snyk.VisualStudio.Extension.Shared.Service { using System.Threading; + using System.Threading.Tasks; using Snyk.VisualStudio.Extension.Shared.CLI; /// @@ -15,7 +16,7 @@ public interface IOssService /// Cancellation token /// object. /// If error on scan. - CliResult Scan(string path, CancellationToken token); + Task ScanAsync(string path, CancellationToken token); /// /// Stop current scan. diff --git a/Snyk.VisualStudio.Extension.Shared/Service/OssService.cs b/Snyk.VisualStudio.Extension.Shared/Service/OssService.cs index 07df7678c..2b33c8448 100644 --- a/Snyk.VisualStudio.Extension.Shared/Service/OssService.cs +++ b/Snyk.VisualStudio.Extension.Shared/Service/OssService.cs @@ -1,6 +1,7 @@ namespace Snyk.VisualStudio.Extension.Shared.Service { using System.Threading; + using System.Threading.Tasks; using Serilog; using Snyk.Common; using Snyk.VisualStudio.Extension.Shared.CLI; @@ -29,7 +30,7 @@ public class OssService : IOssService public bool IsCurrentScanProcessCanceled() => this.cli.ConsoleRunner.IsStopped; /// - public CliResult Scan(string path, CancellationToken token) + public async Task ScanAsync(string path, CancellationToken token) { token.ThrowIfCancellationRequested(); @@ -45,15 +46,15 @@ public CliResult Scan(string path, CancellationToken token) Logger.Information("Custom Endpoint is {CustomEndpoint}", options.CustomEndpoint); Logger.Information("Organization {Organization}", options.Organization); Logger.Information("Ignore Unknown CA is {IgnoreUnknownCA}", options.IgnoreUnknownCA); - Logger.Information("Additional Options is {AdditionalOptions}", options.AdditionalOptions); - Logger.Information("Is Scan All Projects is {IsScanAllProjects}", options.IsScanAllProjects); + Logger.Information("Additional Options is {AdditionalOptions}", await options.GetAdditionalOptionsAsync()); + Logger.Information("Is Scan All Projects is {IsScanAllProjects}", await options.IsScanAllProjectsAsync()); Logger.Information("Solution path is {SolutionPath}", path); Logger.Information("Start scan"); token.ThrowIfCancellationRequested(); - var cliResult = this.cli.Scan(path); + var cliResult = await this.cli.ScanAsync(path); Logger.Information("Scan finished. Is successful: {IsSuccessful}", cliResult.IsSuccessful()); diff --git a/Snyk.VisualStudio.Extension.Shared/Service/SnykSolutionService.cs b/Snyk.VisualStudio.Extension.Shared/Service/SnykSolutionService.cs index da2cf4585..89b305681 100644 --- a/Snyk.VisualStudio.Extension.Shared/Service/SnykSolutionService.cs +++ b/Snyk.VisualStudio.Extension.Shared/Service/SnykSolutionService.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.IO; + using System.Linq; using EnvDTE; using Microsoft.VisualStudio; using Microsoft.VisualStudio.Shell; @@ -28,7 +29,7 @@ public class SnykSolutionService : ISolutionService /// /// Initializes a new instance of the class. /// - private SnykSolutionService() + public SnykSolutionService() { } @@ -105,13 +106,13 @@ public async Task InitializeAsync(ISnykServiceProvider serviceProvider) /// /// Relative file path. /// Full file path. - public string GetFileFullPath(string file) + public async System.Threading.Tasks.Task GetFileFullPathAsync(string file) { string relativePath = file .Replace("/", "\\") .Substring(1, file.Length - 1); - string baseDirPath = this.GetPath(); + string baseDirPath = await this.GetSolutionFolderAsync(); return Path.Combine(baseDirPath, relativePath); } @@ -127,53 +128,34 @@ public string GetFileFullPath(string file) /// If no success, try to get path for flat project (without solution) or web site (in case VS2015). /// /// Solution path string. - public string GetPath() + public async System.Threading.Tasks.Task GetSolutionFolderAsync() { - Logger.Information("Enter GetSolutionPath method"); - - var dte = this.ServiceProvider.DTE; - var solution = dte.Solution; - var projects = this.GetProjects(); + var rootDir = await this.FindRootDirectoryForSolutionAsync(); - string solutionPath = string.Empty; - - // 1 case: Solution with projects. - // 2 case: Solution is folder. - if (this.IsSolutionWithProjects(solution, projects) || this.IsFolder(solution, projects)) + if (rootDir == null || rootDir.IsNullOrEmpty()) { - Logger.Information("Path is solution with projects or folder."); - - solutionPath = solution.FullName; - - if (string.IsNullOrEmpty(solutionPath)) - { - return string.Empty; - } + rootDir = await this.FindRootDirectoryForSolutionFromDteAsync(); + } - if (!File.GetAttributes(solutionPath).HasFlag(FileAttributes.Directory)) - { - Logger.Information("Remove solution file name from path."); + return rootDir; + } - solutionPath = Directory.GetParent(solutionPath).FullName; - } + /// + public string FindRootDirectoryForSolutionProjects(string rootDir, IList projectDirs) + { + if (rootDir == null || rootDir.IsNullOrEmpty()) + { + return null; } - // 3 case: Flat project without solution. - // 4 case: Web site (in 2015) - if (this.IsFlatProjectOrWebSite(solution, projects)) + if (projectDirs.All(dir => dir.StartsWith(rootDir))) { - Logger.Information("Solution is 'dirty'. Get solution path from first project full name"); - - string projectPath = solution.Projects.Item(1).FullName; - - Logger.Information("Project path {ProjectPath}. Get solution path as project directory.", projectPath); - - solutionPath = Directory.GetParent(projectPath).FullName; + return rootDir; } - Logger.Information("Result solution path is {SolutionPath}.", solutionPath); + string newRootDir = Directory.GetParent(rootDir)?.FullName; - return solutionPath; + return this.FindRootDirectoryForSolutionProjects(newRootDir, projectDirs); } /// @@ -187,7 +169,7 @@ public async System.Threading.Tasks.Task> GetFilesAsync() // If solution is folder type when just get all files in solution directory. if (this.IsSolutionOpenedAsFolder()) { - return this.GetSolutionDirectoryFiles(); + return await this.GetSolutionDirectoryFilesAsync(); } // If normal solution, then get all files in solution projects. @@ -206,6 +188,8 @@ public void Clean() private async Task InitializeSolutionEventsAsync() { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + Logger.Information("Enter InitializeSolutionEvents method"); IVsSolution vsSolution = await this.ServiceProvider.GetServiceAsync(typeof(SVsSolution)) as IVsSolution; @@ -219,9 +203,9 @@ private async Task InitializeSolutionEventsAsync() Logger.Information("Leave InitializeSolutionEvents method"); } - private IList GetSolutionDirectoryFiles() + private async System.Threading.Tasks.Task> GetSolutionDirectoryFilesAsync() { - string solutionPath = this.GetPath(); + string solutionPath = await this.GetSolutionFolderAsync(); string[] files = Directory.GetFileSystemEntries(solutionPath, "*", SearchOption.AllDirectories); @@ -231,12 +215,6 @@ private IList GetSolutionDirectoryFiles() return filesList; } - private bool IsFlatProjectOrWebSite(Solution solution, Projects projects) => solution.IsDirty && projects.Count > 0; - - private bool IsSolutionWithProjects(Solution solution, Projects projects) => !solution.IsDirty && projects.Count > 0; - - private bool IsFolder(Solution solution, Projects projects) => !solution.IsDirty && projects.Count == 0; - private async System.Threading.Tasks.Task> GetSolutionItemFilesAsync(Toolkit.SolutionItem solutionItem) { await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); @@ -287,5 +265,124 @@ private async System.Threading.Tasks.Task> GetSolutionFilesA return await this.GetSolutionItemFilesAsync(solutionItem); } + + private string GetDirectoryPath(string path) => Directory.Exists(path) ? path : Directory.GetParent(path).FullName; + + private async System.Threading.Tasks.Task FindRootDirectoryForSolutionAsync() + { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + + var item = await Toolkit.VS.Solutions.GetActiveItemAsync(); + + if (item == null) + { + return null; + } + + var solutionItem = item.FindParent(Toolkit.SolutionItemType.Solution); + + if (solutionItem == null) + { + return null; + } + + var projectDirs = new List(); + + var solutionDir = this.GetDirectoryPath(solutionItem.FullPath); + + try + { + if (solutionItem.Children != null) + { + foreach (var children in solutionItem.Children) + { + if (children.Type == Toolkit.SolutionItemType.Project + || children.Type == Toolkit.SolutionItemType.VirtualProject + || children.Type == Toolkit.SolutionItemType.MiscProject) + { + projectDirs.Add(this.GetDirectoryPath(children.FullPath)); + } + } + } + } + catch (Exception e) + { + Logger.Error(e, "Error on get all project paths"); + } + + return this.FindRootDirectoryForSolutionProjects(solutionDir, projectDirs); + } + + private async System.Threading.Tasks.Task FindRootDirectoryForSolutionFromDteAsync() + { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + + Logger.Information("Enter GetSolutionPath method"); + + var dte = this.ServiceProvider.DTE; + var solution = dte.Solution; + var projects = this.GetProjects(); + + string solutionPath = string.Empty; + + // 1 case: Solution with projects. + // 2 case: Solution is folder. + if (await this.IsSolutionWithProjectsAsync(solution, projects) || await this.IsFolderAsync(solution, projects)) + { + Logger.Information("Path is solution with projects or folder."); + + solutionPath = solution.FullName; + + if (string.IsNullOrEmpty(solutionPath)) + { + return string.Empty; + } + + if (!File.GetAttributes(solutionPath).HasFlag(FileAttributes.Directory)) + { + Logger.Information("Remove solution file name from path."); + + solutionPath = Directory.GetParent(solutionPath).FullName; + } + } + + // 3 case: Flat project without solution. + // 4 case: Web site (in 2015) + if (await this.IsFlatProjectOrWebSiteAsync(solution, projects)) + { + Logger.Information("Solution is 'dirty'. Get solution path from first project full name"); + + string projectPath = solution.Projects.Item(1).FullName; + + Logger.Information("Project path {ProjectPath}. Get solution path as project directory.", projectPath); + + solutionPath = Directory.GetParent(projectPath).FullName; + } + + Logger.Information("Result solution path is {SolutionPath}.", solutionPath); + + return solutionPath; + } + + private async System.Threading.Tasks.Task IsFlatProjectOrWebSiteAsync(Solution solution, Projects projects) + { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + + return solution.IsDirty && projects.Count > 0; + } + + private async System.Threading.Tasks.Task IsFolderAsync(Solution solution, Projects projects) + { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + + return !solution.IsDirty && projects.Count == 0; + } + + private async System.Threading.Tasks.Task IsSolutionWithProjectsAsync(Solution solution, Projects projects) + { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + + return !solution.IsDirty && projects.Count > 0; + } } } \ No newline at end of file diff --git a/Snyk.VisualStudio.Extension.Shared/Service/SnykTasksService.cs b/Snyk.VisualStudio.Extension.Shared/Service/SnykTasksService.cs index 083362950..78d4b4ac9 100644 --- a/Snyk.VisualStudio.Extension.Shared/Service/SnykTasksService.cs +++ b/Snyk.VisualStudio.Extension.Shared/Service/SnykTasksService.cs @@ -368,72 +368,77 @@ private void ScanOss(FeaturesSettings featuresSettings) Logger.Information("Start scan task"); - _ = Task.Run( - () => - { - this.isOssScanning = true; + Task.Run(this.RunOssScanAsync, token).FireAndForget(); + } + catch (Exception ex) + { + Logger.Error(ex, "Error on oss scan"); + } + } - this.FireOssScanningStartedEvent(); + private async Task RunOssScanAsync() + { + this.isOssScanning = true; - var ossService = this.serviceProvider.OssService; + this.FireOssScanningStartedEvent(); - try - { - token.ThrowIfCancellationRequested(); + var ossService = this.serviceProvider.OssService; - try - { - var cliResult = ossService.Scan(this.serviceProvider.SolutionService.GetPath(), token); + try + { + var token = this.ossScanTokenSource.Token; - this.FireOssScanningUpdateEvent(cliResult); + token.ThrowIfCancellationRequested(); - this.FireOssScanningFinishedEvent(); + try + { + var directoryPath = await this.serviceProvider.SolutionService.GetSolutionFolderAsync(); - Logger.Information("Scan finished"); - } - catch (OssScanException e) - { - Logger.Error(e, "Oss scan exception"); + var cliResult = await ossService.ScanAsync(directoryPath, token); - this.FireOssError(e.Error); - } - catch (Exception e) - { - if (ossService.IsCurrentScanProcessCanceled() || this.IsTaskCancelled(e)) - { - this.FireScanningCancelledEvent(); + this.FireOssScanningUpdateEvent(cliResult); - return; - } + this.FireOssScanningFinishedEvent(); - this.FireOssError(e.Message); - } - finally - { - this.isOssScanning = false; + Logger.Information("Scan finished"); + } + catch (OssScanException e) + { + Logger.Error(e, "Oss scan exception"); - this.FireTaskFinished(); - } - } - catch (Exception e) - { - Logger.Error(e, "Error on oss scan"); + this.FireOssError(e.Error); + } + catch (Exception e) + { + if (ossService.IsCurrentScanProcessCanceled() || this.IsTaskCancelled(e)) + { + this.FireScanningCancelledEvent(); - this.FireScanningCancelledEvent(); - } - finally - { - this.DisposeCancellationTokenSource(this.ossScanTokenSource); + return; + } - this.isOssScanning = false; + this.FireOssError(e.Message); + } + finally + { + this.isOssScanning = false; - this.FireTaskFinished(); - } - }, token); + this.FireTaskFinished(); + } } - catch (Exception ex) + catch (Exception e) { - Logger.Error(ex, "Error on oss scan"); + Logger.Error(e, "Error on oss scan"); + + this.FireScanningCancelledEvent(); + } + finally + { + this.DisposeCancellationTokenSource(this.ossScanTokenSource); + + this.isOssScanning = false; + + this.FireTaskFinished(); } } diff --git a/Snyk.VisualStudio.Extension.Shared/Settings/ISnykOptions.cs b/Snyk.VisualStudio.Extension.Shared/Settings/ISnykOptions.cs index 3e97a0f32..b373178c2 100644 --- a/Snyk.VisualStudio.Extension.Shared/Settings/ISnykOptions.cs +++ b/Snyk.VisualStudio.Extension.Shared/Settings/ISnykOptions.cs @@ -1,6 +1,7 @@ namespace Snyk.VisualStudio.Extension.Shared.Settings { using System; + using System.Threading.Tasks; /// /// Interface for Snyk Options/Settings in Visual Studio. @@ -37,16 +38,6 @@ public interface ISnykOptions /// bool IgnoreUnknownCA { get; set; } - /// - /// Gets a value indicating whether CLI additional parameters for current solution/project. - /// - string AdditionalOptions { get; } - - /// - /// Gets a value indicating whether is CLI --all-projects parameter added by default. By default it's enabled. - /// - bool IsScanAllProjects { get; } - /// /// Gets a value indicating whether is Oss scan enabled. /// @@ -72,6 +63,20 @@ public interface ISnykOptions /// bool UsageAnalyticsEnabled { get; set; } + /// + /// Gets a value indicating whether additional options. + /// Get this data using . + /// + /// representing the asynchronous operation. + Task GetAdditionalOptionsAsync(); + + /// + /// Gets a value indicating whether is scan all projects enabled via . + /// Get this data using . + /// + /// representing the asynchronous operation. + Task IsScanAllProjectsAsync(); + /// /// Call CLI auth for user authentication at Snyk and get user api token. /// diff --git a/Snyk.VisualStudio.Extension.Shared/Settings/SnykGeneralOptionsDialogPage.cs b/Snyk.VisualStudio.Extension.Shared/Settings/SnykGeneralOptionsDialogPage.cs index 345c36401..9e980e73f 100644 --- a/Snyk.VisualStudio.Extension.Shared/Settings/SnykGeneralOptionsDialogPage.cs +++ b/Snyk.VisualStudio.Extension.Shared/Settings/SnykGeneralOptionsDialogPage.cs @@ -2,6 +2,7 @@ { using System; using System.Runtime.InteropServices; + using System.Threading.Tasks; using System.Windows.Forms; using Microsoft.VisualStudio.Shell; using Serilog; @@ -15,8 +16,6 @@ [ComVisible(true)] public class SnykGeneralOptionsDialogPage : DialogPage, ISnykOptions { - private static readonly ILogger Logger = LogManager.ForContext(); - private ISnykServiceProvider serviceProvider; private SnykUserStorageSettingsService userStorageSettingsService; @@ -123,18 +122,6 @@ public bool UsageAnalyticsEnabled set => this.userStorageSettingsService?.SaveUsageAnalyticsEnabled(value); } - /// - /// Gets a value indicating whether additional options. - /// Get this data using . - /// - public string AdditionalOptions => this.serviceProvider.UserStorageSettingsService.GetAdditionalOptions(); - - /// - /// Gets a value indicating whether is scan all projects enabled via . - /// Get this data using . - /// - public bool IsScanAllProjects => this.userStorageSettingsService.GetIsAllProjectsEnabled(); - /// public string AnonymousId { @@ -165,6 +152,20 @@ private SnykGeneralSettingsUserControl GeneralSettingsUserControl } } + /// + /// Gets a value indicating whether additional options. + /// Get this data using . + /// + /// representing the asynchronous operation. + public async Task GetAdditionalOptionsAsync() => await this.serviceProvider.UserStorageSettingsService.GetAdditionalOptionsAsync(); + + /// + /// Gets a value indicating whether is scan all projects enabled via . + /// Get this data using . + /// + /// representing the asynchronous operation. + public async Task IsScanAllProjectsAsync() => await this.userStorageSettingsService.GetIsAllProjectsEnabledAsync(); + /// /// Initialize . /// diff --git a/Snyk.VisualStudio.Extension.Shared/Settings/SnykSolutionOptionsUserControl.cs b/Snyk.VisualStudio.Extension.Shared/Settings/SnykSolutionOptionsUserControl.cs index 80bf76d4d..77dd7801c 100644 --- a/Snyk.VisualStudio.Extension.Shared/Settings/SnykSolutionOptionsUserControl.cs +++ b/Snyk.VisualStudio.Extension.Shared/Settings/SnykSolutionOptionsUserControl.cs @@ -2,9 +2,11 @@ { using System; using System.Windows.Forms; + using Microsoft.VisualStudio.Shell; using Serilog; using Snyk.Common; using Snyk.VisualStudio.Extension.Shared.Service; + using Task = System.Threading.Tasks.Task; /// /// Solution settings control. @@ -49,7 +51,7 @@ private void AdditionalOptionsTextBox_TextChanged(object sender, EventArgs e) { string additionalOptions = this.additionalOptionsTextBox.Text.ToString(); - this.userStorageSettingsService.SaveAdditionalOptions(additionalOptions); + this.userStorageSettingsService.SaveAdditionalOptionsAsync(additionalOptions).FireAndForget(); this.CheckOptionConflicts(); } @@ -59,13 +61,13 @@ private void AllProjectsCheckBox_CheckedChanged(object sender, EventArgs e) { if (this.serviceProvider.SolutionService.IsSolutionOpen()) { - this.userStorageSettingsService.SaveIsAllProjectsScanEnabled(this.allProjectsCheckBox.Checked); + this.userStorageSettingsService.SaveIsAllProjectsScanEnabledAsync(this.allProjectsCheckBox.Checked).FireAndForget(); ; this.CheckOptionConflicts(); } } - private void SnykProjectOptionsUserControl_Load(object sender, EventArgs eventArgs) + private async void SnykProjectOptionsUserControl_Load(object sender, EventArgs eventArgs) { this.OnVisibleChanged(eventArgs); @@ -81,7 +83,7 @@ private void SnykProjectOptionsUserControl_Load(object sender, EventArgs eventAr try { - string additionalOptions = this.userStorageSettingsService.GetAdditionalOptions(); + string additionalOptions = await this.userStorageSettingsService.GetAdditionalOptionsAsync(); if (!string.IsNullOrEmpty(additionalOptions)) { @@ -92,26 +94,26 @@ private void SnykProjectOptionsUserControl_Load(object sender, EventArgs eventAr this.additionalOptionsTextBox.Text = string.Empty; } } - catch (Exception exception) + catch (Exception e) { - Logger.Error(exception.Message); + Logger.Error(e, "Error on load additional options"); this.additionalOptionsTextBox.Text = string.Empty; } try { - bool isChecked = this.userStorageSettingsService.GetIsAllProjectsEnabled(); + bool isChecked = await this.userStorageSettingsService.GetIsAllProjectsEnabledAsync(); this.allProjectsCheckBox.Checked = isChecked; } - catch (Exception exception) + catch (Exception e) { - Logger.Error(exception.Message); + Logger.Error(e, "Error on load is all projects enabled"); this.allProjectsCheckBox.Checked = false; - this.userStorageSettingsService.SaveIsAllProjectsScanEnabled(false); + await this.userStorageSettingsService.SaveIsAllProjectsScanEnabledAsync(false); } this.CheckOptionConflicts(); diff --git a/Snyk.VisualStudio.Extension.Shared/Settings/SnykUserStorageSettingsService.cs b/Snyk.VisualStudio.Extension.Shared/Settings/SnykUserStorageSettingsService.cs index 79d8b45a7..ff8836fa8 100644 --- a/Snyk.VisualStudio.Extension.Shared/Settings/SnykUserStorageSettingsService.cs +++ b/Snyk.VisualStudio.Extension.Shared/Settings/SnykUserStorageSettingsService.cs @@ -1,6 +1,7 @@ namespace Snyk.VisualStudio.Extension.Shared.Settings { using System; + using System.Threading.Tasks; using Serilog; using Snyk.Common; using Snyk.VisualStudio.Extension.Shared.Service; @@ -32,11 +33,11 @@ public SnykUserStorageSettingsService(string settingsPath, ISnykServiceProvider /// Get CLI additional options string. /// /// string. - public string GetAdditionalOptions() + public async Task GetAdditionalOptionsAsync() { Logger.Information("Enter GetAdditionalOptions method"); - int solutionPathHash = this.GetSolutionPathHash(); + int solutionPathHash = await this.GetSolutionPathHashAsync(); var settings = this.settingsLoader.Load(); @@ -52,11 +53,11 @@ public string GetAdditionalOptions() /// Get is all projects enabled. /// /// Bool. - public bool GetIsAllProjectsEnabled() + public async Task GetIsAllProjectsEnabledAsync() { Logger.Information("Enter GetIsAllProjectsEnabled method"); - int solutionPathHash = this.GetSolutionPathHash(); + int solutionPathHash = await this.GetSolutionPathHashAsync(); var settings = this.settingsLoader.Load(); @@ -227,11 +228,12 @@ public void SaveCliReleaseLastCheckDate(DateTime lastCheckDate) /// Save additional options string. /// /// CLI options string. - public void SaveAdditionalOptions(string additionalOptions) + /// A representing the asynchronous operation. + public async Task SaveAdditionalOptionsAsync(string additionalOptions) { Logger.Information("Enter SaveAdditionalOptions method"); - int solutionPathHash = this.GetSolutionPathHash(); + int solutionPathHash = await this.GetSolutionPathHashAsync(); var settings = this.settingsLoader.Load(); @@ -264,11 +266,12 @@ public void SaveAdditionalOptions(string additionalOptions) /// Sace is all projects scan enabled. /// /// Bool param. - public void SaveIsAllProjectsScanEnabled(bool isAllProjectsEnabled) + /// A representing the asynchronous operation. + public async Task SaveIsAllProjectsScanEnabledAsync(bool isAllProjectsEnabled) { Logger.Information("Enter SaveIsAllProjectsScan method"); - int solutionPathHash = this.GetSolutionPathHash(); + int solutionPathHash = await this.GetSolutionPathHashAsync(); var settings = this.settingsLoader.Load(); @@ -309,6 +312,6 @@ private SnykSettings LoadSettings() return settings; } - private int GetSolutionPathHash() => this.solutionService.GetPath().ToLower().GetHashCode(); + private async Task GetSolutionPathHashAsync() => (await this.solutionService.GetSolutionFolderAsync()).ToLower().GetHashCode(); } } \ No newline at end of file diff --git a/Snyk.VisualStudio.Extension.Shared/UI/Toolwindow/SnykCode/DataFlowStepsControl.xaml.cs b/Snyk.VisualStudio.Extension.Shared/UI/Toolwindow/SnykCode/DataFlowStepsControl.xaml.cs index 9a56b32ff..766f29029 100644 --- a/Snyk.VisualStudio.Extension.Shared/UI/Toolwindow/SnykCode/DataFlowStepsControl.xaml.cs +++ b/Snyk.VisualStudio.Extension.Shared/UI/Toolwindow/SnykCode/DataFlowStepsControl.xaml.cs @@ -4,14 +4,17 @@ using System.Collections.Generic; using System.IO; using System.Linq; + using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using Microsoft.VisualStudio.PlatformUI; + using Microsoft.VisualStudio.Shell; using Serilog; using Snyk.Code.Library.Domain.Analysis; using Snyk.Common; using Snyk.VisualStudio.Extension.Shared.CLI; using Snyk.VisualStudio.Extension.Shared.Service; + using Task = System.Threading.Tasks.Task; /// /// Interaction logic for DataFlowStepsControl.xaml. @@ -68,7 +71,8 @@ public void AddDataFlowSteps(IList dataFlowSteps) /// Add markers to panel. /// /// Markers from suggestion. - internal void Display(IList markers) + /// A representing the asynchronous operation. + internal async Task DisplayAsync(IList markers) { this.Clear(); @@ -99,16 +103,9 @@ internal void Display(IList markers) { FileName = filePosition, RowNumber = index.ToString(), - LineContent = this.GetLineContent(position.FileName, startLineNumber), - NavigateCommand = new DelegateCommand(new Action(delegate (object o) - { - VsCodeService.Instance.OpenAndNavigate( - this.solutionService.GetFileFullPath(position.FileName), - startLine, - startColumn, - endLine, - endColumn); - })), + LineContent = await this.GetLineContentAsync(position.FileName, startLineNumber), + NavigateCommand = new DelegateCommand((obj) => + this.NavigateToCodeAsync(position.FileName, startLine, startColumn, endLine, endColumn).FireAndForget()), }; index++; @@ -120,9 +117,26 @@ internal void Display(IList markers) this.AddDataFlowSteps(dataFlowSteps.ToList()); } - private string GetLineContent(string file, long lineNumber) + private async Task NavigateToCodeAsync(string fileName, int startLine, int startColumn, int endLine, int endColumn) + { + try + { + VsCodeService.Instance.OpenAndNavigate( + await this.solutionService.GetFileFullPathAsync(fileName), + startLine, + startColumn, + endLine, + endColumn); + } + catch (Exception e) + { + Logger.Error(e, "Error on open and nagigate to source code"); + } + } + + private async Task GetLineContentAsync(string file, long lineNumber) { - string filePath = this.solutionService.GetFileFullPath(file); + string filePath = await this.solutionService.GetFileFullPathAsync(file); string line = string.Empty; @@ -145,7 +159,7 @@ private string GetLineContent(string file, long lineNumber) } catch (Exception e) { - Logger.Error(e.Message); + Logger.Error(e, "Error on get editor line content"); } return line; diff --git a/Snyk.VisualStudio.Extension.Shared/UI/Toolwindow/SnykCode/SnykCodeDescriptionControl.xaml.cs b/Snyk.VisualStudio.Extension.Shared/UI/Toolwindow/SnykCode/SnykCodeDescriptionControl.xaml.cs index 3534364f0..5967ae42c 100644 --- a/Snyk.VisualStudio.Extension.Shared/UI/Toolwindow/SnykCode/SnykCodeDescriptionControl.xaml.cs +++ b/Snyk.VisualStudio.Extension.Shared/UI/Toolwindow/SnykCode/SnykCodeDescriptionControl.xaml.cs @@ -27,7 +27,7 @@ public Suggestion Suggestion this.snykCodeDescription.Text = suggestion.Message; - this.dataFlowStepsControl.Display(suggestion.Markers); + this.dataFlowStepsControl.DisplayAsync(suggestion.Markers); this.externalExampleFixesControl.Display(suggestion.RepoDatasetSize, suggestion.Fixes); } diff --git a/Snyk.VisualStudio.Extension.Shared/UI/Toolwindow/SnykToolWindowControl.xaml.cs b/Snyk.VisualStudio.Extension.Shared/UI/Toolwindow/SnykToolWindowControl.xaml.cs index 520c4ac77..470e4e2fd 100644 --- a/Snyk.VisualStudio.Extension.Shared/UI/Toolwindow/SnykToolWindowControl.xaml.cs +++ b/Snyk.VisualStudio.Extension.Shared/UI/Toolwindow/SnykToolWindowControl.xaml.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; using System.Threading; + using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; @@ -555,7 +556,7 @@ private void VulnerabilitiesTree_SelectetVulnerabilityChanged(object sender, Rou if (this.resultsTree.SelectedItem is SnykCodeVulnerabilityTreeNode) { - this.HandleSnykCodeTreeNodeSelected(); + this.HandleSnykCodeTreeNodeSelectedAsync(); return; } @@ -611,7 +612,7 @@ private void HandleOssTreeNodeSelected() } } - private void HandleSnykCodeTreeNodeSelected() + private async System.Threading.Tasks.Task HandleSnykCodeTreeNodeSelectedAsync() { this.descriptionPanel.Visibility = Visibility.Visible; @@ -622,7 +623,7 @@ private void HandleSnykCodeTreeNodeSelected() var suggestion = snykCodeTreeNode.Suggestion; VsCodeService.Instance.OpenAndNavigate( - this.serviceProvider.SolutionService.GetFileFullPath(suggestion.FileName), + await this.serviceProvider.SolutionService.GetFileFullPathAsync(suggestion.FileName), suggestion.Rows.Item1 - 1, suggestion.Columns.Item1 - 1, suggestion.Rows.Item2 - 1, diff --git a/Snyk.VisualStudio.Extension.Tests/OssServiceTest.cs b/Snyk.VisualStudio.Extension.Tests/OssServiceTest.cs index a2f8685d6..280b1ddfb 100644 --- a/Snyk.VisualStudio.Extension.Tests/OssServiceTest.cs +++ b/Snyk.VisualStudio.Extension.Tests/OssServiceTest.cs @@ -30,20 +30,20 @@ public void OssServiceTest_NoCachedValuesExists_ReturnNewScanResult() var fakeCliResult = new CliResult(); cliMock - .Setup(cli => cli.Scan(It.IsAny())) - .Returns(fakeCliResult); + .Setup(cli => cli.ScanAsync(It.IsAny())) + .ReturnsAsync(fakeCliResult); var ossService = new OssService(serviceProviderMock.Object); var tokenSource = new CancellationTokenSource(); - var cliResult = ossService.Scan(string.Empty, tokenSource.Token); + var cliResult = ossService.ScanAsync(string.Empty, tokenSource.Token); Assert.NotNull(cliResult); } [Fact] - public void OssServiceTest_CachedValuesExists_ReturnCachedResult() + public async System.Threading.Tasks.Task OssServiceTest_CachedValuesExists_ReturnCachedResultAsync() { var serviceProviderMock = new Mock(); var cliMock = new Mock(); @@ -60,23 +60,23 @@ public void OssServiceTest_CachedValuesExists_ReturnCachedResult() var fakeCliResult = new CliResult(); cliMock - .Setup(cli => cli.Scan(It.IsAny())) - .Returns(fakeCliResult); + .Setup(cli => cli.ScanAsync(It.IsAny())) + .ReturnsAsync(fakeCliResult); var ossService = new OssService(serviceProviderMock.Object); var tokenSource = new CancellationTokenSource(); // Run scan first time will setup cache value. - ossService.Scan(string.Empty, tokenSource.Token); + await ossService.ScanAsync(string.Empty, tokenSource.Token); // Get cached value. - var cliResult = ossService.Scan(string.Empty, tokenSource.Token); + var cliResult = ossService.ScanAsync(string.Empty, tokenSource.Token); Assert.NotNull(cliResult); cliMock - .Verify(cli => cli.Scan(It.IsAny()), Times.Exactly(1)); + .Verify(cli => cli.ScanAsync(It.IsAny()), Times.Exactly(1)); } [Fact] @@ -97,23 +97,23 @@ public void OssServiceTest_ClearCache_ReturnNewValue() var fakeCliResult = new CliResult(); cliMock - .Setup(cli => cli.Scan(It.IsAny())) - .Returns(fakeCliResult); + .Setup(cli => cli.ScanAsync(It.IsAny())) + .ReturnsAsync(fakeCliResult); var ossService = new OssService(serviceProviderMock.Object); var tokenSource = new CancellationTokenSource(); - ossService.Scan(string.Empty, tokenSource.Token); + ossService.ScanAsync(string.Empty, tokenSource.Token); ossService.ClearCache(); - var cliResult = ossService.Scan(string.Empty, tokenSource.Token); + var cliResult = ossService.ScanAsync(string.Empty, tokenSource.Token); Assert.NotNull(cliResult); cliMock - .Verify(cli => cli.Scan(It.IsAny()), Times.Exactly(2)); + .Verify(cli => cli.ScanAsync(It.IsAny()), Times.Exactly(2)); } } } diff --git a/Snyk.VisualStudio.Extension.Tests/SnykCliTest.cs b/Snyk.VisualStudio.Extension.Tests/SnykCliTest.cs index 0bf80c8bb..dadd8cef4 100644 --- a/Snyk.VisualStudio.Extension.Tests/SnykCliTest.cs +++ b/Snyk.VisualStudio.Extension.Tests/SnykCliTest.cs @@ -2,6 +2,8 @@ { using System; using System.IO; + using System.Threading.Tasks; + using Moq; using Snyk.VisualStudio.Extension.Shared.CLI; using Snyk.VisualStudio.Extension.Shared.Model; using Snyk.VisualStudio.Extension.Shared.Settings; @@ -9,12 +11,23 @@ public class SnykCliTest { + private Mock optionsMock; + + public SnykCliTest() + { + this.optionsMock = new Mock(); + + this.optionsMock + .Setup(options => options.UsageAnalyticsEnabled) + .Returns(true); + } + [Fact] public void SnykCliTest_CliReturnError_GetApiTokenThrowException() { var cli = new SnykCli { - Options = new SnykMockOptions(), + Options = this.optionsMock.Object, ConsoleRunner = new SnykMockConsoleRunner("cli file note exists"), }; @@ -22,11 +35,11 @@ public void SnykCliTest_CliReturnError_GetApiTokenThrowException() } [Fact] - public void ConvertRawCliStringToCliResultWithCriticalSeverity() + public void SnykCliTest_ConvertRawCliStringToCliResultWithCriticalSeverity_CriticalSeverityVulnExists() { var cli = new SnykCli { - Options = new SnykMockOptions(), + Options = this.optionsMock.Object, }; var cliResult = SnykCli.ConvertRawCliStringToCliResult(this.GetFileContent("CriticalSeverityObject.json")); @@ -45,15 +58,15 @@ public void ConvertRawCliStringToCliResultWithCriticalSeverity() } [Fact] - public void Scan() + public async Task SnykCliTest_RunScan_SuccessfulCliResultAsync() { var cli = new SnykCli { - Options = new SnykMockOptions(), + Options = this.optionsMock.Object, ConsoleRunner = new SnykMockConsoleRunner(this.GetFileContent("VulnerabilitiesSingleObject.json")), }; - var cliResult = cli.Scan(string.Empty); + var cliResult = await cli.ScanAsync(string.Empty); Assert.Single(cliResult.CliVulnerabilitiesList); } @@ -65,7 +78,7 @@ public void SnykCliTest_GetApiToken_Successful() var cli = new SnykCli { - Options = new SnykMockOptions(), + Options = this.optionsMock.Object, ConsoleRunner = new SnykMockConsoleRunner(testGuid), }; @@ -73,11 +86,11 @@ public void SnykCliTest_GetApiToken_Successful() } [Fact] - public void Authenticate() + public void SnykCliTest_Authenticate_Successful() { var cli = new SnykCli { - Options = new SnykMockOptions(), + Options = this.optionsMock.Object, ConsoleRunner = new SnykMockConsoleRunner("Your account has been authenticated. Snyk is now ready to be used."), }; @@ -85,135 +98,154 @@ public void Authenticate() } [Fact] - public void BuildArguments_WithoutOptions() + public async Task SnykCliTest_BuildArguments_WithoutOptionsAsync() { var cli = new SnykCli { - Options = new SnykMockOptions(), + Options = this.optionsMock.Object, }; - Assert.Equal("--json test", cli.BuildScanArguments()); + Assert.Equal("--json test", await cli.BuildScanArgumentsAsync()); } [Fact] - public void BuildArguments_WithCustomEndpointOption() + public async Task SnykCliTest_BuildArguments_WithCustomEndpointOptionAsync() { - var cli = new SnykCli - { - Options = new SnykMockOptions() - { - CustomEndpoint = "https://github.com/snyk/", - }, - }; + this.optionsMock + .Setup(options => options.CustomEndpoint) + .Returns("https://github.com/snyk/"); - Assert.Equal("--json test --API=https://github.com/snyk/", cli.BuildScanArguments()); + var cli = new SnykCli { Options = this.optionsMock.Object, }; + + Assert.Equal("--json test --API=https://github.com/snyk/", await cli.BuildScanArgumentsAsync()); } [Fact] - public void BuildArguments_WithInsecureOption() + public async Task SnykCliTest_BuildArguments_WithInsecureOptionAsync() { - var cli = new SnykCli - { - Options = new SnykMockOptions() - { - IgnoreUnknownCA = true, - }, - }; + this.optionsMock + .Setup(options => options.IgnoreUnknownCA) + .Returns(true); + + var cli = new SnykCli { Options = this.optionsMock.Object, }; - Assert.Equal("--json test --insecure", cli.BuildScanArguments()); + Assert.Equal("--json test --insecure", await cli.BuildScanArgumentsAsync()); } [Fact] - public void BuildArguments_WithOrganizationOption() + public async Task SnykCliTest_BuildArguments_WithOrganizationOptionAsync() { - var cli = new SnykCli - { - Options = new SnykMockOptions() - { - Organization = "test-snyk-organization", - }, - }; + this.optionsMock + .Setup(options => options.Organization) + .Returns("test-snyk-organization"); + + var cli = new SnykCli { Options = this.optionsMock.Object, }; - Assert.Equal("--json test --org=test-snyk-organization", cli.BuildScanArguments()); + Assert.Equal("--json test --org=test-snyk-organization", await cli.BuildScanArgumentsAsync()); } [Fact] - public void BuildArguments_WithAdditionalOptions() + public async Task SnykCliTest_BuildArguments_WithAdditionalOptionsAsync() { - var cli = new SnykCli - { - Options = new SnykMockOptions() - { - AdditionalOptions = "--file=C:\build.pom", - }, - }; + this.optionsMock + .Setup(options => options.GetAdditionalOptionsAsync()) + .ReturnsAsync("--file=C:\build.pom"); - Assert.Equal("--json test --file=C:\build.pom", cli.BuildScanArguments()); + var cli = new SnykCli { Options = this.optionsMock.Object, }; + + Assert.Equal("--json test --file=C:\build.pom", await cli.BuildScanArgumentsAsync()); } [Fact] - public void BuildArguments_WithScanAllProjects() + public async Task SnykCliTest_BuildArguments_WithScanAllProjectsAsync() { - var cli = new SnykCli - { - Options = new SnykMockOptions() - { - IsScanAllProjects = true, - }, - }; + this.optionsMock + .Setup(options => options.IsScanAllProjectsAsync()) + .ReturnsAsync(true); + + var cli = new SnykCli { Options = this.optionsMock.Object, }; - Assert.Equal("--json test --all-projects", cli.BuildScanArguments()); + Assert.Equal("--json test --all-projects", await cli.BuildScanArgumentsAsync()); } [Fact] - public void BuildArguments_WithAllOptions() + public async Task SnykCliTest_BuildArguments_WithAllOptionsAsync() { - var cli = new SnykCli - { - Options = new SnykMockOptions() - { - CustomEndpoint = "https://github.com/snyk/", - IgnoreUnknownCA = true, - Organization = "test-snyk-organization", - AdditionalOptions = "--ignore-policy", - IsScanAllProjects = true, - UsageAnalyticsEnabled = false, - }, - }; + this.optionsMock + .Setup(options => options.CustomEndpoint) + .Returns("https://github.com/snyk/"); + + this.optionsMock + .Setup(options => options.IgnoreUnknownCA) + .Returns(true); + + this.optionsMock + .Setup(options => options.Organization) + .Returns("test-snyk-organization"); - Assert.Equal("--json test --API=https://github.com/snyk/ --insecure --org=test-snyk-organization --ignore-policy --all-projects --DISABLE_ANALYTICS", cli.BuildScanArguments()); + this.optionsMock + .Setup(options => options.UsageAnalyticsEnabled) + .Returns(false); + + this.optionsMock + .Setup(options => options.GetAdditionalOptionsAsync()) + .ReturnsAsync("--ignore-policy"); + + this.optionsMock + .Setup(options => options.IsScanAllProjectsAsync()) + .ReturnsAsync(true); + + var cli = new SnykCli { Options = this.optionsMock.Object, }; + + Assert.Equal( + "--json test --API=https://github.com/snyk/ --insecure --org=test-snyk-organization --ignore-policy --all-projects --DISABLE_ANALYTICS", + await cli.BuildScanArgumentsAsync()); } [Fact] - public void BuildArguments_WithDisableAnalytics() + public async Task SnykCliTest_BuildArguments_WithDisableAnalyticsAsync() { - var cli = new SnykCli - { - Options = new SnykMockOptions() - { - UsageAnalyticsEnabled = false, - }, - }; + this.optionsMock + .Setup(options => options.UsageAnalyticsEnabled) + .Returns(false); + + var cli = new SnykCli { Options = this.optionsMock.Object, }; - Assert.Equal("--json test --DISABLE_ANALYTICS", cli.BuildScanArguments()); + Assert.Equal("--json test --DISABLE_ANALYTICS", await cli.BuildScanArgumentsAsync()); } [Fact] - public void BuildEnvironmentVariables_WithAllOptions() + public void SnykCliTest_BuildEnvironmentVariables_WithAllOptions() { - var cli = new SnykCli - { - Options = new SnykMockOptions() - { - CustomEndpoint = "https://github.com/snyk/", - IgnoreUnknownCA = true, - Organization = "test-snyk-organization", - AdditionalOptions = "--ignore-policy", - IsScanAllProjects = true, - UsageAnalyticsEnabled = false, - ApiToken = "test-token", - }, - }; + this.optionsMock + .Setup(options => options.CustomEndpoint) + .Returns("https://github.com/snyk/"); + + this.optionsMock + .Setup(options => options.IgnoreUnknownCA) + .Returns(true); + + this.optionsMock + .Setup(options => options.Organization) + .Returns("test-snyk-organization"); + + this.optionsMock + .Setup(options => options.UsageAnalyticsEnabled) + .Returns(false); + + this.optionsMock + .Setup(options => options.GetAdditionalOptionsAsync()) + .ReturnsAsync("--ignore-policy"); + + this.optionsMock + .Setup(options => options.IsScanAllProjectsAsync()) + .ReturnsAsync(true); + + this.optionsMock + .Setup(options => options.ApiToken) + .Returns("test-token"); + + var cli = new SnykCli { Options = this.optionsMock.Object, }; var result = cli.BuildScanEnvironmentVariables(); @@ -261,12 +293,9 @@ public void ConvertRawCliStringToCliResult_ErrorJson() } [Fact] - public void ConvertRawCliStringToCliResult_PlainTextError() + public void SnykCliTest_ConvertRawCliStringToCliResult_PlainTextError() { - var cli = new SnykCli - { - Options = new SnykMockOptions(), - }; + var cli = new SnykCli { Options = this.optionsMock.Object, }; var cliResult = SnykCli.ConvertRawCliStringToCliResult(this.GetFileContent("ErrorPlainText.json")); @@ -302,91 +331,4 @@ class MockServiceProvider : IServiceProvider { public object GetService(Type serviceType) => throw new NotImplementedException(); } - - class SnykMockOptions : ISnykOptions - { - private string apiToken = string.Empty; - private string customEndpoint = string.Empty; - private string organization = string.Empty; - private bool ignoreUnknownCA = false; - private string additionalOptions = string.Empty; - private bool isScanAllProjects = false; - private bool usageAnalyticsEnabled = true; - - public SnykMockOptions() { } - - public SnykMockOptions( - string apiToken = "", - string customEndpoint = "", - string organization = "", - string additionalOptions = "", - bool ignoreUnknownCA = false, - bool isScanAllProjects = false) - { - this.CustomEndpoint = customEndpoint; - this.ApiToken = apiToken; - this.Organization = organization; - this.IgnoreUnknownCA = ignoreUnknownCA; - this.AdditionalOptions = additionalOptions; - this.IsScanAllProjects = isScanAllProjects; - } - - public event EventHandler SettingsChanged; - - public string ApiToken - { - get => this.apiToken; - set => this.apiToken = value; - } - - public string CustomEndpoint - { - get => this.customEndpoint; - set => this.customEndpoint = value; - } - - public string Organization - { - get => this.organization; - set => this.organization = value; - } - - public bool IgnoreUnknownCA - { - get => this.ignoreUnknownCA; - set => this.ignoreUnknownCA = value; - } - - public string AdditionalOptions - { - get => this.additionalOptions; - set => this.additionalOptions = value; - } - - public bool IsScanAllProjects - { - get => this.isScanAllProjects; - set => this.isScanAllProjects = value; - } - - public bool UsageAnalyticsEnabled - { - get => this.usageAnalyticsEnabled; - set => this.usageAnalyticsEnabled = value; - } - - public bool OssEnabled => throw new NotImplementedException(); - - public bool SnykCodeSecurityEnabled => throw new NotImplementedException(); - - public bool SnykCodeQualityEnabled => throw new NotImplementedException(); - - public string AnonymousId { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - - public string SnykCodeSettingsUrl => throw new NotImplementedException(); - - public void Authenticate(Action successCallbackAction, Action errorCallbackAction) => throw new NotImplementedException(); - - public void LoadSettingsFromStorage() => throw new NotImplementedException(); - } } diff --git a/Snyk.VisualStudio.Extension.Tests/SnykSolutionServiceTest.cs b/Snyk.VisualStudio.Extension.Tests/SnykSolutionServiceTest.cs new file mode 100644 index 000000000..c5b53a4b8 --- /dev/null +++ b/Snyk.VisualStudio.Extension.Tests/SnykSolutionServiceTest.cs @@ -0,0 +1,33 @@ +namespace Snyk.VisualStudio.Extension.Tests +{ + using System.Collections.Generic; + using Snyk.VisualStudio.Extension.Shared.CLI; + using Xunit; + + /// + /// Test case for . + /// + public class SnykSolutionServiceTest + { + [Fact] + public void SnykSolutionServiceTest_FindRootDirectoryForSolution_ReturnRootDirectory() + { + string soludtionDir = "C:\\projects\\superproj\\dummy\\"; + + var paths = new List + { + "C:\\projects\\superproj\\dummy\\sdf", + "C:\\projects\\superproj\\testproj\\", + "C:\\projects\\superproj\\sdf\testproj1\\", + "C:\\projects\\superproj\\sdf", + "C:\\projects\\superproj\\s\\d\f", + }; + + var solutionService = new SnykSolutionService(); + + var rootDir = solutionService.FindRootDirectoryForSolutionProjects(soludtionDir, paths); + + Assert.Equal("C:\\projects\\superproj", rootDir); + } + } +} diff --git a/Snyk.VisualStudio.Extension.Tests/SnykUserStorageSettingsServiceTest.cs b/Snyk.VisualStudio.Extension.Tests/SnykUserStorageSettingsServiceTest.cs index 0a01d20f6..7390e6e15 100644 --- a/Snyk.VisualStudio.Extension.Tests/SnykUserStorageSettingsServiceTest.cs +++ b/Snyk.VisualStudio.Extension.Tests/SnykUserStorageSettingsServiceTest.cs @@ -1,6 +1,7 @@ namespace Snyk.VisualStudio.Extension.Tests { using System.IO; + using System.Threading.Tasks; using Moq; using Snyk.Common; using Snyk.VisualStudio.Extension.Shared.Service; @@ -13,7 +14,7 @@ public class SnykUserStorageSettingsServiceTest { [Fact] - public void SnykUserStorageSettingsService_ProjectNameNotExists_SaveAdditionalOptionsSuccessfull() + public async Task SnykUserStorageSettingsService_ProjectNameNotExists_SaveAdditionalOptionsSuccessfullAsync() { var serviceProviderMock = new Mock(); var solutionServiceMock = new Mock(); @@ -23,16 +24,16 @@ public void SnykUserStorageSettingsService_ProjectNameNotExists_SaveAdditionalOp .Returns(solutionServiceMock.Object); solutionServiceMock - .Setup(solutionService => solutionService.GetPath()) - .Returns("C:\\Projects\\TestProj"); + .Setup(solutionService => solutionService.GetSolutionFolderAsync()) + .ReturnsAsync("C:\\Projects\\TestProj"); string settingsFilePath = Path.GetTempFileName(); var userStorageSettingsService = new SnykUserStorageSettingsService(settingsFilePath, serviceProviderMock.Object); - userStorageSettingsService.SaveAdditionalOptions("--test-command"); + await userStorageSettingsService.SaveAdditionalOptionsAsync("--test-command"); - Assert.Equal("--test-command", userStorageSettingsService.GetAdditionalOptions()); + Assert.Equal("--test-command", await userStorageSettingsService.GetAdditionalOptionsAsync()); } } }