From 56420d1c05ca214cec81025e0616c1549bc3a210 Mon Sep 17 00:00:00 2001 From: Copilot Date: Wed, 10 Jun 2026 23:27:10 -0500 Subject: [PATCH] test: add DialogService and FileSystemService baseline coverage Cover dialog folder helper guards and file watcher dispose/stop safety paths. --- .../gui-architecture-deferred.md | 2 +- ...113-test-dialog-filesystem-service-plan.md | 19 +++++++++++ src/ModSync.Tests/DialogServiceTests.cs | 32 ++++++++++++++++++ src/ModSync.Tests/FileSystemServiceTests.cs | 33 +++++++++++++++++++ 4 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 docs/plans/2026-06-04-113-test-dialog-filesystem-service-plan.md create mode 100644 src/ModSync.Tests/DialogServiceTests.cs create mode 100644 src/ModSync.Tests/FileSystemServiceTests.cs diff --git a/docs/knowledgebase/gui-architecture-deferred.md b/docs/knowledgebase/gui-architecture-deferred.md index 84f2c7db..42da44e0 100644 --- a/docs/knowledgebase/gui-architecture-deferred.md +++ b/docs/knowledgebase/gui-architecture-deferred.md @@ -62,7 +62,7 @@ Plans: `docs/plans/2026-06-03-012`, `021`–`029`, `033`–`057`. Surface refere | Directory picker init/sync → `SettingsService` | Done | plan `075`, PR pending | | Mod context menu + global flyout → `MenuBuilderService` | Pending | plan `072`, PR #120 | -**Headless tests:** `dotnet test src/ModSync.Tests/ModSync.Tests.csproj --filter SettingsService` (plan `077`, complements PR #123 picker wiring). +**Headless tests:** `dotnet test src/ModSync.Tests/ModSync.Tests.csproj --filter SettingsService` (plan `077`, complements PR #123 picker wiring); `--filter DialogServiceTests` / `FileSystemServiceTests` (plan `113`). **Agent guidance:** Do not add new feature logic inline without a plan; prefer `src/ModSync.GUI/Services/`. diff --git a/docs/plans/2026-06-04-113-test-dialog-filesystem-service-plan.md b/docs/plans/2026-06-04-113-test-dialog-filesystem-service-plan.md new file mode 100644 index 00000000..cd141299 --- /dev/null +++ b/docs/plans/2026-06-04-113-test-dialog-filesystem-service-plan.md @@ -0,0 +1,19 @@ +--- +title: "test: DialogService and FileSystemService basics" +status: shipped +pr: pending +--- + +# test: DialogService and FileSystemService basics + +## Problem + +`DialogService` path-folder helper and `FileSystemService` watcher/dispose lifecycle had no dedicated tests. + +## Solution + +Add `DialogServiceTests` and `FileSystemServiceTests` for constructor validation, empty-path handling, and safe dispose/stop paths. + +## Verification + +`dotnet test src/ModSync.Tests/ModSync.Tests.csproj --filter "FullyQualifiedName~DialogServiceTests|FullyQualifiedName~FileSystemServiceTests" diff --git a/src/ModSync.Tests/DialogServiceTests.cs b/src/ModSync.Tests/DialogServiceTests.cs new file mode 100644 index 00000000..7437ba8d --- /dev/null +++ b/src/ModSync.Tests/DialogServiceTests.cs @@ -0,0 +1,32 @@ +// Copyright 2021-2025 ModSync +// Licensed under the Business Source License 1.1 (BSL 1.1). +// See LICENSE.txt file in the project root for full license information. + +using System; +using System.Threading.Tasks; +using Avalonia.Controls; +using Avalonia.Headless.XUnit; +using ModSync.Services; +using Xunit; + +namespace ModSync.Tests +{ + [Collection(HeadlessTestApp.CollectionName)] + public sealed class DialogServiceTests + { + [Fact(DisplayName = "Constructor rejects null parent window")] + public void Constructor_NullParentWindow_Throws() + { + Assert.Throws(() => new DialogService(null)); + } + + [AvaloniaFact(DisplayName = "GetStorageFolderFromPathAsync returns null for empty path")] + public async Task GetStorageFolderFromPathAsync_EmptyPath_ReturnsNull() + { + var service = new DialogService(new Window()); + + Assert.Null(await service.GetStorageFolderFromPathAsync(string.Empty)); + Assert.Null(await service.GetStorageFolderFromPathAsync(null)); + } + } +} diff --git a/src/ModSync.Tests/FileSystemServiceTests.cs b/src/ModSync.Tests/FileSystemServiceTests.cs new file mode 100644 index 00000000..2cf07d43 --- /dev/null +++ b/src/ModSync.Tests/FileSystemServiceTests.cs @@ -0,0 +1,33 @@ +// Copyright 2021-2025 ModSync +// Licensed under the Business Source License 1.1 (BSL 1.1). +// See LICENSE.txt file in the project root for full license information. + +using ModSync.Services; +using Xunit; + +namespace ModSync.Tests +{ + public sealed class FileSystemServiceTests + { + [Fact(DisplayName = "SetupModDirectoryWatcher does not throw when watcher disabled")] + public void SetupModDirectoryWatcher_Disabled_DoesNotThrow() + { + FileSystemService.SetupModDirectoryWatcher("/tmp", _ => { }); + } + + [Fact(DisplayName = "StopWatcher does not throw on fresh service")] + public void StopWatcher_FreshService_DoesNotThrow() + { + using var service = new FileSystemService(); + service.StopWatcher(); + } + + [Fact(DisplayName = "Dispose can be called multiple times")] + public void Dispose_MultipleCalls_DoesNotThrow() + { + var service = new FileSystemService(); + service.Dispose(); + service.Dispose(); + } + } +}