update the TFS build definitions automated way - tfs

we are migrating from TFS2010 to TFS2015 update3.
we would like to update all existing TFS2010 XAML build definitions with the new TFS2015 XAML controllers & agent names. is there an automated way this can be accomplished , as it needs to be done on all existing build definitions.

You can try using TFS API to update the XAML build definitions:
Below sample for your reference: (Reference this thread for details: Updating the build definition for many TFS projects)
Call it like BuildControllerChangeUtil http://tfsserver:8080/tfs/defaultcollection ProjectName where the projectname is a project whose builds have updated build servers.
using Microsoft.TeamFoundation.Build.Client;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using System;
using System.Collections.Generic;
using System.Linq;
namespace BuildControllerChangeUtil
{
class Program
{
static void Main(string[] args)
{
string tfsDefaultcollection = args[0];
string knownGoodProject = args[1];
var tfs = new TfsTeamProjectCollection(new Uri(tfsDefaultcollection));
var buildServer = tfs.GetService<IBuildServer>();
var knownGoodDefs = buildServer.QueryBuildDefinitions(knownGoodProject);
var testController = knownGoodDefs.First(bd => bd.Name.Equals("Test")).BuildController ;
var releaseController = knownGoodDefs.First(bd => bd.Name.Equals("Release")).BuildController ;
foreach (var teamProject in GetTfsProjects( tfsDefaultcollection ))
{
var buildDetails = buildServer.QueryBuildDefinitions(teamProject);
if (!buildDetails.Any())
{
Console.WriteLine("{0} has no build defintions. ", teamProject);
}
foreach (var thisBuild in buildDetails)
{
if (thisBuild.Name.ToUpperInvariant().Contains("TEST"))
{
SetBuildController(teamProject, thisBuild, testController);
}
else if (thisBuild.Name.ToUpperInvariant().Contains("PRODUCTION"))
{
SetBuildController(teamProject, thisBuild, releaseController);
}
else
{
Console.Error.WriteLine( "Team project {0} had an unknown build name {1}",teamProject , thisBuild.Name);
}
}
}
}
private static void SetBuildController(string teamProject, IBuildDefinition thisBuild, IBuildController bc)
{
Console.WriteLine("setting {0} build {1} build controller to {2}", teamProject , thisBuild.Name, bc.Name );
thisBuild.BuildController = bc;
thisBuild.Save();
}
private static IEnumerable<string> GetTfsProjects(string tfsAddress)
{
var tpcAddress = new Uri(tfsAddress);
var tpc = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(tpcAddress);
tpc.Authenticate();
var workItemStore = new WorkItemStore(tpc);
var projectList = (from Project pr in workItemStore.Projects select pr.Name).ToList();
return projectList;
}
}
}

Related

How to ConfigureServices in Asp.Net core WebAPI from another assembly

In a microservice environment I need to construct a framework for Contract based testing.
I'm currently investigatingh how to isolate a single service from it's external dependencies inorder to execute the Provider tests.
What I need to do is:
Keep the WebApi project intact
Start an instance of the WepApi with some config-differences
Mock out selected dependencies
My Solution structure is this:
Case-Solution/
├── src/
| ├──Case.Api
| └──Case.Application
├── test/
| ├──Case.Api.Unittest
| ├──(other tests)
| ├──Case.Pact.CunsumerTest
| └──Case.Pact.ProviderTest
I've read this guide about Pact Tests in dotnet.
Focusing on Case.Pace.ProviderTest, I need to start Case.Api programmatically from Case.Pact.ProviderTest (and another WebHost for Pact it self) and substitute some of it dependencies.
So far I got this:
public class ProviderApiTests : IDisposable
{
private string ProviderUri { get; }
private string PactServiceUri { get; }
private IWebHost PactServiceWebHost { get; }
private IWebHost CasesWebHost { get; }
private ITestOutputHelper OutputHelper { get; }
public static IConfiguration CaseConfiguration { get; } = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT" ?? "Production"}.json", optional: true)
.AddEnvironmentVariables()
.Build();
public ProviderApiTests(ITestOutputHelper output)
{
OutputHelper = output;
ProviderUri = "http://localhost:9000";
PactServiceUri = "http://localhost:9001";
CasesWebHost = WebHost.CreateDefaultBuilder()
.UseUrls(ProviderUri)
.UseStartup<CaseStartup>()
.UseConfiguration(CaseConfiguration)
.Build();
CasesWebHost.Start();
PactServiceWebHost = WebHost.CreateDefaultBuilder()
.UseUrls(PactServiceUri)
.UseStartup<ProviderServerStartup>()
.Build();
PactServiceWebHost.Start();
}
[Fact]
public void EnsureProviderApiHonoursPactWithConsumer()
{
//Arrange
var config = new PactVerifierConfig
{
Outputters = new List<IOutput>
{
new XUnitOutput(OutputHelper)
},
Verbose = true,
CustomHeader = new KeyValuePair<string, string>("X-apikey", "XXX")
};
//Act //Assert
IPactVerifier pactVerifier = new PactVerifier(config);
pactVerifier.ProviderState($"{PactServiceUri}/provider-states")
.ServiceProvider("CaseProvider", ProviderUri)
.HonoursPactWith("CaseConsumer")
.PactUri(#"..\..\..\..\..\pacts\caseconsumer-caseprovider.json")
.Verify();
}
#region IDisposable Support
//IDisposable code
#endregion
}
In the line containing .UseStartup<CaseStartup>() I've simply copied Startup.cs from Case.Api and changed the needed dependencies, which works fine.
But I want a more generic solution. It's doesn't feel right to just copy code and call it a day :) because it is not generic and reusable for the other services.
So I kept digging, and came up with the following.
Add Controllers from other assembly
I realised, that starting a IWebhost, using StartUp from a different assembly, doesn't automagically add controller from that assembly. This needs to be done explicitly. So I did this:
public void ConfigureServices(IServiceCollection services)
{
var assembly = Assembly.Load("Case.Api");
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
.AddApplicationPart(assembly)
.AddControllersAsServices();
......
}
Awesome!!! So far so good.
Next issue:
Replace dependencies:
Reding this article, I created the extension method for replacing dependencies:
public static void Replace<TRegisteredType>(this IServiceCollection services, TRegisteredType replcement)
{
for (var i = 0; i < services.Count; i++)
{
if (services[i].ServiceType == typeof(TRegisteredType))
{
services[i] = new ServiceDescriptor(typeof(TRegisteredType), replcement);
}
}
}
So I'm able to replace the dependencies I want like so: (in this case a QueryHandler)
public void ConfigureServices(IServiceCollection services)
{
.....
var queryHandler = Substitute.For<IQueryHandler<Query, QueryResult>>();
queryHandler.Handle(Arg.Any<Query>()).Returns(new QueryResult(...));
services.Replace(queryHandler);
......
}
But this doesn't solve my issue with copied code.
My wet dream is to be able to use Startup.cs from Case.Api and somehow tweak the DI to replace the dependencies, without having all the redundant code.
Any input would be highly appriciated.
Thanks :)
I had a similar situation also using Pact.net. But I wanted to use TestServer, unfortunately Pact.net does not have support for httpClient ( Verifying pact with in-memory API ). At the end I use a combination of two libraries, probably not the best to validate all scenarios. I took the consumer part of Pact.net to generate the contract and the Verifier part of Pactify to verify if the provider is honoring the contract. The Verifier requires code modification to be compatible with the Pact.net contract though.
I also used your code example to replace dependencies for mocks using moq.
[TestClass]
public class EndpointShouldHonorContract
{
private HttpClient httpClient;
private ApiWebApplicationFactory<Startup> testServerFactory;
Mock<IRepository> repositoryMock =
new Mock<IRepository>();
public EndpointShouldHonorContract()
{
//omitting code... Creation of mock Data Set https://learn.microsoft.com/en-us/ef/ef6/fundamentals/testing/mocking?redirectedfrom=MSDN#testing-query-scenarios
repositoryMock.Setup(s => s.GetQueryable()).Returns(mockDataSet.Object);
testServerFactory = new ApiWebApplicationFactory<Startup>(services =>
{
services.Replace<IRepository>(repositoryMock.Object);
});
httpClient = testServerFactory.CreateClient();
}
[TestMethod]
public async Task HonorContract()
{
// this is my modified Pactify Verifier
await MyModifiedPactify
.VerifyPact
.PactVerifier
.Create(httpClient)
.Between("consumer", "provider")
//location of contract, there is also an option where you can get contracts from a http location
.RetrievedFromFile(#"\Pacts")
.VerifyAsync();
}
}
Web Api Factory : here I use your extension to replace dependencies
public class ApiWebApplicationFactory<TStartUp>
: WebApplicationFactory<TStartUp> where TStartUp: class
{
Action<IServiceCollection> serviceConfiguration { get; }
public ApiWebApplicationFactory(Action<IServiceCollection> serviceConfiguration) : base()
{
this.serviceConfiguration = serviceConfiguration;
}
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
if (this.serviceConfiguration != null)
{
builder.ConfigureServices(this.serviceConfiguration);
}
}
}
internal static class ServiceCollectionExtensions
{
public static void Replace<TRegisteredType>(this IServiceCollection services, TRegisteredType replacement)
{
for (var i = 0; i < services.Count; i++)
{
if (services[i].ServiceType == typeof(TRegisteredType))
{
services[i] = new ServiceDescriptor(typeof(TRegisteredType), replacement);
}
}
}
}

Steps doesn't generate in Extent Report in specflow

I am generating an extent report in specflow, I have written the code and my test execute successfully and report generating but it displays only the feature name no steps name displayed in the report.
Please suggest me what mistake I am doing in the code.
I am attaching a screenshot of my generated report, When I go to report dashboard it displays the number of steps there.
using AventStack.ExtentReports;
using AventStack.ExtentReports.Reporter;
using AventStack.ExtentReports.Reporter.Configuration;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TechTalk.SpecFlow;
namespace Extent_Report
{
[Binding]
[TestFixture]
class Hooks
{
public static ExtentReports extent;
public static ExtentHtmlReporter htmlReporter;
public static ExtentTest test;
// public static object Theme { get; private set; }
static Hooks()
{
if (extent == null)
{
BasicSetUp();
}
}
[BeforeScenario]
public static void Setup()
{
BasePage.Intitialize();
BasePage.Navigate();
test = extent.CreateTest(ScenarioContext.Current.ScenarioInfo.Title);
}
[AfterScenario]
public void TearDown()
{
if (ScenarioContext.Current.TestError != null)
{
var error = ScenarioContext.Current.TestError;
var errormessage = "<pre>" + error.Message + "</pre>";
extent.AddTestRunnerLogs(errormessage);
test.Log(Status.Error, errormessage);
test.Fail(errormessage);
}
BasePage.Quit();
}
[OneTimeSetUp]
public static void BasicSetUp()
{
string pth = System.Reflection.Assembly.GetCallingAssembly().CodeBase;
// string pth = System.IO.Directory.GetCurrentDirectory();
string actualPath = pth.Substring(0, pth.LastIndexOf("bin"));
string projectPath = new Uri(actualPath).LocalPath;
Console.WriteLine(" -----------Project Path--------------------------------------");
Console.WriteLine(projectPath);
string reportPath = projectPath + "Reports\\TestExecutionRunReport.html";
// Console.WriteLine("Report Path is " + reportPath);
htmlReporter = new ExtentHtmlReporter(reportPath);
htmlReporter.Configuration().Theme = Theme.Dark;
htmlReporter.Configuration().DocumentTitle = "SpecFlow Test Resport Document";
htmlReporter.Configuration().ReportName = "Feature Run Results";
extent = new ExtentReports();
extent.AttachReporter(htmlReporter);
//extent.LoadConfig(projectPath + "Extent-Config.xml");
}
[AfterFeature()]
public static void EndReport()
{
extent.Flush();
}
}
}
Reference:
You need to use hook [After step] or [Before step] and add below content to it
test = test.info(ScenarioStepContext.Current.StepInfo.Text);
you can also manipulate and provide more information in it if required.

Read Process Template Version and TypeID in an ISubscriber plugin for TFS

I'm setting up a TFS ISubscriber plugin and I want to be able to decide whether to trigger based on the installed Process Template Name (DONE), TypeID and Version.
The code to read the name is relatively straightforward:
var ics = context.GetService<ICommonStructureService>();
string ProjectName = string.Empty;
string ProjectState = String.Empty;
int templateId = 0;
CommonStructureProjectProperty[] ProjectProperties = null;
ics.GetProjectProperties(context, projectUri.ToString(), out ProjectName, out ProjectState, out ProjectProperties);
// The Projectproperties contains a property called "Process Template", holding the name.
But I can't find a way to read the other properties... I've nicked this code from looking at the TFS assemblies using Reflector, but it always returns Unknown:
private ArtifactSpec GetProcessTemplateVersionSpec(string projectUri)
{
var commonService = this.context.GetService<CommonStructureService>();
Guid guid = commonService.GetProject(this.context, projectUri).ToProjectReference().Id;
return new ArtifactSpec(ArtifactKinds.ProcessTemplate, guid.ToByteArray(), 0);
}
public ProcessTemplateVersion GetCurrentProjectProcessVersion(Uri projectUri)
{
return this.GetProjectProcessVersion(projectUri.AbsoluteUri, ProcessTemplateVersionPropertyNames.CurrentVersion);
}
public ProcessTemplateVersion GetCreationProjectProcessVersion(Uri projectUri)
{
return this.GetProjectProcessVersion(projectUri.AbsoluteUri, ProcessTemplateVersionPropertyNames.CreationVersion);
}
private ProcessTemplateVersion GetProjectProcessVersion(string projectUri, string versionPropertyName)
{
ArtifactSpec processTemplateVersionSpec = GetProcessTemplateVersionSpec(projectUri);
ProcessTemplateVersion unknown = ProcessTemplateVersion.Unknown;
using (TeamFoundationDataReader reader = context.GetService<TeamFoundationPropertyService>().GetProperties(context, processTemplateVersionSpec, new string[] { versionPropertyName }))
{
foreach (ArtifactPropertyValue value2 in reader)
{
foreach (PropertyValue value3 in value2.PropertyValues)
{
return TeamFoundationSerializationUtility.Deserialize<ProcessTemplateVersion>(value3.Value as string);
}
return unknown;
}
return unknown;
}
}
Even worse, I'd also like to be able to do this from the client Object Model, but that seems even harder.
After consulting with the Microsoft Product Team I'm sad to report that this property is currently only supported by Visual Studio Online and that the values are not even stored for on-premise instances.
There is a sort-of work around from Rene van Osnabrugge to copy these values and place them in the "standard" project properties.
Put the following in your Classification.xml of each process template:
<properties>
<property name="MSPROJ" value="Classification\FieldMapping.xml" isFile="true" />
<property name="Process Template" value="Microsoft Visual Studio Scrum 2.2" />
<property name="Create Version" value="Custom Text is allowed here" />
<property name="Current Version" value="Custom Text is allowed here" />
</properties>
And use this code to read it afterwards:
string uri = #"http://myServer:8080/tfs/defaultcollection";
string teamProjectName = "myTeamProject";
// Get the team project
var tpc = new TfsTeamProjectCollection(new Uri(uri));
tpc.Authenticate();
var ics = tpc.GetService<ICommonStructureService>();
var teamProject = ics.GetProjectFromName(teamProjectName);
// Read the properties
string projectName = string.Empty;
string projectState = string.Empty;
int templateId = 0;
ProjectProperty[] projectProperties = null;
ics.GetProjectProperties(teamProject.Uri, out projectName, out projectState, out templateId, out projectProperties);
// Return the properties
string processtemplate = projectProperties.Where(p => (p.Name == "Process Template")).Select(p => p.Value).FirstOrDefault();
string currentVersion = projectProperties.Where(p => (p.Name == "Current version")).Select(p => p.Value).FirstOrDefault();
string createVersion = projectProperties.Where(p => (p.Name == "Create version")).Select(p => p.Value).FirstOrDefault();
//Update the properties
projectProperties.Where(p => (p.Name == "Current version")).First().Value = "MS Scrum 2.2 - Custom 2.3";
ics.UpdateProjectProperties(teamProject.Uri, projectState, projectProperties);

Get a specific TestSuite by Id using the TFS API

I am trying to get a specific TestSuite using the TFS API for a TestPlan.
The TestSuite could exist anywhere within a TestSuite hierarchy, so, of course I could write a recursive function. I want something more efficient however.
Is there a method I am missing, or maybe a query that I could write?
If you already know the testSuiteId things are quite straightforward. You only need to know the name of your TeamProject teamProjectName:
using System;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.TestManagement.Client;
namespace GetTestSuite
{
class Program
{
static void Main()
{
int testSuiteId = 555;
const string teamProjectName = "myTeamProjectName";
var tpc =
TfsTeamProjectCollectionFactory.GetTeamProjectCollection(
new Uri("http://tfsURI"));
var tstService = (ITestManagementService)tpc.GetService(typeof(ITestManagementService));
var tProject = tstService.GetTeamProject(teamProjectName);
var myTestSuite = tProject.TestSuites.Find(testSuiteId);
}
}
}
If you don't, you probably need to go for a solution similar to the one presented here (it's a S.Raiten post), where recursion does come into picture. Access to a testPlanId is assumed:
using System;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.TestManagement.Client;
namespace GetTestSuite
{
class Program
{
static void Main()
{
int testPlanId = 555;
const string teamProjectName = "myTeamProjectName";
var tpc =
TfsTeamProjectCollectionFactory.GetTeamProjectCollection(
new Uri("http://tfsURI"));
var tstService = (ITestManagementService)tpc.GetService(typeof(ITestManagementService));
var tProject = tstService.GetTeamProject(teamProjectName);
var myTestPlan = tProject.TestPlans.Find(testPlanId);
GetPlanSuites(myTestPlan.RootSuite.Entries);
}
public static void GetPlanSuites(ITestSuiteEntryCollection suites)
{
foreach (ITestSuiteEntry suiteEntry in suites)
{
Console.WriteLine(suiteEntry.Id);
var suite = suiteEntry.TestSuite as IStaticTestSuite;
if (suite != null)
{
if (suite.Entries.Count > 0)
GetPlanSuites(suite.Entries);
}
}
}
}
}

TFS: How can I automatically close matching work items on successful build?

We are using continuous integration as part of our build automation. For every check in, the tfs build server builds the project and deploys to our web servers on success.
When the build fails, it automatically creates a new Bug with the details of the build failure.
Due to CI and the activity on the server, this might result in 10 or 20 failure work items before the build starts succeeding again.
So, I have two options. I'd like to either have the build process see if an open work item already exists for a build failure and just add details to that; OR, I'd like the build server to close all of the build failure items automatically when it starts working again.
Any ideas?
You can create a MSBuild Task to do either of these options. Here is a similar piece of code I use to get you started but since I don't know the details of your work item or process you will have to change it.
This code takes all of the work items associated with a build and updates their status.
If you select your first option you can just change the UpdateWorkItemStatus method and update any existing WIs. For the Second method you will need to do a bit more work as you need to look up the prior build rather than take it as a input.
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Build.Utilities;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using Microsoft.Build.Framework;
using Microsoft.TeamFoundation.Build;
using Microsoft.TeamFoundation.Build.Client;
namespace Nowcom.TeamBuild.Tasks
{
public class UpdateWorkItemState: Task
{
private IBuildDetail _Build;
private void test()
{
TeamFoundationServerUrl = "Teamserver";
BuildUri = "vstfs:///Build/Build/1741";
Execute();
}
public override bool Execute()
{
bool result = true;
try
{
TeamFoundationServer tfs = TeamFoundationServerFactory.GetServer(TeamFoundationServerUrl, new UICredentialsProvider());
tfs.EnsureAuthenticated();
WorkItemStore store = (WorkItemStore)tfs.GetService(typeof(WorkItemStore));
IBuildServer buildServer = (IBuildServer)tfs.GetService(typeof(IBuildServer));
_Build = buildServer.GetAllBuildDetails(new Uri(BuildUri));
//add build step
IBuildStep buildStep = InformationNodeConverters.AddBuildStep(_Build, "UpdateWorkItemStatus", "Updating Work Item Status");
try
{
Log.LogMessageFromText(string.Format("Build Number: {0}", _Build.BuildNumber), MessageImportance.Normal);
List<IWorkItemSummary> assocWorkItems = InformationNodeConverters.GetAssociatedWorkItems(_Build);
// update work item status
UpdateWorkItemStatus(store, assocWorkItems, "Open", "Resolved");
SaveWorkItems(store, assocWorkItems);
}
catch (Exception)
{
UpdateBuildStep(buildStep, false);
throw;
}
UpdateBuildStep(buildStep, result);
}
catch (Exception e)
{
result = false;
BuildErrorEventArgs eventArgs;
eventArgs = new BuildErrorEventArgs("", "", BuildEngine.ProjectFileOfTaskNode, BuildEngine.LineNumberOfTaskNode, BuildEngine.ColumnNumberOfTaskNode, 0, 0, string.Format("UpdateWorkItemState failed: {0}", e.Message), "", "");
BuildEngine.LogErrorEvent(eventArgs);
throw;
}
return result;
}
private static void SaveWorkItems(WorkItemStore store, List<IWorkItemSummary> assocWorkItems)
{
foreach (IWorkItemSummary w in assocWorkItems)
{
WorkItem wi = store.GetWorkItem(w.WorkItemId);
if (wi.IsDirty)
{
wi.Save();
}
}
}
// check in this routine if the workitem is a bug created by your CI process. Check by title or assigned to or description depending on your process.
private void UpdateWorkItemStatus(WorkItemStore store, List<IWorkItemSummary> assocWorkItems, string oldState, string newState)
{
foreach (IWorkItemSummary w in assocWorkItems)
{
Log.LogMessageFromText(string.Format("Updating Workitem Id {0}", w.WorkItemId), MessageImportance.Normal);
WorkItem wi = store.GetWorkItem(w.WorkItemId);
if (wi.Fields.Contains("Microsoft.VSTS.Build.IntegrationBuild") && wi.State != "Resolved")
{
wi.Fields["Microsoft.VSTS.Build.IntegrationBuild"].Value =_Build.BuildNumber;
}
if (wi.State == oldState)
{
wi.State = newState;
foreach (Field field in wi.Fields)
{
if (!field.IsValid)
{
break;
}
}
}
if (wi.IsDirty)
{
wi.Save();
}
}
}
private void UpdateBuildStep(IBuildStep step, bool result)
{
step.Status = result ? BuildStepStatus.Succeeded : BuildStepStatus.Failed;
step.FinishTime = DateTime.Now;
step.Save();
}
[Required]
public string BuildUri { get; set; }
[Required]
public string TeamFoundationServerUrl {get; set;}
}
}
< UpdateWorkItemState
TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
BuildUri="$(BuildUri)"
ContinueOnError="false"/>

Resources