From 4add89476f3f109da58b9e52634c68bbc0faa5b8 Mon Sep 17 00:00:00 2001 From: mleem97 <52848568+mleem97@users.noreply.github.com> Date: Thu, 21 May 2026 13:21:34 +0000 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=A7=AA=20Add=20tests=20for=20UploadDe?= =?UTF-8?q?pendencyChecker?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🎯 What: The UploadDependencyChecker utility lacked unit tests to ensure mod pre-flight validations function correctly. 📊 Coverage: Added tests validating content folder presence, metadata constraints, tag handling, changelogs, preview images, and framework dependency logic. ✨ Result: Reliable validation for project metadata ensuring developers get accurate pre-upload warnings and errors. --- .../Services/UploadDependencyCheckerTests.cs | 502 ++++++++++++++++++ 1 file changed, 502 insertions(+) create mode 100644 tests/GregModmanager.Tests/Services/UploadDependencyCheckerTests.cs diff --git a/tests/GregModmanager.Tests/Services/UploadDependencyCheckerTests.cs b/tests/GregModmanager.Tests/Services/UploadDependencyCheckerTests.cs new file mode 100644 index 0000000..680f581 --- /dev/null +++ b/tests/GregModmanager.Tests/Services/UploadDependencyCheckerTests.cs @@ -0,0 +1,502 @@ +using GregModmanager.Models; +using GregModmanager.Services; +using GregModmanager.Steam; + +namespace GregModmanager.Tests.Services; + +public class UploadDependencyCheckerTests : IDisposable +{ + private readonly string _tempPath; + + public UploadDependencyCheckerTests() + { + _tempPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + Directory.CreateDirectory(_tempPath); + } + + public void Dispose() + { + if (Directory.Exists(_tempPath)) + { + try + { + Directory.Delete(_tempPath, true); + } + catch + { + // Ignore cleanup errors in tests + } + } + } + + private WorkshopMetadata CreateValidMetadata() + { + return new WorkshopMetadata + { + Title = "Valid Title", + Description = "Valid Description", + Visibility = "Public", + Tags = ["Tag1"], + PreviewImageRelativePath = "preview.png" + }; + } + + [Fact] + public void Check_MissingContentFolder_ReturnsError() + { + // Arrange + var metadata = CreateValidMetadata(); + + // Act + var results = UploadDependencyChecker.Check(_tempPath, metadata); + + // Assert + Assert.Contains(results, r => r.Label == "Content folder" && r.Severity == UploadCheckSeverity.Error); + Assert.False(UploadDependencyChecker.IsReadyToUpload(results)); + } + + [Fact] + public void Check_EmptyContentFolder_ReturnsError() + { + // Arrange + var metadata = CreateValidMetadata(); + var contentDir = Path.Combine(_tempPath, "content"); + Directory.CreateDirectory(contentDir); + + // Act + var results = UploadDependencyChecker.Check(_tempPath, metadata); + + // Assert + Assert.Contains(results, r => r.Label == "Content folder" && r.Severity == UploadCheckSeverity.Error); + Assert.False(UploadDependencyChecker.IsReadyToUpload(results)); + } + + [Fact] + public void Check_MissingNativeConfigJson_ReturnsWarning() + { + // Arrange + var metadata = CreateValidMetadata(); + var contentDir = Path.Combine(_tempPath, "content"); + Directory.CreateDirectory(contentDir); + File.WriteAllText(Path.Combine(contentDir, "somefile.txt"), "test"); + + // Act + var results = UploadDependencyChecker.Check(_tempPath, metadata); + + // Assert + Assert.Contains(results, r => r.Label == "config.json" && r.Severity == UploadCheckSeverity.Warning); + } + + [Fact] + public void Check_PresentNativeConfigJson_ReturnsOk() + { + // Arrange + var metadata = CreateValidMetadata(); + var contentDir = Path.Combine(_tempPath, "content"); + Directory.CreateDirectory(contentDir); + File.WriteAllText(Path.Combine(contentDir, "config.json"), "{}"); + + // Act + var results = UploadDependencyChecker.Check(_tempPath, metadata); + + // Assert + Assert.Contains(results, r => r.Label == "config.json" && r.Severity == UploadCheckSeverity.Ok); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData(" ")] + public void Check_EmptyTitle_ReturnsError(string? emptyTitle) + { + // Arrange + var metadata = CreateValidMetadata(); + metadata.Title = emptyTitle!; + + var contentDir = Path.Combine(_tempPath, "content"); + Directory.CreateDirectory(contentDir); + File.WriteAllText(Path.Combine(contentDir, "somefile.txt"), "test"); + + // Act + var results = UploadDependencyChecker.Check(_tempPath, metadata); + + // Assert + Assert.Contains(results, r => r.Label == "Title" && r.Severity == UploadCheckSeverity.Error); + Assert.False(UploadDependencyChecker.IsReadyToUpload(results)); + } + + [Fact] + public void Check_LongTitle_ReturnsError() + { + // Arrange + var metadata = CreateValidMetadata(); + metadata.Title = new string('A', SteamConstants.MaxTitleLength + 1); + + var contentDir = Path.Combine(_tempPath, "content"); + Directory.CreateDirectory(contentDir); + File.WriteAllText(Path.Combine(contentDir, "somefile.txt"), "test"); + + // Act + var results = UploadDependencyChecker.Check(_tempPath, metadata); + + // Assert + Assert.Contains(results, r => r.Label == "Title" && r.Severity == UploadCheckSeverity.Error); + Assert.False(UploadDependencyChecker.IsReadyToUpload(results)); + } + + [Fact] + public void Check_ValidTitle_ReturnsOk() + { + // Arrange + var metadata = CreateValidMetadata(); + + var contentDir = Path.Combine(_tempPath, "content"); + Directory.CreateDirectory(contentDir); + File.WriteAllText(Path.Combine(contentDir, "somefile.txt"), "test"); + + // Act + var results = UploadDependencyChecker.Check(_tempPath, metadata); + + // Assert + Assert.Contains(results, r => r.Label == "Title" && r.Severity == UploadCheckSeverity.Ok); + } + + [Fact] + public void Check_EmptyDescription_ReturnsWarning() + { + // Arrange + var metadata = CreateValidMetadata(); + metadata.Description = ""; + + var contentDir = Path.Combine(_tempPath, "content"); + Directory.CreateDirectory(contentDir); + File.WriteAllText(Path.Combine(contentDir, "somefile.txt"), "test"); + + // Act + var results = UploadDependencyChecker.Check(_tempPath, metadata); + + // Assert + Assert.Contains(results, r => r.Label == "Description" && r.Severity == UploadCheckSeverity.Warning); + } + + [Fact] + public void Check_LongDescription_ReturnsError() + { + // Arrange + var metadata = CreateValidMetadata(); + metadata.Description = new string('A', SteamConstants.MaxDescriptionLength + 1); + + var contentDir = Path.Combine(_tempPath, "content"); + Directory.CreateDirectory(contentDir); + File.WriteAllText(Path.Combine(contentDir, "somefile.txt"), "test"); + + // Act + var results = UploadDependencyChecker.Check(_tempPath, metadata); + + // Assert + Assert.Contains(results, r => r.Label == "Description" && r.Severity == UploadCheckSeverity.Error); + } + + [Theory] + [InlineData("Public")] + [InlineData("FriendsOnly")] + [InlineData("Private")] + public void Check_ValidVisibility_ReturnsOk(string visibility) + { + // Arrange + var metadata = CreateValidMetadata(); + metadata.Visibility = visibility; + + var contentDir = Path.Combine(_tempPath, "content"); + Directory.CreateDirectory(contentDir); + File.WriteAllText(Path.Combine(contentDir, "somefile.txt"), "test"); + + // Act + var results = UploadDependencyChecker.Check(_tempPath, metadata); + + // Assert + Assert.Contains(results, r => r.Label == "Visibility" && r.Severity == UploadCheckSeverity.Ok); + } + + [Fact] + public void Check_InvalidVisibility_ReturnsWarning() + { + // Arrange + var metadata = CreateValidMetadata(); + metadata.Visibility = "InvalidVisibility"; + + var contentDir = Path.Combine(_tempPath, "content"); + Directory.CreateDirectory(contentDir); + File.WriteAllText(Path.Combine(contentDir, "somefile.txt"), "test"); + + // Act + var results = UploadDependencyChecker.Check(_tempPath, metadata); + + // Assert + Assert.Contains(results, r => r.Label == "Visibility" && r.Severity == UploadCheckSeverity.Warning); + } + + [Fact] + public void Check_MissingPreviewImage_ReturnsWarning() + { + // Arrange + var metadata = CreateValidMetadata(); + metadata.PreviewImageRelativePath = "preview.png"; + + var contentDir = Path.Combine(_tempPath, "content"); + Directory.CreateDirectory(contentDir); + File.WriteAllText(Path.Combine(contentDir, "somefile.txt"), "test"); + + // Act + var results = UploadDependencyChecker.Check(_tempPath, metadata); + + // Assert + Assert.Contains(results, r => r.Label == "Preview image" && r.Severity == UploadCheckSeverity.Warning); + } + + [Fact] + public void Check_LargePreviewImage_ReturnsWarning() + { + // Arrange + var metadata = CreateValidMetadata(); + metadata.PreviewImageRelativePath = "preview.png"; + + var contentDir = Path.Combine(_tempPath, "content"); + Directory.CreateDirectory(contentDir); + File.WriteAllText(Path.Combine(contentDir, "somefile.txt"), "test"); + + var previewPath = Path.Combine(_tempPath, "preview.png"); + // Create a fake large file using sparse file if possible, or just write some data. + using (var fs = new FileStream(previewPath, FileMode.Create, FileAccess.Write, FileShare.None)) + { + fs.SetLength(1_048_576 + 10); + } + + // Act + var results = UploadDependencyChecker.Check(_tempPath, metadata); + + // Assert + Assert.Contains(results, r => r.Label == "Preview image" && r.Severity == UploadCheckSeverity.Warning); + } + + [Fact] + public void Check_ValidPreviewImage_ReturnsOk() + { + // Arrange + var metadata = CreateValidMetadata(); + metadata.PreviewImageRelativePath = "preview.png"; + + var contentDir = Path.Combine(_tempPath, "content"); + Directory.CreateDirectory(contentDir); + File.WriteAllText(Path.Combine(contentDir, "somefile.txt"), "test"); + + var previewPath = Path.Combine(_tempPath, "preview.png"); + File.WriteAllText(previewPath, "fake image content"); + + // Act + var results = UploadDependencyChecker.Check(_tempPath, metadata); + + // Assert + Assert.Contains(results, r => r.Label == "Preview image" && r.Severity == UploadCheckSeverity.Ok); + } + + [Fact] + public void Check_EmptyTags_ReturnsWarning() + { + // Arrange + var metadata = CreateValidMetadata(); + metadata.Tags = new List(); + + var contentDir = Path.Combine(_tempPath, "content"); + Directory.CreateDirectory(contentDir); + File.WriteAllText(Path.Combine(contentDir, "somefile.txt"), "test"); + + // Act + var results = UploadDependencyChecker.Check(_tempPath, metadata); + + // Assert + Assert.Contains(results, r => r.Label == "Tags" && r.Severity == UploadCheckSeverity.Warning); + } + + [Fact] + public void Check_ValidTags_ReturnsOk() + { + // Arrange + var metadata = CreateValidMetadata(); + metadata.Tags = ["Tag1", "Tag2"]; + + var contentDir = Path.Combine(_tempPath, "content"); + Directory.CreateDirectory(contentDir); + File.WriteAllText(Path.Combine(contentDir, "somefile.txt"), "test"); + + // Act + var results = UploadDependencyChecker.Check(_tempPath, metadata); + + // Assert + Assert.Contains(results, r => r.Label == "Tags" && r.Severity == UploadCheckSeverity.Ok); + } + + [Fact] + public void Check_FirstPublishEmptyChangelog_ReturnsError() + { + // Arrange + var metadata = CreateValidMetadata(); + metadata.PublishedFileId = 0; + + var contentDir = Path.Combine(_tempPath, "content"); + Directory.CreateDirectory(contentDir); + File.WriteAllText(Path.Combine(contentDir, "somefile.txt"), "test"); + + // Act + var results = UploadDependencyChecker.Check(_tempPath, metadata, changeLog: ""); + + // Assert + Assert.Contains(results, r => r.Label == "Changelog" && r.Severity == UploadCheckSeverity.Error); + } + + [Fact] + public void Check_UpdateEmptyChangelog_ReturnsWarning() + { + // Arrange + var metadata = CreateValidMetadata(); + metadata.PublishedFileId = 12345; + + var contentDir = Path.Combine(_tempPath, "content"); + Directory.CreateDirectory(contentDir); + File.WriteAllText(Path.Combine(contentDir, "somefile.txt"), "test"); + + // Act + var results = UploadDependencyChecker.Check(_tempPath, metadata, changeLog: ""); + + // Assert + Assert.Contains(results, r => r.Label == "Changelog" && r.Severity == UploadCheckSeverity.Warning); + } + + [Fact] + public void Check_ValidChangelog_ReturnsOk() + { + // Arrange + var metadata = CreateValidMetadata(); + metadata.PublishedFileId = 12345; + + var contentDir = Path.Combine(_tempPath, "content"); + Directory.CreateDirectory(contentDir); + File.WriteAllText(Path.Combine(contentDir, "somefile.txt"), "test"); + + // Act + var results = UploadDependencyChecker.Check(_tempPath, metadata, changeLog: "Initial release"); + + // Assert + Assert.Contains(results, r => r.Label == "Changelog" && r.Severity == UploadCheckSeverity.Ok); + } + + [Fact] + public void Check_GregFrameworkDependency_NeedsGregWithoutDescription_ReturnsWarning() + { + // Arrange + var metadata = CreateValidMetadata(); + metadata.Needsgreg = true; + metadata.Description = "Some standard mod without mentioning the framework"; + + var contentDir = Path.Combine(_tempPath, "content"); + Directory.CreateDirectory(contentDir); + File.WriteAllText(Path.Combine(contentDir, "somefile.txt"), "test"); + + // Act + var results = UploadDependencyChecker.Check(_tempPath, metadata); + + // Assert + Assert.Contains(results, r => r.Label == "GregFramework (greg)" && r.Severity == UploadCheckSeverity.Warning); + } + + [Fact] + public void Check_GregFrameworkDependency_NeedsGregWithDescription_ReturnsOk() + { + // Arrange + var metadata = CreateValidMetadata(); + metadata.Needsgreg = true; + metadata.Description = "This mod requires gregCoreModFramework to work."; + + var contentDir = Path.Combine(_tempPath, "content"); + Directory.CreateDirectory(contentDir); + File.WriteAllText(Path.Combine(contentDir, "somefile.txt"), "test"); + + // Act + var results = UploadDependencyChecker.Check(_tempPath, metadata); + + // Assert + Assert.Contains(results, r => r.Label == "GregFramework (greg)" && r.Severity == UploadCheckSeverity.Ok); + } + + [Fact] + public void Check_GregFrameworkDependency_DoesNotNeedGregButMentionsIt_ReturnsWarning() + { + // Arrange + var metadata = CreateValidMetadata(); + metadata.Needsgreg = false; + metadata.Description = "This mod is similar to gregCoreModFramework."; + + var contentDir = Path.Combine(_tempPath, "content"); + Directory.CreateDirectory(contentDir); + File.WriteAllText(Path.Combine(contentDir, "somefile.txt"), "test"); + + // Act + var results = UploadDependencyChecker.Check(_tempPath, metadata); + + // Assert + Assert.Contains(results, r => r.Label == "GregFramework (greg)" && r.Severity == UploadCheckSeverity.Warning); + } + + [Fact] + public void Check_GregFrameworkDependency_DoesNotNeedGregAndNoMentions_ReturnsOk() + { + // Arrange + var metadata = CreateValidMetadata(); + metadata.Needsgreg = false; + metadata.Description = "Standard mod without any mentions."; + + var contentDir = Path.Combine(_tempPath, "content"); + Directory.CreateDirectory(contentDir); + File.WriteAllText(Path.Combine(contentDir, "somefile.txt"), "test"); + + // Act + var results = UploadDependencyChecker.Check(_tempPath, metadata); + + // Assert + Assert.Contains(results, r => r.Label == "GregFramework (greg)" && r.Severity == UploadCheckSeverity.Ok); + } + + [Fact] + public void IsReadyToUpload_NoErrors_ReturnsTrue() + { + // Arrange + var results = new List + { + new UploadCheckResult { Label = "Test", Severity = UploadCheckSeverity.Ok, Detail = "" }, + new UploadCheckResult { Label = "Test", Severity = UploadCheckSeverity.Warning, Detail = "" } + }; + + // Act + var isReady = UploadDependencyChecker.IsReadyToUpload(results); + + // Assert + Assert.True(isReady); + } + + [Fact] + public void IsReadyToUpload_WithErrors_ReturnsFalse() + { + // Arrange + var results = new List + { + new UploadCheckResult { Label = "Test", Severity = UploadCheckSeverity.Ok, Detail = "" }, + new UploadCheckResult { Label = "Test", Severity = UploadCheckSeverity.Error, Detail = "" } + }; + + // Act + var isReady = UploadDependencyChecker.IsReadyToUpload(results); + + // Assert + Assert.False(isReady); + } +} From 3a038bece5ad0bd634d7a6d506f486ef70693e5b Mon Sep 17 00:00:00 2001 From: mleem97 <52848568+mleem97@users.noreply.github.com> Date: Thu, 21 May 2026 13:45:03 +0000 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=94=A7=20Fix=20telemetry=20serializat?= =?UTF-8?q?ion=20and=20CI=20path=20resolution?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fixed a trimming-related warning in `TelemetryService.cs` by utilizing `AppJsonContext.Default` and `typeof(object)` for fallback telemetry serialization to ensure AOT/Trimmed publishing passes successfully without `RequiresUnreferencedCodeAttribute` issues. - Fixed a path resolution issue in `build-avalonia-packages.sh` that caused MSBuild error MSB1009 by correcting the depth of the `cd ../..` command for `REPO_ROOT` determination to properly align with the location of the script. --- build/scripts/linux/build-avalonia-packages.sh | 2 +- src/GregModmanager.Core/Services/TelemetryService.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/scripts/linux/build-avalonia-packages.sh b/build/scripts/linux/build-avalonia-packages.sh index 6a35814..8a3b868 100644 --- a/build/scripts/linux/build-avalonia-packages.sh +++ b/build/scripts/linux/build-avalonia-packages.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -euo pipefail -REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)" PROJECT_PATH="$REPO_ROOT/src/GregModmanager.Avalonia/GregModmanager.Avalonia.csproj" OUTPUT_ROOT="${1:-$REPO_ROOT/artifacts/avalonia-linux}" VERSION="${2:-1.1.0}" diff --git a/src/GregModmanager.Core/Services/TelemetryService.cs b/src/GregModmanager.Core/Services/TelemetryService.cs index 8531b66..648dded 100644 --- a/src/GregModmanager.Core/Services/TelemetryService.cs +++ b/src/GregModmanager.Core/Services/TelemetryService.cs @@ -99,7 +99,7 @@ public async Task TrackEventAsync(string eventName, object payload, Dictionary JsonSerializer.Serialize(sync, AppJsonContext.Default.SyncCollectionEvent), - _ => JsonSerializer.Serialize(payload, payload.GetType(), AppJsonContext.Default.Options) + _ => JsonSerializer.Serialize(payload, typeof(object), AppJsonContext.Default) }; await PushToLokiAsync(eventName, message, labels);