using DocumentService.Application.AddAnnotation; using DocumentService.Application.AddStamp; using DocumentService.Application.ConvertToPdfA; using DocumentService.Application.MergePdfs; using DocumentService.Domain.Models.ValueObjects; using FluentAssertions; using FluentValidation; using FluentValidation.TestHelper; using Xunit; namespace DocumentService.Tests.Unit.Application.Validators; // ─── MergePdfsCommandValidator ──────────────────────────────────────────────── public class MergePdfsCommandValidatorTests { private readonly MergePdfsCommandValidator _validator = new(); [Fact] public void Validate_TwoOrMoreStreams_PassesValidation() { var cmd = new MergePdfsCommand { PdfStreams = [new MemoryStream(), new MemoryStream()] }; _validator.TestValidate(cmd).ShouldNotHaveAnyValidationErrors(); } [Fact] public void Validate_SingleStream_FailsValidation() { var cmd = new MergePdfsCommand { PdfStreams = [new MemoryStream()] }; _validator.TestValidate(cmd).ShouldHaveValidationErrorFor(x => x.PdfStreams); } [Fact] public void Validate_NullStreams_FailsValidation() { var cmd = new MergePdfsCommand { PdfStreams = null! }; var result = _validator.TestValidate(cmd); result.Errors.Should().NotBeEmpty(); } } // ─── AddAnnotationValidator ─────────────────────────────────────────────────── public class AddAnnotationValidatorTests { private readonly AddAnnotationValidator _validator = new(); [Fact] public void Validate_ValidCircleAnnotation_PassesValidation() { var cmd = new AddAnnotationCommand { PdfStream = new MemoryStream("%PDF"u8.ToArray()), AnnotationType = AnnotationType.Circle, PageNumber = 1, Rectangle = (10, 20, 200, 100) }; _validator.TestValidate(cmd).ShouldNotHaveAnyValidationErrors(); } [Fact] public void Validate_FreeTextWithoutContent_FailsValidation() { var cmd = new AddAnnotationCommand { PdfStream = new MemoryStream("%PDF"u8.ToArray()), AnnotationType = AnnotationType.FreeText, PageNumber = 1, Rectangle = (0, 0, 100, 50), Content = null }; _validator.TestValidate(cmd).ShouldHaveValidationErrorFor(x => x.Content); } [Fact] public void Validate_TextMarkupWithoutStyle_FailsValidation() { var cmd = new AddAnnotationCommand { PdfStream = new MemoryStream("%PDF"u8.ToArray()), AnnotationType = AnnotationType.TextMarkup, PageNumber = 1, Rectangle = (0, 0, 100, 50), TextMarkupStyle = null }; _validator.TestValidate(cmd).ShouldHaveValidationErrorFor(x => x.TextMarkupStyle); } [Fact] public void Validate_InvalidColorFormat_FailsValidation() { var cmd = new AddAnnotationCommand { PdfStream = new MemoryStream("%PDF"u8.ToArray()), AnnotationType = AnnotationType.Circle, PageNumber = 1, Rectangle = (0, 0, 100, 50), Color = "GGGGGG" // invalid hex }; _validator.TestValidate(cmd).ShouldHaveValidationErrorFor(x => x.Color); } [Fact] public void Validate_ValidColorFormat_PassesValidation() { var cmd = new AddAnnotationCommand { PdfStream = new MemoryStream("%PDF"u8.ToArray()), AnnotationType = AnnotationType.Circle, PageNumber = 1, Rectangle = (0, 0, 100, 50), Color = "FF0000" }; _validator.TestValidate(cmd).ShouldNotHaveAnyValidationErrors(); } [Fact] public void Validate_ZeroPageNumber_FailsValidation() { var cmd = new AddAnnotationCommand { PdfStream = new MemoryStream("%PDF"u8.ToArray()), AnnotationType = AnnotationType.Circle, PageNumber = 0, Rectangle = (0, 0, 100, 50) }; _validator.TestValidate(cmd).ShouldHaveValidationErrorFor(x => x.PageNumber); } [Fact] public void Validate_InvalidRectangle_FailsValidation() { var cmd = new AddAnnotationCommand { PdfStream = new MemoryStream("%PDF"u8.ToArray()), AnnotationType = AnnotationType.Circle, PageNumber = 1, Rectangle = (100, 100, 50, 50) // X2 < X1 }; _validator.TestValidate(cmd).ShouldHaveValidationErrorFor(x => x.Rectangle); } } // ─── AddStampValidator ──────────────────────────────────────────────────────── public class AddStampValidatorTests { private readonly AddStampValidator _validator = new(); [Fact] public void Validate_ValidTextStamp_PassesValidation() { var cmd = new AddStampCommand { PdfStream = new MemoryStream("%PDF"u8.ToArray()), StampType = StampType.Text, Position = (100, 200), Text = "DRAFT" }; _validator.TestValidate(cmd).ShouldNotHaveAnyValidationErrors(); } [Fact] public void Validate_TextStampWithoutText_FailsValidation() { var cmd = new AddStampCommand { PdfStream = new MemoryStream("%PDF"u8.ToArray()), StampType = StampType.Text, Position = (100, 200), Text = null }; _validator.TestValidate(cmd).ShouldHaveValidationErrorFor(x => x.Text); } [Fact] public void Validate_ImageStampWithoutBytes_FailsValidation() { var cmd = new AddStampCommand { PdfStream = new MemoryStream("%PDF"u8.ToArray()), StampType = StampType.Image, Position = (0, 0), ImageBytes = null }; _validator.TestValidate(cmd).ShouldHaveValidationErrorFor(x => x.ImageBytes); } [Fact] public void Validate_PredefinedStampWithoutType_FailsValidation() { var cmd = new AddStampCommand { PdfStream = new MemoryStream("%PDF"u8.ToArray()), StampType = StampType.Predefined, Position = (0, 0), PredefinedType = null }; _validator.TestValidate(cmd).ShouldHaveValidationErrorFor(x => x.PredefinedType); } [Fact] public void Validate_OpacityAboveOne_FailsValidation() { var cmd = new AddStampCommand { PdfStream = new MemoryStream("%PDF"u8.ToArray()), StampType = StampType.Text, Position = (0, 0), Text = "TEST", Opacity = 1.5 }; _validator.TestValidate(cmd).ShouldHaveValidationErrorFor(x => x.Opacity!.Value); } [Fact] public void Validate_InvalidColorHex_FailsValidation() { var cmd = new AddStampCommand { PdfStream = new MemoryStream("%PDF"u8.ToArray()), StampType = StampType.Text, Position = (0, 0), Text = "TEST", Color = "XYZ" }; _validator.TestValidate(cmd).ShouldHaveValidationErrorFor(x => x.Color!); } } // ─── ConvertToPdfACommandValidator ─────────────────────────────────────────── public class ConvertToPdfAValidatorTests { private readonly ConvertToPdfACommandValidator _validator = new(); [Fact] public void Validate_ValidPdfALevel_PassesValidation() { foreach (var level in new[] { "PDF/A-1b", "PDF/A-2b", "PDF/A-3b", "PDF/A-3u" }) { var cmd = new ConvertToPdfACommand { PdfStream = new MemoryStream(), PdfALevel = level }; _validator.TestValidate(cmd).ShouldNotHaveAnyValidationErrors(); } } [Fact] public void Validate_InvalidPdfALevel_FailsValidation() { var cmd = new ConvertToPdfACommand { PdfStream = new MemoryStream(), PdfALevel = "PDF/A-99z" }; _validator.TestValidate(cmd).ShouldHaveValidationErrorFor(x => x.PdfALevel); } [Fact] public void Validate_EmptyPdfALevel_FailsValidation() { var cmd = new ConvertToPdfACommand { PdfStream = new MemoryStream(), PdfALevel = "" }; _validator.TestValidate(cmd).ShouldHaveValidationErrorFor(x => x.PdfALevel); } [Fact] public void Validate_NullStream_FailsValidation() { var cmd = new ConvertToPdfACommand { PdfStream = null!, PdfALevel = "PDF/A-1b" }; _validator.TestValidate(cmd).ShouldHaveValidationErrorFor(x => x.PdfStream); } }