diff --git a/GenHub/GenHub.Tests/GenHub.Tests.Core/Features/Manifest/ManifestDiscoveryServiceTests.cs b/GenHub/GenHub.Tests/GenHub.Tests.Core/Features/Manifest/ManifestDiscoveryServiceTests.cs
index 6cf445c5d..81b6c547b 100644
--- a/GenHub/GenHub.Tests/GenHub.Tests.Core/Features/Manifest/ManifestDiscoveryServiceTests.cs
+++ b/GenHub/GenHub.Tests/GenHub.Tests.Core/Features/Manifest/ManifestDiscoveryServiceTests.cs
@@ -4,6 +4,7 @@
using GenHub.Features.Manifest;
using Microsoft.Extensions.Logging;
using Moq;
+using System.Text.Json;
using ContentType = GenHub.Core.Models.Enums.ContentType;
namespace GenHub.Tests.Features.Manifest;
@@ -11,7 +12,7 @@ namespace GenHub.Tests.Features.Manifest;
///
/// Unit tests for the class.
///
-public class ManifestDiscoveryServiceTests
+public class ManifestDiscoveryServiceTests : IDisposable
{
///
/// Mock logger for the manifest discovery service.
@@ -28,6 +29,11 @@ public class ManifestDiscoveryServiceTests
///
private readonly ManifestDiscoveryService _discoveryService;
+ ///
+ /// Temporary directory used for filesystem discovery tests.
+ ///
+ private readonly string _tempDirectory;
+
///
/// Initializes a new instance of the class.
///
@@ -36,6 +42,7 @@ public ManifestDiscoveryServiceTests()
_loggerMock = new Mock>();
_cacheMock = new Mock();
_discoveryService = new ManifestDiscoveryService(_loggerMock.Object, _cacheMock.Object);
+ _tempDirectory = Directory.CreateTempSubdirectory("GenHub.ManifestDiscoveryTests.").FullName;
}
///
@@ -84,6 +91,58 @@ public void GetCompatibleManifests_FiltersCorrectly()
Assert.Single(zeroHourCompatible);
}
+ ///
+ /// Tests that manifest discovery finds JSON manifests in nested directories and ignores non-JSON files.
+ ///
+ /// A representing the asynchronous test operation.
+ [Fact]
+ public async Task DiscoverManifestsAsync_DiscoversNestedJsonManifest_AndIgnoresNonJsonFile()
+ {
+ // Arrange
+ const string nestedManifestId = "1.0.genhub.mod.nested";
+ const string ignoredManifestId = "1.0.genhub.mod.ignored";
+ var nestedDirectory = Path.Combine(_tempDirectory, "content", "manifests");
+ Directory.CreateDirectory(nestedDirectory);
+ await File.WriteAllTextAsync(
+ Path.Combine(nestedDirectory, "nested.json"),
+ SerializeManifest(nestedManifestId));
+ await File.WriteAllTextAsync(
+ Path.Combine(_tempDirectory, "ignored.txt"),
+ SerializeManifest(ignoredManifestId));
+
+ // Act
+ var manifests = await _discoveryService.DiscoverManifestsAsync([_tempDirectory]);
+
+ // Assert
+ Assert.Single(manifests);
+ Assert.Contains(nestedManifestId, manifests);
+ Assert.DoesNotContain(ignoredManifestId, manifests);
+ }
+
+ ///
+ /// Tests that a malformed JSON file does not prevent other nested manifests from being discovered.
+ ///
+ /// A representing the asynchronous test operation.
+ [Fact]
+ public async Task DiscoverManifestsAsync_WithMalformedJson_ContinuesDiscoveringNestedManifest()
+ {
+ // Arrange
+ const string nestedManifestId = "1.0.genhub.mod.valid";
+ var nestedDirectory = Path.Combine(_tempDirectory, "content", "manifests");
+ Directory.CreateDirectory(nestedDirectory);
+ await File.WriteAllTextAsync(
+ Path.Combine(nestedDirectory, "valid.json"),
+ SerializeManifest(nestedManifestId));
+ await File.WriteAllTextAsync(Path.Combine(_tempDirectory, "malformed.json"), "{ invalid json");
+
+ // Act
+ var manifests = await _discoveryService.DiscoverManifestsAsync([_tempDirectory]);
+
+ // Assert
+ var manifest = Assert.Single(manifests);
+ Assert.Equal(nestedManifestId, manifest.Key);
+ }
+
///
/// Tests that ValidateDependencies returns false when a required dependency is missing.
///
@@ -156,4 +215,27 @@ public void ValidateDependencies_ReturnsTrue_WhenNoDependencies()
// Assert
Assert.True(result);
}
-}
\ No newline at end of file
+
+ ///
+ /// Deletes temporary files created by filesystem discovery tests.
+ ///
+ public void Dispose()
+ {
+ try
+ {
+ if (Directory.Exists(_tempDirectory))
+ {
+ Directory.Delete(_tempDirectory, true);
+ }
+ }
+ catch (Exception ex) when (ex is IOException or UnauthorizedAccessException)
+ {
+ // Best-effort cleanup should not fail an otherwise successful test.
+ }
+ }
+
+ private static string SerializeManifest(string id)
+ {
+ return JsonSerializer.Serialize(new ContentManifest { Id = ManifestId.Create(id) });
+ }
+}
diff --git a/GenHub/GenHub/Features/Manifest/ManifestDiscoveryService.cs b/GenHub/GenHub/Features/Manifest/ManifestDiscoveryService.cs
index 6f9350c32..9691169ba 100644
--- a/GenHub/GenHub/Features/Manifest/ManifestDiscoveryService.cs
+++ b/GenHub/GenHub/Features/Manifest/ManifestDiscoveryService.cs
@@ -61,7 +61,7 @@ public async Task> DiscoverManifestsAsync(
foreach (var directory in searchDirectories.Where(Directory.Exists))
{
logger.LogInformation("Scanning directory for manifests: {Directory}", directory);
- var manifestFiles = Directory.EnumerateFiles(directory, "FileTypes.JsonFilePattern", SearchOption.AllDirectories);
+ var manifestFiles = Directory.EnumerateFiles(directory, FileTypes.JsonFilePattern, SearchOption.AllDirectories);
foreach (var manifestFile in manifestFiles)
{
try
@@ -242,4 +242,4 @@ private async Task DiscoverEmbeddedManifestsAsync(CancellationToken cancellation
}
}
}
-}
\ No newline at end of file
+}