Skip to content
Merged
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,5 @@ dist
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

/.vs/
1 change: 1 addition & 0 deletions dotnet/G5e.AzureDevOpsServerMCP.slnx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<Solution>
<Folder Name="/Solution Items/">
<File Path=".gitignore" />
<File Path="Directory.Build.props" />
<File Path="Directory.Packages.props" />
</Folder>
Expand Down
4 changes: 2 additions & 2 deletions dotnet/tests/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
<ItemGroup>
<!-- Testing packages -->
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.8.1" />
<PackageVersion Include="xunit" Version="2.9.3" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
<PackageVersion Include="MSTest.TestFramework" Version="3.8.2" />
<PackageVersion Include="MSTest.TestAdapter" Version="3.8.2" />
<PackageVersion Include="coverlet.collector" Version="10.0.1" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,8 @@
<ItemGroup>
<PackageReference Include="coverlet.collector" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio" />
</ItemGroup>

<ItemGroup>
<Using Include="Xunit" />
<PackageReference Include="MSTest.TestFramework" />
<PackageReference Include="MSTest.TestAdapter" />
</ItemGroup>

<ItemGroup>
Expand All @@ -22,4 +18,4 @@
</None>
</ItemGroup>

</Project>
</Project>
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
using System.Text.Json;
using G5e.AzureDevOpsServerMCP.Application.Services;
using G5e.AzureDevOpsServerMCP.Tools;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace G5e.AzureDevOpsServerMCP.IntegrationTests;

[TestClass]
public class RepositoryToolsFixtureTests
{
[Fact]
[TestMethod]
public async Task CreateFeatureBranch_UsesFixtureBackedService_AndSerializesExpectedShape()
{
// Arrange
Expand All @@ -20,12 +22,12 @@ public async Task CreateFeatureBranch_UsesFixtureBackedService_AndSerializesExpe
using var document = JsonDocument.Parse(json);
var root = document.RootElement;

Assert.Equal("feature/TEST-123", root.GetProperty("branchName").GetString());
Assert.NotEmpty(root.GetProperty("objectId").GetString()!);
Assert.True(root.GetProperty("success").GetBoolean());
Assert.AreEqual("feature/TEST-123", root.GetProperty("branchName").GetString());
Assert.IsFalse(string.IsNullOrEmpty(root.GetProperty("objectId").GetString()));
Assert.IsTrue(root.GetProperty("success").GetBoolean());
}

[Fact]
[TestMethod]
public async Task CreateFeatureBranch_WhenServiceThrows_ReturnsSerializedError()
{
// Arrange
Expand All @@ -39,10 +41,11 @@ public async Task CreateFeatureBranch_WhenServiceThrows_ReturnsSerializedError()
using var document = JsonDocument.Parse(json);
var root = document.RootElement;

Assert.Equal("Branch already exists", root.GetProperty("error").GetString());
Assert.Equal("InvalidOperationException", root.GetProperty("type").GetString());
Assert.AreEqual("Branch already exists", root.GetProperty("error").GetString());
Assert.AreEqual("InvalidOperationException", root.GetProperty("type").GetString());
}
[Fact]

[TestMethod]
public async Task CreatePullRequestForWorkItem_ReturnsSerializedResult()
{
var sut = new RepositoryTools(new FakeRepositoryService());
Expand All @@ -60,13 +63,13 @@ public async Task CreatePullRequestForWorkItem_ReturnsSerializedResult()
using var document = JsonDocument.Parse(json);
var root = document.RootElement;

Assert.Equal(99, root.GetProperty("pullRequestId").GetInt32());
Assert.Equal("Implement TEST-123", root.GetProperty("title").GetString());
Assert.Equal("active", root.GetProperty("status").GetString());
Assert.True(root.GetProperty("success").GetBoolean());
Assert.AreEqual(99, root.GetProperty("pullRequestId").GetInt32());
Assert.AreEqual("Implement TEST-123", root.GetProperty("title").GetString());
Assert.AreEqual("active", root.GetProperty("status").GetString());
Assert.IsTrue(root.GetProperty("success").GetBoolean());
}

[Fact]
[TestMethod]
public async Task CreatePullRequestForWorkItem_WhenServiceThrows_ReturnsSerializedError()
{
var sut = new RepositoryTools(new ThrowingRepositoryService(new InvalidOperationException("source branch not found")));
Expand All @@ -84,41 +87,41 @@ public async Task CreatePullRequestForWorkItem_WhenServiceThrows_ReturnsSerializ
using var document = JsonDocument.Parse(json);
var root = document.RootElement;

Assert.Equal("source branch not found", root.GetProperty("error").GetString());
Assert.Equal("InvalidOperationException", root.GetProperty("type").GetString());
Assert.AreEqual("source branch not found", root.GetProperty("error").GetString());
Assert.AreEqual("InvalidOperationException", root.GetProperty("type").GetString());
}

[Fact]
public async Task LinkBranchToWorkItem_ReturnsSerializedResult()
{
var sut = new RepositoryTools(new FakeRepositoryService());
[TestMethod]
public async Task LinkBranchToWorkItem_ReturnsSerializedResult()
{
var sut = new RepositoryTools(new FakeRepositoryService());

var json = await sut.LinkBranchToWorkItem("DefaultCollection", "TestProject", "TestRepo", "feature/TEST-123", 42);
var json = await sut.LinkBranchToWorkItem("DefaultCollection", "TestProject", "TestRepo", "feature/TEST-123", 42);

using var document = JsonDocument.Parse(json);
var root = document.RootElement;
using var document = JsonDocument.Parse(json);
var root = document.RootElement;

Assert.Equal(42, root.GetProperty("workItemId").GetInt32());
Assert.Equal("feature/TEST-123", root.GetProperty("branchName").GetString());
Assert.Equal("TestRepo", root.GetProperty("repository").GetString());
Assert.True(root.GetProperty("success").GetBoolean());
}
Assert.AreEqual(42, root.GetProperty("workItemId").GetInt32());
Assert.AreEqual("feature/TEST-123", root.GetProperty("branchName").GetString());
Assert.AreEqual("TestRepo", root.GetProperty("repository").GetString());
Assert.IsTrue(root.GetProperty("success").GetBoolean());
}

[Fact]
public async Task LinkBranchToWorkItem_WhenServiceThrows_ReturnsSerializedError()
{
var sut = new RepositoryTools(new ThrowingRepositoryService(new InvalidOperationException("repository not found")));
[TestMethod]
public async Task LinkBranchToWorkItem_WhenServiceThrows_ReturnsSerializedError()
{
var sut = new RepositoryTools(new ThrowingRepositoryService(new InvalidOperationException("repository not found")));

var json = await sut.LinkBranchToWorkItem("DefaultCollection", "TestProject", "TestRepo", "feature/TEST-123", 42);
var json = await sut.LinkBranchToWorkItem("DefaultCollection", "TestProject", "TestRepo", "feature/TEST-123", 42);

using var document = JsonDocument.Parse(json);
var root = document.RootElement;
using var document = JsonDocument.Parse(json);
var root = document.RootElement;

Assert.Equal("repository not found", root.GetProperty("error").GetString());
Assert.Equal("InvalidOperationException", root.GetProperty("type").GetString());
}
Assert.AreEqual("repository not found", root.GetProperty("error").GetString());
Assert.AreEqual("InvalidOperationException", root.GetProperty("type").GetString());
}

private sealed class FakeRepositoryService : IRepositoryService
private sealed class FakeRepositoryService : IRepositoryService
{
public Task<CreateBranchResult> CreateBranchAsync(
string collection,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
using System.Text.Json;
using System.Text.Json;
using G5e.AzureDevOpsServerMCP.Application.Services;
using G5e.AzureDevOpsServerMCP.Tools;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace G5e.AzureDevOpsServerMCP.IntegrationTests;

[TestClass]
public class WorkItemToolsFixtureTests
{
[Fact]
[TestMethod]
public async Task GetWorkItemContext_UsesFixtureBackedService_AndSerializesExpectedShape()
{
var fixturePath = Path.Combine(AppContext.BaseDirectory, "Fixtures", "work-item-context-result.json");
Expand All @@ -20,17 +22,17 @@ public async Task GetWorkItemContext_UsesFixtureBackedService_AndSerializesExpec
var workItem = root.GetProperty("workItem");
var comments = root.GetProperty("comments");

Assert.Equal(1, workItem.GetProperty("id").GetInt32());
Assert.Equal("Als ontwikkelaar wil ik work items ophalen via een MCP server zodat mijn AI-assistent context heeft over mijn taken", workItem.GetProperty("title").GetString());
Assert.Equal("User Story", workItem.GetProperty("type").GetString());
Assert.Equal("New", workItem.GetProperty("state").GetString());
Assert.Equal("Gadeyne Bram", workItem.GetProperty("assignedTo").GetString());
Assert.Equal(1, root.GetProperty("commentCount").GetInt32());
Assert.Equal(1, comments.GetArrayLength());
Assert.Contains("Spike afgerond", comments[0].GetProperty("content").GetString(), StringComparison.OrdinalIgnoreCase);
Assert.AreEqual(1, workItem.GetProperty("id").GetInt32());
Assert.AreEqual("Als ontwikkelaar wil ik work items ophalen via een MCP server zodat mijn AI-assistent context heeft over mijn taken", workItem.GetProperty("title").GetString());
Assert.AreEqual("User Story", workItem.GetProperty("type").GetString());
Assert.AreEqual("New", workItem.GetProperty("state").GetString());
Assert.AreEqual("Gadeyne Bram", workItem.GetProperty("assignedTo").GetString());
Assert.AreEqual(1, root.GetProperty("commentCount").GetInt32());
Assert.AreEqual(1, comments.GetArrayLength());
StringAssert.Contains(comments[0].GetProperty("content").GetString(), "Spike afgerond", StringComparison.OrdinalIgnoreCase);
}

[Fact]
[TestMethod]
public async Task GetWorkItemContext_WhenServiceThrows_ReturnsSerializedError()
{
var sut = new WorkItemTools(new ThrowingWorkItemContextService(new InvalidOperationException("fixture failure")));
Expand All @@ -40,8 +42,8 @@ public async Task GetWorkItemContext_WhenServiceThrows_ReturnsSerializedError()
using var document = JsonDocument.Parse(json);
var root = document.RootElement;

Assert.Equal("fixture failure", root.GetProperty("error").GetString());
Assert.Equal("InvalidOperationException", root.GetProperty("type").GetString());
Assert.AreEqual("fixture failure", root.GetProperty("error").GetString());
Assert.AreEqual("InvalidOperationException", root.GetProperty("type").GetString());
}

private sealed class FixtureBackedWorkItemContextService : IWorkItemContextService
Expand Down Expand Up @@ -98,7 +100,7 @@ public Task<CreateWorkItemResult> CreateWorkItemAsync(string collection, string
=> Task.FromException<CreateWorkItemResult>(_exception);
}

[Fact]
[TestMethod]
public async Task AddWorkItemComment_ReturnsSerializedCommentId()
{
var sut = new WorkItemTools(new FakeAddCommentWorkItemContextService());
Expand All @@ -108,11 +110,11 @@ public async Task AddWorkItemComment_ReturnsSerializedCommentId()
using var document = JsonDocument.Parse(json);
var root = document.RootElement;

Assert.Equal(42, root.GetProperty("commentId").GetInt32());
Assert.True(root.GetProperty("success").GetBoolean());
Assert.AreEqual(42, root.GetProperty("commentId").GetInt32());
Assert.IsTrue(root.GetProperty("success").GetBoolean());
}

[Fact]
[TestMethod]
public async Task AddWorkItemComment_WhenServiceThrows_ReturnsSerializedError()
{
var sut = new WorkItemTools(new ThrowingWorkItemContextService(new InvalidOperationException("comment failed")));
Expand All @@ -122,8 +124,8 @@ public async Task AddWorkItemComment_WhenServiceThrows_ReturnsSerializedError()
using var document = JsonDocument.Parse(json);
var root = document.RootElement;

Assert.Equal("comment failed", root.GetProperty("error").GetString());
Assert.Equal("InvalidOperationException", root.GetProperty("type").GetString());
Assert.AreEqual("comment failed", root.GetProperty("error").GetString());
Assert.AreEqual("InvalidOperationException", root.GetProperty("type").GetString());
}

private sealed class FakeAddCommentWorkItemContextService : IWorkItemContextService
Expand All @@ -141,7 +143,7 @@ public Task<CreateWorkItemResult> CreateWorkItemAsync(string collection, string
=> Task.FromResult(new CreateWorkItemResult { WorkItemId = 3, Title = title, Type = workItemType, Url = string.Empty });
}

[Fact]
[TestMethod]
public async Task UpdateWorkItemComment_ReturnsSerializedCommentDetails()
{
var sut = new WorkItemTools(new FakeAddCommentWorkItemContextService());
Expand All @@ -151,14 +153,14 @@ public async Task UpdateWorkItemComment_ReturnsSerializedCommentDetails()
using var document = JsonDocument.Parse(json);
var root = document.RootElement;

Assert.Equal(100, root.GetProperty("commentId").GetInt32());
Assert.Equal(1, root.GetProperty("workItemId").GetInt32());
Assert.Equal("Updated comment text via MCP", root.GetProperty("text").GetString());
Assert.Equal(2, root.GetProperty("version").GetInt32());
Assert.True(root.GetProperty("success").GetBoolean());
Assert.AreEqual(100, root.GetProperty("commentId").GetInt32());
Assert.AreEqual(1, root.GetProperty("workItemId").GetInt32());
Assert.AreEqual("Updated comment text via MCP", root.GetProperty("text").GetString());
Assert.AreEqual(2, root.GetProperty("version").GetInt32());
Assert.IsTrue(root.GetProperty("success").GetBoolean());
}

[Fact]
[TestMethod]
public async Task UpdateWorkItemComment_WhenServiceThrows_ReturnsSerializedError()
{
var sut = new WorkItemTools(new ThrowingWorkItemContextService(new InvalidOperationException("update failed")));
Expand All @@ -168,45 +170,45 @@ public async Task UpdateWorkItemComment_WhenServiceThrows_ReturnsSerializedError
using var document = JsonDocument.Parse(json);
var root = document.RootElement;

Assert.Equal("update failed", root.GetProperty("error").GetString());
Assert.Equal("InvalidOperationException", root.GetProperty("type").GetString());
Assert.AreEqual("update failed", root.GetProperty("error").GetString());
Assert.AreEqual("InvalidOperationException", root.GetProperty("type").GetString());
}

[Fact]
[TestMethod]
public async Task CreateWorkItem_ReturnsSerializedWorkItemDetails()
{
var sut = new WorkItemTools(new FakeCreateWorkItemService());
var json = await sut.CreateWorkItem("DefaultCollection", "UZG.IZ.PrestIZ", "Task", "New task via MCP");
using var document = JsonDocument.Parse(json);
var root = document.RootElement;
Assert.Equal(99, root.GetProperty("workItemId").GetInt32());
Assert.Equal("New task via MCP", root.GetProperty("title").GetString());
Assert.Equal("Task", root.GetProperty("type").GetString());
Assert.True(root.GetProperty("success").GetBoolean());
Assert.AreEqual(99, root.GetProperty("workItemId").GetInt32());
Assert.AreEqual("New task via MCP", root.GetProperty("title").GetString());
Assert.AreEqual("Task", root.GetProperty("type").GetString());
Assert.IsTrue(root.GetProperty("success").GetBoolean());
}

[Fact]
[TestMethod]
public async Task CreateWorkItem_WithDescription_ReturnsSerializedWorkItem()
{
var sut = new WorkItemTools(new FakeCreateWorkItemService());
var json = await sut.CreateWorkItem("DefaultCollection", "UZG.IZ.PrestIZ", "Bug", "Critical bug", "This is a critical issue that needs fixing");
using var document = JsonDocument.Parse(json);
var root = document.RootElement;
Assert.Equal(99, root.GetProperty("workItemId").GetInt32());
Assert.Equal("Critical bug", root.GetProperty("title").GetString());
Assert.Equal("Bug", root.GetProperty("type").GetString());
Assert.True(root.GetProperty("success").GetBoolean());
Assert.AreEqual(99, root.GetProperty("workItemId").GetInt32());
Assert.AreEqual("Critical bug", root.GetProperty("title").GetString());
Assert.AreEqual("Bug", root.GetProperty("type").GetString());
Assert.IsTrue(root.GetProperty("success").GetBoolean());
}

[Fact]
[TestMethod]
public async Task CreateWorkItem_WhenServiceThrows_ReturnsSerializedError()
{
var sut = new WorkItemTools(new ThrowingWorkItemContextService(new InvalidOperationException("Invalid work item type")));
var json = await sut.CreateWorkItem("DefaultCollection", "UZG.IZ.PrestIZ", "InvalidType", "Test");
using var document = JsonDocument.Parse(json);
var root = document.RootElement;
Assert.Equal("Invalid work item type", root.GetProperty("error").GetString());
Assert.Equal("InvalidOperationException", root.GetProperty("type").GetString());
Assert.AreEqual("Invalid work item type", root.GetProperty("error").GetString());
Assert.AreEqual("InvalidOperationException", root.GetProperty("type").GetString());
}

private sealed class FakeCreateWorkItemService : IWorkItemContextService
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,13 @@
<ItemGroup>
<PackageReference Include="coverlet.collector" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio" />
<PackageReference Include="MSTest.TestFramework" />
<PackageReference Include="MSTest.TestAdapter" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\G5e.AzureDevOpsServerMCP.Application\G5e.AzureDevOpsServerMCP.Application.csproj" />
<ProjectReference Include="..\..\src\G5e.AzureDevOpsServerMCP.Infrastructure.AzureDevOps\G5e.AzureDevOpsServerMCP.Infrastructure.AzureDevOps.csproj" />
</ItemGroup>

<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>

</Project>
</Project>
Loading