Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
38 changes: 37 additions & 1 deletion FluentTc.Tests/AcceptanceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1194,7 +1194,43 @@ public void GetAllProjects()

// Assert
A.CallTo(() => teamCityCaller.Get<ProjectWrapper>(@"/app/rest/projects/")).MustHaveHappened();
}
}

[TestCase("FluentTC")]
[TestCase("Test 1")]
public void GetProject_ByName(string projectName)
{
// Arrange
var teamCityCaller = CreateTeamCityCaller();
A.CallTo(() => teamCityCaller.GetFormat<ProjectWrapper>(A<string>._, A<object[]>._))
.Returns(new ProjectWrapper { Count = "1", Project = new List<Project> { new Project{Name = projectName} } });
//Act
var result = new RemoteTc().Connect(_ => _.AsGuest(), teamCityCaller)
.GetProject(project => project.Name(projectName));

// Assert
result.Should().NotBeNull();
result.Name.Should().Be(projectName);
}

[TestCase("1234")]
[TestCase("ABcd")]
[TestCase("129@$$jd")]
public void GetProject_ById(string projectId)
{
// Arrange
var teamCityCaller = CreateTeamCityCaller();
A.CallTo(() => teamCityCaller.GetFormat<ProjectWrapper>(A<string>._, A<object[]>._))
.Returns(new ProjectWrapper { Count = "1", Project = new List<Project> { new Project{Id = projectId}} });

//Act
var result = new RemoteTc().Connect(_ => _.AsGuest(), teamCityCaller)
.GetProject(project => project.Name(projectId));

// Assert
result.Should().NotBeNull();
result.Id.Should().Be(projectId);
}

[Test]
public void GetAllBuildConfigurationTemplates()
Expand Down
1 change: 1 addition & 0 deletions FluentTc.Tests/FluentTc.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@
<Compile Include="Locators\TeamCityConfigurationBuilderTests.cs" />
<Compile Include="Locators\UserHavingBuilderTests.cs" />
<Compile Include="Locators\VCSRootEntryBuilderTests.cs" />
<Compile Include="ProjectRetrievalTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RemoteTcTests.cs" />
<Compile Include="AgentsRetrieverTests.cs" />
Expand Down
112 changes: 112 additions & 0 deletions FluentTc.Tests/ProjectRetrievalTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
using System;
using System.Collections.Generic;
using FakeItEasy;
using FluentAssertions;
using FluentTc.Domain;
using FluentTc.Engine;
using FluentTc.Exceptions;
using FluentTc.Locators;
using NUnit.Framework;

namespace FluentTc.Tests
{
[TestFixture]
public class ProjectRetrievalTests
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are acceptance tests and they should reside inside AcceptanceTests

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will move these to AcceptanceTests and will make better overall tests for the method. I started here when I was trying t make more robust tests, and did not move these out.

{
[TestCase("FluentTC")]
[TestCase("Test 1")]
[TestCase("Test!@#$%^&*()")]
public void GetProject_ById(string projectId)
{
// Arrange
var teamCityCaller = A.Fake<ITeamCityCaller>();
A.CallTo(
() =>
teamCityCaller.GetFormat<ProjectWrapper>("/app/rest/projects/{0}",
string.Format("id:{0}", projectId)))
.Returns(new ProjectWrapper
{
Project = new List<Project> { new Project { Id = projectId, Name = "Test 1" } },
Count = "1"
});

var projectsRetriever = new ProjectsRetriever(new BuildProjectHavingBuilderFactory(), teamCityCaller);

// Act
var result = projectsRetriever.GetProject(project => project.Id(projectId));

// Assert
result.Should().NotBeNull();
result.Name.Should().Be("Test 1");
result.Id.Should().Be(projectId);
}

[TestCase("1234")]
[TestCase("ABcd")]
[TestCase("129@$$jd")]
public void GetProject_ByName(string projectName)
{
// Arrange
var teamCityCaller = A.Fake<ITeamCityCaller>();
A.CallTo(
() =>
teamCityCaller.GetFormat<ProjectWrapper>("/app/rest/projects/{0}",
string.Format("name:{0}", projectName)))
.Returns(new ProjectWrapper
{
Project = new List<Project> { new Project { Id = "Project1", Name = projectName } },
Count = "1"
});

var projectsRetriever = new ProjectsRetriever(new BuildProjectHavingBuilderFactory(), teamCityCaller);

// Act
var result = projectsRetriever.GetProject(project => project.Name(projectName));

// Assert
result.Should().NotBeNull();
result.Name.Should().Be(projectName);
result.Id.Should().Be("Project1");
}

[Test]
public void GetProject_MultipleProjects_MoreThanOneProjectFoundExceptionThrown()
{
// Arrange
var teamCityCaller = A.Fake<ITeamCityCaller>();
A.CallTo(
() =>
teamCityCaller.GetFormat<ProjectWrapper>("/app/rest/projects/{0}", "name:exception"))
.Returns(new ProjectWrapper { Project = new List<Project>{new Project(), new Project()}, Count = "2" });

var projectsRetriever = new ProjectsRetriever(new BuildProjectHavingBuilderFactory(), teamCityCaller);

// Act
Action action = () => projectsRetriever.GetProject(project => project.Name("exception"));

// Assert
action.ShouldThrow<MoreThanOneProjectFoundException>();
}

[Test]
public void GetProject_NoProjects_ProjectNotFoundException()
{
// Arrange
var teamCityCaller = A.Fake<ITeamCityCaller>();
A.CallTo(
() =>
teamCityCaller.GetFormat<ProjectWrapper>(
"/app/rest/projects?locator={0}",
A<object[]>.That.IsSameSequenceAs(new[] { "enabled:False" })))
.Returns(new ProjectWrapper { Project = new List<Project>(), Count = "0" });

var projectsRetriever = new ProjectsRetriever(new BuildProjectHavingBuilderFactory(), teamCityCaller);

// Act
Action action = () => projectsRetriever.GetProject(project => project.Name("exception"));

// Assert
action.ShouldThrow<ProjectNotFoundException>();
}
}
}
16 changes: 16 additions & 0 deletions FluentTc/ConnectedTc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,18 @@ BuildConfiguration CreateBuildConfiguration(Action<IBuildProjectHavingBuilder> h
void DisableAgent(Action<IAgentHavingBuilder> having);
void EnableAgent(Action<IAgentHavingBuilder> having);
void AttachBuildConfigurationToTemplate(Action<IBuildConfigurationHavingBuilder> having, string buildTemplateId);
/// <summary>
/// Retrieves a project that has the projectId as an Id.
/// </summary>
/// <param name="projectId">The expected id for the wanted project.</param>
/// <returns>Project</returns>
Project GetProjectById(string projectId);
/// <summary>
/// Retrieves a project that matches having parameter.
/// </summary>
/// <param name="having">Retrieve project that matches the criteria</param>
/// <returns>Project</returns>
Project GetProject(Action<IBuildProjectHavingBuilder> having);
IList<BuildConfiguration> GetBuildConfigurationsRecursively(string projectId);
IList<Project> GetAllProjects();
IList<string> DownloadArtifacts(int buildId, string destinationPath);
Expand Down Expand Up @@ -406,6 +417,11 @@ public Project GetProjectById(string projectId)
return m_ProjectsRetriever.GetProject(projectId);
}

public Project GetProject(Action<IBuildProjectHavingBuilder> having)
{
return m_ProjectsRetriever.GetProject(having);
}

public IList<Project> GetProjects(Action<IBuildProjectHavingBuilder> having)
{
return m_ProjectsRetriever.GetProjects(having);
Expand Down
1 change: 1 addition & 0 deletions FluentTc/Domain/ProjectWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ namespace FluentTc.Domain
{
public class ProjectWrapper
{
public string Count { get; set; }
public List<Project> Project { get; set; }
}
}
36 changes: 26 additions & 10 deletions FluentTc/Engine/ProjectsRetriever.cs
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FluentTc.Domain;
using FluentTc.Locators;
using EasyHttp.Http;
using FluentTc.Exceptions;

namespace FluentTc.Engine
{
internal interface IProjectsRetriever
{
IList<Project> GetProjects(Action<IBuildProjectHavingBuilder> having = null);
Project GetProject(string projectId);

Project GetProject(Action<IBuildProjectHavingBuilder> having = null);
void SetFields(Action<IBuildProjectHavingBuilder> having, Action<IBuildProjectFieldValueBuilder> fields);

}

internal class ProjectsRetriever : IProjectsRetriever
{
private readonly IBuildProjectHavingBuilderFactory m_BuildProjectHavingBuilderFactory;
private readonly ITeamCityCaller m_TeamCityCaller;
private const string TeamCityProjectPrefix = "/app/rest/projects";

public ProjectsRetriever(IBuildProjectHavingBuilderFactory buildProjectHavingBuilderFactory,
ITeamCityCaller teamCityCaller)
Expand All @@ -30,19 +32,21 @@ public ProjectsRetriever(IBuildProjectHavingBuilderFactory buildProjectHavingBui
public IList<Project> GetProjects(Action<IBuildProjectHavingBuilder> having = null)
{
var locator = having == null ? string.Empty : GetLocator(having);
return m_TeamCityCaller.GetFormat<ProjectWrapper>("/app/rest/projects/{0}", locator).Project;
var projects = m_TeamCityCaller.GetFormat<ProjectWrapper>(GetApiCall("/{0}"), locator).Project;
return projects ?? new List<Project>();
}

private string GetLocator(Action<IBuildProjectHavingBuilder> having)
public Project GetProject(string projectId)
{
var buildProjectHavingBuilder = m_BuildProjectHavingBuilderFactory.CreateBuildProjectHavingBuilder();
having(buildProjectHavingBuilder);
return buildProjectHavingBuilder.GetLocator();
return m_TeamCityCaller.GetFormat<Project>(GetApiCall("/id:{0}"), projectId);
}

public Project GetProject(string projectId)
public Project GetProject(Action<IBuildProjectHavingBuilder> having)
{
return m_TeamCityCaller.GetFormat<Project>("/app/rest/projects/id:{0}", projectId);
var projects = GetProjects(having);
if (!projects.Any()) throw new ProjectNotFoundException();
if (projects.Count > 1) throw new MoreThanOneProjectFoundException();
return projects.Single();
}

public void SetFields(Action<IBuildProjectHavingBuilder> having, Action<IBuildProjectFieldValueBuilder> fields)
Expand All @@ -57,8 +61,20 @@ public void SetFields(Action<IBuildProjectHavingBuilder> having, Action<IBuildPr
.ForEach(
f =>
m_TeamCityCaller.PutFormat(f.Value, HttpContentTypes.TextPlain,
"/app/rest/projects/{0}/{1}", projectConfigurationHavingBuilder.GetLocator(), f.Name));
GetApiCall("/{0}/{1}"), projectConfigurationHavingBuilder.GetLocator(), f.Name));
}

private string GetLocator(Action<IBuildProjectHavingBuilder> having)
{
var buildProjectHavingBuilder = m_BuildProjectHavingBuilderFactory.CreateBuildProjectHavingBuilder();
having(buildProjectHavingBuilder);
return buildProjectHavingBuilder.GetLocator();
}

private string GetApiCall(string appendix)
{
var result = TeamCityProjectPrefix + appendix;
return result;
}
}
}
25 changes: 25 additions & 0 deletions FluentTc/Exceptions/MoreThanOneProjectFoundException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;
using System.Runtime.Serialization;

namespace FluentTc.Exceptions
{
[Serializable]
public class MoreThanOneProjectFoundException : Exception
{
public MoreThanOneProjectFoundException()
{
}

public MoreThanOneProjectFoundException(string message) : base(message)
{
}

public MoreThanOneProjectFoundException(string message, Exception innerException) : base(message, innerException)
{
}

protected MoreThanOneProjectFoundException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
}
25 changes: 25 additions & 0 deletions FluentTc/Exceptions/ProjectNotFoundException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;
using System.Runtime.Serialization;

namespace FluentTc.Exceptions
{
[Serializable]
public class ProjectNotFoundException : Exception
{
public ProjectNotFoundException()
{
}

public ProjectNotFoundException(string message) : base(message)
{
}

public ProjectNotFoundException(string message, Exception innerException) : base(message, innerException)
{
}

protected ProjectNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
}
2 changes: 2 additions & 0 deletions FluentTc/FluentTc.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@
<Compile Include="Domain\TestOccurrences.cs" />
<Compile Include="Engine\VCSRootAttacher.cs" />
<Compile Include="Engine\VCSRootCreator.cs" />
<Compile Include="Exceptions\MoreThanOneProjectFoundException.cs" />
<Compile Include="Exceptions\ProjectNotFoundException.cs" />
<Compile Include="Extensions\StringExtensions.cs" />
<Compile Include="Locators\BuildProjectFieldValueBuilder.cs" />
<Compile Include="Locators\BuildState.cs" />
Expand Down
2 changes: 0 additions & 2 deletions FluentTc/RemoteTc.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
using System;
using System.Linq;
using FluentTc.Domain;
using FluentTc.Engine;
using FluentTc.Locators;

namespace FluentTc
Expand Down