Skip to content

Commit 24c27e6

Browse files
authored
(GH-356) Get work items from build (#357)
1 parent 4ac8137 commit 24c27e6

10 files changed

+308
-31
lines changed

src/Cake.AzureDevOps.Tests/Fakes/FakeAllSetBuildClientFactory.cs

+8
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
namespace Cake.AzureDevOps.Tests.Fakes
22
{
33
using System;
4+
using System.Collections.Generic;
45
using System.Threading;
56
using Cake.AzureDevOps.Authentication;
67
using Microsoft.TeamFoundation.Build.WebApi;
78
using Microsoft.TeamFoundation.Core.WebApi;
9+
using Microsoft.VisualStudio.Services.WebApi;
810
using Moq;
911

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

34+
mock.Setup(arg => arg.GetBuildWorkItemsRefsAsync(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int?>(), null, default))
35+
.ReturnsAsync((string projectName, int buildId, int? top, object userState, CancellationToken token) => new List<ResourceRef>
36+
{
37+
new ResourceRef { Id = "42" },
38+
});
39+
3240
mock = this.Setup(mock);
3341

3442
return mock.Object;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
namespace Cake.AzureDevOps.Tests.Fakes
2+
{
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Threading;
6+
using Cake.AzureDevOps.Authentication;
7+
using Microsoft.TeamFoundation.TestManagement.WebApi;
8+
using Microsoft.TeamFoundation.WorkItemTracking.WebApi;
9+
using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models;
10+
using Moq;
11+
12+
public class FakeAllSetWorkItemTrackingClientFactory : FakeWorkItemTrackingClientFactory
13+
{
14+
public override WorkItemTrackingHttpClient CreateWorkItemTrackingClient(Uri collectionUrl, IAzureDevOpsCredentials credentials)
15+
{
16+
var mock = new Mock<WorkItemTrackingHttpClient>(MockBehavior.Strict, collectionUrl, credentials.ToVssCredentials());
17+
18+
mock.Setup(arg => arg.GetWorkItemsAsync(It.IsAny<IEnumerable<int>>(), It.IsAny<IEnumerable<string>>(), It.IsAny<DateTime?>(), It.IsAny<WorkItemExpand?>(), It.IsAny<WorkItemErrorPolicy?>(), null, default))
19+
.ReturnsAsync((IEnumerable<int> workItemIds, IEnumerable<string> fields, DateTime? asOf, WorkItemExpand? expand, WorkItemErrorPolicy? errorPolicy, object userState, CancellationToken token) =>
20+
{
21+
var result = new List<WorkItem>();
22+
23+
foreach (var workItemId in workItemIds)
24+
{
25+
result.Add(new WorkItem { Id = workItemId });
26+
}
27+
28+
return result;
29+
});
30+
31+
mock = this.Setup(mock);
32+
33+
return mock.Object;
34+
}
35+
}
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
namespace Cake.AzureDevOps.Tests.Fakes
2+
{
3+
using System;
4+
using Cake.AzureDevOps.Authentication;
5+
using Microsoft.TeamFoundation.WorkItemTracking.WebApi;
6+
using Microsoft.VisualStudio.Services.Identity;
7+
using Moq;
8+
9+
public abstract class FakeWorkItemTrackingClientFactory : IWorkItemTrackingClientFactory
10+
{
11+
public abstract WorkItemTrackingHttpClient CreateWorkItemTrackingClient(Uri collectionUrl, IAzureDevOpsCredentials credentials);
12+
13+
public WorkItemTrackingHttpClient CreateWorkItemTrackingClient(Uri collectionUrl, IAzureDevOpsCredentials credentials, out Identity authorizedIdentity)
14+
{
15+
authorizedIdentity = new Identity { ProviderDisplayName = "FakeUser", Id = Guid.NewGuid(), IsActive = true };
16+
return this.CreateWorkItemTrackingClient(collectionUrl, credentials);
17+
}
18+
19+
protected virtual Mock<WorkItemTrackingHttpClient> Setup(Mock<WorkItemTrackingHttpClient> m)
20+
{
21+
return m;
22+
}
23+
}
24+
}

src/Cake.AzureDevOps.Tests/Pipelines/AzureDevOpsBuildTests.cs

+34-5
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ public void Should_Return_Empty_List_If_Build_Is_Invalid()
2323
fixture.Log,
2424
fixture.Settings,
2525
fixture.BuildClientFactory,
26-
fixture.TestManagementClientFactory);
26+
fixture.TestManagementClientFactory,
27+
fixture.WorkItemTrackingClientFactory);
2728

2829
// When
2930
var result = build.GetTestRuns();
@@ -42,7 +43,8 @@ public void Should_Return_Empty_List_If_Build_Does_Not_Contain_Test_Runs()
4243
fixture.Log,
4344
fixture.Settings,
4445
fixture.BuildClientFactory,
45-
fixture.TestManagementClientFactory);
46+
fixture.TestManagementClientFactory,
47+
fixture.WorkItemTrackingClientFactory);
4648

4749
// When
4850
var result = build.GetTestRuns();
@@ -64,7 +66,8 @@ public void Should_Return_List_Of_Test_Runs_With_X_Test_Results_If_X_Is_Less_The
6466
fixture.Log,
6567
fixture.Settings,
6668
fixture.BuildClientFactory,
67-
fixture.TestManagementClientFactory);
69+
fixture.TestManagementClientFactory,
70+
fixture.WorkItemTrackingClientFactory);
6871

6972
// When
7073
var result = build.GetTestRuns(testRunsCount);
@@ -87,7 +90,8 @@ public void Should_Throw_If_Input_Test_Outcomes_Are_Invalid()
8790
fixture.Log,
8891
fixture.Settings,
8992
fixture.BuildClientFactory,
90-
fixture.TestManagementClientFactory);
93+
fixture.TestManagementClientFactory,
94+
fixture.WorkItemTrackingClientFactory);
9195

9296
// When
9397
var result = Record.Exception(() => build.GetTestRuns(null, new string[] { "FakeOutcome" }));
@@ -105,7 +109,8 @@ public void Should_Return_List_Of_Test_Runs_With_Test_Results()
105109
fixture.Log,
106110
fixture.Settings,
107111
fixture.BuildClientFactory,
108-
fixture.TestManagementClientFactory);
112+
fixture.TestManagementClientFactory,
113+
fixture.WorkItemTrackingClientFactory);
109114

110115
// When
111116
var result = build.GetTestRuns();
@@ -126,5 +131,29 @@ public void Should_Return_List_Of_Test_Runs_With_Test_Results()
126131
new AzureDevOpsTestResult { AutomatedTestName = "t3", Outcome = "Passed", ErrorMessage = string.Empty });
127132
}
128133
}
134+
135+
public sealed class TheGetWorkItemsMethod
136+
{
137+
[Fact]
138+
public void Should_Return_List_Of_WorkItems()
139+
{
140+
// Given
141+
var fixture = new BuildFixture(BuildFixture.ValidAzureDevOpsCollectionUrl, "Foo", 42);
142+
var build = new AzureDevOpsBuild(
143+
fixture.Log,
144+
fixture.Settings,
145+
fixture.BuildClientFactory,
146+
fixture.TestManagementClientFactory,
147+
fixture.WorkItemTrackingClientFactory);
148+
149+
// When
150+
var result = build.GetWorkItems();
151+
152+
// Then
153+
result.ShouldNotBeNull();
154+
result.ShouldHaveSingleItem();
155+
result.First().WorkItemId.ShouldBe(42);
156+
}
157+
}
129158
}
130159
}

src/Cake.AzureDevOps.Tests/Pipelines/BuildFixture.cs

+3
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,16 @@ public BuildFixture(string collectionUrl, string projectName, int buildId)
2323

2424
public ITestManagementClientFactory TestManagementClientFactory { get; set; }
2525

26+
public IWorkItemTrackingClientFactory WorkItemTrackingClientFactory { get; set; }
27+
2628
public AzureDevOpsBuildSettings Settings { get; set; }
2729

2830
private void InitialzeFakes()
2931
{
3032
this.Log = new FakeLog();
3133
this.BuildClientFactory = new FakeAllSetBuildClientFactory();
3234
this.TestManagementClientFactory = new FakeAllSetTestManagementClientFactory();
35+
this.WorkItemTrackingClientFactory = new FakeAllSetWorkItemTrackingClientFactory();
3336
}
3437
}
3538
}

src/Cake.AzureDevOps/AzureDevOpsAliases.Pipelines.cs

+103-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
namespace Cake.AzureDevOps
22
{
33
using System.Collections.Generic;
4+
using Cake.AzureDevOps.Boards.WorkItemTracking;
45
using Cake.AzureDevOps.Pipelines;
56
using Cake.Core;
67
using Cake.Core.Annotations;
@@ -47,7 +48,7 @@ public static AzureDevOpsBuild AzureDevOpsBuild(
4748
context.NotNull(nameof(context));
4849
settings.NotNull(nameof(settings));
4950

50-
var build = new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory());
51+
var build = new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory(), new WorkItemTrackingClientFactory());
5152

5253
if (build.HasBuildLoaded)
5354
{
@@ -279,7 +280,7 @@ public static bool AzureDevOpsBuildIsFailing(
279280
settings.NotNull(nameof(settings));
280281

281282
return
282-
new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory())
283+
new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory(), new WorkItemTrackingClientFactory())
283284
.IsBuildFailing();
284285
}
285286

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

329330
return
330-
new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory())
331+
new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory(), new WorkItemTrackingClientFactory())
331332
.GetChanges();
332333
}
333334

335+
/// <summary>
336+
/// Gets the work item ids associated with an Azure Pipelines build.
337+
/// </summary>
338+
/// <param name="context">The context.</param>
339+
/// <param name="settings">Settings for getting the build.</param>
340+
/// <example>
341+
/// <para>Get work item ids associated with an Azure Pipelines build:</para>
342+
/// <code>
343+
/// <![CDATA[
344+
/// var buildSettings =
345+
/// new AzureDevOpsBuildSettings(
346+
/// new Uri("http://myserver:8080/defaultcollection"),
347+
/// "MyProject",
348+
/// 42,
349+
/// AzureDevOpsAuthenticationNtlm());
350+
///
351+
/// var workItemIds =
352+
/// AzureDevOpsBuildWorkItemIds(
353+
/// buildSettings);
354+
///
355+
/// Information("Work item ids:");
356+
/// foreach (var id in workItemIds)
357+
/// {
358+
/// Information(" {0}", id);
359+
/// }
360+
/// ]]>
361+
/// </code>
362+
/// </example>
363+
/// <returns>The work item ids associated with the build.
364+
/// Returns an empty list if build could not be found and
365+
/// <see cref="AzureDevOpsBuildSettings.ThrowExceptionIfBuildCouldNotBeFound"/> is set to <c>false</c>.</returns>
366+
/// <exception cref="AzureDevOpsBuildNotFoundException">If build could not be found and
367+
/// <see cref="AzureDevOpsBuildSettings.ThrowExceptionIfBuildCouldNotBeFound"/> is set to <c>true</c>.</exception>
368+
[CakeMethodAlias]
369+
[CakeAliasCategory("Azure Pipelines")]
370+
[CakeNamespaceImport("Cake.AzureDevOps.Pipelines")]
371+
public static IEnumerable<int> AzureDevOpsBuildWorkItemIds(
372+
this ICakeContext context,
373+
AzureDevOpsBuildSettings settings)
374+
{
375+
context.NotNull(nameof(context));
376+
settings.NotNull(nameof(settings));
377+
378+
return
379+
new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory(), new WorkItemTrackingClientFactory())
380+
.GetWorkItemIds();
381+
}
382+
383+
/// <summary>
384+
/// Gets the work items associated with an Azure Pipelines build.
385+
/// </summary>
386+
/// <param name="context">The context.</param>
387+
/// <param name="settings">Settings for getting the build.</param>
388+
/// <example>
389+
/// <para>Get work items associated with an Azure Pipelines build:</para>
390+
/// <code>
391+
/// <![CDATA[
392+
/// var buildSettings =
393+
/// new AzureDevOpsBuildSettings(
394+
/// new Uri("http://myserver:8080/defaultcollection"),
395+
/// "MyProject",
396+
/// 42,
397+
/// AzureDevOpsAuthenticationNtlm());
398+
///
399+
/// var workItems =
400+
/// AzureDevOpsBuildWorkItems(
401+
/// buildSettings);
402+
///
403+
/// Information("Work item:");
404+
/// foreach (var workItem in workItems)
405+
/// {
406+
/// Information(" {0}: {1}", workItem.Id, workItem.Title);
407+
/// }
408+
/// ]]>
409+
/// </code>
410+
/// </example>
411+
/// <returns>The work items associated with the build.
412+
/// Returns an empty list if build could not be found and
413+
/// <see cref="AzureDevOpsBuildSettings.ThrowExceptionIfBuildCouldNotBeFound"/> is set to <c>false</c>.</returns>
414+
/// <exception cref="AzureDevOpsBuildNotFoundException">If build could not be found and
415+
/// <see cref="AzureDevOpsBuildSettings.ThrowExceptionIfBuildCouldNotBeFound"/> is set to <c>true</c>.</exception>
416+
[CakeMethodAlias]
417+
[CakeAliasCategory("Azure Pipelines")]
418+
[CakeNamespaceImport("Cake.AzureDevOps.Pipelines")]
419+
public static IEnumerable<AzureDevOpsWorkItem> AzureDevOpsBuildWorkItems(
420+
this ICakeContext context,
421+
AzureDevOpsBuildSettings settings)
422+
{
423+
context.NotNull(nameof(context));
424+
settings.NotNull(nameof(settings));
425+
426+
return
427+
new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory(), new WorkItemTrackingClientFactory())
428+
.GetWorkItems();
429+
}
430+
334431
/// <summary>
335432
/// Gets the timeline entries for an Azure Pipelines build.
336433
/// </summary>
@@ -375,7 +472,7 @@ public static IEnumerable<AzureDevOpsTimelineRecord> AzureDevOpsBuildTimelineRec
375472
settings.NotNull(nameof(settings));
376473

377474
return
378-
new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory())
475+
new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory(), new WorkItemTrackingClientFactory())
379476
.GetTimelineRecords();
380477
}
381478

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

425522
return
426-
new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory())
523+
new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory(), new WorkItemTrackingClientFactory())
427524
.GetArtifacts();
428525
}
429526

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

473570
return
474-
new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory())
571+
new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory(), new WorkItemTrackingClientFactory())
475572
.GetTestRuns();
476573
}
477574

src/Cake.AzureDevOps/AzureDevOpsAliases.WorkItemTracking.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public static AzureDevOpsWorkItem AzureDevOpsWorkItem(
6161
/// Make sure the build has the 'Allow Scripts to access OAuth token' option enabled.
6262
/// </summary>
6363
/// <param name="context">The context.</param>
64-
/// <param name="workItemId">ID of the work witem.</param>
64+
/// <param name="workItemId">ID of the work item.</param>
6565
/// <example>
6666
/// <para>Get an Azure DevOps work item:</para>
6767
/// <code>

src/Cake.AzureDevOps/Boards/WorkItemTracking/AzureDevOpsWorkItem.cs

+4-5
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
{
33
using System;
44
using System.Collections.Generic;
5-
using System.Linq;
6-
using Cake.AzureDevOps.Authentication;
75
using Cake.Core.Diagnostics;
86
using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models;
97
using Microsoft.VisualStudio.Services.Common;
@@ -36,15 +34,16 @@ public AzureDevOpsWorkItem(ICakeLog log, AzureDevOpsWorkItemSettings settings)
3634
/// <param name="log">The Cake log context.</param>
3735
/// <param name="settings">Settings for accessing AzureDevOps.</param>
3836
/// <param name="workItem">The work item.</param>
39-
internal AzureDevOpsWorkItem(ICakeLog log, AzureDevOpsWorkItemSettings settings, WorkItem workItem)
37+
/// <param name="workItemTrackingClientFactory">A factory to communicate with work item tracking client.</param>
38+
internal AzureDevOpsWorkItem(ICakeLog log, AzureDevOpsWorkItemSettings settings, WorkItem workItem, IWorkItemTrackingClientFactory workItemTrackingClientFactory)
4039
{
4140
log.NotNull(nameof(log));
4241
settings.NotNull(nameof(settings));
4342
workItem.NotNull(nameof(workItem));
4443

4544
this.log = log;
4645
this.workItem = workItem;
47-
this.workItemTrackingClientFactory = new WorkItemTrackingClientFactory();
46+
this.workItemTrackingClientFactory = workItemTrackingClientFactory;
4847
this.settings = settings;
4948
}
5049

@@ -140,7 +139,7 @@ internal AzureDevOpsWorkItem(
140139
/// <summary>
141140
/// Gets the URL for accessing the web portal of the Azure DevOps collection.
142141
/// </summary>
143-
public Uri CollectionUrl => this.settings.CollectionUrl;
142+
public Uri CollectionUrl => this.settings.CollectionUrl;
144143

145144
/// <summary>
146145
/// Gets the ID of the work item.

0 commit comments

Comments
 (0)