using Explorer.Application; using Explorer.Domain; using Explorer.Domain.Abstractions; namespace Explorer.Application.Tests; public class ConversionPlannerTests { private static readonly DateTimeOffset T0 = DateTimeOffset.Parse("2024-06-01T12:00:00Z"); [Fact] public void Video_files_become_mp4_outputs() { var fs = Tree() .Dir(@"C:\src") .File(@"C:\src\clip.mov", 10, T0) .File(@"C:\src\notes.txt", 1, T0) .Dir(@"C:\dst"); var plan = Build([@"C:\src"], @"C:\dst", ConversionKind.VideoToMp4, fs); Assert.True(plan.CanEnqueue); var op = Assert.Single(plan.Operations); Assert.Equal(TransferOp.Convert, op.Op); Assert.Equal(@"C:\src\clip.mov", op.SourcePath); Assert.Equal(@"C:\dst\clip.mp4", op.DestinationPath); Assert.Contains(plan.Issues, i => i.Message.Contains("not a match", StringComparison.OrdinalIgnoreCase)); } [Fact] public void Online_only_files_are_skipped() { var fs = Tree() .Dir(@"C:\src") .File(@"C:\src\clip.mov", 10, T0) .Dir(@"C:\dst"); var plan = Build( [@"C:\src\clip.mov"], @"C:\dst", ConversionKind.VideoToMp4, fs, wouldHydrate: item => item.Name == "clip.mov"); Assert.False(plan.CanEnqueue); Assert.Contains(plan.Issues, i => i.Message.Contains("Online-only", StringComparison.OrdinalIgnoreCase)); } [Fact] public void Missing_ffmpeg_is_an_error() { var fs = Tree().Dir(@"C:\src").File(@"C:\src\clip.mov", 10, T0).Dir(@"C:\dst"); var plan = Build([@"C:\src\clip.mov"], @"C:\dst", ConversionKind.VideoToMp4, fs, ffmpegAvailable: false); Assert.False(plan.CanEnqueue); Assert.Contains(plan.Issues, i => i.Message.Contains("FFmpeg", StringComparison.OrdinalIgnoreCase)); } [Fact] public void Unique_names_avoid_overwrite() { var fs = Tree() .Dir(@"C:\src") .File(@"C:\src\clip.mov", 10, T0) .File(@"C:\src\clip.mkv", 10, T0) .Dir(@"C:\dst"); var plan = Build( [@"C:\src"], @"C:\dst", ConversionKind.VideoToMp4, fs, pathExists: path => path.Equals(@"C:\dst\clip.mp4", StringComparison.OrdinalIgnoreCase)); Assert.True(plan.CanEnqueue); Assert.Contains(plan.Operations, o => o.DestinationPath == @"C:\dst\clip (2).mp4"); Assert.Contains(plan.Operations, o => o.DestinationPath == @"C:\dst\clip (3).mp4"); } [Fact] public void Heic_maps_to_jpeg() { var fs = Tree() .Dir(@"C:\src") .File(@"C:\src\IMG_0001.HEIC", 4, T0) .Dir(@"C:\dst"); var plan = Build([@"C:\src\IMG_0001.HEIC"], @"C:\dst", ConversionKind.HeicToJpeg, fs); var op = Assert.Single(plan.Operations); Assert.Equal(@"C:\dst\IMG_0001.jpg", op.DestinationPath); Assert.Equal(nameof(ConversionKind.HeicToJpeg), op.NewName); } private static OperationPlan Build( string[] sources, string dest, ConversionKind kind, IFileSystemEnumerator fs, bool ffmpegAvailable = true, Func? pathExists = null, Func? wouldHydrate = null) => new ConversionPlanner().Build( sources, dest, kind, fs, ffmpegAvailable, FfmpegLocator.MissingHint, pathExists ?? (_ => false), wouldHydrate); private static TreeEnumerator Tree() => new(); }