Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(GH-356) Get work items from build #357

Merged
merged 6 commits into from
Sep 30, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
namespace Cake.AzureDevOps.Tests.Fakes
{
using System;
using System.Collections.Generic;
using System.Threading;
using Cake.AzureDevOps.Authentication;
using Microsoft.TeamFoundation.Build.WebApi;
using Microsoft.TeamFoundation.Core.WebApi;
using Microsoft.VisualStudio.Services.WebApi;
using Moq;

public class FakeAllSetBuildClientFactory : FakeBuildClientFactory
Expand All @@ -29,6 +31,12 @@ public override BuildHttpClient CreateBuildClient(Uri collectionUrl, IAzureDevOp
Project = new TeamProjectReference { Name = projectName },
});

mock.Setup(arg => arg.GetBuildWorkItemsRefsAsync(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int?>(), null, default))
.ReturnsAsync((string projectName, int buildId, int? top, object userState, CancellationToken token) => new List<ResourceRef>
{
new ResourceRef { Id = "42" },
});

mock = this.Setup(mock);

return mock.Object;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
namespace Cake.AzureDevOps.Tests.Fakes
{
using System;
using System.Collections.Generic;
using System.Threading;
using Cake.AzureDevOps.Authentication;
using Microsoft.TeamFoundation.TestManagement.WebApi;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models;
using Moq;

public class FakeAllSetWorkItemTrackingClientFactory : FakeWorkItemTrackingClientFactory
{
public override WorkItemTrackingHttpClient CreateWorkItemTrackingClient(Uri collectionUrl, IAzureDevOpsCredentials credentials)
{
var mock = new Mock<WorkItemTrackingHttpClient>(MockBehavior.Strict, collectionUrl, credentials.ToVssCredentials());

mock.Setup(arg => arg.GetWorkItemsAsync(It.IsAny<IEnumerable<int>>(), It.IsAny<IEnumerable<string>>(), It.IsAny<DateTime?>(), It.IsAny<WorkItemExpand?>(), It.IsAny<WorkItemErrorPolicy?>(), null, default))
.ReturnsAsync((IEnumerable<int> workItemIds, IEnumerable<string> fields, DateTime? asOf, WorkItemExpand? expand, WorkItemErrorPolicy? errorPolicy, object userState, CancellationToken token) =>
{
var result = new List<WorkItem>();

foreach (var workItemId in workItemIds)
{
result.Add(new WorkItem { Id = workItemId });
}

return result;
});

mock = this.Setup(mock);

return mock.Object;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace Cake.AzureDevOps.Tests.Fakes
{
using System;
using Cake.AzureDevOps.Authentication;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi;
using Microsoft.VisualStudio.Services.Identity;
using Moq;

public abstract class FakeWorkItemTrackingClientFactory : IWorkItemTrackingClientFactory
{
public abstract WorkItemTrackingHttpClient CreateWorkItemTrackingClient(Uri collectionUrl, IAzureDevOpsCredentials credentials);

public WorkItemTrackingHttpClient CreateWorkItemTrackingClient(Uri collectionUrl, IAzureDevOpsCredentials credentials, out Identity authorizedIdentity)
{
authorizedIdentity = new Identity { ProviderDisplayName = "FakeUser", Id = Guid.NewGuid(), IsActive = true };
return this.CreateWorkItemTrackingClient(collectionUrl, credentials);
}

protected virtual Mock<WorkItemTrackingHttpClient> Setup(Mock<WorkItemTrackingHttpClient> m)
{
return m;
}
}
}
39 changes: 34 additions & 5 deletions src/Cake.AzureDevOps.Tests/Pipelines/AzureDevOpsBuildTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ public void Should_Return_Empty_List_If_Build_Is_Invalid()
fixture.Log,
fixture.Settings,
fixture.BuildClientFactory,
fixture.TestManagementClientFactory);
fixture.TestManagementClientFactory,
fixture.WorkItemTrackingClientFactory);

// When
var result = build.GetTestRuns();
Expand All @@ -42,7 +43,8 @@ public void Should_Return_Empty_List_If_Build_Does_Not_Contain_Test_Runs()
fixture.Log,
fixture.Settings,
fixture.BuildClientFactory,
fixture.TestManagementClientFactory);
fixture.TestManagementClientFactory,
fixture.WorkItemTrackingClientFactory);

// When
var result = build.GetTestRuns();
Expand All @@ -64,7 +66,8 @@ public void Should_Return_List_Of_Test_Runs_With_X_Test_Results_If_X_Is_Less_The
fixture.Log,
fixture.Settings,
fixture.BuildClientFactory,
fixture.TestManagementClientFactory);
fixture.TestManagementClientFactory,
fixture.WorkItemTrackingClientFactory);

// When
var result = build.GetTestRuns(testRunsCount);
Expand All @@ -87,7 +90,8 @@ public void Should_Throw_If_Input_Test_Outcomes_Are_Invalid()
fixture.Log,
fixture.Settings,
fixture.BuildClientFactory,
fixture.TestManagementClientFactory);
fixture.TestManagementClientFactory,
fixture.WorkItemTrackingClientFactory);

// When
var result = Record.Exception(() => build.GetTestRuns(null, new string[] { "FakeOutcome" }));
Expand All @@ -105,7 +109,8 @@ public void Should_Return_List_Of_Test_Runs_With_Test_Results()
fixture.Log,
fixture.Settings,
fixture.BuildClientFactory,
fixture.TestManagementClientFactory);
fixture.TestManagementClientFactory,
fixture.WorkItemTrackingClientFactory);

// When
var result = build.GetTestRuns();
Expand All @@ -126,5 +131,29 @@ public void Should_Return_List_Of_Test_Runs_With_Test_Results()
new AzureDevOpsTestResult { AutomatedTestName = "t3", Outcome = "Passed", ErrorMessage = string.Empty });
}
}

public sealed class TheGetWorkItemsMethod
{
[Fact]
public void Should_Return_List_Of_WorkItems()
{
// Given
var fixture = new BuildFixture(BuildFixture.ValidAzureDevOpsCollectionUrl, "Foo", 42);
var build = new AzureDevOpsBuild(
fixture.Log,
fixture.Settings,
fixture.BuildClientFactory,
fixture.TestManagementClientFactory,
fixture.WorkItemTrackingClientFactory);

// When
var result = build.GetWorkItems();

// Then
result.ShouldNotBeNull();
result.ShouldHaveSingleItem();
result.First().WorkItemId.ShouldBe(42);
}
}
}
}
3 changes: 3 additions & 0 deletions src/Cake.AzureDevOps.Tests/Pipelines/BuildFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,16 @@ public BuildFixture(string collectionUrl, string projectName, int buildId)

public ITestManagementClientFactory TestManagementClientFactory { get; set; }

public IWorkItemTrackingClientFactory WorkItemTrackingClientFactory { get; set; }

public AzureDevOpsBuildSettings Settings { get; set; }

private void InitialzeFakes()
{
this.Log = new FakeLog();
this.BuildClientFactory = new FakeAllSetBuildClientFactory();
this.TestManagementClientFactory = new FakeAllSetTestManagementClientFactory();
this.WorkItemTrackingClientFactory = new FakeAllSetWorkItemTrackingClientFactory();
}
}
}
109 changes: 103 additions & 6 deletions src/Cake.AzureDevOps/AzureDevOpsAliases.Pipelines.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
namespace Cake.AzureDevOps
{
using System.Collections.Generic;
using Cake.AzureDevOps.Boards.WorkItemTracking;
using Cake.AzureDevOps.Pipelines;
using Cake.Core;
using Cake.Core.Annotations;
Expand Down Expand Up @@ -47,7 +48,7 @@ public static AzureDevOpsBuild AzureDevOpsBuild(
context.NotNull(nameof(context));
settings.NotNull(nameof(settings));

var build = new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory());
var build = new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory(), new WorkItemTrackingClientFactory());

if (build.HasBuildLoaded)
{
Expand Down Expand Up @@ -279,7 +280,7 @@ public static bool AzureDevOpsBuildIsFailing(
settings.NotNull(nameof(settings));

return
new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory())
new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory(), new WorkItemTrackingClientFactory())
.IsBuildFailing();
}

Expand Down Expand Up @@ -327,10 +328,106 @@ public static IEnumerable<AzureDevOpsChange> AzureDevOpsBuildChanges(
settings.NotNull(nameof(settings));

return
new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory())
new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory(), new WorkItemTrackingClientFactory())
.GetChanges();
}

/// <summary>
/// Gets the work item ids associated with an Azure Pipelines build.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="settings">Settings for getting the build.</param>
/// <example>
/// <para>Get work item ids associated with an Azure Pipelines build:</para>
/// <code>
/// <![CDATA[
/// var buildSettings =
/// new AzureDevOpsBuildSettings(
/// new Uri("http://myserver:8080/defaultcollection"),
/// "MyProject",
/// 42,
/// AzureDevOpsAuthenticationNtlm());
///
/// var workItemIds =
/// AzureDevOpsBuildWorkItemIds(
/// buildSettings);
///
/// Information("Work item ids:");
/// foreach (var id in workItemIds)
/// {
/// Information(" {0}", id);
/// }
/// ]]>
/// </code>
/// </example>
/// <returns>The work item ids associated with the build.
/// Returns an empty list if build could not be found and
/// <see cref="AzureDevOpsBuildSettings.ThrowExceptionIfBuildCouldNotBeFound"/> is set to <c>false</c>.</returns>
/// <exception cref="AzureDevOpsBuildNotFoundException">If build could not be found and
/// <see cref="AzureDevOpsBuildSettings.ThrowExceptionIfBuildCouldNotBeFound"/> is set to <c>true</c>.</exception>
[CakeMethodAlias]
[CakeAliasCategory("Azure Pipelines")]
[CakeNamespaceImport("Cake.AzureDevOps.Pipelines")]
public static IEnumerable<int> AzureDevOpsBuildWorkItemIds(
this ICakeContext context,
AzureDevOpsBuildSettings settings)
{
context.NotNull(nameof(context));
settings.NotNull(nameof(settings));

return
new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory(), new WorkItemTrackingClientFactory())
.GetWorkItemIds();
}

/// <summary>
/// Gets the work items associated with an Azure Pipelines build.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="settings">Settings for getting the build.</param>
/// <example>
/// <para>Get work items associated with an Azure Pipelines build:</para>
/// <code>
/// <![CDATA[
/// var buildSettings =
/// new AzureDevOpsBuildSettings(
/// new Uri("http://myserver:8080/defaultcollection"),
/// "MyProject",
/// 42,
/// AzureDevOpsAuthenticationNtlm());
///
/// var workItems =
/// AzureDevOpsBuildWorkItems(
/// buildSettings);
///
/// Information("Work item:");
/// foreach (var workItem in workItems)
/// {
/// Information(" {0}: {1}", workItem.Id, workItem.Title);
/// }
/// ]]>
/// </code>
/// </example>
/// <returns>The work item ids associated with the build.
pascalberger marked this conversation as resolved.
Show resolved Hide resolved
/// Returns an empty list if build could not be found and
/// <see cref="AzureDevOpsBuildSettings.ThrowExceptionIfBuildCouldNotBeFound"/> is set to <c>false</c>.</returns>
/// <exception cref="AzureDevOpsBuildNotFoundException">If build could not be found and
/// <see cref="AzureDevOpsBuildSettings.ThrowExceptionIfBuildCouldNotBeFound"/> is set to <c>true</c>.</exception>
[CakeMethodAlias]
[CakeAliasCategory("Azure Pipelines")]
[CakeNamespaceImport("Cake.AzureDevOps.Pipelines")]
public static IEnumerable<AzureDevOpsWorkItem> AzureDevOpsBuildWorkItems(
this ICakeContext context,
AzureDevOpsBuildSettings settings)
{
context.NotNull(nameof(context));
settings.NotNull(nameof(settings));

return
new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory(), new WorkItemTrackingClientFactory())
.GetWorkItems();
}

/// <summary>
/// Gets the timeline entries for an Azure Pipelines build.
/// </summary>
Expand Down Expand Up @@ -375,7 +472,7 @@ public static IEnumerable<AzureDevOpsTimelineRecord> AzureDevOpsBuildTimelineRec
settings.NotNull(nameof(settings));

return
new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory())
new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory(), new WorkItemTrackingClientFactory())
.GetTimelineRecords();
}

Expand Down Expand Up @@ -423,7 +520,7 @@ public static IEnumerable<AzureDevOpsBuildArtifact> AzureDevOpsBuildArtifacts(
settings.NotNull(nameof(settings));

return
new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory())
new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory(), new WorkItemTrackingClientFactory())
.GetArtifacts();
}

Expand Down Expand Up @@ -471,7 +568,7 @@ public static IEnumerable<AzureDevOpsTestRun> AzureDevOpsBuildTestRuns(
settings.NotNull(nameof(settings));

return
new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory())
new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory(), new WorkItemTrackingClientFactory())
.GetTestRuns();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public static AzureDevOpsWorkItem AzureDevOpsWorkItem(
/// Make sure the build has the 'Allow Scripts to access OAuth token' option enabled.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="workItemId">ID of the work witem.</param>
/// <param name="workItemId">ID of the work item.</param>
/// <example>
/// <para>Get an Azure DevOps work item:</para>
/// <code>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
{
using System;
using System.Collections.Generic;
using System.Linq;
using Cake.AzureDevOps.Authentication;
using Cake.Core.Diagnostics;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models;
using Microsoft.VisualStudio.Services.Common;
Expand Down Expand Up @@ -36,15 +34,16 @@ public AzureDevOpsWorkItem(ICakeLog log, AzureDevOpsWorkItemSettings settings)
/// <param name="log">The Cake log context.</param>
/// <param name="settings">Settings for accessing AzureDevOps.</param>
/// <param name="workItem">The work item.</param>
internal AzureDevOpsWorkItem(ICakeLog log, AzureDevOpsWorkItemSettings settings, WorkItem workItem)
/// <param name="workItemTrackingClientFactory">A factory to communicate with work item tracking client.</param>
internal AzureDevOpsWorkItem(ICakeLog log, AzureDevOpsWorkItemSettings settings, WorkItem workItem, IWorkItemTrackingClientFactory workItemTrackingClientFactory)
{
log.NotNull(nameof(log));
settings.NotNull(nameof(settings));
workItem.NotNull(nameof(workItem));

this.log = log;
this.workItem = workItem;
this.workItemTrackingClientFactory = new WorkItemTrackingClientFactory();
this.workItemTrackingClientFactory = workItemTrackingClientFactory;
this.settings = settings;
}

Expand Down Expand Up @@ -140,7 +139,7 @@ internal AzureDevOpsWorkItem(
/// <summary>
/// Gets the URL for accessing the web portal of the Azure DevOps collection.
/// </summary>
public Uri CollectionUrl => this.settings.CollectionUrl;
public Uri CollectionUrl => this.settings.CollectionUrl;

/// <summary>
/// Gets the ID of the work item.
Expand Down
Loading