zcbot/windows-node/Zcbot.WindowsNode.Tests/AnsysAcceptanceViewModelTes...

110 lines
4.0 KiB
C#

namespace Zcbot.WindowsNode.Tests;
public sealed class AnsysAcceptanceViewModelTests : IDisposable
{
private readonly string root = Path.Combine(
Path.GetTempPath(), "zcbot-ansys-view-model-tests", Guid.NewGuid().ToString("N"));
[Fact]
public async Task PassedAcceptanceEnablesTheMachineGateAfterConfirmation()
{
Directory.CreateDirectory(root);
var reportPath = Path.Combine(root, "acceptance-report.json");
var service = new FakeAnsysAcceptanceService(
new AdapterAcceptanceResult(true, reportPath, "passed"));
using var viewModel = new AnsysAcceptanceViewModel(service);
viewModel.ChooseReportDirectoryRequested += () => root;
viewModel.ConfirmRunRequested += () => true;
viewModel.ConfirmEnableGateRequested += () => true;
viewModel.RunCommand.Execute(null);
await WaitUntilAsync(() => !viewModel.IsRunning && service.RunCount == 1);
Assert.True(viewModel.CanEnableGate);
Assert.Equal("success", viewModel.Tone);
Assert.Contains(reportPath, viewModel.Message);
viewModel.EnableGateCommand.Execute(null);
Assert.True(service.IsGateEnabled);
Assert.False(viewModel.CanEnableGate);
Assert.Contains("重新启动", viewModel.Message);
}
[Fact]
public async Task CancellationKeepsTheExecutionGateClosed()
{
Directory.CreateDirectory(root);
var service = new FakeAnsysAcceptanceService(null) { BlockRun = true };
using var viewModel = new AnsysAcceptanceViewModel(service);
viewModel.ChooseReportDirectoryRequested += () => root;
viewModel.ConfirmRunRequested += () => true;
viewModel.RunCommand.Execute(null);
await service.RunStarted.Task.WaitAsync(TimeSpan.FromSeconds(2));
viewModel.CancelCommand.Execute(null);
await WaitUntilAsync(() => !viewModel.IsRunning);
Assert.False(viewModel.CanEnableGate);
Assert.Equal("验收已停止,执行门保持关闭。", viewModel.Message);
}
[Fact]
public void ReportValidationRequiresAnExplicitPassedValue()
{
Directory.CreateDirectory(root);
var passed = Path.Combine(root, "passed.json");
var failed = Path.Combine(root, "failed.json");
File.WriteAllText(passed, "{\"passed\":true}");
File.WriteAllText(failed, "{\"passed\":false}");
Assert.True(AnsysAcceptanceService.ReportPassed(passed));
Assert.False(AnsysAcceptanceService.ReportPassed(failed));
Assert.False(AnsysAcceptanceService.ReportPassed(Path.Combine(root, "missing.json")));
}
public void Dispose()
{
if (Directory.Exists(root)) Directory.Delete(root, recursive: true);
}
private static async Task WaitUntilAsync(Func<bool> condition)
{
var deadline = DateTime.UtcNow.AddSeconds(2);
while (!condition() && DateTime.UtcNow < deadline)
{
await Task.Delay(10);
}
Assert.True(condition());
}
private sealed class FakeAnsysAcceptanceService(AdapterAcceptanceResult? result)
: IAnsysAcceptanceService
{
internal bool BlockRun { get; init; }
internal int RunCount { get; private set; }
internal TaskCompletionSource RunStarted { get; } = new(
TaskCreationOptions.RunContinuationsAsynchronously);
public bool IsAvailable => true;
public bool IsGateEnabled { get; private set; }
public async Task<AdapterAcceptanceResult> RunAsync(
string workRoot,
IProgress<string> progress,
CancellationToken cancellationToken)
{
RunCount++;
RunStarted.TrySetResult();
progress.Report("running");
if (BlockRun)
{
await Task.Delay(Timeout.InfiniteTimeSpan, cancellationToken);
}
return result ?? throw new InvalidOperationException("missing result");
}
public void EnableGate(string reportPath) => IsGateEnabled = true;
}
}