Пример #1
0
        /// <summary>
        /// Initializes a new instance of the <see cref="SarifWorkItemFiler"> class.</see>
        /// </summary>
        /// <param name="filingUri">
        /// The uri to the remote filing host.
        /// </param>
        /// <param name="filingContext">
        /// A starting context object that configures the work item filing operation. In the
        /// current implementation, this context is copied for each SARIF file (if any) split
        /// from the input log and then further elaborated upon.
        /// </param>
        public SarifWorkItemFiler(Uri filingUri = null, SarifWorkItemContext filingContext = null)
        {
            this.FilingContext = filingContext ?? new SarifWorkItemContext {
                HostUri = filingUri
            };
            filingUri = filingUri ?? this.FilingContext.HostUri;

            if (filingUri == null)
            {
                throw new ArgumentNullException(nameof(filingUri));
            }
            ;

            if (filingUri != this.FilingContext.HostUri)
            {
                // Inconsistent URIs were provided in 'filingContext' and 'filingUri'; arguments.
                throw new InvalidOperationException(WorkItemsResources.InconsistentHostUrisProvided);
            }

            this.FilingClient = FilingClientFactory.Create(this.FilingContext.HostUri);

            this.Logger = ServiceProviderFactory.ServiceProvider.GetService <ILogger>();
            Assembly.GetExecutingAssembly().LogIdentity();

            this.FiledWorkItems = new List <WorkItemModel>();
        }
        public static string CreateWorkItemDescription(this SarifLog log, SarifWorkItemContext context, IList <Uri> locationUris)
        {
            int           totalResults        = log.GetAggregateFilableResultsCount();
            List <string> toolNames           = log.GetToolNames();
            string        phrasedToolNames    = toolNames.ToAndPhrase();
            string        multipleToolsFooter = toolNames.Count > 1 ? WorkItemsResources.MultipleToolsFooter : string.Empty;

            Uri runRepositoryUri     = log?.Runs.FirstOrDefault()?.VersionControlProvenance?.FirstOrDefault().RepositoryUri;
            Uri detectionLocationUri = !string.IsNullOrEmpty(runRepositoryUri?.OriginalString) ? runRepositoryUri : locationUris?[0];

            string detectionLocation = (detectionLocationUri?.IsAbsoluteUri == true && detectionLocationUri?.Scheme == "https")
                ? context.CreateLinkText(detectionLocationUri.OriginalString, detectionLocationUri?.OriginalString)
                : detectionLocationUri?.OriginalString;

            if (locationUris?.Count > 1)
            {
                int additionalLocations = locationUris.Count - 1;
                detectionLocation = $"{detectionLocation} (+{additionalLocations} locations)";
            }

            // This work item contains {0} {1} issue(s) detected in {2}{3}. Click the 'Scans' tab to review results.
            string description =
                string.Format(
                    WorkItemsResources.WorkItemBodyTemplateText, // This work item contains
                    totalResults,                                // {0}
                    phrasedToolNames,                            // {1} issue(s) detected in
                    detectionLocation,                           // {2}
                    multipleToolsFooter);                        // {3}. Click the 'Scans' tab to review results.\r\n

            return(description);
        }
        public void WorkItemFilingContext_IncludeCustomBugFooter()
        {
            var context = new SarifWorkItemContext();

            string customBugFooter = "This text is important to all my bugs.";

            context.AzureDevOpsDescriptionFooter = customBugFooter;
            var workItemModel = new SarifWorkItemModel(sarifLog: TestData.CreateSimpleLog(), context);

            workItemModel.BodyOrDescription.Should().Contain(customBugFooter);
        }
        public void WorkItemFiler_PerResultSplitStrategyPartitionsProperly()
        {
            SarifLog             sarifLog = TestData.CreateSimpleLog();
            SarifWorkItemContext context  = CreateAzureDevOpsTestContext();

            context.SplittingStrategy = SplittingStrategy.PerResult;

            int numberOfResults = sarifLog.Runs.Sum(run => run.Results.Count);

            context.SetProperty(ExpectedWorkItemsCount, numberOfResults);

            TestWorkItemFiler(sarifLog, context, true);
        }
        public void SarifWorkItemModel_PopulatesDescription()
        {
            var      context  = new SarifWorkItemContext();
            SarifLog sarifLog = TestData.CreateOneIdThreeLocations();

            var workItemModel = new SarifWorkItemModel(sarifLog, context);

            workItemModel.BodyOrDescription.Should().NotBeNullOrEmpty();
            workItemModel.BodyOrDescription.Should().Contain(nameof(TestData.TestToolName));
            workItemModel.BodyOrDescription.Should().Contain(sarifLog.Runs[0].VersionControlProvenance[0].RepositoryUri.OriginalString);
            workItemModel.BodyOrDescription.Should().Contain(WorkItemsResources.GeneralFooterText);
            workItemModel.BodyOrDescription.Should().NotContain(WorkItemsResources.AdoViewingOptions);
        }
        public void SarifWorkItemModel_PopulatesAdoDescription()
        {
            // Context object specifies ADO as a filing client provider by default
            var      context  = new SarifWorkItemContext();
            SarifLog sarifLog = TestData.CreateOneIdThreeLocations();

            var workItemModel = new SarifWorkItemModel(sarifLog, context);

            workItemModel.BodyOrDescription.Should().NotBeNullOrEmpty();
            workItemModel.BodyOrDescription.Should().Contain(nameof(TestData.TestToolName));
            workItemModel.BodyOrDescription.Should().Contain(sarifLog.Runs[0].VersionControlProvenance[0].RepositoryUri.OriginalString);
            workItemModel.BodyOrDescription.Should().Contain(WorkItemsResources.AzureDevOpsDefaultDescriptionFooter);
            workItemModel.BodyOrDescription.Should().NotContain(WorkItemsResources.GitHubDefaultDescriptionFooter);
        }
        public void SarifWorkItemModel_IncorporatesMultipleToolNamesIntoAdoDescription()
        {
            var context = new SarifWorkItemContext();

            context.CurrentProvider = FilingClient.SourceControlProvider.AzureDevOps;
            SarifLog sarifLog = TestData.CreateTwoRunThreeResultLog();

            var workItemModel = new SarifWorkItemModel(sarifLog, context);

            workItemModel.BodyOrDescription.Should().NotBeNullOrEmpty();
            workItemModel.BodyOrDescription.Should().Contain(nameof(TestData.TestToolName));
            workItemModel.BodyOrDescription.Should().Contain(nameof(TestData.SecondTestToolName));
            workItemModel.BodyOrDescription.Should().Contain(TestData.FileLocations.Location1);
        }
        public void WorkItemFilingContext_NullSarifLogRaisesArgumentNullException()
        {
            var areaPathTransformer = new AreaPathFromUri();

            var context = new SarifWorkItemContext();

            context.AddWorkItemModelTransformer(areaPathTransformer);
            context.Transformers[0].GetType().Should().Be(areaPathTransformer.GetType());

            var workItemModel = new SarifWorkItemModel(sarifLog: TestData.CreateSimpleLog(), context);

            context.Transformers[0].Transform(workItemModel);
            workItemModel.Area.Should().BeNull();
        }
        public void SarifWorkItemModel_PopulatesAdoDescription()
        {
            var context = new SarifWorkItemContext();

            context.CurrentProvider = FilingClient.SourceControlProvider.AzureDevOps;
            SarifLog sarifLog = TestData.CreateOneIdThreeLocations();

            var workItemModel = new SarifWorkItemModel(sarifLog, context);

            workItemModel.BodyOrDescription.Should().NotBeNullOrEmpty();
            workItemModel.BodyOrDescription.Should().Contain(nameof(TestData.TestToolName));
            workItemModel.BodyOrDescription.Should().Contain(sarifLog.Runs[0].VersionControlProvenance[0].RepositoryUri.OriginalString);
            workItemModel.BodyOrDescription.Should().Contain(WorkItemsResources.ViewScansTabResults);
        }
Пример #10
0
        public void WorkItemFiler_PerRunSplitStrategyPartitionsProperlyGithub()
        {
            SarifWorkItemContext context = GitHubTestContext;

            SarifLog sarifLog = TestData.CreateSimpleLog();

            // Our default splitting strategy is PerRun, that is, one
            // work item (and corresponding attachment) should be filed
            // for each run in the log file.
            int numberOfRuns = sarifLog.Runs.Count;

            context.SetProperty(ExpectedWorkItemsCount, numberOfRuns);

            TestWorkItemFiler(sarifLog, context, false);
        }
        public void WorkItemFilingContext_FetchUriSuccessfully()
        {
            var areaPathTransformer = new AreaPathFromUri();

            var context = new SarifWorkItemContext();

            context.AddWorkItemModelTransformer(areaPathTransformer);
            context.Transformers[0].GetType().Should().Be(areaPathTransformer.GetType());

            SarifLog sarifLog = TestData.CreateOneIdThreeLocations();

            var workItemModel = new SarifWorkItemModel(sarifLog, context);

            context.Transformers[0].Transform(workItemModel);
            workItemModel.Area.Should().Be(TestData.FileLocations.Location1);
        }
Пример #12
0
        public void SarifWorkItemExtensions_GetAggregateResultCount_ComputeResultCountsFromLogs()
        {
            var      context  = new SarifWorkItemContext();
            SarifLog sarifLog = TestData.CreateOneIdThreeLocations();

            int resultCount = sarifLog.GetAggregateFilableResultsCount(false);

            resultCount.Should().Be(1);

            sarifLog    = TestData.CreateTwoRunThreeResultLog();
            resultCount = sarifLog.GetAggregateFilableResultsCount(false);
            resultCount.Should().Be(3);

            sarifLog    = TestData.CreateEmptyRun();
            resultCount = sarifLog.GetAggregateFilableResultsCount(false);
            resultCount.Should().Be(0);
        }
        private SarifWorkItemContext RoundTripThroughXml(SarifWorkItemContext sarifWorkItemContext)
        {
            string temp = Path.GetTempFileName();

            try
            {
                sarifWorkItemContext.SaveToXml(temp);
                sarifWorkItemContext = new SarifWorkItemContext();
                sarifWorkItemContext.LoadFromXml(temp);
            }
            finally
            {
                if (File.Exists(temp))
                {
                    File.Delete(temp);
                }
            }
            return(sarifWorkItemContext);
        }
Пример #14
0
        private static Mock <SarifWorkItemFiler> CreateMockSarifWorkItemFiler(SarifWorkItemContext context = null)
        {
            context = context ?? GitHubTestContext;

            var mockFiler = new Mock <SarifWorkItemFiler>(context.HostUri, context);

            mockFiler
            .Setup(x => x.FileWorkItems(It.IsAny <string>()))
            .CallBase();

            mockFiler
            .Setup(x => x.FileWorkItems(It.IsAny <SarifLog>()))
            .CallBase();

            mockFiler
            .Setup(x => x.FileWorkItems(It.IsAny <Uri>()))
            .CallBase();

            return(mockFiler);
        }
Пример #15
0
        public void SarifWorkItemExtensions_GetRunToolNames_FetchesAllRunToolNames()
        {
            var      context  = new SarifWorkItemContext();
            SarifLog sarifLog = TestData.CreateOneIdThreeLocations();

            List <string> toolNames = sarifLog.GetToolNames();

            toolNames.Count.Should().Be(1);
            toolNames.Should().Contain(TestData.TestToolName);

            sarifLog  = TestData.CreateTwoRunThreeResultLog();
            toolNames = sarifLog.GetToolNames();
            toolNames.Count.Should().Be(2);
            toolNames.Should().Contain(TestData.TestToolName);
            toolNames.Should().Contain(TestData.SecondTestToolName);

            sarifLog  = TestData.CreateEmptyRun();
            toolNames = sarifLog.GetToolNames();
            toolNames.Count.Should().Be(0);
        }
        public void WorkItemFilingContext_RoundTripsWorkItemModelTransformer()
        {
            var munger = new Munger();

            var context = new SarifWorkItemContext();

            context.AddWorkItemModelTransformer(munger);
            context.Transformers[0].GetType().Should().Be(munger.GetType());

            context = RoundTripThroughXml(context);

            context.Transformers[0].GetType().Should().Be(munger.GetType());

            string newAreaPath = Guid.NewGuid().ToString();

            context.SetProperty(Munger.NewAreaPath, newAreaPath);

            var workItemModel = new SarifWorkItemModel(sarifLog: TestData.CreateSimpleLog(), context);

            context.Transformers[0].Transform(workItemModel);
            workItemModel.Area.Should().Be(newAreaPath);
        }
Пример #17
0
        internal static void FileWorkItemsHelper(SarifLog sarifLog, SarifWorkItemContext filingContext, FilingClient filingClient)
        {
            // The helper below will initialize the sarif work item model with a copy
            // of the root pipeline filing context. This context will then be initialized
            // based on the current sarif log file that we're processing.

            SarifWorkItemModel workItemModel = new SarifWorkItemModel(sarifLog, filingContext);

            try
            {
                // Populate the work item with the target organization/repository information.
                // In ADO, certain fields (such as the area path) will defaut to the
                // project name and so this information is used in at least that context.
                workItemModel.RepositoryOrProject = filingClient.ProjectOrRepository;
                workItemModel.OwnerOrAccount      = filingClient.AccountOrOrganization;

                foreach (SarifWorkItemModelTransformer transformer in workItemModel.Context.Transformers)
                {
                    transformer.Transform(workItemModel);
                }

                filingClient.FileWorkItems(new[] { workItemModel }).Wait();

                // TODO: We need to process updated work item models to persist filing
                //       details back to the input SARIF file, if that was specified.
                //       This code should either return or persist the updated models
                //       via a property, so that the file work items command can do
                //       this work.
                //
                //       https://github.com/microsoft/sarif-sdk/issues/1774
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine(ex);
            }
        }
Пример #18
0
 public SarifWorkItemContext(SarifWorkItemContext initializer) : base(initializer)
 {
 }
Пример #19
0
        private void TestWorkItemFiler(SarifLog sarifLog, SarifWorkItemContext context)
        {
            // ONE. Create test data that the low-level ADO client mocks
            //      will flow back to the SARIF work item filer.
            var attachmentReference = new AttachmentReference()
            {
                Id  = Guid.NewGuid(),
                Url = Guid.NewGuid().ToString()
            };

            var workItem = new WorkItem
            {
                Id    = DateTime.UtcNow.Millisecond,
                Links = new ReferenceLinks()
            };

            // The fake URI to the filed item that we'll expect the filer to receive
            string bugUriText     = "https://example.com/" + Guid.NewGuid().ToString();
            string bugHtmlUriText = "https://example.com/" + Guid.NewGuid().ToString();

            Uri bugUri     = new Uri(bugUriText, UriKind.RelativeOrAbsolute);
            Uri bugHtmlUri = new Uri(bugHtmlUriText, UriKind.RelativeOrAbsolute);

            workItem.Url = bugUriText;
            workItem.Links.AddLink("html", bugHtmlUriText);

            // TWO. Define variables to capture whether we enter all expected ADO client methods.
            bool connectCalled = false;
            int  createWorkItemCalled = 0, createAttachmentCalled = 0;

            // THREE. Create a default mock SARIF filer and client, configured by an AzureDevOps context
            //        (which creates a default ADO filing client underneath).
            SarifWorkItemFiler filer = CreateMockSarifWorkItemFiler(context).Object;

            var workItemTrackingHttpClientMock = new Mock <IWorkItemTrackingHttpClient>();

            workItemTrackingHttpClientMock
            .Setup(x => x.CreateAttachmentAsync(It.IsAny <MemoryStream>(), It.IsAny <string>(), It.IsAny <string>(), It.IsAny <string>(), It.IsAny <object>(), It.IsAny <CancellationToken>()))
            .ReturnsAsync(attachmentReference)     // Return our test attachment, defined above.
            .Callback <MemoryStream, string, string, string, object, CancellationToken>(
                (stream, fileName, uploadType, areaPath, userState, cancellationToken) =>
            {
                // Verify that the ADO client receives the request to create an attachment
                createAttachmentCalled++;
            });

            workItemTrackingHttpClientMock
            .Setup(x => x.CreateWorkItemAsync(It.IsAny <JsonPatchDocument>(), It.IsAny <string>(), It.IsAny <string>(), It.IsAny <bool?>(), It.IsAny <bool?>(), It.IsAny <bool?>(), It.IsAny <object>(), It.IsAny <CancellationToken>()))
            .ReturnsAsync(workItem)     // Return our test work item, defined above
            .Callback <JsonPatchDocument, string, string, bool?, bool?, bool?, object, CancellationToken>(
                (document, project, type, validateOnly, bypassRules, suppressNotifications, userState, cancellationToken) =>
            {
                // Verify that the ADO client receives the request to file the bug
                createWorkItemCalled++;
            });

            // FOUR. Create a mock VssConnection instance, which handles the auth connection
            //       and retrieval of the ADO filing client. We are required to put this
            //       mock behind an interface due to an inability

            var vssConnectionMock = new Mock <IVssConnection>();

            vssConnectionMock
            .Setup(x => x.ConnectAsync(It.IsAny <Uri>(), It.IsAny <string>()))
            .Returns(Task.CompletedTask)
            .Callback <Uri, string>(
                (uri, pat) =>
            {
                // The following data values flow to us via test constants which are
                // captured in the default context object that initialized the filer.
                pat.Should().Be(TestData.NotActuallyASecret);

                // We configure the filer with a full ADO URI that contains the account and
                // project, e.g., https://dev.azure.com/myaccount/myproject. By the time the
                // ADO client receives it, however, the project has been stripped off
                // (because it is not required for making the connection).
                AzureDevOpsFilingUri.OriginalString.StartsWith(uri.ToString()).Should().BeTrue();

                // Verify that we received the connection request.
                connectCalled = true;
            });

            // Our GetClientAsync is overridden to provide our low-level ADO mock.
            vssConnectionMock
            .Setup(x => x.GetClientAsync())
            .ReturnsAsync(workItemTrackingHttpClientMock.Object);

            // FIVE. We are required to inject the low level ADO connection wrapper. We are
            //       required to do so due to the complexity of creating and initializing
            //       required objects. MOQ cannot override constructors and (related)
            //       in general cannot instantiate a type without a parameterless ctor.
            //       Even when a concrete class can be instantiated, its various properties
            //       might not be easily stubbed (as for a non-virtual property accessor).
            //       For these cases, we need to insert an interface in our system, and
            //       create wrappers around those concrete types, where the interface is
            //       directly mapped or otherwise adapted to the concrete type's methods.
            //       Moq can then simply manufacture a class that implements that interface
            //       in order to control behaviors.
            //
            //       Rather than introducing a type factory or more sophisticated pattern
            //       of injection, we use the very simple expedient of declaring an internal
            //       property to hold a mock instance. In production, if that property is null,
            //       the system instantiates a wrapper around the standard types for use.

            var adoFilingClient = (AzureDevOpsFilingClient)filer.FilingClient;

            adoFilingClient._vssConection = vssConnectionMock.Object;


            string   sarifLogText    = JsonConvert.SerializeObject(sarifLog);
            SarifLog updatedSarifLog = filer.FileWorkItems(sarifLogText);

            // Did we see all the execution we expected?
            connectCalled.Should().BeTrue();

            int expectedWorkItemsCount = context.GetProperty(ExpectedWorkItemsCount);

            createWorkItemCalled.Should().Be(expectedWorkItemsCount);
            createAttachmentCalled.Should().Be(expectedWorkItemsCount);

            // This property is a naive mechanism to ensure that the code
            // executed comprehensively (i.e., that execution was not limited
            // due to unhandled exceptions). This is required because we have
            // not really implemented a proper async API with appropriate handling
            // for exceptions and other negative conditions. I wouldn't expect this
            // little helper to survive but it closes the loop for the current
            // rudimentary in-flight implementation.
            filer.FilingResult.Should().Be(FilingResult.Succeeded);

            filer.FiledWorkItems.Count.Should().Be(expectedWorkItemsCount);

            foreach (WorkItemModel filedWorkItem in filer.FiledWorkItems)
            {
                // Finally, make sure that our test data flows back properly through the filer.

                filedWorkItem.Attachment.Should().NotBeNull();
                JsonConvert.SerializeObject(filedWorkItem.Attachment.Text).Should().NotBeNull();

                filedWorkItem.Uri.Should().Be(bugUri);
                filedWorkItem.HtmlUri.Should().Be(bugHtmlUri);
            }

            // Validate that we updated the SARIF log with work itme URIs.
            //
            updatedSarifLog.Should().NotBeEquivalentTo(sarifLog);

            foreach (Run run in updatedSarifLog.Runs)
            {
                foreach (Result result in run.Results)
                {
                    result.WorkItemUris.Should().NotBeNull();
                    result.WorkItemUris.Count.Should().Be(1);
                    result.WorkItemUris[0].Should().Be(bugHtmlUri);

                    result.TryGetProperty(SarifWorkItemFiler.PROGRAMMABLE_URIS_PROPERTY_NAME, out List <Uri> programmableUris)
                    .Should().BeTrue();

                    programmableUris.Should().NotBeNull();
                    programmableUris.Count.Should().Be(1);
                    programmableUris[0].Should().Be(bugUri);
                }
            }
        }
Пример #20
0
        private void FileWorkItemsHelper(SarifLog sarifLog, SarifWorkItemContext filingContext, FilingClient filingClient)
        {
            string logGuid = sarifLog.GetProperty<Guid>("guid").ToString();

            // The helper below will initialize the sarif work item model with a copy
            // of the root pipeline filing context. This context will then be initialized
            // based on the current sarif log file that we're processing.
            // First intializes the contexts provider to the value in the current filing client.
            filingContext.CurrentProvider = filingClient.Provider;
            var sarifWorkItemModel = new SarifWorkItemModel(sarifLog, filingContext);

            try
            {
                // Populate the work item with the target organization/repository information.
                // In ADO, certain fields (such as the area path) will defaut to the 
                // project name and so this information is used in at least that context.
                sarifWorkItemModel.OwnerOrAccount = filingClient.AccountOrOrganization;
                sarifWorkItemModel.RepositoryOrProject = filingClient.ProjectOrRepository;

                foreach (SarifWorkItemModelTransformer transformer in sarifWorkItemModel.Context.Transformers)
                {
                    SarifWorkItemModel updatedSarifWorkItemModel = transformer.Transform(sarifWorkItemModel);

                    // If a transformer has set the model to null, that indicates 
                    // it should be pulled from the work flow (i.e., not filed).
                    if (updatedSarifWorkItemModel == null)
                    {
                        Dictionary<string, object> customDimentions = new Dictionary<string, object>();
                        customDimentions.Add("TransformerType", transformer.GetType().FullName);
                        LogMetricsForProcessedModel(sarifLog, sarifWorkItemModel, FilingResult.Canceled, customDimentions);
                        return;
                    }

                    sarifWorkItemModel = updatedSarifWorkItemModel;
                }

                Task<IEnumerable<WorkItemModel>> task = filingClient.FileWorkItems(new[] { sarifWorkItemModel });
                task.Wait();
                this.FiledWorkItems.AddRange(task.Result);


                // IMPORTANT: as we update our partitioned logs, we are actually modifying the input log file 
                // as well. That's because our partitioning is configured to reuse references to existing
                // run and result objects, even though they are partitioned into a separate log file. 
                // This approach also us to update the original log file with the filed work item details
                // without requiring us to build a map of results between the original log and its
                // partioned log files.
                //
                UpdateLogWithWorkItemDetails(sarifLog, sarifWorkItemModel.HtmlUri, sarifWorkItemModel.Uri);

                LogMetricsForProcessedModel(sarifLog, sarifWorkItemModel, FilingResult.Succeeded);
            }
            catch (Exception ex)
            {
                this.Logger.LogError(ex, "An exception was raised filing log '{logGuid}'.", logGuid);

                Dictionary<string, object> customDimentions = new Dictionary<string, object>();
                customDimentions.Add("ExceptionType", ex.GetType().FullName);
                customDimentions.Add("ExceptionMessage", ex.Message);
                customDimentions.Add("ExceptionStackTrace", ex.ToString());
                LogMetricsForProcessedModel(sarifLog, sarifWorkItemModel, FilingResult.ExceptionRaised, customDimentions);
            }
        }
Пример #21
0
        private void TestWorkItemFiler(SarifLog sarifLog, SarifWorkItemContext context, bool adoClient)
        {
            // ONE. Create test data that the low-level ADO client mocks
            //      will flow back to the SARIF work item filer.
            var attachmentReference = new AttachmentReference()
            {
                Id  = Guid.NewGuid(),
                Url = Guid.NewGuid().ToString()
            };

            var workItem = new WorkItem
            {
                Id    = DateTime.UtcNow.Millisecond,
                Links = new ReferenceLinks()
            };

            // The fake URI to the filed item that we'll expect the filer to receive
            string bugUriText     = "https://example.com/" + Guid.NewGuid().ToString();
            string bugHtmlUriText = "https://example.com/" + Guid.NewGuid().ToString();

            Uri bugUri     = new Uri(bugUriText, UriKind.RelativeOrAbsolute);
            Uri bugHtmlUri = new Uri(bugHtmlUriText, UriKind.RelativeOrAbsolute);

            workItem.Url = bugUriText;
            workItem.Links.AddLink("html", bugHtmlUriText);

            // TWO. Reset variables to capture whether we enter all expected client methods.
            ConnectCalled        = false;
            CreateWorkItemCalled = CreateAttachmentCount = UpdateIssueCount = 0;

            // THREE. Create a default mock SARIF filer and client.
            SarifWorkItemFiler filer = CreateMockSarifWorkItemFiler(context).Object;

            // FOUR. Based on which client we are using (ADO or GitHub), create the correct context.
            //       This implies created both the connection mocks and the mocks for filing, updating, and attaching work items.
            //       We are required to put this mock behind an interface due to an inability to mock these types directly.

            FilingClient filingClient;

            if (adoClient == true)
            {
                filingClient = CreateAdoMocksAndFilingClient(attachmentReference, workItem, filer);
            }
            else
            {
                filingClient = CreateGitHubMocksAndFilingClient(bugUriText, bugHtmlUriText, filer);
            }

            string   sarifLogText    = JsonConvert.SerializeObject(sarifLog);
            SarifLog updatedSarifLog = filer.FileWorkItems(sarifLogText);

            // Did we see all the execution we expected?
            ConnectCalled.Should().BeTrue();

            int expectedWorkItemsCount = context.GetProperty(ExpectedWorkItemsCount);

            CreateWorkItemCalled.Should().Be(expectedWorkItemsCount);
            CreateAttachmentCount.Should().Be(adoClient ? expectedWorkItemsCount : 0);

            // This property is a naive mechanism to ensure that the code
            // executed comprehensively (i.e., that execution was not limited
            // due to unhandled exceptions). This is required because we have
            // not really implemented a proper async API with appropriate handling
            // for exceptions and other negative conditions. I wouldn't expect this
            // little helper to survive but it closes the loop for the current
            // rudimentary in-flight implementation.
            filer.FilingResult.Should().Be(FilingResult.Succeeded);

            filer.FiledWorkItems.Count.Should().Be(expectedWorkItemsCount);

            foreach (WorkItemModel filedWorkItem in filer.FiledWorkItems)
            {
                // Finally, make sure that our test data flows back properly through the filer.

                filedWorkItem.Attachment.Should().NotBeNull();
                JsonConvert.SerializeObject(filedWorkItem.Attachment.Text).Should().NotBeNull();

                filedWorkItem.Uri.Should().Be(bugUri);
                filedWorkItem.HtmlUri.Should().Be(bugHtmlUri);
            }

            // Validate that we updated the SARIF log with work itme URIs.
            //
            updatedSarifLog.Should().NotBeEquivalentTo(sarifLog);

            foreach (Run run in updatedSarifLog.Runs)
            {
                foreach (Result result in run.Results)
                {
                    result.WorkItemUris.Should().NotBeNull();
                    result.WorkItemUris.Count.Should().Be(1);
                    result.WorkItemUris[0].Should().Be(bugHtmlUri);

                    result.TryGetProperty(SarifWorkItemFiler.PROGRAMMABLE_URIS_PROPERTY_NAME, out List <Uri> programmableUris)
                    .Should().BeTrue();

                    programmableUris.Should().NotBeNull();
                    programmableUris.Count.Should().Be(1);
                    programmableUris[0].Should().Be(bugUri);
                }
            }
        }
Пример #22
0
 /// <summary>
 /// Initializes a new instance of the <see cref="SarifWorkItemFiler"> class.</see>
 /// </summary>
 /// <param name="filingClient">
 /// A client for communicating with a work item filing host (for example, GitHub or Azure DevOps).
 /// </param>
 /// <param name="filingContext">
 /// A starting context object that configures the work item filing operation. In the
 /// current implementation, this context is copied for each SARIF file (if any) split
 /// from the input log and then further elaborated upon.
 /// </param>
 public SarifWorkItemFiler(SarifWorkItemContext filingContext)
 {
     this.FilingContext = filingContext ?? throw new ArgumentNullException(nameof(filingContext));
     this.FilingClient  = FilingClientFactory.Create(filingContext.ProjectUri);
 }
Пример #23
0
 private static SarifWorkItemFiler CreateWorkItemFiler(SarifWorkItemContext context = null)
 => CreateMockSarifWorkItemFiler(context).Object;
Пример #24
0
        public SarifWorkItemModel FileWorkItemInternal(SarifLog sarifLog, SarifWorkItemContext filingContext, FilingClient filingClient)
        {
            using (Logger.BeginScopeContext(nameof(FileWorkItemInternal)))
            {
                string logGuid = sarifLog.GetProperty <Guid>("guid").ToString();

                // The helper below will initialize the sarif work item model with a copy
                // of the root pipeline filing context. This context will then be initialized
                // based on the current sarif log file that we're processing.
                // First intializes the contexts provider to the value in the current filing client.
                filingContext.CurrentProvider = filingClient.Provider;
                var sarifWorkItemModel = new SarifWorkItemModel(sarifLog, filingContext);

                try
                {
                    // Populate the work item with the target organization/repository information.
                    // In ADO, certain fields (such as the area path) will defaut to the
                    // project name and so this information is used in at least that context.
                    sarifWorkItemModel.OwnerOrAccount      = filingClient.AccountOrOrganization;
                    sarifWorkItemModel.RepositoryOrProject = filingClient.ProjectOrRepository;

                    if (filingContext.SyncWorkItemMetadata)
                    {
                        Task <WorkItemModel> getMetadataTask = filingClient.GetWorkItemMetadata(sarifWorkItemModel);
                        getMetadataTask.Wait();
                        sarifWorkItemModel = (SarifWorkItemModel)getMetadataTask.Result;
                    }

                    using (Logger.BeginScopeContext("RunTransformers"))
                    {
                        foreach (SarifWorkItemModelTransformer transformer in sarifWorkItemModel.Context.Transformers)
                        {
                            SarifWorkItemModel updatedSarifWorkItemModel = transformer.Transform(sarifWorkItemModel);

                            // If a transformer has set the model to null, that indicates
                            // it should be pulled from the work flow (i.e., not filed).
                            if (updatedSarifWorkItemModel == null)
                            {
                                Dictionary <string, object> customDimentions = new Dictionary <string, object>();
                                customDimentions.Add("TransformerType", transformer.GetType().FullName);
                                LogMetricsForProcessedModel(sarifLog, sarifWorkItemModel, FilingResult.Canceled, customDimentions);
                                return(null);
                            }

                            sarifWorkItemModel = updatedSarifWorkItemModel;
                        }
                    }

                    Task <IEnumerable <WorkItemModel> > task = filingClient.FileWorkItems(new[] { sarifWorkItemModel });
                    task.Wait();
                    this.FiledWorkItems.AddRange(task.Result);

                    LogMetricsForProcessedModel(sarifLog, sarifWorkItemModel, FilingResult.Succeeded);
                }
                catch (Exception ex)
                {
                    this.Logger.LogError(ex, "An exception was raised filing log '{logGuid}'.", logGuid);

                    Dictionary <string, object> customDimentions = new Dictionary <string, object>();
                    customDimentions.Add("ExceptionType", ex.GetType().FullName);
                    customDimentions.Add("ExceptionMessage", ex.Message);
                    customDimentions.Add("ExceptionStackTrace", ex.ToString());
                    LogMetricsForProcessedModel(sarifLog, sarifWorkItemModel, FilingResult.ExceptionRaised, customDimentions);
                }

                return(sarifWorkItemModel);
            }
        }