zcbot/windows-node/Zcbot.WindowsNode.Tests/JobPageViewModelTests.cs

89 lines
3.0 KiB
C#

namespace Zcbot.WindowsNode.Tests;
public sealed class JobPageViewModelTests
{
[Fact]
public void Refresh_PopulatesJobsFormatsStateAndSelectsTheFirstItem()
{
var active = Snapshot("software_running", "正在生成图表", minutesAgo: 2);
var succeeded = Snapshot("succeeded", "历史任务", minutesAgo: 10);
var viewModel = new JobPageViewModel(new FakeJobMonitorService(active, succeeded));
viewModel.Refresh();
Assert.Equal(2, viewModel.Jobs.Count);
Assert.Same(viewModel.Jobs[0], viewModel.SelectedJob);
Assert.Equal("软件执行中", viewModel.Jobs[0].StageText);
Assert.StartsWith("已执行 02:", viewModel.Jobs[0].ProgressText);
Assert.Equal("活动任务 1 个 · 最近记录 2 条", viewModel.Summary);
Assert.Contains(active.JobId.ToString(), viewModel.SelectedDetail);
}
[Fact]
public void Refresh_PreservesSelectionByJobIdAcrossSnapshots()
{
var first = Snapshot("accepted", "第一项", minutesAgo: 3);
var second = Snapshot("failed", "第二项", minutesAgo: 2);
var service = new FakeJobMonitorService(first, second);
var viewModel = new JobPageViewModel(service);
viewModel.Refresh();
viewModel.SelectedJob = viewModel.Jobs[1];
service.Snapshots = [
second with { Detail = "更新后的错误" },
first,
];
viewModel.Refresh();
Assert.Equal(second.JobId, viewModel.SelectedJob?.JobId);
Assert.Contains("更新后的错误", viewModel.SelectedDetail);
Assert.Equal("error", viewModel.SelectedJob?.Tone);
}
[Fact]
public void Refresh_ReportsReadFailuresWithoutDroppingTheCurrentList()
{
var service = new FakeJobMonitorService(Snapshot("accepted", "任务", 1));
var viewModel = new JobPageViewModel(service);
viewModel.Refresh();
service.Failure = new IOException("locked");
viewModel.Refresh();
Assert.Single(viewModel.Jobs);
Assert.Contains("locked", viewModel.Summary);
}
private static JobDisplaySnapshot Snapshot(
string stage,
string title,
int minutesAgo)
{
var updatedAt = DateTimeOffset.UtcNow.AddMinutes(-minutesAgo);
return new JobDisplaySnapshot(
Guid.NewGuid(),
"origin.plot@v2",
title,
"input.csv",
stage,
stage == "succeeded" ? 100 : 25,
title,
updatedAt.AddMinutes(-1),
updatedAt,
stage == "succeeded");
}
private sealed class FakeJobMonitorService(params JobDisplaySnapshot[] snapshots)
: IJobMonitorService
{
internal IReadOnlyList<JobDisplaySnapshot> Snapshots { get; set; } = snapshots;
internal Exception? Failure { get; set; }
public IReadOnlyList<JobDisplaySnapshot> ReadSnapshots(int limit = 50)
{
if (Failure is not null) throw Failure;
return Snapshots;
}
}
}