Files
nexus/backend-tests/NexusMcpToolsTests.cs
T
AzuTear f5552218bc
CI - Build & Test / Backend (.NET) (push) Successful in 42s
CI - Build & Test / Frontend (Vue/TS) (push) Successful in 2m46s
CI - Build & Test / Security Check (push) Successful in 3s
CI - Build & Test / Deploy Nexus (push) Successful in 56s
feat: ship agent-first mission control v0.2.57
2026-07-31 22:39:47 +02:00

152 lines
5.0 KiB
C#

using Microsoft.Extensions.Logging.Abstractions;
using ModelContextProtocol.Server;
using Nexus.Api.Controllers;
using Nexus.Api.Data;
using Nexus.Api.Models;
using Nexus.Api.Services;
using Xunit;
namespace Nexus.Api.Tests;
public sealed class NexusMcpToolsTests
{
[Fact]
public void NexusMcpTools_RegistersExpectedToolNames()
{
var toolNames = typeof(NexusMcpTools)
.GetMethods()
.Select(method => method.GetCustomAttributes(typeof(McpServerToolAttribute), inherit: false)
.OfType<McpServerToolAttribute>()
.FirstOrDefault())
.Where(attribute => attribute is not null)
.Select(attribute => attribute!.Name ?? string.Empty)
.Order()
.ToArray();
Assert.Equal(
[
"nexus_agent_overview",
"nexus_append_activity",
"nexus_create_child_task",
"nexus_create_task",
"nexus_get_activity",
"nexus_get_agent_proposal",
"nexus_get_board",
"nexus_get_children",
"nexus_get_task",
"nexus_handoff",
"nexus_propose_agent",
"nexus_update_status"
], toolNames);
}
[Fact]
public void NexusMcpTaskState_OnlyContainsCanonicalStates()
{
Assert.Equal(
[
nameof(NexusMcpTaskState.Backlog),
nameof(NexusMcpTaskState.InProgress),
nameof(NexusMcpTaskState.Blocked),
nameof(NexusMcpTaskState.Done),
nameof(NexusMcpTaskState.Review)
], Enum.GetNames<NexusMcpTaskState>());
Assert.DoesNotContain("Delegated", Enum.GetNames<NexusMcpTaskState>());
}
[Fact]
public async Task McpTools_ReadAndWrite_UseBridgeService()
{
await using var fixture = await TaskWorkflowFixture.CreateAsync();
fixture.SetCallerAgent("iris");
var tools = CreateTools(fixture);
var createResult = await tools.CreateTask(
title: "MCP parent",
detail: "Created through MCP tool facade",
priority: "High",
assignedTo: "iris",
ct: CancellationToken.None);
Assert.True(createResult.Ok);
Assert.NotNull(createResult.Data);
Assert.Equal("MCP parent", createResult.Data!.Title);
var activityResult = await tools.AppendActivity(
createResult.Data.Id,
"MCP checkpoint",
"comment",
CancellationToken.None);
Assert.True(activityResult.Ok);
Assert.Equal("MCP checkpoint", activityResult.Data!.Message);
var statusResult = await tools.UpdateStatus(
createResult.Data.Id,
NexusMcpTaskState.InProgress,
CancellationToken.None);
Assert.True(statusResult.Ok);
Assert.Equal(TaskStateHelper.ToStateString(TaskState.InProgress), statusResult.Data!.State);
var board = await tools.GetBoard(CancellationToken.None);
Assert.Contains(board.InProgress, task => task.Id == createResult.Data.Id);
var taskResult = await tools.GetTask(createResult.Data.Id, CancellationToken.None);
Assert.True(taskResult.Ok);
Assert.Equal("MCP parent", taskResult.Data!.Title);
}
[Fact]
public async Task McpTools_UpdateStatus_RejectsUnauthorizedAgent()
{
await using var fixture = await TaskWorkflowFixture.CreateAsync();
var task = await fixture.TaskService.CreateDashboardTaskAsync(
"Programmer cannot move",
"State changes stay with Iris/Bao.",
"iris",
"Normal",
"programmer",
null,
CancellationToken.None);
fixture.SetCallerAgent("programmer");
var tools = CreateTools(fixture);
var result = await tools.UpdateStatus(task.Id, NexusMcpTaskState.Done, CancellationToken.None);
Assert.False(result.Ok);
Assert.Equal("nexus_update_status", result.Command);
Assert.Contains("not authorized", result.Error, StringComparison.OrdinalIgnoreCase);
}
[Fact]
public async Task McpTools_ServiceKey_ResolvesAsNexusSystem()
{
await using var fixture = await TaskWorkflowFixture.CreateAsync();
fixture.HttpContextAccessor.HttpContext = TaskWorkflowFixture.CreateHttpContext(
headers: new Dictionary<string, string>
{
["X-Nexus-Api-Key"] = "test-service-key"
});
var tools = CreateTools(fixture);
var result = await tools.CreateTask(
title: "System MCP task",
assignedTo: "iris",
ct: CancellationToken.None);
Assert.True(result.Ok);
Assert.Equal("bao", result.Data!.Source);
}
private static NexusMcpTools CreateTools(TaskWorkflowFixture fixture)
=> new(
fixture.TaskBridgeService,
fixture.AgentService,
fixture.HttpContextAccessor,
fixture.Configuration,
NullLogger<NexusMcpTools>.Instance);
}