Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
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;

/// <summary>
/// Unit tests for the <see cref="ManifestDiscoveryService"/> class.
/// </summary>
public class ManifestDiscoveryServiceTests
public class ManifestDiscoveryServiceTests : IDisposable
{
/// <summary>
/// Mock logger for the manifest discovery service.
Expand All @@ -28,6 +29,11 @@ public class ManifestDiscoveryServiceTests
/// </summary>
private readonly ManifestDiscoveryService _discoveryService;

/// <summary>
/// Temporary directory used for filesystem discovery tests.
/// </summary>
private readonly string _tempDirectory;

/// <summary>
/// Initializes a new instance of the <see cref="ManifestDiscoveryServiceTests"/> class.
/// </summary>
Expand All @@ -36,6 +42,7 @@ public ManifestDiscoveryServiceTests()
_loggerMock = new Mock<ILogger<ManifestDiscoveryService>>();
_cacheMock = new Mock<IManifestCache>();
_discoveryService = new ManifestDiscoveryService(_loggerMock.Object, _cacheMock.Object);
_tempDirectory = Directory.CreateTempSubdirectory("GenHub.ManifestDiscoveryTests.").FullName;
}

/// <summary>
Expand Down Expand Up @@ -84,6 +91,58 @@ public void GetCompatibleManifests_FiltersCorrectly()
Assert.Single(zeroHourCompatible);
}

/// <summary>
/// Tests that manifest discovery finds JSON manifests in nested directories and ignores non-JSON files.
/// </summary>
/// <returns>A <see cref="Task"/> representing the asynchronous test operation.</returns>
[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);
}

/// <summary>
/// Tests that a malformed JSON file does not prevent other nested manifests from being discovered.
/// </summary>
/// <returns>A <see cref="Task"/> representing the asynchronous test operation.</returns>
[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);
}

/// <summary>
/// Tests that ValidateDependencies returns false when a required dependency is missing.
/// </summary>
Expand Down Expand Up @@ -156,4 +215,27 @@ public void ValidateDependencies_ReturnsTrue_WhenNoDependencies()
// Assert
Assert.True(result);
}
}

/// <summary>
/// Deletes temporary files created by filesystem discovery tests.
/// </summary>
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.
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

private static string SerializeManifest(string id)
{
return JsonSerializer.Serialize(new ContentManifest { Id = ManifestId.Create(id) });
}
}
4 changes: 2 additions & 2 deletions GenHub/GenHub/Features/Manifest/ManifestDiscoveryService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public async Task<Dictionary<string, ContentManifest>> 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
Expand Down Expand Up @@ -242,4 +242,4 @@ private async Task DiscoverEmbeddedManifestsAsync(CancellationToken cancellation
}
}
}
}
}
Loading