using Ludarium.Domain; using Ludarium.Infrastructure; namespace Ludarium.IntegrationTests; public sealed class ReadOnlyLibraryFileSystemTests : IDisposable { private readonly string root = Path.Combine(Path.GetTempPath(), $"ludarium-exclusions-{Guid.NewGuid():N}"); [Fact] public async Task EnumerationAppliesBoundedFolderAndFileGlobExclusions() { Directory.CreateDirectory(Path.Combine(root, "roms", "nes")); Directory.CreateDirectory(Path.Combine(root, "tools", "emulator", "assets")); await File.WriteAllBytesAsync(Path.Combine(root, "roms", "nes", "game.nes"), [1]); await File.WriteAllBytesAsync(Path.Combine(root, "roms", "nes", "readme.txt"), [2]); await File.WriteAllBytesAsync(Path.Combine(root, "tools", "emulator", "emu.exe"), [3]); await File.WriteAllBytesAsync(Path.Combine(root, "tools", "emulator", "assets", "icon.png"), [4]); var library = LibraryRoot.Create("Synthetic", root, LibraryKind.Mixed) with { Exclusions = ["tools/**", "roms/*/*.txt"] }; var observed = new List(); await foreach (var file in new ReadOnlyLibraryFileSystem().EnumerateAsync(library, CancellationToken.None)) observed.Add(file.RelativePath); Assert.Equal(["roms/nes/game.nes"], observed); } [Fact] public void VerificationReportsWhetherTheMountedSourceContainsEntries() { Directory.CreateDirectory(root); var fileSystem = new ReadOnlyLibraryFileSystem(); Assert.False(fileSystem.Verify(root).HasEntries); Directory.CreateDirectory(Path.Combine(root, "collection")); Assert.True(fileSystem.Verify(root).HasEntries); } [Fact] public void ResolutionRejectsAFileReachedThroughASymbolicLink() { Directory.CreateDirectory(root); var outside = Path.Combine(Path.GetTempPath(), $"ludarium-outside-{Guid.NewGuid():N}.bin"); var link = Path.Combine(root, "linked.bin"); File.WriteAllText(outside, "not library content"); try { try { File.CreateSymbolicLink(link, outside); } catch (Exception exception) when (exception is PlatformNotSupportedException or UnauthorizedAccessException or IOException) { return; } var library = LibraryRoot.Create("Synthetic", root, LibraryKind.Mixed); Assert.Throws(() => new ReadOnlyLibraryFileSystem().ResolveContainedPath(library, "linked.bin")); } finally { if (File.Exists(link)) File.Delete(link); if (File.Exists(outside)) File.Delete(outside); } } public void Dispose() { if (Directory.Exists(root)) Directory.Delete(root, true); GC.SuppressFinalize(this); } }