应文浩wenhao.ying@xiaobao100.com bce2612b64 feat: 完善具体实现
2026-04-06 15:25:34 +08:00

252 lines
8.1 KiB
C#

using Xunit;
using FluentAssertions;
using NSubstitute;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using FreeCode.Core.Models;
using FreeCode.Core.Enums;
using CoreTaskStatus = FreeCode.Core.Enums.TaskStatus;
namespace FreeCode.Tests.Unit.Models;
public sealed class BackgroundTaskTests
{
[Fact]
public void LocalShellTask_ExposesLocalShellTypeAndCommand()
{
var task = new LocalShellTask { TaskId = "task-1", Command = "pwd" };
task.TaskType.Should().Be(BackgroundTaskType.LocalShell);
task.Command.Should().Be("pwd");
}
[Fact]
public void LocalShellTask_AllowsProcessAndOutputMutation()
{
var startInfo = new ProcessStartInfo("bash", "-lc pwd");
var task = new LocalShellTask { TaskId = "task-1", Command = "pwd", ProcessStartInfo = startInfo };
task.Stdout = "output";
task.Stderr = "error";
task.ExitCode = 0;
task.ProcessStartInfo.Should().BeSameAs(startInfo);
task.Stdout.Should().Be("output");
task.Stderr.Should().Be("error");
task.ExitCode.Should().Be(0);
}
[Fact]
public void LocalAgentTask_ExposesLocalAgentTypeAndMetadata()
{
var task = new LocalAgentTask
{
TaskId = "task-1",
Prompt = "analyze",
Model = "gpt-5.4",
AgentType = "explore",
WorkingDirectory = "/repo"
};
task.TaskType.Should().Be(BackgroundTaskType.LocalAgent);
task.Prompt.Should().Be("analyze");
task.Model.Should().Be("gpt-5.4");
task.AgentType.Should().Be("explore");
task.WorkingDirectory.Should().Be("/repo");
}
[Fact]
public void LocalAgentTask_StartsWithEmptyMessagesCollection()
{
var task = new LocalAgentTask { TaskId = "task-1", Prompt = "analyze" };
task.Messages.Should().BeEmpty();
}
[Fact]
public void LocalAgentTask_MessagesCollectionIsMutable()
{
var task = new LocalAgentTask { TaskId = "task-1", Prompt = "analyze" };
var message = new Message
{
MessageId = "msg-1",
Role = MessageRole.User,
Content = new object(),
Timestamp = new DateTime(2026, 4, 6, 10, 0, 0, DateTimeKind.Utc)
};
task.Messages.Add(message);
task.Messages.Should().ContainSingle().Which.Should().BeSameAs(message);
}
[Fact]
public void RemoteAgentTask_ExposesRemoteAgentTypeAndSessionUrl()
{
var task = new RemoteAgentTask { TaskId = "task-1", SessionUrl = "https://example.test/session/1" };
task.TaskType.Should().Be(BackgroundTaskType.RemoteAgent);
task.SessionUrl.Should().Be("https://example.test/session/1");
}
[Fact]
public void RemoteAgentTask_UsesShadowedStringStatusAlongsideBaseEnumStatus()
{
var task = new RemoteAgentTask { TaskId = "task-1", SessionUrl = "https://example.test/session/1" };
task.Status = "waiting";
((BackgroundTask)task).Status = CoreTaskStatus.Running;
task.Status.Should().Be("waiting");
((BackgroundTask)task).Status.Should().Be(CoreTaskStatus.Running);
}
[Fact]
public void InProcessTeammateTask_ExposesTeammateTypeAndProperties()
{
var task = new InProcessTeammateTask
{
TaskId = "task-1",
AgentName = "Ada",
AgentType = "reviewer",
Color = "blue",
WorkingDirectory = "/repo"
};
task.TaskType.Should().Be(BackgroundTaskType.InProcessTeammate);
task.AgentName.Should().Be("Ada");
task.AgentType.Should().Be("reviewer");
task.Color.Should().Be("blue");
task.WorkingDirectory.Should().Be("/repo");
}
[Fact]
public void LocalWorkflowTask_ExposesWorkflowTypeAndSteps()
{
var steps = new List<object> { "step-1", 2 };
var task = new LocalWorkflowTask { TaskId = "task-1", WorkflowName = "Build", Steps = steps };
task.TaskType.Should().Be(BackgroundTaskType.LocalWorkflow);
task.WorkflowName.Should().Be("Build");
task.Steps.Should().BeSameAs(steps);
task.CurrentStepIndex.Should().Be(0);
}
[Fact]
public void LocalWorkflowTask_CurrentStepIndexIsMutable()
{
var task = new LocalWorkflowTask { TaskId = "task-1", WorkflowName = "Build", Steps = [] };
task.CurrentStepIndex = 2;
task.CurrentStepIndex.Should().Be(2);
}
[Fact]
public void MonitorMcpTask_ExposesMonitorTypeAndReconnectAttempt()
{
var task = new MonitorMcpTask { TaskId = "task-1", ServerName = "filesystem" };
task.TaskType.Should().Be(BackgroundTaskType.MonitorMcp);
task.ServerName.Should().Be("filesystem");
task.ReconnectAttempt.Should().Be(0);
}
[Fact]
public void DreamTask_ExposesDreamTypeAndReason()
{
var task = new DreamTask { TaskId = "task-1", TriggerReason = "idle-compaction" };
task.TaskType.Should().Be(BackgroundTaskType.Dream);
task.TriggerReason.Should().Be("idle-compaction");
}
[Fact]
public void BackgroundTask_CommonStateIsMutable()
{
var task = new LocalShellTask { TaskId = "task-1", Command = "pwd" };
var startedAt = new DateTime(2026, 4, 6, 10, 0, 0, DateTimeKind.Utc);
var completedAt = startedAt.AddMinutes(1);
task.Status = CoreTaskStatus.Completed;
task.StartedAt = startedAt;
task.CompletedAt = completedAt;
task.ErrorMessage = "none";
task.IsBackgrounded = false;
task.Status.Should().Be(CoreTaskStatus.Completed);
task.StartedAt.Should().Be(startedAt);
task.CompletedAt.Should().Be(completedAt);
task.ErrorMessage.Should().Be("none");
task.IsBackgrounded.Should().BeFalse();
}
[Theory]
[MemberData(nameof(AllTasks))]
public void BackgroundTask_DefaultStatusIsPending(BackgroundTask task)
{
task.Status.Should().Be(CoreTaskStatus.Pending);
}
[Theory]
[MemberData(nameof(AllTasks))]
public void BackgroundTask_DefaultIsBackgroundedIsTrue(BackgroundTask task)
{
task.IsBackgrounded.Should().BeTrue();
}
[Theory]
[MemberData(nameof(AllTasks))]
public void BackgroundTask_DefaultStartedAtIsNull(BackgroundTask task)
{
task.StartedAt.Should().BeNull();
}
[Theory]
[MemberData(nameof(AllTasks))]
public void BackgroundTask_DefaultCompletedAtIsNull(BackgroundTask task)
{
task.CompletedAt.Should().BeNull();
}
[Theory]
[MemberData(nameof(AllTasks))]
public void BackgroundTask_DefaultErrorMessageIsNull(BackgroundTask task)
{
task.ErrorMessage.Should().BeNull();
}
[Theory]
[MemberData(nameof(AllTasks))]
public void BackgroundTask_PreservesTaskId(BackgroundTask task)
{
task.TaskId.Should().NotBeNullOrEmpty();
}
[Theory]
[MemberData(nameof(AllTasks))]
public void BackgroundTask_ExposesExpectedTaskType(BackgroundTask task)
{
task.TaskType.Should().BeOneOf(
BackgroundTaskType.LocalShell,
BackgroundTaskType.LocalAgent,
BackgroundTaskType.RemoteAgent,
BackgroundTaskType.InProcessTeammate,
BackgroundTaskType.LocalWorkflow,
BackgroundTaskType.MonitorMcp,
BackgroundTaskType.Dream);
}
public static IEnumerable<object[]> AllTasks()
{
yield return [new LocalShellTask { TaskId = "shell-1", Command = "pwd" }];
yield return [new LocalAgentTask { TaskId = "agent-1", Prompt = "analyze" }];
yield return [new RemoteAgentTask { TaskId = "remote-1", SessionUrl = "https://example.test/session/1" }];
yield return [new InProcessTeammateTask { TaskId = "mate-1", AgentName = "Ada", WorkingDirectory = "/repo" }];
yield return [new LocalWorkflowTask { TaskId = "workflow-1", WorkflowName = "Build", Steps = [] }];
yield return [new MonitorMcpTask { TaskId = "mcp-1", ServerName = "filesystem" }];
yield return [new DreamTask { TaskId = "dream-1", TriggerReason = "idle-compaction" }];
}
}