Refactor: Rename DocumentOperator to DocumentService
Updated project and solution files to rename and restructure `DocumentOperator` to `DocumentService`. Modified project references in `DocumentService.API.csproj`, `DocumentService.Application.csproj`, and `DocumentService.Infrastructure.csproj` to reflect the new naming convention. Updated `DocumentService.sln` to remove old project references, add new ones, and adjust configuration and nested project mappings accordingly. This change aligns the project structure with the new naming convention.
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
using DocumentService.API.Controllers; // For ExtractSwissQrCodeBase64Request DTO
|
||||
using DocumentService.Application.Common.DTOs;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using Xunit;
|
||||
|
||||
namespace DocumentService.Tests.Integration.API;
|
||||
|
||||
public sealed class ExtractSwissQrCodeEndpointTests : IClassFixture<WebApplicationFactory<Program>>
|
||||
{
|
||||
private readonly HttpClient _client;
|
||||
private readonly JsonSerializerOptions _jsonOptions;
|
||||
|
||||
public ExtractSwissQrCodeEndpointTests(WebApplicationFactory<Program> factory)
|
||||
{
|
||||
_client = factory.CreateClient();
|
||||
_jsonOptions = new JsonSerializerOptions
|
||||
{
|
||||
PropertyNameCaseInsensitive = true
|
||||
};
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task POST_ExtractSwissQrCode_ValidRequest_Returns200()
|
||||
{
|
||||
// Arrange
|
||||
var validPdfBase64 = GetEmbeddedResourceAsBase64("DocumentService.Tests.TestData.Pdfs.valid.pdf");
|
||||
|
||||
var request = new ExtractSwissQrCodeBase64Request
|
||||
{
|
||||
Base64Pdf = validPdfBase64
|
||||
};
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsJsonAsync("/api/pdf/qr-code/extract-swiss", request);
|
||||
|
||||
// Assert
|
||||
// Note: The test PDF (valid.pdf) may not actually contain a Swiss QR Code
|
||||
// so this test might return 404. We're testing the endpoint wiring here.
|
||||
// A real test would need a PDF with a Swiss QR Code on the last page.
|
||||
response.StatusCode.Should().BeOneOf(HttpStatusCode.OK, HttpStatusCode.NotFound);
|
||||
|
||||
if (response.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
var result = await response.Content.ReadFromJsonAsync<SwissQrCodeExtractionResult>(_jsonOptions);
|
||||
result.Should().NotBeNull();
|
||||
result.Bill.Should().NotBeNull();
|
||||
result.RawLines.Should().NotBeEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task POST_ExtractSwissQrCode_InvalidBase64_Returns400()
|
||||
{
|
||||
// Arrange
|
||||
var request = new ExtractSwissQrCodeBase64Request
|
||||
{
|
||||
Base64Pdf = "INVALID_BASE64!!!"
|
||||
};
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsJsonAsync("/api/pdf/qr-code/extract-swiss", request);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
||||
|
||||
var problemDetails = await response.Content.ReadAsStringAsync();
|
||||
// FormatException message contains "Base-64" (with hyphen)
|
||||
problemDetails.Should().MatchRegex("(?i)base.?64", "should contain Base64 validation error");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task POST_ExtractSwissQrCode_NullReferences_AcceptedByValidation()
|
||||
{
|
||||
// Arrange: References are OPTIONAL - null should not cause validation error (400)
|
||||
// Using pdfWithSwissQRCode.pdf which actually has a QR code, so we get 200
|
||||
var validPdfBase64 = GetEmbeddedResourceAsBase64("DocumentService.Tests.TestData.Pdfs.pdfWithSwissQRCode.pdf");
|
||||
|
||||
var request = new
|
||||
{
|
||||
References = (List<string>?)null, // Optional field - should not cause 400
|
||||
Base64Pdf = validPdfBase64
|
||||
};
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsJsonAsync("/api/pdf/qr-code/extract-swiss", request);
|
||||
|
||||
// Assert: Should NOT be 400 (validation error), should be 200 or 404
|
||||
response.StatusCode.Should().NotBe(HttpStatusCode.BadRequest,
|
||||
"null References should be accepted (optional field)");
|
||||
|
||||
// If extraction succeeds, verify empty references array
|
||||
if (response.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
var result = await response.Content.ReadFromJsonAsync<SwissQrCodeExtractionResult>();
|
||||
result.Should().NotBeNull();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task POST_ExtractSwissQrCode_EmptyPdf_Returns400()
|
||||
{
|
||||
// Arrange
|
||||
var request = new
|
||||
{
|
||||
References = new List<string> { "REF-001" },
|
||||
Base64Pdf = (string?)null
|
||||
};
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsJsonAsync("/api/pdf/qr-code/extract-swiss", request);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to load embedded resource as Base64 string
|
||||
/// </summary>
|
||||
private static string GetEmbeddedResourceAsBase64(string resourceName)
|
||||
{
|
||||
var assembly = typeof(ExtractSwissQrCodeEndpointTests).Assembly;
|
||||
using var stream = assembly.GetManifestResourceStream(resourceName);
|
||||
|
||||
if (stream == null)
|
||||
{
|
||||
throw new FileNotFoundException($"Embedded resource not found: {resourceName}");
|
||||
}
|
||||
|
||||
using var memoryStream = new MemoryStream();
|
||||
stream.CopyTo(memoryStream);
|
||||
return Convert.ToBase64String(memoryStream.ToArray());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,383 @@
|
||||
using DocumentService.Application.Common.DTOs;
|
||||
using DocumentService.Client.Models.Requests;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using Xunit;
|
||||
|
||||
namespace DocumentService.Tests.Integration.API;
|
||||
|
||||
/// <summary>
|
||||
/// Integration tests for PdfAttachmentController.
|
||||
/// Tests /api/pdf/attachments/check endpoint with both multipart and Base64 input.
|
||||
/// </summary>
|
||||
public class PdfAttachmentControllerTests : IClassFixture<WebApplicationFactory<Program>>
|
||||
{
|
||||
private readonly WebApplicationFactory<Program> _factory;
|
||||
private readonly HttpClient _client;
|
||||
|
||||
public PdfAttachmentControllerTests(WebApplicationFactory<Program> factory)
|
||||
{
|
||||
_factory = factory;
|
||||
_client = _factory.CreateClient();
|
||||
}
|
||||
|
||||
#region Helper Methods
|
||||
|
||||
/// <summary>
|
||||
/// Loads a test PDF from embedded resources.
|
||||
/// </summary>
|
||||
private static async Task<byte[]> LoadTestPdfAsync(string filename)
|
||||
{
|
||||
var assembly = typeof(PdfAttachmentControllerTests).Assembly;
|
||||
var resourceName = $"DocumentService.Tests.TestData.Pdfs.{filename}";
|
||||
|
||||
using var stream = assembly.GetManifestResourceStream(resourceName);
|
||||
if (stream == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Test resource '{resourceName}' not found");
|
||||
}
|
||||
|
||||
using var ms = new MemoryStream();
|
||||
await stream.CopyToAsync(ms);
|
||||
return ms.ToArray();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Base64 JSON Tests
|
||||
|
||||
[Fact]
|
||||
public async Task POST_CheckAttachments_Base64_PdfWithoutAttachments_Returns200()
|
||||
{
|
||||
// Arrange
|
||||
byte[] pdfBytes = await LoadTestPdfAsync("pdfWithSwissQRCode.pdf");
|
||||
string base64Pdf = Convert.ToBase64String(pdfBytes);
|
||||
var request = new CheckPdfAttachmentsRequest { Base64Pdf = base64Pdf };
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsJsonAsync("/api/pdf/attachments/check", request);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
|
||||
var result = await response.Content.ReadFromJsonAsync<AttachmentCheckResult>();
|
||||
result.Should().NotBeNull();
|
||||
result!.HasAttachments.Should().BeFalse();
|
||||
result.AttachmentCount.Should().Be(0);
|
||||
result.Attachments.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task POST_CheckAttachments_Base64_PdfWithMultipleAttachments_Returns200()
|
||||
{
|
||||
// Arrange
|
||||
byte[] pdfBytes = await LoadTestPdfAsync("pdfWithMoreThanOneAttachment.pdf");
|
||||
string base64Pdf = Convert.ToBase64String(pdfBytes);
|
||||
var request = new CheckPdfAttachmentsRequest { Base64Pdf = base64Pdf };
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsJsonAsync("/api/pdf/attachments/check", request);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
|
||||
var result = await response.Content.ReadFromJsonAsync<AttachmentCheckResult>();
|
||||
result.Should().NotBeNull();
|
||||
result!.HasAttachments.Should().BeTrue();
|
||||
result.AttachmentCount.Should().Be(6, "PDF has exactly 6 attachments");
|
||||
result.Attachments.Should().HaveCount(6);
|
||||
|
||||
// Verify each attachment has required properties
|
||||
foreach (var attachment in result.Attachments)
|
||||
{
|
||||
attachment.FileName.Should().NotBeNullOrEmpty();
|
||||
attachment.MimeType.Should().NotBeNullOrEmpty();
|
||||
attachment.Size.Should().BeGreaterThan(0);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task POST_CheckAttachments_Base64_InvalidBase64_Returns400()
|
||||
{
|
||||
// Arrange
|
||||
var request = new CheckPdfAttachmentsRequest { Base64Pdf = "invalid-base64!!!" };
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsJsonAsync("/api/pdf/attachments/check", request);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
||||
|
||||
var problemDetails = await response.Content.ReadAsStringAsync();
|
||||
// FormatException message contains "Base-64" (with hyphen)
|
||||
problemDetails.Should().MatchRegex("(?i)base.?64", "should contain Base64 validation error");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task POST_CheckAttachments_Base64_EmptyPdf_Returns400()
|
||||
{
|
||||
// Arrange
|
||||
var request = new CheckPdfAttachmentsRequest { Base64Pdf = string.Empty };
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsJsonAsync("/api/pdf/attachments/check", request);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
||||
|
||||
var problemDetails = await response.Content.ReadAsStringAsync();
|
||||
// Empty Base64 causes FormatException or empty stream error
|
||||
problemDetails.Should().MatchRegex("(Base64|empty|stream)", "should contain validation error message");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Multipart/Form-Data Tests
|
||||
|
||||
[Fact]
|
||||
public async Task POST_CheckAttachments_Multipart_PdfWithoutAttachments_Returns200()
|
||||
{
|
||||
// Arrange
|
||||
byte[] pdfBytes = await LoadTestPdfAsync("pdfWithSwissQRCode.pdf");
|
||||
|
||||
using var content = new MultipartFormDataContent();
|
||||
var fileContent = new ByteArrayContent(pdfBytes);
|
||||
fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
|
||||
content.Add(fileContent, "file", "pdfWithSwissQRCode.pdf");
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsync("/api/pdf/attachments/check", content);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
|
||||
var result = await response.Content.ReadFromJsonAsync<AttachmentCheckResult>();
|
||||
result.Should().NotBeNull();
|
||||
result!.HasAttachments.Should().BeFalse();
|
||||
result.AttachmentCount.Should().Be(0);
|
||||
result.Attachments.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task POST_CheckAttachments_Multipart_PdfWithMultipleAttachments_Returns200()
|
||||
{
|
||||
// Arrange
|
||||
byte[] pdfBytes = await LoadTestPdfAsync("pdfWithMoreThanOneAttachment.pdf");
|
||||
|
||||
using var content = new MultipartFormDataContent();
|
||||
var fileContent = new ByteArrayContent(pdfBytes);
|
||||
fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
|
||||
content.Add(fileContent, "file", "pdfWithMoreThanOneAttachment.pdf");
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsync("/api/pdf/attachments/check", content);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
|
||||
var result = await response.Content.ReadFromJsonAsync<AttachmentCheckResult>();
|
||||
result.Should().NotBeNull();
|
||||
result!.HasAttachments.Should().BeTrue();
|
||||
result.AttachmentCount.Should().Be(6);
|
||||
result.Attachments.Should().HaveCount(6);
|
||||
|
||||
// Verify first attachment details
|
||||
var firstAttachment = result.Attachments.First();
|
||||
firstAttachment.FileName.Should().NotBeNullOrEmpty();
|
||||
firstAttachment.MimeType.Should().NotBeNullOrEmpty();
|
||||
firstAttachment.Size.Should().BeGreaterThan(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task POST_CheckAttachments_Multipart_MissingFile_Returns400()
|
||||
{
|
||||
// Arrange
|
||||
using var content = new MultipartFormDataContent(); // No file added
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsync("/api/pdf/attachments/check", content);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task POST_CheckAttachments_Multipart_CorruptedPdf_Returns500()
|
||||
{
|
||||
// Arrange
|
||||
byte[] corruptedBytes = "This is not a valid PDF content"u8.ToArray();
|
||||
|
||||
using var content = new MultipartFormDataContent();
|
||||
var fileContent = new ByteArrayContent(corruptedBytes);
|
||||
fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
|
||||
content.Add(fileContent, "file", "corrupted.pdf");
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsync("/api/pdf/attachments/check", content);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.InternalServerError);
|
||||
|
||||
var problemDetails = await response.Content.ReadAsStringAsync();
|
||||
// Note: Generic error message for security reasons (doesn't expose internal details)
|
||||
problemDetails.Should().Contain("error");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Edge Cases
|
||||
|
||||
[Fact]
|
||||
public async Task POST_CheckAttachments_Base64_PdfWithSwissQrCode_Returns200()
|
||||
{
|
||||
// Arrange
|
||||
byte[] pdfBytes = await LoadTestPdfAsync("pdfWithSwissQRCode.pdf");
|
||||
string base64Pdf = Convert.ToBase64String(pdfBytes);
|
||||
var request = new CheckPdfAttachmentsRequest { Base64Pdf = base64Pdf };
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsJsonAsync("/api/pdf/attachments/check", request);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
|
||||
var result = await response.Content.ReadFromJsonAsync<AttachmentCheckResult>();
|
||||
result.Should().NotBeNull();
|
||||
result!.HasAttachments.Should().BeFalse("Swiss QR PDF has no attachments");
|
||||
result.AttachmentCount.Should().Be(0);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Extract Attachments Tests (Multipart)
|
||||
|
||||
[Fact]
|
||||
public async Task POST_ExtractAttachments_Multipart_ValidPdfWithAttachments_Returns200WithZip()
|
||||
{
|
||||
// Arrange
|
||||
byte[] pdfBytes = await LoadTestPdfAsync("pdfWithMoreThanOneAttachment.pdf");
|
||||
|
||||
using var content = new MultipartFormDataContent();
|
||||
var fileContent = new ByteArrayContent(pdfBytes);
|
||||
fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
|
||||
content.Add(fileContent, "file", "test.pdf");
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsync("/api/pdf/attachments/extract", content);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
response.Content.Headers.ContentType?.MediaType.Should().Be("application/zip");
|
||||
response.Content.Headers.ContentDisposition?.FileName.Should().Be("attachments.zip");
|
||||
|
||||
byte[] zipBytes = await response.Content.ReadAsByteArrayAsync();
|
||||
zipBytes.Should().NotBeEmpty("ZIP should contain data");
|
||||
|
||||
// Verify ZIP structure
|
||||
using var zipStream = new MemoryStream(zipBytes);
|
||||
using var zipArchive = new System.IO.Compression.ZipArchive(zipStream, System.IO.Compression.ZipArchiveMode.Read);
|
||||
|
||||
zipArchive.Entries.Should().HaveCount(6, "PDF contains 6 attachments");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task POST_ExtractAttachments_Multipart_EmptyPdf_Returns400()
|
||||
{
|
||||
// Arrange
|
||||
using var content = new MultipartFormDataContent();
|
||||
var fileContent = new ByteArrayContent(Array.Empty<byte>());
|
||||
fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
|
||||
content.Add(fileContent, "file", "empty.pdf");
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsync("/api/pdf/attachments/extract", content);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task POST_ExtractAttachments_Multipart_CorruptedPdf_Returns500()
|
||||
{
|
||||
// Arrange
|
||||
byte[] corruptedBytes = "This is not a valid PDF content"u8.ToArray();
|
||||
|
||||
using var content = new MultipartFormDataContent();
|
||||
var fileContent = new ByteArrayContent(corruptedBytes);
|
||||
fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
|
||||
content.Add(fileContent, "file", "corrupted.pdf");
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsync("/api/pdf/attachments/extract", content);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.InternalServerError);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Extract Attachments Tests (Base64)
|
||||
|
||||
[Fact]
|
||||
public async Task POST_ExtractAttachments_Base64_ValidPdfWithAttachments_Returns200WithZip()
|
||||
{
|
||||
// Arrange
|
||||
byte[] pdfBytes = await LoadTestPdfAsync("pdfWithMoreThanOneAttachment.pdf");
|
||||
string base64Pdf = Convert.ToBase64String(pdfBytes);
|
||||
var request = new ExtractPdfAttachmentsRequest { Base64Pdf = base64Pdf };
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsJsonAsync("/api/pdf/attachments/extract", request);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
response.Content.Headers.ContentType?.MediaType.Should().Be("application/zip");
|
||||
response.Content.Headers.ContentDisposition?.FileName.Should().Be("attachments.zip");
|
||||
|
||||
byte[] zipBytes = await response.Content.ReadAsByteArrayAsync();
|
||||
zipBytes.Should().NotBeEmpty("ZIP should contain data");
|
||||
|
||||
// Verify ZIP structure
|
||||
using var zipStream = new MemoryStream(zipBytes);
|
||||
using var zipArchive = new System.IO.Compression.ZipArchive(zipStream, System.IO.Compression.ZipArchiveMode.Read);
|
||||
|
||||
zipArchive.Entries.Should().HaveCount(6, "PDF contains 6 attachments");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task POST_ExtractAttachments_Base64_InvalidBase64_Returns400()
|
||||
{
|
||||
// Arrange
|
||||
var request = new ExtractPdfAttachmentsRequest { Base64Pdf = "invalid-base64!!!" };
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsJsonAsync("/api/pdf/attachments/extract", request);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
||||
|
||||
var problemDetails = await response.Content.ReadAsStringAsync();
|
||||
// FormatException message contains "Base-64" (with hyphen)
|
||||
problemDetails.Should().MatchRegex("(?i)base.?64", "should contain Base64 validation error");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task POST_ExtractAttachments_Base64_EmptyPdf_Returns400()
|
||||
{
|
||||
// Arrange
|
||||
var request = new ExtractPdfAttachmentsRequest { Base64Pdf = string.Empty };
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsJsonAsync("/api/pdf/attachments/extract", request);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
||||
|
||||
var problemDetails = await response.Content.ReadAsStringAsync();
|
||||
problemDetails.Should().MatchRegex("(Base64|empty|stream)", "should contain validation error message");
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,500 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using DocumentService.API.Controllers;
|
||||
using DocumentService.Domain.Models.ValueObjects;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
|
||||
namespace DocumentService.Tests.Integration.API;
|
||||
|
||||
public class PdfOperationsControllerTests : IClassFixture<WebApplicationFactory<Program>>
|
||||
{
|
||||
private readonly HttpClient _client;
|
||||
|
||||
public PdfOperationsControllerTests(WebApplicationFactory<Program> factory)
|
||||
{
|
||||
_client = factory.CreateClient();
|
||||
}
|
||||
|
||||
private static Stream LoadTestPdfAsStream(string fileName)
|
||||
{
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
var resourceName = $"DocumentService.Tests.TestData.Pdfs.{fileName}";
|
||||
return assembly.GetManifestResourceStream(resourceName)
|
||||
?? throw new FileNotFoundException($"Embedded resource not found: {resourceName}");
|
||||
}
|
||||
|
||||
private static string LoadTestPdfAsBase64(string fileName)
|
||||
{
|
||||
using var stream = LoadTestPdfAsStream(fileName);
|
||||
using var ms = new MemoryStream();
|
||||
stream.CopyTo(ms);
|
||||
return Convert.ToBase64String(ms.ToArray());
|
||||
}
|
||||
|
||||
#region Merge Endpoint Tests (existing - keeping for reference)
|
||||
|
||||
[Fact]
|
||||
public async Task MergeFromFiles_ValidPdfs_ReturnsMergedPdf()
|
||||
{
|
||||
// Arrange
|
||||
using var content = new MultipartFormDataContent();
|
||||
|
||||
using var pdf1Stream = LoadTestPdfAsStream("valid.pdf");
|
||||
using var pdf2Stream = LoadTestPdfAsStream("valid.pdf");
|
||||
|
||||
var pdf1Content = new StreamContent(pdf1Stream);
|
||||
var pdf2Content = new StreamContent(pdf2Stream);
|
||||
|
||||
pdf1Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
|
||||
pdf2Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
|
||||
|
||||
content.Add(pdf1Content, "files", "file1.pdf");
|
||||
content.Add(pdf2Content, "files", "file2.pdf");
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsync("/api/pdf/operations/merge", content);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
response.Content.Headers.ContentType?.MediaType.Should().Be("application/pdf");
|
||||
|
||||
byte[] mergedPdf = await response.Content.ReadAsByteArrayAsync();
|
||||
mergedPdf.Should().NotBeEmpty();
|
||||
mergedPdf.Length.Should().BeGreaterThan(100); // Sanity check
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MergeFromBase64_ValidPdfs_ReturnsMergedPdf()
|
||||
{
|
||||
// Arrange
|
||||
string base64Pdf1 = LoadTestPdfAsBase64("valid.pdf");
|
||||
string base64Pdf2 = LoadTestPdfAsBase64("valid.pdf");
|
||||
|
||||
var request = new MergePdfsBase64Request
|
||||
{
|
||||
Base64Pdfs = new List<string> { base64Pdf1, base64Pdf2 },
|
||||
PageRanges = null
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(request);
|
||||
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsync("/api/pdf/operations/merge", content);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
byte[] mergedPdf = await response.Content.ReadAsByteArrayAsync();
|
||||
mergedPdf.Should().NotBeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MergeFromBase64_WithPageRanges_ReturnsMergedPdf()
|
||||
{
|
||||
// Arrange
|
||||
string base64Pdf1 = LoadTestPdfAsBase64("valid.pdf");
|
||||
string base64Pdf2 = LoadTestPdfAsBase64("valid.pdf");
|
||||
|
||||
var request = new MergePdfsBase64Request
|
||||
{
|
||||
Base64Pdfs = new List<string> { base64Pdf1, base64Pdf2 },
|
||||
PageRanges = new List<string?> { "1", "1" } // Only first page from each
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(request);
|
||||
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsync("/api/pdf/operations/merge", content);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
byte[] mergedPdf = await response.Content.ReadAsByteArrayAsync();
|
||||
mergedPdf.Should().NotBeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MergeFromFiles_OnePdf_Returns400()
|
||||
{
|
||||
// Arrange
|
||||
using var content = new MultipartFormDataContent();
|
||||
using var pdfStream = LoadTestPdfAsStream("valid.pdf");
|
||||
var pdfContent = new StreamContent(pdfStream);
|
||||
pdfContent.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
|
||||
content.Add(pdfContent, "files", "file1.pdf");
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsync("/api/pdf/operations/merge", content);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MergeFromBase64_InvalidBase64_Returns400()
|
||||
{
|
||||
// Arrange
|
||||
var request = new MergePdfsBase64Request
|
||||
{
|
||||
Base64Pdfs = new List<string> { "INVALID_BASE64!!!", "ANOTHER_INVALID" }
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(request);
|
||||
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsync("/api/pdf/operations/merge", content);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MergeFromBase64_InvalidPageRange_Returns400()
|
||||
{
|
||||
// Arrange
|
||||
string base64Pdf = LoadTestPdfAsBase64("valid.pdf");
|
||||
|
||||
var request = new MergePdfsBase64Request
|
||||
{
|
||||
Base64Pdfs = new List<string> { base64Pdf, base64Pdf },
|
||||
PageRanges = new List<string?> { "999-1000", null } // Exceeds page count
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(request);
|
||||
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsync("/api/pdf/operations/merge", content);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MergeFromFiles_CorruptedPdf_Returns500()
|
||||
{
|
||||
// Arrange
|
||||
using var content = new MultipartFormDataContent();
|
||||
|
||||
byte[] corruptedData = "NOT A PDF FILE"u8.ToArray();
|
||||
var corruptedContent = new ByteArrayContent(corruptedData);
|
||||
corruptedContent.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
|
||||
|
||||
using var validPdfStream = LoadTestPdfAsStream("valid.pdf");
|
||||
var validContent = new StreamContent(validPdfStream);
|
||||
validContent.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
|
||||
|
||||
content.Add(corruptedContent, "files", "corrupted.pdf");
|
||||
content.Add(validContent, "files", "valid.pdf");
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsync("/api/pdf/operations/merge", content);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.InternalServerError);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Annotate Endpoint Tests
|
||||
|
||||
[Fact]
|
||||
public async Task AnnotateFromFile_TextMarkupHighlight_ReturnsAnnotatedPdf()
|
||||
{
|
||||
// Arrange
|
||||
using var content = new MultipartFormDataContent();
|
||||
|
||||
using var pdfStream = LoadTestPdfAsStream("valid.pdf");
|
||||
var pdfContent = new StreamContent(pdfStream);
|
||||
pdfContent.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
|
||||
content.Add(pdfContent, "file", "test.pdf");
|
||||
|
||||
content.Add(new StringContent(AnnotationType.TextMarkup.ToString()), "annotationType");
|
||||
content.Add(new StringContent("1"), "pageNumber");
|
||||
content.Add(new StringContent("100"), "x1");
|
||||
content.Add(new StringContent("100"), "y1");
|
||||
content.Add(new StringContent("200"), "x2");
|
||||
content.Add(new StringContent("120"), "y2");
|
||||
content.Add(new StringContent("Important text"), "content");
|
||||
content.Add(new StringContent("Test Author"), "author");
|
||||
content.Add(new StringContent("FFFF00"), "color");
|
||||
content.Add(new StringContent(TextMarkupStyle.Highlight.ToString()), "textMarkupStyle");
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsync("/api/pdf/operations/annotate", content);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
response.Content.Headers.ContentType?.MediaType.Should().Be("application/pdf");
|
||||
|
||||
byte[] annotatedPdf = await response.Content.ReadAsByteArrayAsync();
|
||||
annotatedPdf.Should().NotBeEmpty();
|
||||
annotatedPdf.Length.Should().BeGreaterThan(100);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AnnotateFromBase64_FreeText_ReturnsAnnotatedPdf()
|
||||
{
|
||||
// Arrange
|
||||
string base64Pdf = LoadTestPdfAsBase64("valid.pdf");
|
||||
|
||||
var request = new AddAnnotationBase64Command
|
||||
{
|
||||
Base64Pdf = base64Pdf,
|
||||
AnnotationType = AnnotationType.FreeText,
|
||||
PageNumber = 1,
|
||||
X1 = 50,
|
||||
Y1 = 50,
|
||||
X2 = 150,
|
||||
Y2 = 100,
|
||||
Content = "Free text annotation",
|
||||
Author = "John Doe",
|
||||
Color = "FF0000"
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(request);
|
||||
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsync("/api/pdf/operations/annotate", content);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
byte[] annotatedPdf = await response.Content.ReadAsByteArrayAsync();
|
||||
annotatedPdf.Should().NotBeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AnnotateFromBase64_StickyNote_ReturnsAnnotatedPdf()
|
||||
{
|
||||
// Arrange
|
||||
string base64Pdf = LoadTestPdfAsBase64("valid.pdf");
|
||||
|
||||
var request = new AddAnnotationBase64Command
|
||||
{
|
||||
Base64Pdf = base64Pdf,
|
||||
AnnotationType = AnnotationType.StickyNote,
|
||||
PageNumber = 1,
|
||||
X1 = 300,
|
||||
Y1 = 300,
|
||||
X2 = 320,
|
||||
Y2 = 320,
|
||||
Content = "Please review this section",
|
||||
Author = "Reviewer"
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(request);
|
||||
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsync("/api/pdf/operations/annotate", content);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
byte[] annotatedPdf = await response.Content.ReadAsByteArrayAsync();
|
||||
annotatedPdf.Should().NotBeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AnnotateFromFile_Circle_ReturnsAnnotatedPdf()
|
||||
{
|
||||
// Arrange
|
||||
using var content = new MultipartFormDataContent();
|
||||
|
||||
using var pdfStream = LoadTestPdfAsStream("valid.pdf");
|
||||
var pdfContent = new StreamContent(pdfStream);
|
||||
pdfContent.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
|
||||
content.Add(pdfContent, "file", "test.pdf");
|
||||
|
||||
content.Add(new StringContent(AnnotationType.Circle.ToString()), "annotationType");
|
||||
content.Add(new StringContent("1"), "pageNumber");
|
||||
content.Add(new StringContent("100"), "x1");
|
||||
content.Add(new StringContent("200"), "y1");
|
||||
content.Add(new StringContent("200"), "x2");
|
||||
content.Add(new StringContent("300"), "y2");
|
||||
content.Add(new StringContent("Circle annotation"), "content");
|
||||
content.Add(new StringContent("00FF00"), "color");
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsync("/api/pdf/operations/annotate", content);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
byte[] annotatedPdf = await response.Content.ReadAsByteArrayAsync();
|
||||
annotatedPdf.Should().NotBeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AnnotateFromBase64_Square_ReturnsAnnotatedPdf()
|
||||
{
|
||||
// Arrange
|
||||
string base64Pdf = LoadTestPdfAsBase64("valid.pdf");
|
||||
|
||||
var request = new AddAnnotationBase64Command
|
||||
{
|
||||
Base64Pdf = base64Pdf,
|
||||
AnnotationType = AnnotationType.Square,
|
||||
PageNumber = 1,
|
||||
X1 = 250,
|
||||
Y1 = 250,
|
||||
X2 = 350,
|
||||
Y2 = 350,
|
||||
Color = "0000FF"
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(request);
|
||||
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsync("/api/pdf/operations/annotate", content);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
byte[] annotatedPdf = await response.Content.ReadAsByteArrayAsync();
|
||||
annotatedPdf.Should().NotBeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AnnotateFromBase64_InvalidBase64_Returns400()
|
||||
{
|
||||
// Arrange
|
||||
var request = new AddAnnotationBase64Command
|
||||
{
|
||||
Base64Pdf = "INVALID_BASE64!!!",
|
||||
AnnotationType = AnnotationType.Circle,
|
||||
PageNumber = 1,
|
||||
X1 = 0,
|
||||
Y1 = 0,
|
||||
X2 = 100,
|
||||
Y2 = 100
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(request);
|
||||
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsync("/api/pdf/operations/annotate", content);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AnnotateFromBase64_InvalidPageNumber_Returns400()
|
||||
{
|
||||
// Arrange
|
||||
string base64Pdf = LoadTestPdfAsBase64("valid.pdf");
|
||||
|
||||
var request = new AddAnnotationBase64Command
|
||||
{
|
||||
Base64Pdf = base64Pdf,
|
||||
AnnotationType = AnnotationType.FreeText,
|
||||
PageNumber = 999, // Exceeds page count
|
||||
X1 = 0,
|
||||
Y1 = 0,
|
||||
X2 = 100,
|
||||
Y2 = 100,
|
||||
Content = "Test"
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(request);
|
||||
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsync("/api/pdf/operations/annotate", content);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AnnotateFromBase64_FreeTextWithoutContent_Returns400()
|
||||
{
|
||||
// Arrange
|
||||
string base64Pdf = LoadTestPdfAsBase64("valid.pdf");
|
||||
|
||||
var request = new AddAnnotationBase64Command
|
||||
{
|
||||
Base64Pdf = base64Pdf,
|
||||
AnnotationType = AnnotationType.FreeText,
|
||||
PageNumber = 1,
|
||||
X1 = 0,
|
||||
Y1 = 0,
|
||||
X2 = 100,
|
||||
Y2 = 100
|
||||
// Missing required Content
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(request);
|
||||
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsync("/api/pdf/operations/annotate", content);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AnnotateFromBase64_TextMarkupWithoutStyle_Returns400()
|
||||
{
|
||||
// Arrange
|
||||
string base64Pdf = LoadTestPdfAsBase64("valid.pdf");
|
||||
|
||||
var request = new AddAnnotationBase64Command
|
||||
{
|
||||
Base64Pdf = base64Pdf,
|
||||
AnnotationType = AnnotationType.TextMarkup,
|
||||
PageNumber = 1,
|
||||
X1 = 0,
|
||||
Y1 = 0,
|
||||
X2 = 100,
|
||||
Y2 = 100
|
||||
// Missing required TextMarkupStyle
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(request);
|
||||
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsync("/api/pdf/operations/annotate", content);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AnnotateFromBase64_InvalidColorFormat_Returns400()
|
||||
{
|
||||
// Arrange
|
||||
string base64Pdf = LoadTestPdfAsBase64("valid.pdf");
|
||||
|
||||
var request = new AddAnnotationBase64Command
|
||||
{
|
||||
Base64Pdf = base64Pdf,
|
||||
AnnotationType = AnnotationType.Circle,
|
||||
PageNumber = 1,
|
||||
X1 = 0,
|
||||
Y1 = 0,
|
||||
X2 = 100,
|
||||
Y2 = 100,
|
||||
Color = "INVALID" // Invalid hex format
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(request);
|
||||
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsync("/api/pdf/operations/annotate", content);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,320 @@
|
||||
using DocumentService.Application.Common.DTOs;
|
||||
using DocumentService.Client.Models.Requests;
|
||||
using FluentAssertions;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using Xunit;
|
||||
|
||||
namespace DocumentService.Tests.Integration.API;
|
||||
|
||||
public class PdfValidationControllerTests : IClassFixture<WebApplicationFactory<Program>>
|
||||
{
|
||||
private readonly WebApplicationFactory<Program> _factory;
|
||||
private readonly HttpClient _client;
|
||||
|
||||
public PdfValidationControllerTests(WebApplicationFactory<Program> factory)
|
||||
{
|
||||
_factory = factory;
|
||||
_client = _factory.CreateClient();
|
||||
}
|
||||
|
||||
#region Base64 JSON Tests
|
||||
|
||||
[Fact]
|
||||
public async Task POST_ValidatePdf_Base64_ValidPdf_Returns200()
|
||||
{
|
||||
// Arrange
|
||||
// Verwende ein echtes Test-PDF (embedded resource aus Unit Tests)
|
||||
var assembly = typeof(PdfValidationControllerTests).Assembly;
|
||||
var resourceName = "DocumentService.Tests.TestData.Pdfs.valid.pdf";
|
||||
|
||||
byte[] pdfBytes;
|
||||
using (var stream = assembly.GetManifestResourceStream(resourceName))
|
||||
{
|
||||
if (stream == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Test resource '{resourceName}' not found");
|
||||
}
|
||||
|
||||
using var ms = new MemoryStream();
|
||||
await stream.CopyToAsync(ms);
|
||||
pdfBytes = ms.ToArray();
|
||||
}
|
||||
|
||||
var base64Pdf = Convert.ToBase64String(pdfBytes);
|
||||
var request = new ValidatePdfBase64Request { Base64Pdf = base64Pdf };
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsJsonAsync("/api/pdf/validation/validate", request);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
|
||||
var result = await response.Content.ReadFromJsonAsync<PdfValidationResult>();
|
||||
result.Should().NotBeNull();
|
||||
result!.PageCount.Should().BeGreaterThan(0);
|
||||
result.FileSizeBytes.Should().BeGreaterThan(0);
|
||||
result.PdfVersion.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task POST_ValidatePdf_Base64_InvalidBase64_Returns400()
|
||||
{
|
||||
// Arrange
|
||||
var request = new ValidatePdfBase64Request { Base64Pdf = "invalid-base64!!!" }; // Kein gültiges Base64
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsJsonAsync("/api/pdf/validation/validate", request);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
||||
|
||||
var problemDetails = await response.Content.ReadAsStringAsync();
|
||||
// FormatException message contains "Base-64" (with hyphen)
|
||||
problemDetails.Should().MatchRegex("(?i)base.?64", "should contain Base64 validation error");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task POST_ValidatePdf_Base64_EmptyPdf_Returns400()
|
||||
{
|
||||
// Arrange
|
||||
var request = new ValidatePdfBase64Request { Base64Pdf = string.Empty }; // Leerer String
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsJsonAsync("/api/pdf/validation/validate", request);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
||||
|
||||
var problemDetails = await response.Content.ReadAsStringAsync();
|
||||
// Empty Base64 causes FormatException or empty stream error
|
||||
problemDetails.Should().MatchRegex("(Base64|empty|stream)", "should contain validation error message");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Multipart/Form-Data Tests
|
||||
|
||||
[Fact]
|
||||
public async Task POST_ValidatePdf_Multipart_ValidPdf_Returns200()
|
||||
{
|
||||
// Arrange
|
||||
var assembly = typeof(PdfValidationControllerTests).Assembly;
|
||||
var resourceName = "DocumentService.Tests.TestData.Pdfs.valid.pdf";
|
||||
|
||||
byte[] pdfBytes;
|
||||
using (var stream = assembly.GetManifestResourceStream(resourceName))
|
||||
{
|
||||
if (stream == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Test resource '{resourceName}' not found");
|
||||
}
|
||||
|
||||
using var ms = new MemoryStream();
|
||||
await stream.CopyToAsync(ms);
|
||||
pdfBytes = ms.ToArray();
|
||||
}
|
||||
|
||||
// Create multipart/form-data content
|
||||
using var content = new MultipartFormDataContent();
|
||||
var fileContent = new ByteArrayContent(pdfBytes);
|
||||
fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
|
||||
content.Add(fileContent, "file", "test.pdf");
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsync("/api/pdf/validation/validate", content);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
|
||||
var result = await response.Content.ReadFromJsonAsync<PdfValidationResult>();
|
||||
result.Should().NotBeNull();
|
||||
result!.PageCount.Should().BeGreaterThan(0);
|
||||
result.FileSizeBytes.Should().BeGreaterThan(0);
|
||||
result.PdfVersion.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task POST_ValidatePdf_Multipart_EmptyFile_Returns400()
|
||||
{
|
||||
// Arrange
|
||||
using var content = new MultipartFormDataContent();
|
||||
var fileContent = new ByteArrayContent(Array.Empty<byte>());
|
||||
fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
|
||||
content.Add(fileContent, "file", "empty.pdf");
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsync("/api/pdf/validation/validate", content);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
||||
|
||||
var problemDetails = await response.Content.ReadAsStringAsync();
|
||||
problemDetails.Should().Contain("cannot be empty");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task POST_ValidatePdf_Multipart_NoFile_Returns400()
|
||||
{
|
||||
// Arrange
|
||||
using var content = new MultipartFormDataContent();
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsync("/api/pdf/validation/validate", content);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region PDF/A Validation Tests (Base64)
|
||||
|
||||
[Fact]
|
||||
public async Task POST_ValidatePdfA_Base64_ValidPdf_Returns200()
|
||||
{
|
||||
// Arrange
|
||||
var assembly = typeof(PdfValidationControllerTests).Assembly;
|
||||
var resourceName = "DocumentService.Tests.TestData.Pdfs.valid.pdf";
|
||||
|
||||
byte[] pdfBytes;
|
||||
using (var stream = assembly.GetManifestResourceStream(resourceName))
|
||||
{
|
||||
if (stream == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Test resource '{resourceName}' not found");
|
||||
}
|
||||
|
||||
using var ms = new MemoryStream();
|
||||
await stream.CopyToAsync(ms);
|
||||
pdfBytes = ms.ToArray();
|
||||
}
|
||||
|
||||
var base64Pdf = Convert.ToBase64String(pdfBytes);
|
||||
var request = new ValidatePdfABase64Request { Base64Pdf = base64Pdf };
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsJsonAsync("/api/pdf/validation/validate-pdfa", request);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
|
||||
var result = await response.Content.ReadFromJsonAsync<PdfAValidationResult>();
|
||||
result.Should().NotBeNull();
|
||||
result!.IsValid.Should().BeTrue();
|
||||
result.PageCount.Should().BeGreaterThan(0);
|
||||
result.PdfVersion.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task POST_ValidatePdfA_Base64_InvalidBase64_Returns400()
|
||||
{
|
||||
// Arrange
|
||||
var request = new ValidatePdfABase64Request { Base64Pdf = "invalid-base64!!!" };
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsJsonAsync("/api/pdf/validation/validate-pdfa", request);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
||||
|
||||
var problemDetails = await response.Content.ReadAsStringAsync();
|
||||
// FormatException message contains "Base-64" (with hyphen)
|
||||
problemDetails.Should().MatchRegex("(?i)base.?64", "should contain Base64 validation error");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task POST_ValidatePdfA_Base64_EmptyPdf_Returns400()
|
||||
{
|
||||
// Arrange
|
||||
var request = new ValidatePdfABase64Request { Base64Pdf = string.Empty };
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsJsonAsync("/api/pdf/validation/validate-pdfa", request);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
||||
|
||||
var problemDetails = await response.Content.ReadAsStringAsync();
|
||||
// Empty Base64 causes FormatException or empty stream error
|
||||
problemDetails.Should().MatchRegex("(Base64|empty|stream)", "should contain validation error message");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region PDF/A Validation Tests (Multipart)
|
||||
|
||||
[Fact]
|
||||
public async Task POST_ValidatePdfA_Multipart_ValidPdf_Returns200()
|
||||
{
|
||||
// Arrange
|
||||
var assembly = typeof(PdfValidationControllerTests).Assembly;
|
||||
var resourceName = "DocumentService.Tests.TestData.Pdfs.valid.pdf";
|
||||
|
||||
byte[] pdfBytes;
|
||||
using (var stream = assembly.GetManifestResourceStream(resourceName))
|
||||
{
|
||||
if (stream == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Test resource '{resourceName}' not found");
|
||||
}
|
||||
|
||||
using var ms = new MemoryStream();
|
||||
await stream.CopyToAsync(ms);
|
||||
pdfBytes = ms.ToArray();
|
||||
}
|
||||
|
||||
// Create multipart/form-data content
|
||||
using var content = new MultipartFormDataContent();
|
||||
var fileContent = new ByteArrayContent(pdfBytes);
|
||||
fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
|
||||
content.Add(fileContent, "file", "test.pdf");
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsync("/api/pdf/validation/validate-pdfa", content);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
|
||||
var result = await response.Content.ReadFromJsonAsync<PdfAValidationResult>();
|
||||
result.Should().NotBeNull();
|
||||
result!.IsValid.Should().BeTrue();
|
||||
result.PageCount.Should().BeGreaterThan(0);
|
||||
result.PdfVersion.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task POST_ValidatePdfA_Multipart_EmptyFile_Returns400()
|
||||
{
|
||||
// Arrange
|
||||
using var content = new MultipartFormDataContent();
|
||||
var fileContent = new ByteArrayContent(Array.Empty<byte>());
|
||||
fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
|
||||
content.Add(fileContent, "file", "empty.pdf");
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsync("/api/pdf/validation/validate-pdfa", content);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
||||
|
||||
var problemDetails = await response.Content.ReadAsStringAsync();
|
||||
problemDetails.Should().Contain("cannot be empty");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task POST_ValidatePdfA_Multipart_NoFile_Returns400()
|
||||
{
|
||||
// Arrange
|
||||
using var content = new MultipartFormDataContent();
|
||||
|
||||
// Act
|
||||
var response = await _client.PostAsync("/api/pdf/validation/validate-pdfa", content);
|
||||
|
||||
// Assert
|
||||
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
Reference in New Issue
Block a user