using Moq; using PersonalToolBox.Models; using PersonalToolBox.Services; using PersonalToolBox.ViewModels; namespace PersonalToolBox.Tests.ViewModels; public class GroupEditViewModelTests { private readonly Mock _dataServiceMock = new(); private readonly Mock _logServiceMock = new(); private readonly AppConfig _config; public GroupEditViewModelTests() { _config = new AppConfig { Tools = [ new() { Id = "t1", Name = "VS Code", IsValid = true }, new() { Id = "t2", Name = "Postman", IsValid = true }, new() { Id = "t3", Name = "SomeGroup", IsGroup = true, SubToolIds = new List { "t1" }, IsValid = true } ] }; _dataServiceMock.Setup(d => d.Config).Returns(_config); } [Fact] public void Constructor_AddMode_LoadsAllToolsIncludingGroups() { var vm = new GroupEditViewModel(_dataServiceMock.Object, _logServiceMock.Object); Assert.Equal("添加组合", vm.WindowTitle); Assert.Equal(3, vm.AvailableTools.Count); // t1, t2, t3 (new groups can include other groups) } [Fact] public void Constructor_EditMode_ExcludesSelfAndAncestors() { // Group A (t3) contains t1 -> Group B being edited should exclude itself var group = new ToolItem { Id = "t4", Name = "GroupB", IsGroup = true, SubToolIds = new List() }; // t3 contains t4, so t3 is an ancestor of t4 _config.Tools.First(t => t.Id == "t3").SubToolIds.Add("t4"); var vm = new GroupEditViewModel(_dataServiceMock.Object, _logServiceMock.Object, group); // t4 (self) and t3 (ancestor) should be excluded Assert.Equal(2, vm.AvailableTools.Count); // t1, t2 only } [Fact] public void Constructor_EditMode_PreselectedTools() { var group = new ToolItem { Name = "ExistingGroup", IsGroup = true, SubToolIds = new List { "t1" } }; var vm = new GroupEditViewModel(_dataServiceMock.Object, _logServiceMock.Object, group); Assert.Equal("编辑组合", vm.WindowTitle); Assert.Equal("ExistingGroup", vm.Name); Assert.True(vm.AvailableTools.First(t => t.Tool.Id == "t1").IsSelected); Assert.False(vm.AvailableTools.First(t => t.Tool.Id == "t2").IsSelected); } [Fact] public void Save_EmptyName_LogsWarning() { var vm = new GroupEditViewModel(_dataServiceMock.Object, _logServiceMock.Object); vm.SaveCommand.Execute(null); _logServiceMock.Verify(x => x.Warning(It.Is(s => s.Contains("名称不能为空"))), Times.Once); } [Fact] public void Save_AddMode_CreatesGroupAndCloses() { var vm = new GroupEditViewModel(_dataServiceMock.Object, _logServiceMock.Object) { Name = "NewGroup" }; vm.AvailableTools.First(t => t.Tool.Id == "t1").IsSelected = true; bool? closeResult = null; vm.CloseAction = (r) => closeResult = r; vm.SaveCommand.Execute(null); Assert.True(vm.Saved); Assert.True(closeResult); Assert.Single(_config.Tools.Where(t => t.IsGroup && t.Name == "NewGroup")); Assert.Contains("t1", _config.Tools.First(t => t.Name == "NewGroup").SubToolIds); _dataServiceMock.Verify(x => x.Save(), Times.Once); } [Fact] public void Cancel_ClosesWithoutSaving() { var vm = new GroupEditViewModel(_dataServiceMock.Object, _logServiceMock.Object); bool? closeResult = null; vm.CloseAction = (r) => closeResult = r; vm.CancelCommand.Execute(null); Assert.False(vm.Saved); Assert.False(closeResult); } }