using System.IO; using Moq; using PersonalToolBox.Models; using PersonalToolBox.Services; namespace PersonalToolBox.Tests.Services; public class JsonDataServiceTests { private readonly Mock _logServiceMock = new(); private readonly string _testDir; public JsonDataServiceTests() { _testDir = Path.Combine(Path.GetTempPath(), $"toolbox_test_{Guid.NewGuid():N}"); Directory.CreateDirectory(_testDir); } [Fact] public void Load_FileNotExists_CreatesDefaultConfig() { // Use a path that doesn't exist var configPath = Path.Combine(_testDir, "nonexistent.json"); var service = CreateService(configPath); service.Load(); Assert.NotNull(service.Config); Assert.Equal("Dark", service.Config.Theme); Assert.Empty(service.Config.Tools); // Verify warning was logged VerifyLog(LogLevel.Info, "默认配置"); } [Fact] public void Save_ThenLoad_PreservesData() { var configPath = Path.Combine(_testDir, "config.json"); var service = CreateService(configPath); service.Config.Theme = "Light"; service.Config.Categories.Add(new Category { Id = "1", Name = "dev" }); service.Config.Tools.Add(new ToolItem { Name = "TestTool", ExecutablePath = Path.Combine(_testDir, "test.exe") }); service.Save(); // Load into a new service var service2 = CreateService(configPath); service2.Load(); Assert.Equal("Light", service2.Config.Theme); Assert.Single(service2.Config.Categories); Assert.Equal("dev", service2.Config.Categories[0].Name); Assert.Single(service2.Config.Tools); Assert.Equal("TestTool", service2.Config.Tools[0].Name); } [Fact] public void Load_InvalidPath_ToolSetToInvalid() { var configPath = Path.Combine(_testDir, "config.json"); var service = CreateService(configPath); service.Config.Tools.Add(new ToolItem { Name = "MissingTool", ExecutablePath = @"C:\definitely\does\not\exist.exe" }); service.Save(); var service2 = CreateService(configPath); service2.Load(); var tool = service2.Config.Tools[0]; Assert.False(tool.IsValid); VerifyLog(LogLevel.Warning, "路径失效"); } [Fact] public void Load_ValidPath_ToolRemainsValid() { var configPath = Path.Combine(_testDir, "config.json"); var service = CreateService(configPath); // Create a real temp file var tempFile = Path.Combine(_testDir, "real_tool.exe"); File.WriteAllText(tempFile, "dummy"); service.Config.Tools.Add(new ToolItem { Name = "RealTool", ExecutablePath = tempFile }); service.Save(); var service2 = CreateService(configPath); service2.Load(); var tool = service2.Config.Tools[0]; Assert.True(tool.IsValid); } [Fact] public void Load_UrlPath_AlwaysValid() { var configPath = Path.Combine(_testDir, "config.json"); var service = CreateService(configPath); service.Config.Tools.Add(new ToolItem { Name = "WebTool", ExecutablePath = "https://example.com/tool" }); service.Save(); var service2 = CreateService(configPath); service2.Load(); var tool = service2.Config.Tools[0]; Assert.True(tool.IsValid); } [Fact] public void Load_EmptyExecutablePath_SkipsValidation() { var configPath = Path.Combine(_testDir, "config.json"); var service = CreateService(configPath); service.Config.Tools.Add(new ToolItem { Name = "NoPathTool", ExecutablePath = "" }); service.Save(); var service2 = CreateService(configPath); service2.Load(); var tool = service2.Config.Tools[0]; Assert.True(tool.IsValid); } [Fact] public void Load_CorruptedJson_LogsErrorAndUsesDefaults() { var configPath = Path.Combine(_testDir, "config.json"); File.WriteAllText(configPath, "this is not json {{{"); var service = CreateService(configPath); service.Load(); Assert.NotNull(service.Config); Assert.Equal("Dark", service.Config.Theme); VerifyLog(LogLevel.Error, "解析失败"); } // ───────────────────────────── helpers ───────────────────────────── private JsonDataService CreateService(string filePath) { var service = new JsonDataService(_logServiceMock.Object); // Replace the file path via reflection typeof(JsonDataService) .GetField("_filePath", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)? .SetValue(service, filePath); return service; } private void VerifyLog(LogLevel level, string fragment) { // 根据日志级别验证对应方法被调用 switch (level) { case LogLevel.Info: _logServiceMock.Verify(x => x.Info(It.Is(s => s.Contains(fragment))), Times.AtLeastOnce); break; case LogLevel.Warning: _logServiceMock.Verify(x => x.Warning(It.Is(s => s.Contains(fragment))), Times.AtLeastOnce); break; case LogLevel.Error: _logServiceMock.Verify(x => x.Error(It.Is(s => s.Contains(fragment))), Times.AtLeastOnce); break; } } }