using Moq; using PersonalToolBox.Models; using PersonalToolBox.Services; namespace PersonalToolBox.Tests.Services; public class ProcessExecutionServiceTests { private readonly Mock _logServiceMock = new(); private readonly Mock _dataServiceMock = new(); private readonly AppConfig _config = new(); public ProcessExecutionServiceTests() { _dataServiceMock.Setup(d => d.Config).Returns(_config); } [Fact] public async Task Execute_NullTool_LogsError() { var service = new ProcessExecutionService(_logServiceMock.Object, _dataServiceMock.Object); await service.ExecuteAsync(null!); _logServiceMock.Verify(x => x.Error(It.Is(s => s.Contains("空工具项"))), Times.Once); } [Fact] public async Task Execute_InvalidTool_LogsWarning() { var tool = new ToolItem { Name = "BadTool", ExecutablePath = @"C:\nonexistent.exe", IsValid = false }; var service = new ProcessExecutionService(_logServiceMock.Object, _dataServiceMock.Object); await service.ExecuteAsync(tool); _logServiceMock.Verify(x => x.Warning(It.Is(s => s.Contains("无法运行") && s.Contains("BadTool"))), Times.Once); } [Fact] public async Task Execute_ProcessFails_LogsError() { var tool = new ToolItem { Name = "FailTool", ExecutablePath = @"Z:\impossible_path\not_really.exe", IsValid = true }; var service = new ProcessExecutionService(_logServiceMock.Object, _dataServiceMock.Object); await service.ExecuteAsync(tool); _logServiceMock.Verify(x => x.Error(It.Is(s => s.Contains("FailTool") && s.Contains("失败"))), Times.Once); } [Fact] public async Task Execute_Success_LogsInfo() { var tool = new ToolItem { Name = "记事本", ExecutablePath = "notepad.exe", IsValid = true }; var service = new ProcessExecutionService(_logServiceMock.Object, _dataServiceMock.Object); await service.ExecuteAsync(tool); _logServiceMock.Verify(x => x.Info(It.Is(s => s.Contains("成功启动") && s.Contains("记事本"))), Times.Once); } [Fact] public async Task Execute_Group_BatchExecutesSubTools() { var sub1 = new ToolItem { Id = "s1", Name = "Sub1", ExecutablePath = "cmd.exe", IsValid = true }; var sub2 = new ToolItem { Id = "s2", Name = "Sub2", ExecutablePath = "cmd.exe", IsValid = true }; _config.Tools.Add(sub1); _config.Tools.Add(sub2); var group = new ToolItem { Name = "MyGroup", IsGroup = true, SubToolIds = new List { "s1", "s2" }, IsValid = true }; var service = new ProcessExecutionService(_logServiceMock.Object, _dataServiceMock.Object); await service.ExecuteAsync(group); _logServiceMock.Verify(x => x.Info(It.Is(s => s.Contains("开始启动组合"))), Times.Once); _logServiceMock.Verify(x => x.Info(It.Is(s => s.Contains("组合") && s.Contains("启动完成"))), Times.Once); _logServiceMock.Verify(x => x.Info(It.Is(s => s.Contains("成功启动") && s.Contains("Sub1"))), Times.Once); } [Fact] public async Task Execute_Group_OrphanSubIds_Skipped() { var sub1 = new ToolItem { Id = "s1", Name = "Sub1", ExecutablePath = "cmd.exe", IsValid = true }; _config.Tools.Add(sub1); var group = new ToolItem { Name = "Group", IsGroup = true, SubToolIds = new List { "s1", "missing_id" }, IsValid = true }; var service = new ProcessExecutionService(_logServiceMock.Object, _dataServiceMock.Object); await service.ExecuteAsync(group); _logServiceMock.Verify(x => x.Warning(It.Is(s => s.Contains("找不到 ID") && s.Contains("missing_id"))), Times.Once); _logServiceMock.Verify(x => x.Info(It.Is(s => s.Contains("成功启动") && s.Contains("Sub1"))), Times.Once); } }