From 755902816431815d45b21b819d43d5240634b960 Mon Sep 17 00:00:00 2001 From: TekH Date: Fri, 28 Aug 2026 11:46:28 +0200 Subject: [PATCH] test: add FluentValidation unit tests for all command/query validators ValidatorTests.cs covers 21 scenarios across 4 validators: MergePdfsCommandValidator (3): - TwoOrMoreStreams passes, SingleStream fails, NullStreams fails AddAnnotationValidator (7): - Valid circle passes, FreeText without content fails, TextMarkup without style fails, invalid/valid color hex, zero page number fails, invalid rectangle (X2 1.0 fails, invalid color hex fails ConvertToPdfACommandValidator (4): - All valid levels pass, invalid level fails, empty level fails, null stream fails --- .../Application/Validators/ValidatorTests.cs | 270 ++++++++++++++++++ 1 file changed, 270 insertions(+) create mode 100644 DocumentOperator.Tests/Unit/Application/Validators/ValidatorTests.cs diff --git a/DocumentOperator.Tests/Unit/Application/Validators/ValidatorTests.cs b/DocumentOperator.Tests/Unit/Application/Validators/ValidatorTests.cs new file mode 100644 index 0000000..ccaa024 --- /dev/null +++ b/DocumentOperator.Tests/Unit/Application/Validators/ValidatorTests.cs @@ -0,0 +1,270 @@ +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); + } +}