using AmiReel.Models; using AmiReel.Services; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace AmiReel.Tests.Services; [TestClass] public class ShortsPipelineTests { private readonly List _tempDirectories = []; [TestCleanup] public void Cleanup() { foreach (string directory in _tempDirectories) { try { Directory.Delete(directory, true); } catch { } } } private string CreateTempDirectory() { string directory = Path.Combine(Path.GetTempPath(), "AmiReelTests", Guid.NewGuid().ToString("N")); Directory.CreateDirectory(directory); _tempDirectories.Add(directory); return directory; } private static ShortSettings ValidSettings(string input, string font, ShortStyle style = ShortStyle.Crop, string background = "") => new() { InputFile = input, OutputPath = Path.Combine(Path.GetDirectoryName(input) ?? "", "out.mp4"), Title = "Test Short", Hook = "THIS RAN ON AN AMIGA", Website = "AMIGADB.NET", Start = "00:00:00", DurationSeconds = 30, Style = style, BackgroundImage = background, FontFile = font, FfmpegPath = "", FfprobePath = "", }; [TestMethod] public void EscapeFfmpegPath_WithBackslashesColonsAndQuotes_EscapesAllThree() { // Act string result = ShortsPipeline.EscapeFfmpegPath(@"C:\Fonts\font's.ttf"); // Assert Assert.AreEqual(@"C\:\\Fonts\\font\'s.ttf", result); } [TestMethod] public void EscapeFfmpegPath_EscapesBackslashesBeforeIntroducingNewOnes() { // Act: if colon/quote escaping ran before backslash escaping, the backslashes // they introduce would themselves get doubled, which must not happen. string result = ShortsPipeline.EscapeFfmpegPath("C:\\a"); // Assert Assert.AreEqual("C\\:\\\\a", result); } [TestMethod] public void BuildMeta_WithAllFieldsPopulated_JoinsWithMiddleDot() { // Arrange ShortSettings settings = ValidSettings("in.mp4", "font.ttf") with { Group = "Pirates", Year = "1991", Type = "Cracktro" }; // Act string meta = ShortsPipeline.BuildMeta(settings); // Assert Assert.AreEqual("Pirates · 1991 · Cracktro", meta); } [TestMethod] public void BuildMeta_WithSomeFieldsBlank_SkipsBlankFields() { // Arrange ShortSettings settings = ValidSettings("in.mp4", "font.ttf") with { Group = "Pirates", Year = "", Type = " " }; // Act string meta = ShortsPipeline.BuildMeta(settings); // Assert Assert.AreEqual("Pirates", meta); } [TestMethod] public void BuildMeta_WithAllFieldsBlank_ReturnsEmptyString() { // Arrange ShortSettings settings = ValidSettings("in.mp4", "font.ttf"); // Act string meta = ShortsPipeline.BuildMeta(settings); // Assert Assert.AreEqual("", meta); } [TestMethod] public void Validate_WithMissingInputFile_ThrowsFileNotFoundException() { // Arrange string directory = CreateTempDirectory(); ShortSettings settings = ValidSettings(Path.Combine(directory, "missing.mp4"), "font.ttf"); // Act & Assert Assert.ThrowsExactly(() => ShortsPipeline.Validate(settings)); } [TestMethod] public void Validate_WithBlankTitle_ThrowsArgumentException() { // Arrange string directory = CreateTempDirectory(); string input = Path.Combine(directory, "clip.mp4"); File.WriteAllText(input, "data"); string font = Path.Combine(directory, "font.ttf"); File.WriteAllText(font, "data"); ShortSettings settings = ValidSettings(input, font) with { Title = "" }; // Act & Assert Assert.ThrowsExactly(() => ShortsPipeline.Validate(settings)); } [TestMethod] public void Validate_WithZeroDuration_ThrowsArgumentException() { // Arrange string directory = CreateTempDirectory(); string input = Path.Combine(directory, "clip.mp4"); File.WriteAllText(input, "data"); string font = Path.Combine(directory, "font.ttf"); File.WriteAllText(font, "data"); ShortSettings settings = ValidSettings(input, font) with { DurationSeconds = 0 }; // Act & Assert Assert.ThrowsExactly(() => ShortsPipeline.Validate(settings)); } [TestMethod] public void Validate_WithMissingFontFile_ThrowsFileNotFoundException() { // Arrange string directory = CreateTempDirectory(); string input = Path.Combine(directory, "clip.mp4"); File.WriteAllText(input, "data"); ShortSettings settings = ValidSettings(input, Path.Combine(directory, "missing-font.ttf")); // Act & Assert Assert.ThrowsExactly(() => ShortsPipeline.Validate(settings)); } [TestMethod] public void Validate_BrandStyleWithoutBackgroundImage_ThrowsFileNotFoundException() { // Arrange string directory = CreateTempDirectory(); string input = Path.Combine(directory, "clip.mp4"); File.WriteAllText(input, "data"); string font = Path.Combine(directory, "font.ttf"); File.WriteAllText(font, "data"); ShortSettings settings = ValidSettings(input, font, ShortStyle.Brand); // Act & Assert Assert.ThrowsExactly(() => ShortsPipeline.Validate(settings)); } [TestMethod] public void Validate_NonBrandStyleWithoutBackgroundImage_DoesNotThrow() { // Arrange string directory = CreateTempDirectory(); string input = Path.Combine(directory, "clip.mp4"); File.WriteAllText(input, "data"); string font = Path.Combine(directory, "font.ttf"); File.WriteAllText(font, "data"); ShortSettings settings = ValidSettings(input, font, ShortStyle.Crop); // Act & Assert (no exception) ShortsPipeline.Validate(settings); } [TestMethod] public void GetBaseFilter_ForEveryStyle_ReturnsAFilterEndingInBaseTag() { foreach (ShortStyle style in Enum.GetValues()) { // Act string filter = ShortsPipeline.GetBaseFilter(style); // Assert Assert.IsFalse(string.IsNullOrWhiteSpace(filter), $"{style} produced an empty filter."); StringAssert.Contains(filter, "[base]", $"{style} filter does not label an output as [base]."); } } [TestMethod] public void BuildFilterComplex_IncludesAllFourTextOverlaysAndTheirTimingGates() { // Act string result = ShortsPipeline.BuildFilterComplex( "[0:v]copy[base]", fontEsc: "C\\:\\\\font.ttf", hookEsc: "hook.txt", titleEsc: "title.txt", metaEsc: "meta.txt", websiteEsc: "website.txt", hookEnd: 2.7, infoStart: 23, ctaStart: 26.5); // Assert StringAssert.Contains(result, "textfile='hook.txt'"); StringAssert.Contains(result, "textfile='title.txt'"); StringAssert.Contains(result, "textfile='meta.txt'"); StringAssert.Contains(result, "textfile='website.txt'"); StringAssert.Contains(result, "enable='between(t,0,2.7)'"); StringAssert.Contains(result, "enable='gte(t,23)'"); StringAssert.Contains(result, "enable='gte(t,26.5)'"); StringAssert.Contains(result, "[vout]"); } }