Files
2026-04-13 14:36:18 +08:00

141 lines
5.7 KiB
C#

using Moq;
using System;
using System.IO;
using System.Threading.Tasks;
using XP.Common.Logging.Interfaces;
using XplorePlane.Models;
using XplorePlane.Services;
using XplorePlane.Services.AppState;
using XplorePlane.Services.Recipe;
using Xunit;
using Xunit.Abstractions;
namespace XplorePlane.Tests.Services
{
/// <summary>
/// RecipeService 单元测试。
/// 验证配方创建、步骤记录、文件加载错误处理和名称校验。
/// </summary>
public class RecipeServiceTests
{
private readonly Mock<IAppStateService> _mockAppState;
private readonly Mock<IPipelineExecutionService> _mockPipeline;
private readonly Mock<ILoggerService> _mockLogger;
private readonly RecipeService _service;
private readonly ITestOutputHelper _output;
public RecipeServiceTests(ITestOutputHelper output)
{
_output = output;
_mockAppState = new Mock<IAppStateService>();
_mockPipeline = new Mock<IPipelineExecutionService>();
_mockLogger = new Mock<ILoggerService>();
_mockLogger.Setup(l => l.ForModule<RecipeService>()).Returns(_mockLogger.Object);
// Setup default state returns
_mockAppState.Setup(s => s.MotionState).Returns(MotionState.Default);
_mockAppState.Setup(s => s.RaySourceState).Returns(RaySourceState.Default);
_mockAppState.Setup(s => s.DetectorState).Returns(DetectorState.Default);
_service = new RecipeService(
_mockAppState.Object,
_mockPipeline.Object,
_mockLogger.Object);
}
// ── CreateRecipe 验证 ──
[Fact]
public void CreateRecipe_ValidName_ReturnsEmptyRecipe()
{
var recipe = _service.CreateRecipe("TestRecipe");
_output.WriteLine($"CreateRecipe('TestRecipe'): Name={recipe.Name}, Id={recipe.Id}, Steps.Count={recipe.Steps.Count}, CreatedAt={recipe.CreatedAt}");
Assert.Equal("TestRecipe", recipe.Name);
Assert.Empty(recipe.Steps);
Assert.NotEqual(Guid.Empty, recipe.Id);
Assert.True(recipe.CreatedAt <= DateTime.UtcNow);
Assert.True(recipe.UpdatedAt <= DateTime.UtcNow);
}
[Fact]
public void CreateRecipe_EmptyName_ThrowsArgumentException()
{
var ex = Assert.Throws<ArgumentException>(() => _service.CreateRecipe(string.Empty));
_output.WriteLine($"CreateRecipe('') threw: {ex.GetType().Name}, Message={ex.Message}");
}
[Fact]
public void CreateRecipe_NullName_ThrowsArgumentException()
{
var ex = Assert.Throws<ArgumentException>(() => _service.CreateRecipe(null!));
_output.WriteLine($"CreateRecipe(null) threw: {ex.GetType().Name}, Message={ex.Message}");
}
[Fact]
public void CreateRecipe_WhitespaceName_ThrowsArgumentException()
{
var ex = Assert.Throws<ArgumentException>(() => _service.CreateRecipe(" "));
_output.WriteLine($"CreateRecipe(' ') threw: {ex.GetType().Name}, Message={ex.Message}");
}
// ── RecordCurrentStep 验证 ──
[Fact]
public void RecordCurrentStep_CapturesCurrentState()
{
var motionState = new MotionState(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
var raySourceState = new RaySourceState(true, 120.0, 80.0);
var detectorState = new DetectorState(true, true, 30.0, "2048x2048");
_mockAppState.Setup(s => s.MotionState).Returns(motionState);
_mockAppState.Setup(s => s.RaySourceState).Returns(raySourceState);
_mockAppState.Setup(s => s.DetectorState).Returns(detectorState);
var recipe = _service.CreateRecipe("TestRecipe");
var pipeline = new PipelineModel { Name = "TestPipeline" };
var step = _service.RecordCurrentStep(recipe, pipeline);
_output.WriteLine($"RecordCurrentStep: StepIndex={step.StepIndex}, MotionState.XM={step.MotionState.XM}, RaySource.Voltage={step.RaySourceState.Voltage}, Detector.Resolution={step.DetectorState.Resolution}, Pipeline={step.Pipeline.Name}");
Assert.Equal(0, step.StepIndex);
Assert.Same(motionState, step.MotionState);
Assert.Same(raySourceState, step.RaySourceState);
Assert.Same(detectorState, step.DetectorState);
Assert.Same(pipeline, step.Pipeline);
}
// ── LoadAsync 错误处理 ──
[Fact]
public async Task LoadAsync_FileNotExists_ThrowsFileNotFoundException()
{
var nonExistentPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString() + ".json");
_output.WriteLine($"LoadAsync 不存在的文件: {nonExistentPath}");
var ex = await Assert.ThrowsAsync<FileNotFoundException>(
() => _service.LoadAsync(nonExistentPath));
_output.WriteLine($"抛出异常: {ex.GetType().Name}, Message={ex.Message}");
}
[Fact]
public async Task LoadAsync_InvalidJson_ThrowsInvalidDataException()
{
var tempFile = Path.GetTempFileName();
try
{
await File.WriteAllTextAsync(tempFile, "{ this is not valid json !!! }");
_output.WriteLine($"LoadAsync 无效JSON文件: {tempFile}");
var ex = await Assert.ThrowsAsync<InvalidDataException>(
() => _service.LoadAsync(tempFile));
_output.WriteLine($"抛出异常: {ex.GetType().Name}, Message={ex.Message}");
}
finally
{
if (File.Exists(tempFile))
File.Delete(tempFile);
}
}
}
}